0

Now I have a controller:

public async Task<IActionResult> Location()
        {
            Models.LocationModel Location = null;
            //some logic
            return View(Location);
        }

And here is the View:

@model Vishdo.Models.LocationModel
<div>
    <h5>@Model.City</h5>
    <span>@Model.Name</span>
    <span>@Model.Phone</span>
</div>

When I access the View by the URL(https://localhost:44372/home/location) directly, all works well.

Now I add it into the _Layout.cshtml like this:

@Html.PartialAsync("/Views/Home/Location.cshtml")

After it ran, it reports an error on the View Location.cshtml:

System.NullReferenceException
  HResult=0x80004003
  Message=Object reference not set to an instance of an object.
  Source=Sample.Views
  StackTrace:
   at AspNetCore.Views_Home_Location.<ExecuteAsync>d__0.MoveNext() in E:\Project\Sample\Views\Home\Location.cshtml:line 3

I added a breakpoint in the Location controller and found it never works. So the Model always is null and reports this error.

In addition, I add this into the _Layout.cshtml for this feature is needed by all the pages and the _Layout.cshml has not a controller so I have to do it like this.

What's wrong with my code? How can I solve it? Thank you.

Melon NG
  • 2,568
  • 6
  • 27
  • 52

2 Answers2

2

I added a breakpoint in the Location controller and found it never works. So the Model always is null and reports this error.

It's the expected behavior, @Html.PartialAsync() will not trigger the controller action, it directly search the partival view under the view folder.

In addition, you can achieve it with ViewComponent. I made a demo like below:

1.Create a folder named ViewComponents in your project, and create a class named LocationViewComponent

enter image description here

public class LocationViewComponent : ViewComponent
{
    public IViewComponentResult Invoke()
    {
        var Location = new LocationModel()
        {
            City = "AAA",
            Name = "BBB",
            Phone = "CCC"
        };
        return View(Location);
    }
} 

2.The default view name for a view component is Default.cshtml, add it to the Shared folder like below:

enter image description here

Default.cshtml

@model LocationModel

<div>
    <h5>@Model.City</h5>
    <span>@Model.Name</span>
    <span>@Model.Phone</span>
</div>

3.Call the ViewComponent in the layout:

<div class="container">
    <main role="main" class="pb-3">
        @RenderBody()
        @await Component.InvokeAsync("Location")
    </main>
</div>

Result:

enter image description here

For more details, you can refer to the document

mj1313
  • 7,930
  • 2
  • 12
  • 32
  • By the way, as you said @Html.PartialAsync() will not trigger the controller action. Is there any other method to achieve this without using the ViewComponent? – Melon NG Nov 19 '20 at 07:27
  • The options gave by @Munesh Kumar are also feasible. – mj1313 Nov 19 '20 at 07:45
2

Since @Html.PartialAsync() will not trigger the controller action so you've multiple options here to solve your problem.

1- you should get the Location model from any service and pass it to @Html.PartialAsync() like below:

@inject LocationService locationService
@{
    var locationModel = locationService.GetLocation();
}

and then pass this locationModel to @Html.PartialAsync()

@Html.PartialAsync("/Views/Home/Location.cshtml", locationModel );

2- Use ViewComponent as described above.

3- Use @Html.Action() by implementing this as a HtmlHelper extension. Refer this here
Munesh Kumar
  • 481
  • 3
  • 10
  • I am trying the first way. The model transfer to the View successfully while finally, it renders a strange string " System.Threading.Tasks.Task`1[Microsoft.AspNetCore.Html.IHtmlContent] ". – Melon NG Nov 19 '20 at 08:01
  • All right, I should add a.Result after the PartialAsync. It works now. – Melon NG Nov 19 '20 at 08:02