2016-07-25 2 views
2

Я пытаюсь создать html-помощник для создания списка флажков, которые будут иметь состояние проверки, сохраненное с помощью сеансов. Он работает по большей части, помня о состоянии флажка, когда вы проверяете и снимаете флажки с разных ящиков и нажимаете «Отправить». Однако, если у вас есть флажки, отмеченные и отправленные, и вы возвращаетесь и очищаете флажки и повторно отправляете (когда они ВСЕ очищены) - кажется, что хотите запомнить последние варианты. Вот то, что я написал ...Как я могу сохранить список флажков в MVC

[HomeController]

public ActionResult Index() 
{ 
    TestViewModel tvm = new TestViewModel(); 
    return View(tvm); 
} 

[HttpPost] 
public ActionResult Index(TestViewModel viewModel) 
{ 
    viewModel.SessionCommit(); 
    return View(viewModel); 
} 

[Index View]

@model TestApp.Models.TestViewModel 

@{ 
    ViewBag.Title = "Index"; 
} 

@using (Html.BeginForm()) 
{ 
    <p>Checkboxes:</p> 
    @Html.CheckedListFor(x => x.SelectedItems, Model.CheckItems, Model.SelectedItems) 

    <input type="submit" name="Submit form" /> 
} 

[TestViewModel]

// Simulate the checklist data source 
public Dictionary<int, string> CheckItems 
{ 
    get 
    { 
     return new Dictionary<int, string>() 
     { 
      {1, "Item 1"}, 
      {2, "Item 2"}, 
      {3, "Item 3"}, 
      {4, "Item 4"} 
     }; 
    } 
} 

// Holds the checked list selections 
public int[] SelectedItems { get; set; } 


// Contructor 
public TestViewModel() 
{ 
    SelectedItems = GetSessionIntArray("seld", new int[0]); 
} 


// Save selections to session 
public void SessionCommit() 
{ 
    System.Web.HttpContext.Current.Session["seld"] = SelectedItems; 
} 


// Helper to get an int array from session 
int[] GetSessionIntArray(string sessionVar, int[] defaultValue) 
{ 
    if (System.Web.HttpContext.Current.Session == null || System.Web.HttpContext.Current.Session[sessionVar] == null) 
     return defaultValue; 

    return (int[])System.Web.HttpContext.Current.Session[sessionVar]; 
} 

[HTML-помощник]

public static MvcHtmlString CheckedList(this HtmlHelper htmlHelper, string PropertyName, Dictionary<int, string> ListItems, int[] SelectedItemArray) 
{ 
    StringBuilder result = new StringBuilder(); 
    foreach(var item in ListItems) 
    { 
     result.Append(@"<label>"); 
     var builder = new TagBuilder("input"); 
     builder.Attributes["type"] = "checkbox"; 
     builder.Attributes["name"] = PropertyName; 
     builder.Attributes["id"] = PropertyName; 
     builder.Attributes["value"] = item.Key.ToString(); 
     builder.Attributes["data-val"] = item.Key.ToString(); 
     if (SelectedItemArray.Contains(item.Key)) 
      builder.Attributes["checked"] = "checked"; 

     result.Append(builder.ToString(TagRenderMode.SelfClosing)); 
     result.AppendLine(string.Format(" {0}</label>", item.Value)); 
    } 
    return MvcHtmlString.Create(result.ToString()); 
} 
public static MvcHtmlString CheckedListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, Dictionary<int, string> ListItems, int[] SelectedItemArray) 
{ 
    var name = ExpressionHelper.GetExpressionText(expression); 
    var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); 
    return CheckedList(htmlHelper, name, ListItems, SelectedItemArray); 
} 

Я прочитал this SO question, и я думаю, что это может быть связано с моделью связующего, не зная, когда нет галочки проверяются, но даже если я прошел через это и различные другие должности - я больше не продвигаюсь вперед.

В одном сообщении я увидел, что скрытое поле часто используется в сочетании с флажком для передачи «ложного» состояния флажка, но я не мог заставить его работать с несколькими флажками, отправляя обратно в одно свойство ,

Может ли кто-нибудь пролить свет на это?

EDITED: включить demonstration project Я выделил в этом сообщении. Надеюсь, это поможет кому-то помочь мне!

ответ

3

Ваша основная проблема и причина, по которой предыдущие выборы «запоминаются» при снятии всех элементов, - это то, что у вас есть конструктор в вашей модели, который вызывает GetSessionIntArray(), который получает значения, которые вы сохраняли в последний раз, когда вы отправили форма. DefaultModelBinder работает, сначала инициализируя вашу модель (включая вызов ее конструктора по умолчанию), а затем устанавливая значения своих свойств на основе значений формы. В следующем сценарии

Шаг 1: Переход к Index() методе

  • Если предположить, что его первый вызов и никаких пунктов не были добавлены к Session, то значение SelectedItems возвращаемого GetSessionIntArray() является int[0], который не делает соответствуют любым значениям в CheckItems, поэтому флажки не отмечены.

Шаг 2: Проверьте первые 2 флажка и отправьте.

  • DefaultModelBinder инициализирует новый экземпляр TestViewModel и вызывает конструктор. Значение SelectedItems снова int[0] (ничего не было добавлено к Session еще). Значения формы затем считываются, а значение SelectedItems теперь int[1, 2] (значения отмеченных флажков).Вызывается код внутри метода, а int[1, 2] добавляется к Session перед возвратом представления.

Шаг 3: Отметьте все флажки и отправьте еще раз.

  • Ваша модель снова инициализируется, но на этот раз конструктор считывает значения из Session и значение SelectedItems является int[1,2]. DefaultModelBinder считывает значения формы для SelectedItems, но их нет (флажки не отмечены), поэтому нет ничего, чтобы установить значение SelectedItemsint[1,2]. Затем Вы возвращаетесь мнение и ваш помощник проверяет первые 2 галочки на основе значения SelectedItems

Вы могли бы решить эту проблему, удалив конструктор из модели и модификации кода в метод расширения для проверки null

if (SelectedItemArray != null && SelectedItemArray.Contains(item.Key)) 
{ 
    .... 

Однако есть и другие вопросы, с вами реализацией, в том числе

  1. Ваших производящий дублировать id attri butes для каждого флажка (использование вами builder.Attributes["id"] = PropertyName;), которое является недопустимым html.

  2. builder.Attributes["data-val"] = item.Key.ToString(); не имеет смысла (он генерирует data-val="1", data-val="1" и т. Д.). Предполагая, что вам нужны атрибуты для ненавязчивой проверки на стороне клиента, тогда атрибуты будут data-val="true" data-val-required="The SelectedItems field is required.". Но тогда вам потребуется связанный заполнитель для сообщения об ошибке (как генерируется @Html.ValidationMessageFor() и атрибут каждого флажка name должны быть различны (т.е. с использованием шагового -. name="[0].SelectedItems" и т.д.)

  3. Вы, используя значение свойства для связывания, но правильный подход (как все встроенные в использовании метода расширения) является первым получить значение из ModelState, то из ViewDataDictionary и, наконец, если значения не найдены, то текущая модель собственности.

  4. You никогда не используйте значение var metadata = ModelMetadata....., хотя вы должны быть (чтобы вы могли удалить последний параметр (int[] SelectedItemArray) из метод, который фактически повторяет значение expression.

Замечание: Использование скрытого поля в вашем случае не применимо. Метод CheckboxFor() генерирует дополнительный скрытый ввод, потому что метод привязывается к свойству bool, и он гарантирует, что значение всегда отправляется.

Моя рекомендация будет заключаться в использовании пакета, такого как MvcCheckBoxList (я не пробовал это сам, поскольку у меня есть собственный метод расширения), по крайней мере, пока вы не потратите некоторое время на изучение исходного кода MVC, чтобы лучше понять, как создать HtmlHelper методы расширения (извинения, если это звучит суровым).

+0

Отличный отклик и почти понял. Не могли бы вы уточнить 3 и 4 в своем ответе. Я понимаю (в 4), что я не использую выражение, но как вы получаете значение из выражения в виде массива int? Я слышу вашу рекомендацию, однако я хочу научиться делать это самостоятельно, вместо того, чтобы полагаться на пакеты/черные ящики. Только начал MVC пару месяцев назад, так что все еще работаю над этим. – Simon

+0

Пункт 3: обратитесь к [private static MvcHtmlString InputHelper (...)] (https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/Html/InputExtensions.cs), чтобы узнать, как хелперы обычно устанавливают атрибут 'value' и порядок проверки. И чтобы понять, почему сначала проверяется «ModelState», обратитесь к второй части [этого ответа] (http://stackoverflow.com/questions/26654862/textboxfor-displaying-initial-value-not-the-value-updated-from -code/26664111 # 26664111) –

+0

Пункт 4: Свойство 'Model'' ModelMetadata содержит модель, поэтому вы можете использовать 'return CheckedList (htmlHelper, name, ListItems, metadata.Model);' и удалить последний параметр –

Смежные вопросы