A recent comment on one of my earlier articles got me thinking again about ASP.NET MVC model binding and validation. The comment was from Bart Calixto, and was to the effect that my concern over MVC validation and repeating forms was misplaced – that simply binding to a collection meant that MVC could handle this itself.
My immediate response was that the problem remains – that by default MVC does not generate unique IDs for repeating forms and creates malformed HTML (and causes issues with client-side validation). However – Bart is right that it is possible to get MVC to manage the client-side IDs properly by binding to a collection. So why don’t I go down that route? Because the price of fixing the client-side IDs/validation is that the server-side model binding relies upon a single form containing the main object and the collection(s) associated with it. My design used small repeating forms, one for each collection item (I was using a lot of Ajax and wanted to pass as little data as possible between server and client). Collection binding would solve the client-side problems I highlighted, but only at the cost of breaking the server-side model binding. Still – I think it’s worth looking at the alternative approach and seeing what it does for us – and what it doesn’t.
In order to bind a collection of complex objects, you need to set up editor templates and call them inside a for loop in the view.
So – first, what is an editor template?
Normally you call EditorFor() on simple data and get a standard HTML widget. Assigning EditorFor() to a string property will automatically generate a text input:
Gives us this output:
However, we can create a view that contains many such elements and is designed to be the editor for a more complex object. In this case, I want to edit a Player object from my HTML5 game LaLaLadders. The first thing I need to do is add a new folder EditorTemplates under the Shared folder.
Like so much else in MVC, you use conventional names and locations to tell the system what to do. The next step is to add a view called Player[.cshtml/.vbhtml] underneath the folder:
Inside the file, add in the appropriate markup:
Now all we need to do is point the individual Player objects at this template inside a for loop in the main edit view and tell EditorFor() which template to use:
This then generates a form with all the players:
And each Player is given a unique id and name:
And these work perfectly well with client-side validation:
And the complex object is bound appropriately on the server:
So why does this not solve my problem?
The answer lies in the little ‘save’ button sitting after each individual player. It is completely redundant, because there is only one form. When I save, I am saving everything. But that’s not what I want. What I want – and what I have in the actual application – is repeating forms. I want to edit JUST the individual Player‘s details, not the whole object. I don’t want to be sending all that data back to the server – just the little bit I need.
So, no problem you might think. Just have the Player editor template contain a form that posts to a separate EditPlayer method and bind a single Player. Okay, let’s try that…
First, we have to move our iterator out of the main form so we don’t end up with nested forms:
Then we need to amend the Player editor so it contains a form that posts to EditPlayer:
We run it, and everything looks good. We end up with individual forms, each of which has the same iterated IDs and Names – e.g. Players_2_Name and Players.Name.
And that’s where we hit the problem. The model binder was fine with those names when it was dealing with the Players collection – but not now that we’re dealing with an individual Player. The result is our model is not bound on the server:
So, although you can get MVC to sort out its issues with client-side IDs and validation, you might not always want to. Sometimes, the cure can be worse than the disease. My LaLaLadders application would have been much more complicated, and would have used a heck of a lot more bandwidth, if I’d gone down this path… which is not to say that it isn’t the right solution in most circumstances.
The conclusion of all this? I still don’t think MVC handles repeating forms very well, but it does have some good tools for handling collections within a single form, if that’s what you’re looking for.
For other related information, check out this course from Learning Tree: