Display Name
Display Name

Reputation: 15111

Inconsistent requirement of Model prefix for asp.net core tag helpers

I noticed a strange behavior in which there is inconsistent requirement of Model prefix for asp.net core tag helpers below. asp-for cannot accept Model prefix but asp-items must have Model prefix. My head explodes.

@model ProblemVM
<select 
     asp-for="Problem.TagId"
     asp-items="Model.Tags.ToSelectListItem(Model.Problem.TagId)"
/>

public class ProblemVM
{
    public IEnumerable<Tag> Tags { get; set; }
    public Problem Problem{ get; set; }
}

Related classes.

public abstract class ISelectListItemable
{
    [Key]
    public int Id { get; set; }

    [Required]
    public virtual string Name { get; set; }
}

public class Tag: ISelectListItemable
{

    [Display(Name = "Tag Name")]
    public override string Name { get; set; }
}

public class Problem : ISelectListItemable
{
    [Display(Name = "Problem Name")]
    public override string Name { get; set; }

    public int TagId { get; set; }

    [ForeignKey(nameof(TagId))]
    public virtual Tag Tag { get; set; }
}

    public static IEnumerable<SelectListItem> ToSelectListItem<T>(this IEnumerable<T> items, int selectedValue)
        where T : ISelectListItemable

    {
        return from item in items
               select new SelectListItem
               {
                   Text = item.Name,
                   Value = item.Id.ToString(),
                   Selected = item.Id.Equals(selectedValue)
               };
    }

Question

What is the rule of using Model prefix for tag helpers?

Upvotes: 0

Views: 279

Answers (2)

Chris Pratt
Chris Pratt

Reputation: 239380

It's about the actual type of the property the attribute of the tag helper corresponds to. The asp-for attribute maps to a For property on the built-in tag helpers which is typed as ModelExpression. So, it literally is looking for an expression relative to the model of the view.

The asp-items attribute, on the other hand is typed as IEnumerable<SelectListItem>, and thus literally needs a concrete value, not just an expression. The fact that it just so happens to be coming from a prop on your model is inconsequential. The value could come from ViewData, or be satisfied directly inline.

There are certain situations, though, when you still need to include Model for an attribute like asp-for. This is generally when your model itself is a list, dictionary, etc. and you need to index. You can't just add something like [i].Foo as an expression, so in that case you would do @Model[i].Foo.

Upvotes: 2

Gabriel Luci
Gabriel Luci

Reputation: 40958

The documentation does say that:

The asp-for attribute value is a special case and doesn't require a Model prefix, the other Tag Helper attributes do (such as asp-items)

The reason I see is that asp-for will always be from your model. But asp-items can be any collection. It doesn't have to be from your model. So if you do want it to be from your model, you need to tell it that.

Upvotes: 1

Related Questions