asp.net mvc - How can I persist a check box list in MVC -


i'm trying build html helper creating list of checkboxes, have check state persisted using sessions. works part, remembering check box states when check , uncheck various boxes , click submit. however, if have boxes checked , submitted, , go , clear checkboxes , resubmit (when cleared) - seems want remember last selections. here i've written...

[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 checklist data source public dictionary<int, string> checkitems {         {         return new dictionary<int, string>()         {             {1, "item 1"},             {2, "item 2"},             {3, "item 3"},             {4, "item 4"}         };     } }  // holds checked list selections public int[] selecteditems { get; set; }   // contructor public testviewmodel() {     selecteditems = getsessionintarray("seld", new int[0] ); }   // save selections session public void sessioncommit() {     system.web.httpcontext.current.session["seld"] = selecteditems; }   // helper int array 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]; } 

[the html helper]

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); } 

i've read this question , think may model binder not knowing when there no checkboxes checked, though i've gone through , various other posts - i'm no further forward.

in 1 post, saw hidden field used in combination checkbox pass 'false' state of checkbox, couldn't working multiple checkboxes posting single property.

can shed light on this?

edited : include demonstration project i've highlighted in post. me!

your main issue, , reason why previous selections being 'remembered' when un-check items have constructor in model calls getsessionintarray() gets values stored last time submitted form. defaultmodelbinder works first initializing model (including calling default constructor) , setting values of properties based form values. in following scenario

step 1: navigate index() method

  • assuming first call , no items have been added session, value of selecteditems returned getsessionintarray() int[0], not match values in checkitems, no checkboxes checked.

step 2: check first 2 checkboxes , submit.

  • the defaultmodelbinder initializes new instance of testviewmodel , calls constructor. value of selecteditems again int[0] (nothing has been added session yet). form values read , value of selecteditems int[1, 2] (the values of checked checkboxes). code inside method called , int[1, 2] added session before returning view.

step 3: un-check checkboxes , submit again.

  • your model again initialized, time constructor reads values session , value of selecteditems int[1,2]. defaultmodelbinder reads form values selecteditems, there none (un-checked checkboxes not submit value) there nothing set , value of selecteditems remains int[1,2]. return view , helper checks first 2 checkboxes based on value of selecteditems

you solve removing constructor model , modifying code in extension method test null

if (selecteditemarray != null && selecteditemarray.contains(item.key)) {     .... 

however there other issues implementation, including

  1. your generating duplicate id attributes each checkbox (your use of builder.attributes["id"] = propertyname;) invalid html.

  2. builder.attributes["data-val"] = item.key.tostring(); makes no sense (it generates data-val="1", data-val="1" etc). assuming want attributes unobtrusive client side validation, attributes data-val="true" data-val-required="the selecteditems field required.". need associated placeholder error message (as generated @html.validationmessagefor() , name attribute of each checkbox need distinct (i.e. using indexers - name="[0].selecteditems" etc).

  3. your using value of property binding, correct approach (as built in extension method use) first value modelstate, viewdatadictionary , if no values found, actual model property.

  4. you never use value of var metadata = modelmetadata..... although should (so can remove last parameter (int[] selecteditemarray) method, in effect repeating value of expression.

side note: use of hidden field not applicable in case. checkboxfor() method generates additional hidden input because method binds bool property, , ensures value submitted.

my recommendation use package such mvccheckboxlist (i have not tried 1 myself have own extension method), @ least until spend time studying mvc source code better understand how create htmlhelper extension methods (apologies if sounds harsh).


Comments

Popular posts from this blog

jOOQ update returning clause with Oracle -

java - Warning equals/hashCode on @Data annotation lombok with inheritance -

java - BasicPathUsageException: Cannot join to attribute of basic type -