IEnumerable in my ViewModel is not displayed with

2020-04-14 19:14发布

ViewModel

[Validator(typeof(ProdutoCategoriaValidator))]
public class ProdutoCategoriaViewModel
{
    [HiddenInput(DisplayValue = false)]
    public Guid ID { get; set; }

    public IEnumerable<SelectListItem> Tipos { get; set; }  // <<<<-------  Is not showing in my view

    [AdditionalMetadata("data-bind", "event: { change: function(data) { Link(data.Nome());  }}")]
    public string Nome { get; set; }

    [DataType(DataType.Url)]
    [AdditionalMetadata("Prefixo", "Produtos/{tipo-de-produto}#")]
    public string Link { get; set; }

    public int? Ordem { get; set; }

    public ProdutoCategoriaViewModel()
    {
        ID = Guid.NewGuid();
    }
}

Solution

Solution explorer

View (_Formulario.cshtml)

@model ProdutoCategoriaViewModel
@using (Html.BeginForm(null, null, FormMethod.Post, new { id="form-produtocategoria", data_bind = "submit: salvar" }))
{
    @Html.AntiForgeryToken()

    <legend>@Html.MvcSiteMap().SiteMapTitle()</legend>

    <fieldset>
        @Html.ValidationSummary(false, "Verifique os erros abaixo:")
        @Html.EditorForModel()
    </fieldset>

    <div class="buttons">
        @Html.ActionLink("Cancelar", "Index")
        <input type="submit" value="SALVAR" />
    </div>
}

SelectListItem.cshtml

@model IEnumerable<SelectListItem>

@Html.DropDownListFor(m => m, Model)

<p>Test</p>

Result

Full image: http://i.imgur.com/I7HxA.png

View result

Notes

  • I've tried to put the attribute "UIHint" but still nothing is displayed!

Questions

What am I doing wrong?

2条回答
叛逆
2楼-- · 2020-04-14 19:41

By default when you use Html.EditorForModel don't expect this to recurse down to complex properties such as your Tipos property which is of type IEnumerable<SelectListItem>. Brad Wilson explained this in his blog post (more specifically read the Shallow Dive vs. Deep Dive section towards the end of the post). You will need to write a custom editor template for the Object type if you want this to happen.

Another possibility is to specify the template name:

@Html.EditorFor(x => x.Tipos, "SelectListItem")

Also bear in mind that your editor template for the SelectListItem is wrong because you are binding the DropDownListFor to the model as first argument. Don't forget that the first argument of this helper must be a scalar property that will be used to hold the selected value. You need a string or integer property on your view model for this. The second argument represents the collection.

Another important aspect about editor templates is that when you have a property of type IEnumerable<T> and an editor template called T.cshtml this editor template must be strongly typed to the T class and not IEnumerable<T> as you did with your SelectListItem.cshtml template. This doesn't apply if you use UIHint or specify the template name as second argument to the EditorFor helper. n this case the template will be typed to the collection.

So to recap, you could either implement a custom object editor template as Brad Wilson suggested that will recurse down to complex properties or you could modify your _Formulario.cshtml view to specify EditorFor each individual elements.

查看更多
小情绪 Triste *
3楼-- · 2020-04-14 19:44

A @foreach loop renders something that looks right, but the resulting markup will have the same id for each row's controls. It also will not post the enumerable collection back with the model instance.

There are two ways to make this work such that you have a unique id for each item in the collection, and so that the collection is hydrated on postbacks:

1. Use the default editor template rather than a named one

// editor name parameter must be omitted; default editor template's model type
// should be a single instance of the child object, not an IEnumerable. This 
// convention looks wrong, but it fully works:
@Html.EditorFor(parentObject => parentObject.Items) 

2. Use a @for loop, not a @foreach:

@for (int i = 0; i < parentObject.Items.Count ; i++) {
    // the model binder uses the indexer to disambiguate the collection items' controls: 
    @Html.EditorFor(c => Model.Items[i], "MyEditorTemplate") 
}

This will not work, however:

// this will error out; the model type will not match the view's at runtime:
@Html.EditorFor(parentObject => parentObject.Items, "MyEditorTemplate")

Nor will this:

@foreach(var item in parentObject.Items) {
    // this will render, but won't post the collection items back with the model instance: 
    @Html.EditorFor(c => item, "MyEditorTemplate") 
}

For a detailed answer why this is, look at this question: MVC can't override EditorTemplate name when used in EditorFor for child object.

查看更多
登录 后发表回答