How to use DisplayTemplates in ASP.Net Core Razor Pages with Model Inheritance?
Asked Answered
M

1

7

Trying to get DisplayTemplates in ASP.Net Core 2.2 to work with classes that inherit from a base class similar to this question How to handle an entity model with inheritance in a view?

The Principal DisplayTemplate is being used for all items in the list, what am I missing?

PageModel

public class IndexModel : PageModel
{
    public List<Principal> Principals { get; set; } = new List<Principal>();

    public void OnGet()
    {
        Principals.Add(new Principal { Id = 1, Name = "Principal 1" });
        Principals.Add(new UserPrincipal { Id = 1, Name = "User 1", Age = 30 });
        Principals.Add(new GroupPrincipal { Id = 1, Name = "Group 1", Members = 5 });
        Principals.Add(new UserPrincipal { Id = 1, Name = "User 2", Age = 40 });
        Principals.Add(new GroupPrincipal { Id = 1, Name = "Group 2", Members = 3 });
    }
}

public class Principal
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class UserPrincipal : Principal
{
    public int Age { get; set; }
}

public class GroupPrincipal : Principal
{
    public int Members { get; set; }
}

RazorPage

@page
@model IndexModel

@foreach(var principal in Model.Principals)
{
    @Html.DisplayFor(p => principal)
}

~/Pages/Shared/DisplayTemplates/Principal.cshtml

@model Principal
<div>
    <h4>Principal</h4>
    @Model.Name
</div>

~/Pages/Shared/DisplayTemplates/UserPrincipal.cshtml

@model UserPrincipal
<div>
    <h4>User</h4>
    @Model.Name, Age @Model.Age
</div>

~/Pages/Shared/DisplayTemplates/GroupPrincipal.cshtml

@model GroupPrincipal
<div>
    <h4>Group</h4>
    @Model.Name, Members @Model.Members
</div>

Megilp answered 3/10, 2019 at 18:29 Comment(5)
In your razor page have you tried @Html.DisplayFor(principal => principal) ?Driver
That generates the error: "A local or parameter named 'principal' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter"Megilp
What i used in past it could be in ~/Views/Shared/DisplayTemplates/_Principle.cshtml or ~/Views/Principals/_Principle.cshtml if you are using it in Page folder I'm not sure how view engine or route path was configured first try it on default path else are you using it inside area?Zooplasty
@Zooplasty I get the same outcome whether they are located in the Pages folder or View folder. Using an ASP.NET Core Web Application project with either .NET Core/Framework this doesn't work. Using a ASP.NET Web Application (.NET Framework) project works.Megilp
found this answer here see if that is something helpful, and this may also helpZooplasty
M
10

The reason

The Principal DisplayTemplate is being used for all items in the list,

That's because the expression in @Html.DisplayFor(expression) won't be executed at all. They are parsed statically instead.

For example, if we have an expression m.a.b.c where a is null at runtime, the @Html.DisplayFor(m => m.a.b.c) is still able to know the template for c.

Since you're declaring the Principals as type List<Principal> , even if you make the Model.Principals hold UserPrincipal or GroupPrincipal at runtime, the "parser" still treat the principal as a base Principal type: They don't inspect the real type of instance. They just parse the type statically.

How to fix

Pass a template name when invoking Html.DisplayFor() so that the Html Helper knows the real template you want to use:

@page
@model IndexModel

@foreach (var principal in Model.Principals)
{
    @Html.DisplayFor(p => principal, principal.GetType().Name)
}

Demo

enter image description here

Mcpeak answered 4/10, 2019 at 2:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.