简体   繁体   中英

Adding dynamically added controls to the ViewModel

I have the following ViewModel which I fill with some predefined data. One of that predefined data is a List of ApplicationParameter ViewModels which is set to initially display n number of ApplicationParameters.

What I wanted to achieve is to allow user to add additional number of ApplicationParameters ViewModels on a button click which I achieved by calling PartialView with the ApplicationParameter code. As expected controls do add automatically but they aren't recognized in the ViewModel as they don't have the correct naming (as they are being nested).

What to do to make dynamically added controls visible to the ViewModel returned on the POST.

ViewModels

public class ApplicationVM
{

    public int idApplication { get; set; }
    public int idCompany { get; set; }
    public string Company { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Link { get; set; }
    public string Photo { get; set; }

    public List<ApplicationParameterVM> ApplicationParameters { get; set; }
}

public class ApplicationParameterVM
{
    public string Name { get; set; }
    public bool Keep { get; set; }
}

Action view (code snippet)

 <tbody id="editorRows">
     @for (int i = 0; i < Model.ApplicationParameters.Count(); i++)
         {
            <tr>
                <td>
                    @Html.TextBoxFor(m => Model.ApplicationParameters[i].Name, new { @class = "form-control" })
                     @Html.CheckBoxFor(m => Model.ApplicationParameters[i].Keep)
                  </td>
              </tr>
           }
   </tbody>

Action view for adding ApplicationParameterVM

   @model AccessMarketmind.Areas.Administration.ViewModels.ApplicationParameterVM
   @{
        Layout = null;
    }

<tr class="application-parameters">
     <td>
         @Html.TextBoxFor(m => Model.Name, new { @class = "form-control" })
         @Html.CheckBoxFor(m => Model.Keep)
     </td>
</tr>

I know this one looks trivial and could be easily done using Javascript, but the thing is that I have more complicated ViewModels (read: heavily nested) in which Javascript isn't an option.

The basic hack in these kind of dynamic forms is that the form data which is getting posted should match with the property names.

Ex: the final property names of your dynamic control array should be. ApplicationParameters[0].Name and ApplicationParameters[0].Keep and
ApplicationParameters[1].Name and ApplicationParameters[1].Keep .....

To achieve that please use the loosely coupled helper Html.TextBox() only.

After useful answers I was able to get the thing working exactly the way I wanted to. What I did is a combination of server and client side code and the idea is to basically add an additional property into the ViewModel which I named ControlName and is of type string. What is great about this that if your ViewModel changes you can easily adjust the code to the current needs wihout too much hassle, not to mention that you can easily go to multiple levels of depth just by adding ControlName property to the each ViewModel sublevel.

NOTE: The following example isn't the one mentioned in the OP

First you'll need some kind of wrapper (in my case a table) and a link which will allow you to create new rows of controls:

        @Html.ActionLink("Add attribute", "AddProductCategoryAttribute", null, new { id = "addProductCategoryAttribute" })
        <table class="table table-condensed table-striped">
            <thead>
                <tr>
                    <th>

                    </th>
                    <th>
                        Attribute
                    </th>
                    <th>
                        Measure
                    </th>
                </tr>
            </thead>
            <tbody id="productCategoryAttributes"></tbody>
        </table>

On the client side you'll need something like this

    <script type="text/javascript">
    $("#addProductCategoryAttribute").click(function () {
        elementCount = $(".productCategoryAttribute").length;

        $.ajax({
            url: this.href + "?elementCount=" + elementCount,
            cache: false,
            success: function (html) {
                $("#productCategoryAttributes").append(html);
            }
        });
        return false;
    })
</script>

Note the appended elementCount to the link - this querystring is used to let the Action method know how many of elements already exist. When talking about Action method, this is how it looks now.

    public ActionResult AddProductCategoryAttribute(int elementCount)
    {
        ProductCategoryOverview.ProductCategoryAttributeVM productCategoryAttributeVM = new ProductCategoryOverview.ProductCategoryAttributeVM();
        productCategoryAttributeVM.ControlName = "ChangeThisNameToWhateverIsNeeded[" + elementCount.ToString() + "].";
        productCategoryAttributeVM.Keep = true;

        return View(productCategoryAttributeVM);
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM