0

I have a problem similar to the hypothetical below. I have added partial views to another view multiple times but I'm not sure how to get them to bind properly to the view model etc. Also, the validation seems to trigger for every single fooName if a single fooName is wrong. I've added the index to the viewbag as you can see but I'm not really sure how to use it yet.

Note:Using MVC5.2

View Models

public class Thing
{
    public String thingName { get; set; }
    public List<Foo> Foos { get; set; }
}
public class Foo
{
    public String fooName { get; set; }
}

Foo View

@model Project.Models.Foo

<div class="form-horizontal">
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })

    <div class="form-group">
        <div class="col-md-12">
            @Html.LabelFor(model => model.fooName, htmlAttributes: new { @class = "control-label" })
            @Html.EditorFor(model => model.fooName, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.fooName, "", new { @class = "text-danger" })
        </div>
    </div>
</div>

Thing View

@model Project.Models.Thing
@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    <div class="form-horizontal">
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })

        <div class="form-group">
            <div class="col-md-12">
                @Html.LabelFor(model => model.thingName, htmlAttributes: new { @class = "control-label" })
                @Html.EditorFor(model => model.thingName, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.thingName, "", new { @class = "text-danger" })
            </div>
        </div>
    </div>
    <div class="add-foo">
        @* at some point add more foos with ajax, for now k.i.s.s. *@
        @Html.Partial("/Views/Foo/Create.cshtml", new ViewDataDictionary { { "id", 1 } })
        @Html.Partial("/Views/Foo/Create.cshtml", new ViewDataDictionary { { "id", 2 } })
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Create" class="btn btn-default" />
        </div>
    </div>
}
2
  • 1
    Possibly related: stackoverflow.com/questions/29486946/… Commented Sep 8, 2016 at 18:46
  • 1
    If your wanting to dynamically add new items to the collection, refer this answer for some options. Commented Sep 8, 2016 at 22:08

1 Answer 1

1

The problem here is that the Foo partial is rendering without context of the larger model at play. As a result you're getting a bunch of inputs with all the same names and ids, i.e.:

<input type="text" name="fooName" id="fooName" class="form-control">
...
<input type="text" name="fooName" id="fooName" class="form-control">
...

That's why the validation is triggering on all of them, because they are all the same. In order to have the partial behave correctly, you would need to pass some context into it. For example, if you were iterating over existing instances of something you could do:

@for (var i = 0; i < Model.Foos.Count; i++)
{
    @Html.Partial("_Foo", Model.Foos[i])
}

Based on the Model.Foos[i] bit, Razor would then generate proper input names like Foos[0].fooName, Foos[1].fooName, etc.

Or, you can override the HtmlFieldPrefix:

@Html.Partial("_Foo", foo1, new ViewDataDictionary { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "Foos[0]" } })
@Html.Partial("_Foo", foo2, new ViewDataDictionary { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "Foos[1]" } })
...

Since you're planning on being able to add additional ones dynamically via JavaScript, your best bet though is to rely on something like Knockout or Angular to render the fields for you based on a JavaScript array. Then, when you add new Foo instances to that array, the library will automatically add additional fields to the page with an indexed name attribute.

Sign up to request clarification or add additional context in comments.

2 Comments

At this point in the process Foos do not exist yet as this is the page for creating them. So when I tell razor Foos[i] I get nullReferenceException. I feel like I should be using a lambda expression there to pass in a template. How is that supposed to work?
That was just an example to illustrate that Razor needs context in order to generate the right names. If you're creating these dynamically, then it's JavaScript, not Razor that will need to generate the inputs. You just need to make sure that the input names align with what the model binder will expect on POST.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.