DisplayNameFor () de la lista <Objeto> en el modelo

87

Creo que esto es bastante simple, simplemente parece que no puedo encontrar la manera correcta de mostrar el nombre para mostrar de un elemento dentro de una lista dentro de mi modelo.

Mi modelo simplificado:

public class PersonViewModel
{
    public long ID { get; set; }

    private List<PersonNameViewModel> names = new List<PersonNameViewModel>();

    [Display(Name = "Names")]
    public List<PersonNameViewModel> Names { get { return names; } set { names = value; } }      
}

y nombres:

public class PersonNameViewModel
{
    public long ID { get; set; }

    [Display(Name = "Set Primary")]
    public bool IsPrimary { get; set; }

    [Display(Name = "Full Name")]
    public string FullName { get; set; }
}

Ahora me gustaría hacer una tabla para mostrar todos los nombres de una persona y obtener DisplayNameFor FullName. Obviamente,

@Html.DisplayNameFor(model => model.Names.FullName);

no funcionaría, y

@Html.DisplayNameFor(model => model.Names[0].FullName);  

se romperá si no hay nombres. ¿Existe una "mejor manera" de obtener el nombre para mostrar aquí?

Jonesopolis
fuente

Respuestas:

143

Esto realmente funciona, incluso sin elementos en la lista:

@Html.DisplayNameFor(model => model.Names[0].FullName)

Funciona porque MVC analiza la expresión en lugar de ejecutarla. Esto le permite encontrar esa propiedad y atributo correctos sin necesidad de que haya un elemento en la lista.

Vale la pena señalar que el parámetro ( modelarriba) ni siquiera necesita ser utilizado. Esto también funciona:

@Html.DisplayNameFor(dummy => Model.Names[0].FullName)

Como hace esto:

@{ Namespace.Of.PersonNameViewModel dummyModel = null; }
@Html.DisplayNameFor(dummyParam => dummyModel.FullName)
Tim S.
fuente
Supongo que si en lugar de a List<PersonNameViewModel>tiene un IEnumerable<PersonNameViewModel>, entonces también es seguro usar la expresión model => model.Names.First().FullName. ¿Es esto correcto? Dicho esto, creo que me gusta el dummyModelmejor ejemplo de los tres. De lo contrario, podría tener varias propiedades en las que deba escribir o pegar model.Names[0].. Por otra parte, tal vez debería refactorizar la sección a una vista parcial que acepte Listo IEnumerablecomo modelo.
aaaantoine
6
Probé y FirstOrDefault()funciona con una lista vacía.
Josh K
Desafortunadamente, esto no funciona en la propiedad IEnumerable.
Willy David Jr
7

Hay otra forma de hacerlo, y supongo que es más clara:

public class Model
{
    [Display(Name = "Some Name for A")]
    public int PropA { get; set; }

    [Display(Name = "Some Name for B")]
    public string PropB { get; set; }
}

public class ModelCollection
{
    public List<Model> Models { get; set; }

    public Model Default
    {
        get { return new Model(); }
    }
}

Y luego, en la vista:

@model ModelCollection

<div class="list">
    @foreach (var m in Model.Models)
    {
        <div class="item">
            @Html.DisplayNameFor(model => model.Default.PropA): @m.PropA
            <br />
            @Html.DisplayNameFor(model => model.Default.PropB): @m.PropB
        </div>
    }
</div>
T-moty
fuente
1

Me gusta la solución de T-moty. Necesitaba una solución con genéricos, por lo que mi solución es esencialmente esta:

public class CustomList<T> : List<T> where T : new()
{       
    public static async Task<CustomList<T>> CreateAsync(IQueryable<T> source)
    {           
        return new CustomList<T>(List<T> list); //do whatever you need in the contructor, constructor omitted
    }

    private T defaultInstance = new T();

    public T Default
    {
        get { return defaultInstance; }
    }
}

Usar esto en la vista es lo mismo que en su ejemplo. Creo una única instancia de un objeto vacío, por lo que no creo una nueva instancia cada vez que hago referencia a Default .

Tenga en cuenta que la restricción new () es necesaria para llamar a new T () . Si su clase de modelo no tiene un constructor predeterminado, o necesita agregar argumentos al constructor, puede usar esto:

private T defaultInstance = (T)Activator.CreateInstance(typeof(T), new object[] { "args" });

La vista tendrá una línea @model como:

@model CustomList<Namespace.Of.PersonNameViewModel.Model>
shox
fuente
Tenga en cuenta que en DisplayNameForrealidad nunca llama geta la propiedad, por lo que no importa si crea una instancia.
NetMage
1

Como solución alternativa, puede probar:

@Html.DisplayNameFor(x => x.GetEnumerator().Current.ItemName)

¡Funcionará incluso si la lista está vacía!

Fábio Fabian
fuente
¿Por qué debería usar esta solución en lugar de la solución de Tim S?
EKanadily