Reputation: 1298
Is it possible to bind to a collection using the ModelBinderAttribute
?
Here's my action method parameter:
[ModelBinder(typeof(SelectableLookupAllSelectedModelBinder))] List<SelectableLookup> classificationItems
And here's my custom model binder:
public class SelectableLookupAllSelectedModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = bindingContext.Model as SelectableLookup ??
(SelectableLookup)DependencyResolver.Current.GetService(typeof(SelectableLookup));
model.UId = int.Parse(bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue);
model.InitialState = true;
model.SelectedState = true;
return model;
}
}
And here's the posted JSON data for this parameter:
"classificationItems":["19","20","21","22"]}
And here's how the ValueProvider sees it:
viewModel.classificationItems[0]
AttemptedValue = "19"
viewModel.classificationItems[1]
AttemptedValue = "20"
viewModel.classificationItems[2]
AttemptedValue = "21"
viewModel.classificationItems[3]
AttemptedValue = "22"
This isn't currently working because firstly there's a prefix ("viewModel") which i can sort out, but secondly bindingContext.ModelName
is "classificationItems" which is the name of the parameter being bound to and not the indexed item in the list, ie "classificationItems[0]"
I should add that when i declare this binder as a global ModelBinder in global.asax it works fine...
Upvotes: 0
Views: 889
Reputation: 35042
Your custom model binder is being used for the whole List, not just for each particular item. As you are writing a new binder from scratch by implementing IModelBinder you would need to deal with adding all of the items to the List and the list prefexies, etc. This is not trivial code, check the DefaultModelBinder here.
Instead, you could extend the DefaultModelBinder
class, letting it to work as usual and then setting those 2 properties as true:
public class SelectableLookupAllSelectedModelBinder: DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
//Let the default model binder do its work so the List<SelectableLookup> is recreated
object model = base.BindModel(controllerContext, bindingContext);
if (model == null)
return null;
List<SelectableLookup> lookupModel = model as List<SelectableLookup>;
if(lookupModel == null)
return model;
//The DefaultModelBinder has already done its job and model is of type List<SelectableLookup>
//Set both InitialState and SelectedState as true
foreach(var lookup in lookupModel)
{
lookup.InitialState = true;
lookup.SelectedState = true;
}
return model;
}
The prefix could be dealt with by adding a bind attribute to the action parameter like [Bind(Prefix="viewModel")]
So in the end your action method parameter would look like:
[Bind(Prefix="viewModel")]
[ModelBinder(typeof(SelectableLookupAllSelectedModelBinder))]
List<SelectableLookup> classificationItems
Upvotes: 2