开发者

"Add item" JavaScript buttons for forms bound to collections -- is the situation any better in MVC3?

So I asked a question last year about having an "add item" button that'll generate a new row in a form in JavaScript that I can return to an MVC POST action handler.

For instance, if I have a form generated like this:

@for(int i = Model.Notes.Count - 1; i >= 0; i--)
{
    <tr>
        <td>@Html.HiddenFor(m => m.Notes[i].Id, new Dictionary<string, object> { { "data-index", i } } )
        @Html.TextBoxFor(m => m.Notes[i].Timestamp, new { @class = "datepicker" })</td>
        <td>@Html.DropDownListFor(m => m.Notes[i].PersonId, new SelectList(Model.People, "Id", "Name"))</td>
        <td>@Html.EditorFor(m => m.Notes[i].Amount)</td>
        <td>@Html.EditorFor(m => m.Notes[i].Comment)</td>
    </tr>
}

The HTML it generates for a single row:

<tr>
                <td><input data-index="2" data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="Notes_2__Id" name="Notes[2].Id" type="hidden" value="16192">
                <input class="datepicker hasDatepicker valid" data-val="true" data-开发者_如何学运维val-required="The Timestamp field is required." id="Notes_2__Timestamp" name="Notes[2].Timestamp" type="text" value="11/04/2011 2:11:21 PM"></td>
                <td><select id="Notes_2__PersonId" name="Notes[2].PersonId">
<option value="8367">Rei</option>
<option value="8668">Ray</option>
<option value="8883">Roy</option>
<option value="8814">Rob</option>
<option value="8886">Ron</option>
</select></td>
                <td><input class="text-box single-line" data-val="true" data-val-number="The field Amount must be a number." data-val-required="The Amount field is required." id="Notes_2__Amount" name="Notes[2].Amount" type="text" value="0.00"></td>
                <td><input class="text-box single-line" id="Notes_2__Comment" name="Notes[2].Comment" type="text" value="Some comment"></td>
            </tr>

If I want to add an item in MVC2, I had to write some really dry JavaScript code to determine the index of the latest item, generate all the trs, tds, and input HTML and set the attributes to match.

Not only is this really non-trivial, if I ever change something in the cshtml, I have to be very careful to make sure all the JavaScript matches. It's a really nasty duplication of code.

Is there any better way to do it in MVC3?

Thanks in advance,

Rei


Here's a blog post illustrating that the situation could be better even in MVC 2 :-) And the situation could be even further improved by using editor templates (which also exist in MVC 2) and never write a single for loop in a view.


I have literally just finished doing something similar. I needed to be able to add and remove rows and have them re-indexed. I'm not entirely sure what your problem is but I think its related to this.

My function has support for something that you shouldnt have a problem with. My fields are originally generated with the format of aaa.[n].bbb when it needs to be aaa[n].bbb but im sure you can change it to fit your needs.

In my case I APPEND a partial view on the end of AllEntries and the partial view has a div insite of it called Entry. I hope that makes sense. I use a default index of 0 and then when the partial view has been loaded in via ajax I just call the reindex function so that no tracking of the current index is needed and thus also making it reusable.

My partial view is generated like this:

@model List<Address>
 @{ViewData.TemplateInfo.HtmlFieldPrefix = "Addresses";}

 <div class="editor-label">
            @Html.LabelFor(model => model[0].BuildingName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model[0].BuildingName)
            @Html.ValidationMessageFor(model => model[0].BuildingName)
        </div>

...etc

Assuming you have something like:

<div id="AllEntries">

   <div id="Entry">
     //Form inputs...
   </div>

   <div id="Entry">
     //Form inputs...
   </div>

</div>

ReindexElements($('#Entry', $('AllEntries').children());

//Used to reindex input elements within a given set of divs
         //Supports changing from HtmlFieldPrefix and manual indexing
         //E.G Set = $('#Entry' $('AllEntries').children());
         function ReindexElements(Set) {
             $(Set).each(function (i, e) {
                 $('input', this).each(function (ii, element) {
                     //Current element name
                     var FieldName = (element.name);

                     //aaaa.[n].bbbb
                     //This format is created when using ViewData.TemplateInfo.HtmlFieldPrefix = "aaaa"
                     //Incorrect for reading back into a model so it must be converted to the correct format described below
                     var IncorrectFormatRegex = '((?:[a-z][a-z]+))(\\.)(\\[.*?\\])(\\.)((?:[a-z][a-z]+))';

                     //aaaa[n].bbbb
                     //Correct format for being read back into an MVC model using TryUpdateModel()
                     var CorrectFormatRegex = '((?:[a-z][a-z]+))(\\[.*?\\])(\\.)((?:[a-z][a-z]+))';

                     var NewFieldName;

                     var RegexSplit = new RegExp(CorrectFormatRegex, ["i"]);
                     var RegexArray = RegexSplit.exec(FieldName);
                     if (RegexArray != null) {
                         //Field name is in correct format
                         NewFieldName = RegexArray[1] + "[" + i + "]." + RegexArray[4];

                     } else {

                         var RegexSplit = new RegExp(IncorrectFormatRegex, ["i"]);
                         var RegexArray = RegexSplit.exec(FieldName);
                         if (RegexArray != null) {
                             //Field name is incorrect format, lets fix that
                             NewFieldName = RegexArray[1] + "[" + i + "]." + RegexArray[5];
                         } else {
                             //Hasnt matched either regex ... Name must be incorrect, exit loop
                             return false;
                         }
                     }

                     if (FieldName != NewFieldName) {
                         //Rename element
                         $(element).attr('name', NewFieldName);
                     } else {
                         //Attributes dont need renaming, exit loop
                         return false;
                     }
                 });
             });
         }

This may have absolutely nothing to do with what your doing but I hope it helps.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜