DisplayNameFor () From List <Object> dans le modèle

87

Je pense que c'est assez simple, je n'arrive tout simplement pas à trouver la bonne façon d'afficher le nom d'affichage d'un élément dans une liste de mon modèle.

Mon modèle simplifié:

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; } }      
}

et noms:

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; }
}

Maintenant, j'aimerais créer une table pour afficher tous les noms d'une personne et obtenir le DisplayNameFor FullName. Évidemment,

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

ne fonctionnerait pas, et

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

se cassera s'il n'y a pas de noms. Existe-t-il un «meilleur moyen» d'obtenir le nom d'affichage ici?

Jonesopolis
la source

Réponses:

143

Cela fonctionne réellement, même sans éléments dans la liste:

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

Cela fonctionne car MVC analyse l'expression au lieu de l'exécuter. Cela lui permet de trouver la propriété et l'attribut appropriés sans avoir besoin d'un élément dans la liste.

Il convient de noter que le paramètre ( modelci-dessus) n'a même pas besoin d'être utilisé. Cela fonctionne aussi:

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

Comme ça:

@{ Namespace.Of.PersonNameViewModel dummyModel = null; }
@Html.DisplayNameFor(dummyParam => dummyModel.FullName)
Tim S.
la source
Je suppose que si au lieu d'un List<PersonNameViewModel>vous avez un IEnumerable<PersonNameViewModel>, alors il est également prudent d'utiliser l'expression model => model.Names.First().FullName. Est-ce correct? Cela dit, je pense que j'aime le dummyModelmeilleur exemple des trois. Sinon, vous pourriez avoir plusieurs propriétés dans lesquelles vous devez taper ou coller model.Names[0].. Là encore, vous devriez peut-être refactoriser la section en une vue partielle qui accepte le Listou IEnumerablecomme modèle.
aaaantoine
6
J'ai testé et FirstOrDefault()travaille avec une liste vide.
Josh K
Malheureusement, cela ne fonctionne pas sur la propriété IEnumerable.
Willy David Jr
7

Il y a une autre façon de le faire, et je suppose que c'est plus clair:

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(); }
    }
}

Et puis, dans la vue:

@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
la source
1

J'aime la solution de T-moty. J'avais besoin d'une solution utilisant des génériques, donc ma solution est essentiellement la suivante:

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; }
    }
}

Utiliser ceci dans la vue est le même que son exemple. Je crée une seule instance d'un objet vide donc je ne crée pas une nouvelle instance à chaque fois que je référence Default .

Notez que la contrainte new () est nécessaire pour appeler new T () . Si votre classe de modèle n'a pas de constructeur par défaut, ou si vous devez ajouter des arguments au constructeur, vous pouvez utiliser ceci:

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

La vue aura une ligne @model comme:

@model CustomList<Namespace.Of.PersonNameViewModel.Model>
Shox
la source
Notez que DisplayNameForn'appelle jamais réellement getla propriété pour, donc peu importe si vous créez une instance.
NetMage
1

Comme solution alternative, vous pouvez essayer:

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

Cela fonctionnera même si la liste est vide!

Fábio Fabian
la source
pourquoi devrais-je utiliser cette solution au lieu de la solution de Tim S?
EKanadily