Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Razor Pages - Can't pass different model to partial view within a page handler

I'm not quite sure if this is possible to do but wanted to check. I have a razor page that has a few different handler methods. In some of them, I return a partial view result.

Example:

public class BoardMeetingsModel : PageModel
{ 
      //ctor
      //properties

      public IActionResult OnGetFetchCreateMeetingPartial()
          {
             return Partial("_CreateMeetingPartial", new ManipulationDto());
          }
}

My partial view is setup as below:

@using Models.ManipulationModels
@model ManipulationDto

It's a partial page so I am not using the @page directive (partial page is named _CreateMeetingPartial.cshtml. When I pass in the ManipulationModel though, I run into below error

The model item passed into the ViewDataDictionary is of type 'Models.ManipulationDto', but this ViewDataDictionary instance requires a model item of type 'Pages.BoardMeetingsModel'.

I am not calling the partial with my razor page. I am directly returning a partial page because I am consuming the returned data in a javascript modal. Is it even possible to override this behavior? By default it always expects the base PageModel (i.e. BoardMeetingsModel) to be passed in.

I'm surprised that even though I am explicitly passing in a model that exists, the partial view is still expecting a pagemodel instead of the model I explicitly stated for the partial view.

like image 735
Help123 Avatar asked Feb 05 '19 05:02

Help123


2 Answers

This appears to be bug in newly introduced Partial() in ASP.NET Core 2.2 where model parameter seems to be completely redundant, because "this" is the only thing it will accept.

If you however use PartialViewResult() it will work. Should be simpler and more readable than the accepted solution.

Just swap this

return Partial("_CreateMeetingPartial", new ManipulationDto());

with this

return new PartialViewResult
{
    ViewName = "_CreateMeetingPartial",
    ViewData = new ViewDataDictionary<ManipulationDto>(ViewData, new ManipulationDto())
};
like image 84
Xeevis Avatar answered Nov 08 '22 06:11

Xeevis


To resolve above issue, I had to do below. Note that I do not have [BindProperty] attribute on my ManipulationDto property because I have multiple models on my page. If you have multiple models and you have validation (e.g. required properties), all of them will trigger in razor pages which is different from MVC. The way to handle it in my case was to pass the model directly as a parameter but also making sure to have a public property which I can assign all the values in case model state validation fails.

If you do not have multiple unique models, each with their own validation, you can just apply the bindproperty attribute and not worry.

public class BoardMeetingsModel : PageModel
{ 
      //this gets initialized to a new empty object in the constructor (i.e. MeetingToManipulate = new ManipulationDto();)
      public ManipulationDto MeetingToManipulate { get; set; }

      //ctor
      //properties

      public IActionResult OnGetFetchCreateMeetingPartial(ManipulationDto meetingToManipulate)
          {
             //send the page model as the object as razor pages expects 
             //the page model as the object value for partial result
             return Partial("_CreateMeetingPartial", this);
          }

      public async Task<IActionResult> OnPostCreateMeetingAsync(CancellationToken cancellationToken, ManipulationDto MeetingToCreate)
        {
            if (!ModelState.IsValid)
            {
                //set the pagemodel property to be the values submitted by the user
                //not doing this will cause all model state errors to be lost
                this.MeetingToManipulate = MeetingToCreate;
                return Partial("_CreateMeetingPartial", this);
            }
            //else continue
         }
}
like image 28
Help123 Avatar answered Nov 08 '22 08:11

Help123