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 ofselecteditems
returnedgetsessionintarray()
int[0]
, not match values incheckitems
, no checkboxes checked.
step 2: check first 2 checkboxes , submit.
- the
defaultmodelbinder
initializes new instance oftestviewmodel
, calls constructor. value ofselecteditems
againint[0]
(nothing has been addedsession
yet). form values read , value ofselecteditems
int[1, 2]
(the values of checked checkboxes). code inside method called ,int[1, 2]
addedsession
before returning view.
step 3: un-check checkboxes , submit again.
- your model again initialized, time constructor reads values
session
, value ofselecteditems
int[1,2]
.defaultmodelbinder
reads form valuesselecteditems
, there none (un-checked checkboxes not submit value) there nothing set , value ofselecteditems
remainsint[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
id
attributes 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()
,name
attribute 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