1

I've been reading multiple posts with this same issue and it seems the consensus is to use sub-model objects along with Editor Templates. I've not refactored my code to do this and I'm still getting a null model returned on post. I can't figure out where I'm going wrong. Here's my proof of concept code:

Controller:

[HttpPost]
    public ActionResult CreateNewMatter(NewMatterModel model)
    {
        WorkflowRepository repo = new WorkflowRepository();
        repo.SaveNewMatterWorkflow(model.NewMatterIndex.ClientCode, model.NewMatterIndex.ClientName, model.NewMatterIndex.MatterCode, model.NewMatterIndex.MatterName);

        return View();
    }

Main View:

@model NBI_Flow.Web.Models.ActionModels.NewMatterModel

@{
    ViewBag.Title = "Create New Matter";
}

<div class="screen-container">
    @using (Html.BeginForm("CreateNewMatter", "Action", FormMethod.Post, new { enctype = "multipart/form-data" }))
    {
        <div id="top-control-panel">
            <div id="button-row">
                <input type="button" id="home-button" value="Home" onclick="location.href = '@Url.Action("Index", "Home")'; return false;" />
                <input type="submit" id="save-button" value="Save" />
                <input type="button" id="delete-button" value="Delete" onclick="location.href = '@Url.Action("Index", "Home")'; return false;" />
                <input type="button" id="submit-button" value="Submit" onclick="saveNewMatter();" />
            </div>
            <div id="status-row">
                <div class="status-block">
                    <label><span>Request Number:</span>013603</label><br />
                    <label><span>Request Status:</span>01. Draft</label><br />
                    <label><span>Client #:</span>27619</label>
                </div>
                <div class="status-block">
                    <label><span>Request Type:</span>Existing Client</label><br />
                    <label><span>Created On:</span>02/29/2016 09:43:10 AM</label><br />
                    <label><span>Client Name:</span>Greenfield Partner LLC</label>
                </div>
                <div class="status-block">
                    <label><span>Primary Billing Partner:</span></label><br />
                    <label><span>Created By:</span>Brian Legg</label><br />
                    <label><span>BABAC Partner:</span></label>
                </div>
            </div>
        </div>
    
        @Html.Hidden("SuccessUrl", Url.Action("Index", "Home"))
        
        <div id="new-matter-container">
            <ul>
                <li><a href="#tab0" id="_tab0">Intro</a></li>
                <li><a href="#tab1" id="_tab1">Matter Details</a></li>
                <li><a href="#tab2" id="_tab2">BABAC</a></li>
                <li id="tab3tab"><a href="#tab3" id="_tab3">Client Relationship</a></li>
                <li><a href="#tab4" id="_tab4">Risk Management</a></li>
                <li><a href="#tab5" id="_tab5">Relevant Parties/Conflicts</a></li>
                <li><a href="#tab6" id="_tab6">Attachments</a></li>
                <li><a href="#tab7" id="_tab7">Comments</a></li>
                <li><a href="#tab8" id="_tab8">Audit</a></li>
                <li><a href="#tab9" id="_tab9">Copy Request</a></li>
                <li><a href="#tab10" id="_tab10">Proxies</a></li>
            </ul>
            <div id="tab0">
                @Html.EditorFor(model => model.NewMatterIndex)
            </div>
            <div id="tab1">
                @Html.Partial("_MatterDetails")
            </div>
            <div id="tab2">
                @Html.Partial("_BABAC")
            </div>
            <div id="tab3">
                @Html.Partial("_ClientRelationship")
            </div>
            <div id="tab4">
                @Html.Partial("_RiskManagement")
            </div>
            <div id="tab5">
                @Html.Partial("_RelevantParties")
            </div>
            <div id="tab6">
                @Html.Partial("_Attachments")
            </div>
            <div id="tab7">
                @Html.Partial("_Comments")
            </div>
            <div id="tab8">
                @Html.Partial("_Audit")
            </div>
            <div id="tab9">
                @Html.Partial("_CopyRequest")
            </div>
            <div id="tab10">
                @Html.Partial("_Proxies")
            </div>
        </div>
    }
</div>

I know there are a lot of partials there, but I'm only concerned with that first EditorFor. Once it works I will convert the rest to EditorTemplates.

EditorTemplate (partial):

@model NBI_Flow.Web.Models.ActionModels.NewMatterIndex

<div id="intro-section">

    <label>Requesting Attorney:</label>
    <select id="attorney-list">
        <option value="0">Select...</option>
        <option value="1">Abramowitz, Laurie</option>
        <option value="2">Adivari, Heather</option>
        <option value="3">Adler, Sara</option>
        <option value="4">Ainsztein, Zachary</option>
        <option value="5">Allardyce, Aaron L</option>
        <option value="6">Alten, Klaus</option>
        <option value="7">Alvarado, Daniela</option>
        <option value="8">Alyonycheva, Tatiana N</option>
    </select>

    <br /><br />

    <label>Please enter the matter name:</label>
    <input type="text" id="matterName" />

    <br /><br />

    @* 100% throw away code *@

    <label>Please enter the client code:</label>
    @*<input type="text" id="clientCode" />*@
    @Html.TextBoxFor(m => Model.ClientCode)
  ..........

It's the "Model.ClientCode" which I am trying to get to post. When I click the submit button my posted model looks like this:

enter image description here

Any help is greatly appreciated. Let me know if I've left out some important piece of data. Thank you!

BrianLegg
  • 1,658
  • 3
  • 21
  • 36
  • If you want to use a partial, then you need to pass the `HtmlFieldPrefix` to the partial (refer [this answer](http://stackoverflow.com/questions/29808573/getting-the-values-from-a-nested-complex-object-that-is-passed-to-a-partial-view/29809907#29809907)) but using an `EditorTemplate` for each of your nested models will do that automatically –  Mar 07 '16 at 23:02

1 Answers1

3

Using an editor template is never a requirement. I'm not sure where you're getting that advice, but it should be taken with a grain of salt. The heart of the matter boils down to the name attribute assigned to your input/select/etc. element and what the modelbinder expects that name to be on POST.

For your example here, to bind ClientCode, the modelbinder expects a post value with the name of NewMatterIndex.ClientCode. If it's anything else, just ClientCode for example, then the modelbinder will discard the value.

Now, when dealing with partials (which editor templates are a form of), it's all about context. In a partial, the context is the model for that partial and, importantly, not the model for the main view. So, for example, if you pass Model.NewMatterIndex into a partial as the model, then that is now all the context Razor has when rendering that partial. As a result, if you then do something like:

@Html.EditorFor(m => m.ClientCode)

Inside that partial, the resulting name attribute will be just ClientCode, not NewMatterIndex.ClientCode as it needs to be.

Editor templates are a partial solution only in the respect that EditorFor maintains some of the parent context. When you call something like:

@Html.EditorFor(m => m.NewMatterIndex)

It will properly prefix each of the inputs rendered inside with NewMatterIndex.. That's no guarantee that the names will always be correct, as there could be other issues. However, in this one specific scenario, it would fix the issue. The reason you're still having issues, is likely due to a typo in your editor template. You have:

@Html.EditorFor(m => Model.ClientCode)

Whereas that should be:

@Html.EditorFor(m => m.ClientCode)

They look similar, but actually mean very different things. Also, while your question resolves around this one particular property, it seems that you're both manually creating the HTML for all other inputs and not assigning any of them name attributes. Without a name, the value will not even be posted, let alone bound to anything.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Thank you for the in-depth answer! I do realize that all of the other properties are not bound. This was a proof of concept that is slowly becoming a working project. So, I will bind all of them once I get 1 property to successfully bind. As for the solution.... I tried changing to "@Html.TextBoxFor(m => m.ClientCode)" and I still get the same result. The code still runs, but I get a null value on post. I will keep trying things based on your comments. Thanks – BrianLegg Mar 07 '16 at 20:30
  • Disregard! It works now, I was just doing something goofy when testing it. Thanks a lot. Marking as the answer. – BrianLegg Mar 07 '16 at 20:34