bitshift
bitshift

Reputation: 6842

ViewComponent returns but model is null in view when using route value

I'm still new to Razor pages and view components, but have been around the block a few times with ASP.NET.

I'm not sure why my view component doesn't have its model populated. One thing I noticed I had done incorrectly is putting a route parameter at the top; e.g.,

@page "{handler?}"

I know (now at least) that View Components are not intended to be routed to directly as endpoints, but now I'm curious what does using a @page with a route attribute do to prevent the model binding from happening? I only discovered by trial/error that removing this lets my model get populated within the View Component.

Here is the code

ViewComponent

public class TestViewComponent : ViewComponent
{

    public async Task<IViewComponentResult> InvokeAsync(Data.CustomerListModel cust)
    {
        List<Data.CustomerListModel> clist = new List<Data.CustomerListModel>();
        clist.Add(new Data.CustomerListModel() { Customer = "fred" });
        clist.Add(new Data.CustomerListModel() { Customer = "wilma" });

        var dlist = clist.AsEnumerable();

        return View(dlist);
    }
}

View for ViewComponent, Default.html

@page "{handler?}"
@using System.Collections.Generic
@addTagHelper *, TelerikAspnetCoreApp1
@model IEnumerable<Data.CustomerListModel>

<h1>Default</h1>

<h3>Machine name @Environment.MachineName</h3>
<ul>
    @foreach (Data.CustomerListModel c in Model)
    {
        <li>@c.Customer</li>
    }
</ul>

CustomerListModel

namespace TelerikAspNetCoreApp1.Data
{
    public class CustomerListModel
    {
        public string Customer { get; set; }
    }
}

Parent view, Index.cshtml

<div>
    @await Component.InvokeAsync("Test",new { Name = "tester123" })
</div>

Upvotes: 1

Views: 2838

Answers (3)

peter70
peter70

Reputation: 1113

I just had a similar problem. I'm ashamed because it was a brutal rookie mistake. My mistake was that the objects I sent and received respectively were different. I my cshtml I had:

@if (Model is IHasBanner {HasMessage: true } model)
{
    @await Component.InvokeAsync("MessageArea", model)
}

Then in my ViewComponent I had:

public async Task<IViewComponentResult> InvokeAsync(Banner banner)
    => this.View("Default", banner);

And in my View I had:

@if (Model is IHasBanner {HasMessage: true} model)
{
    message = model.Banner.Text;
}

So the mess was perfect!

That's why you have to pay attention to which objects you work with during implementation! What is given, what is received. I was surprised that my Model was null within the View, but it was just a result of my own confusion.

Upvotes: 0

johnye2e
johnye2e

Reputation: 140

Don't use @page in the page that you want to be executed by return View(<page_name>, <model>) and the Model property won't be null anymore.

Upvotes: 2

Xueli Chen
Xueli Chen

Reputation: 12685

The view of ViewComponent looks a lot like a Razor view file used in an ASP.NET Core app with controllers and views. Then @page directive is used in Razor page and it makes the file into an MVC action - which means that it handles requests directly, without going through a controller. @page affects the behavior of other Razor constructs.

You could take aside time to read the MS documentations on the View Component and Razor Pages :

https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-3.0

https://learn.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-3.0&tabs=visual-studio

Upvotes: 0

Related Questions