Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC 3 - Render additional Editor templates for children objects

I'm trying to create a form that allows the user to create an an entity with 0-multiple children objects, the main object being a Lead which has 0-many Beneficiary and Payer objects. The relation between the three is as follows:

Lead has many Beneficiaries (one to many)
Lead has many Payers (many to many) joined through LeadsPayers

In my editor for my lead view model I have a loop that generates the editor for each beneficiary and payer attached to the lead but I want to be able to have a button that will dynamically add another beneficiary or payer to the lead and use the editor template so i don't have duplicated html in my javascript code.

I have this working to bind everything to the Lead when my form gets posted to the controller I just don't know how to reuse the editor template code to generate compatible html with my current form, has anyone done this before? There has to be a better way than an ajax button that renders the html

like image 777
Jimmy Avatar asked Feb 26 '23 00:02

Jimmy


2 Answers

This helped me get on the right track but wasn't fully what I wanted to do and as this doesn't seem easily supported in MVC here is my work around to get this all to work with model binding in the controller.

My end goal

To dynamically add children objects to a form with the proper html so that they would be auto bound to the parent when passed to the control method, meaning I needed HTML like this

<input name='Lead_Beneficiaries_1__{Property}' id=... />

And not like this (which is what the blog post results in)

<input name='Beneficiary__{Property}' />

Which doesn't auto bind to the parent when posted to the controller.

Solution

Instead of using Ajax to render the editor template html from the controller, I auto render one editor template when the form is loaded and then more can be loaded as needed by the user. JQuery code

$("#addBeneficiary").click(function(e) {
    e.preventDefault();
    var beneficiary = $(".beneficiary:last").clone();
    var count = parseInt(beneficiary.find("input:first").attr("id").match(/\d+/), 10);
    beneficiary.find("input").each(function(indx, element) { 
        var name = $(element).attr('name').replace(count, count+1),
            id   = $(element).attr('id').replace(count, count+1);
        $(element).attr('name', name);
        $(element).attr('id', id);
        $(element).val(''); 
    });
    $("#beneficiaries").append(beneficiary);
});

I just wrapped my editor template in a div so it can be easily grabbed as a whole from jquery, cloned it, emptied the values of the inputs and incremented the counter for each element so when posted I get a collection of children entities on my parent without having to explicitly catch them in the method or pull them from the formcollection dictionary.

While this probably isn't the best solution, it gets the job done in the way I intended and I didn't end up duplicating HTML for the editor template. Also if the form errors since the parent object has the multiple children they get auto rendered automatically by my form code and nothing is lost because of client side additions.

like image 141
Jimmy Avatar answered Apr 03 '23 23:04

Jimmy


For a variable length list with jquery you may take a look at the following blog post.

In your main view:

@using (Html.BeginForm())
{
    ... some other input fields on the main view model       

    @Html.EditorFor(x => x.Beneficiaries)

    <input type="submit" value="OK" />
}

and inside the editor template (~/Views/Shared/EditorTemplate/Beneficiary.cshtml):

@model AppName.Models.Beneficiary
<div>
    @Html.LabelFor(x => x.BeneficiaryName)
    @Html.TextBoxFor(x => x.BeneficiaryName)
</div>
....

The naming convention is important: the editor template should be called XXX.cshtml and located in ~/Views/Shared/EditorTemplates folder where XXX is the type of the collection:

public IEnumerable<XXX> Beneficiaries { get; set; }
like image 21
Darin Dimitrov Avatar answered Apr 03 '23 23:04

Darin Dimitrov