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 ofselecteditemsreturnedgetsessionintarray()int[0], not match values incheckitems, no checkboxes checked.
step 2: check first 2 checkboxes , submit.
- the
defaultmodelbinderinitializes new instance oftestviewmodel, calls constructor. value ofselecteditemsagainint[0](nothing has been addedsessionyet). form values read , value ofselecteditemsint[1, 2](the values of checked checkboxes). code inside method called ,int[1, 2]addedsessionbefore returning view.
step 3: un-check checkboxes , submit again.
- your model again initialized, time constructor reads values
session, value ofselecteditemsint[1,2].defaultmodelbinderreads form valuesselecteditems, there none (un-checked checkboxes not submit value) there nothing set , value ofselecteditemsremainsint[1,2]. return view , helper checks first 2 checkboxes based on value ofselecteditems
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
your generating duplicate
idattributes each checkbox (your use ofbuilder.attributes["id"] = propertyname;) invalid html.builder.attributes["data-val"] = item.key.tostring();makes no sense (it generatesdata-val="1",data-val="1"etc). assuming want attributes unobtrusive client side validation, attributesdata-val="true" data-val-required="the selecteditems field required.". need associated placeholder error message (as generated@html.validationmessagefor(),nameattribute of each checkbox need distinct (i.e. using indexers -name="[0].selecteditems"etc).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.you never use value of
var metadata = modelmetadata.....although should (so can remove last parameter (int[] selecteditemarray) method, in effect repeating value ofexpression.
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
Post a Comment