0
votes

I'm trying to make a simple chat app to get started with ASP.NET and my partial view seems to be recieving the data type passed to Index.cshtml instead of the data I intended to pass to PersistantMessagesPartial.cshtml.

Error: InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List1[WebChat.Areas.Identity.Data.AppUser]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.List1[WebChat.Models.MessageModel]'.

In HomeController.cs:

 public async Task<IActionResult> Index()
    {
        Debug.WriteLine("debug1");
        return View(await _AppUserDBContext.userList.ToListAsync());
    }

    public async Task<IActionResult> _PersistantMessagesPartial()
    {
        Debug.WriteLine("debug2");
        return PartialView("_PersistantMessagesPartial", await _MessageModelDBContext.messageList.ToListAsync());
    }

In _PersistantMessagesPartial.cshtml:

@model List<WebChat.Models.MessageModel>
<script>
    console.log("test");
</script>
@foreach(var item in Model)
{
    <script>
        console.log("MessagePartial");
        console.log(@item.Contents);
    </script>
}

How i render the partial, in _Layout.cshtml:

<partial name="_PersistantMessagesPartial" />

Index.cshtml recieves a list of AppUser, and that works correctly. I'm not sure how to make _PersistantMessagesPartial recieve a list of MessageModel instead of the list of AppUser.

1
You have to add the model to your partial view tag. Like this: <partial name="_PersistantMessagesPartial" model="@Model.GoesHere" />. I think it is getting the model from your Index view passed to it. It doesn't call your _PersistantMessagesPartial in your controller, you might need to make a view model or turn the partial view and action into a View Component - zgood
@zgood suggestion of view component is valid, and preferred. However, the info here: docs.microsoft.com/en-us/aspnet/core/mvc/views/… can help you pass in a model that is distinct from the parent's ViewDataDictionary.. - SRQ Coder

1 Answers

3
votes

Instead of passing the list of objects I would create a class:

public class PersistantMessagesPartialModel{
    public List<WebChat.Models.MessageModel> Messages{get;set;}
}

and pass the object using for attribute like this:

@model IndexViewModel

<partial name="_PersistantMessagesPartial.cshtml" for="PersistantMessagesPartialModel" />

Then your partial view would look like:

@model PersistantMessagesPartialModel

@foreach(var item in Model.Messages)
{
    <script>
        console.log("MessagePartial");
        console.log(@item.Contents);
    </script>
}

The controller would look like this:

  [HttpGet]
  public async Task<IActionResult> Index()
  {
       var viewModel = new IndexViewModel(){
          PersistantMessagesPartialModel = new 
          PersistantMessagesPartialModel(){
                Messages = await 
           _AppUserDBContext.userList.ToListAsync()
            };
      return View(viewModel);
   }

The IndexViewModel would look like:

public class IndexViewModel{
    public PersistantMessagesPartialModel  
    PersistantMessagesPartialModel {get;set;}
}

I know that ASP.NET MVC allows you to define a model of the given page as a list of objects however I believe in the rule that each .cshtml page should have a single model class. It gives you huge flexibility when you want to add some new logic to the view. Just imagine a simple case when you want to add some if statement in your partial view which would work based on some value set in the backend of your app. In such a case you would need to use ViewBag, ViewData objects to pass it to the view and this is a bit harder to maintain especially if your app is big. In my solution, you would just add this extra field to the PersistantMessagesPartialModel, and even in a case when you want to do some renaming etc. its just faster and safer.