Reputation: 158
I have a model that contains a collection, such as this:
class MyModel { public List<MySubModel> SubModels { get; set; } }
In the view, I want to dynamically add/remove from this list using Javascript before submitting. Right now I have this:
$("#new-submodel").click(function () { var i = $("#submodels").children().size(); var html = '<div>\ <label for="SubModels[' + i + '].SomeProperty">SomeProperty</label>\ <input name="SubModels[' + i + '].SomeProperty" type="textbox" />\ </div>' $("#submodels").append(html); });
This works, but it's ugly. And, if I want to show those labels/textboxes for the existing items, there's no clean way to do that either (without duplicating).
I feel like I should be able to use Razor helpers or something to do this. Any ideas? Help me stay DRY.
Upvotes: 8
Views: 8760
Reputation: 10940
You approach may lead to unexpected errors if you when you are removing or adding the divs. For example you have 4 items, you remove the first item, then $('#submodels').children().size()
will return 3, but your last inserted div has the name attribute value set SubModels[3].SomeProperty
which results in a conflict. And if your posted values contain SubModels[1]
but not SubModels[0]
the default model binder will fail to bind the list (it will bind it as null). I had to learn this the hard way...
To eliminate the aforementioned problem (and your's) I suggest you do something like this:
$("#addBtn").click(function() {
var html = '<div class="submodel">\
<label>SomeProperty</label>\
<input type="textbox" />\
</div>'; // you can convert this to a html helper!
$("#submodels").append(html);
refreshNames(); // trigger after html is inserted
});
$(refreshNames); // trigger on document ready, so the submodels generated by the server get inserted!
function refreshNames() {
$("#submodels").find(".submodel").each(function(i) {
$(this).find("label").attr('for', 'SubModels[' + i + '].SomeProperty');
$(this).find("label").attr('input', 'SubModels[' + i + '].SomeProperty');
});
}
Then your view (or even better an EditorTemplate for the SubModel type) can also generate code like:
<div class="submodel">
@Html.LabelFor(x => x.SomeProperty);
@Html.EditorFor(x => x.SomeProperty);
</div>
It would also be possible to convert the code generation to a html helper class, and use it in the EditorTemplate and in the JavaScript code
Upvotes: 7
Reputation: 1038790
I would recommend you going through the following blog post.
Upvotes: 4