Creating model within partial razor page itself - c#

I am looking to rewrite an existing ASP.NET MVC application to .Net 5 Razor pages and one of the issues I am struggling to resolve is how to create a partial razor page that creates it's own model rather than having the model passed in from the parent page.
In MVC, I would simply add an action to the controller and specify this within the view;
#Html.Action("_SideNavigation", "Shared")
Ultimately, this would call the _SideNavigation action on the share controller, which would then create the model and pass it back to the view.
I am trying to achieve something similar in Razor Pages, I do not want to define the model of the partial within the model of the parent view, such as this;
<partial name="_Partial1" model="Model.PartialModel" />
I have tried creating an empty model within the parent page and assumed this would then call the OnGet() method within the partial itself;
<partial name="_Test" model="new _TestModel()" />
public class _TestModel : PageModel
{
public TopNavigation TopNavigation { get; set; } = new TopNavigation();
public void OnGet()
{
TopNavigation = new TopNavigation { NotificationCount = 1 };
}
}
and then referencing the model in the page;
#Model.TopNavigation.NotificationCount
But it is always null.
Is this behaviour not available in razor pages?

Related

Post partial view form

Can someone help me understand partial views, forms and posting in ASP.NET Core Razor.
I have a Search.cshtml partial view located in "~/Client/Search" :
#model Web.Pages.Client.SearchModel
#using (Html.BeginForm())
{
<div>
#Html.RadioButtonFor(x => x.searchType, (int)ApplicationCore.Interfaces.SearchType.mobile, new { Name = "SearchType" }) Mobile
#Html.RadioButtonFor(x => x.searchType, (int)ApplicationCore.Interfaces.SearchType.phone, new { Name = "SearchType" }) Phone
#Html.RadioButtonFor(x => x.searchType, (int)ApplicationCore.Interfaces.SearchType.email, new { Name = "SearchType" }) Email
</div>
#Html.TextBoxFor(x => x.searchFilter)
<input type="submit" value="Search"/>
}
With code page Search.cshtml.cs :
public class SearchModel : PageModel
{
public SearchType searchType { get; set; }
public string searchFilter { get; set; }
private readonly IClientService _clientService;
private readonly Infrastructure.Data.DBContext _context;
public SearchModel(Infrastructure.Data.DBContext context, IClientService clientService)
{
_context = context;
_clientService = clientService;
searchFilter = string.Empty;
searchType = SearchType.mobile;
}
public async Task<IActionResult> OnPostAsync()
{
return RedirectToPage("./Index");
}
}
If I load the "~/Client/Search" Partial View directly it loads and on post it correctly fires the OnPosAsync() action.
However if the "~/Client/Search" Partial View is rendered from the "~/Session/CheckIn" parent View :
#await Html.PartialAsync("~/Client/Search", Model._searchModel)
The OnPostAsync() within the "~/Client/Search" Partial View no longer fires.
I have tried all sorts of combinations to define "action", "controller" within the Html.BeginForm in the Partial View, however I can never get the OnPostAsync() within the Partial View to fire.
Any pointers? Read a lot of articles and forum posts however there are no clear descriptions or walkthroughs to help me understand this and get the Partial View action method firing on postback from parent View.
This is why Razor Pages are a blight: they obfuscate logic, allowing people to build stuff without ever actually understanding how any of it works. </rant>
There's nothing special about a partial view. It's just a view like any other view. What makes it "partial" is the context in which it's used, i.e. injecting it into the rendering of a view. As such, Razor Pages lets you add a code-behind, because it's just a view, and any view can have a Razor Pages code-behind. However, when used like a partial, that code-behind is not actually utilized, and there's your problem.
Also, you need to bear in mind that the whole concept of partial views only exists server-side. Once the response has been returned, all you have is just an HTML document. The browser couldn't care less whether you used one partial view, 100 partial views or no partial views to create the response server-side. As such, using a partial doesn't somehow magically buy you the ability to just work with a single section of your page, such that when you post, only that section is changed. For that, you need AJAX. Otherwise, doing a post, whether from a "partial view" or not, will cause the entire view to be changed in the browser window.
In other words, you need something server-side that will respond to that post request by returning a new full view, not just your partial, or you need to make the request client-side via AJAX, and return just your partial view. However, then, you're responsible for replacing whatever HTML should be replaced with that response, yourself.

Net Core Inline Razor Markup Null Model

My first go with Razor Pages inline markup. Running into this weird issue after passing a ViewModel to a PartialView.
Of course in my parent page I pass the ViewModel to the PartialView:
#{Html.RenderPartial("Partial/_RequestView", Model.NewRequest);}
public class IndexModel : PageModel
{
private readonly IActiveDirectoryClient _activeDirectoryClient;
private readonly ITravelClient _travelClient;
public IEnumerable<TravelRequestViewModel> Requests { get; set; }
In the partial view, I have no issue referencing the model in a lambda expression
#Html.HiddenFor(model => model.RequestId)
However when I attempt to reference the Model in razor markup inline the Model is null. Any ideas?
<p>#Model.Name</p>
The NewRequest property is set within the OnGetAsync() method in the parent page
public async Task<IActionResult> OnGetAsync()
{
NewRequest = BuildNewRequest();
if (NewRequest == null)
throw new NullReferenceException("Unable to build new travel request");
return await Task.FromResult(Page());
}
Answered my own question. MUST remove the #page directive in order for the partial view to work and for the #Model to be recognized.
Kinda bizarre that VS 2017 templates adds this directive for a partial view, I can only assume it's a bug.

Dynamically add Views in ASP.NET MVC

I was originally developing a project for WPF, using MVVM, which had the benefit of allowing me to populate a list of views that I wanted available. Each view had a "Next" button that would progress to the next view in the list.
However, now I am trying to do the same in ASP.NET MVC. This is my first time using MVC, but I have an XML file, from which I need to generate this UI. These views, which are chosen from the script, also have components in them that are dynamic -- sometimes ViewA might need 3 "input views" nested in it, sometimes it might need 1.
I was achieving that before with ListBox, ItemsSource, and DataTemplate. So my question is this: how can I dynamically populate which views to display, and (more importantly) how can I dynamically fill those views with x number of control A, and y number of control B?
First off, a high-level overview of the project structure...
YourProjectName
Controllers
ProductController.cs
Models
ProductViewModel.cs
Views
_ProductPartial.cshtml
ListProducts.cshtml
ProductViewModel.cs
public class ProductViewModel
{
public string Name { get; set; }
public string Description { get; set; }
}
ProductController.cs
public class ProductController : Controller
{
public ActionResult Index()
{
// Create your model (this could be anything...)
var model = new List<ProductViewModel>
{
new ProductViewModel { Name = "Apple", Description = "A red apple" },
new ProductViewModel { Name = "Orange", Description = "An orange orange" }
};
// Return the main view and your model
return View("ListProducts", model);
}
}
_ProductPartial.cshtml
#model YourProjectName.Models.ProductViewModel
<h1>#Model.Name</h1>
<p>#Model.Description</p>
ListProducts.cshtml
#model System.Collections.Generic.List<YourProjectname.Models.ProductViewModel>
#foreach (var product in Model)
{
Html.Partial("_ProductPartial", product)
}
Summary
Now if you request that controller action (localhost/Product/Index or whatever it ends up being for you), the controller will create the model, render the parent view, and the parent view will render as many of the product partial views as necessary depending on the product model collection we defined in the controller. Views and partial views don't require models, but I imagine you will be using a model class of some sort to help you determine what/where/how many partial views to render in your parent views. This is about as basic as it gets but it should get you started in using partial views.

using View with layout when both require specific Model

I`m writing an ASP.Net MVC application with Razor.
Assume that I have HomeController and some views for it.
1. View1
2. View2
3. View3
All this views use common _MyLayout file, which should look like this:
When the links are clicked, the views are rendered by RenderBody() method.
Each view is strongly typed: it requires its own Model.
Everything was fine untill I decided to add special Model to _MyLayout view.
But now I get error
The model item passed into the dictionary is of type 'TestUp.Models.UserModels.PendingTestsModel', but this dictionary requires a model item of type 'TestUp.Models.UserModels.UserNavigationModel'.
Here is controllers code
public ActionResult View1()
{
ModelForView1 model = new ModelForView1();
return View(model);
}
public ActionResult View2()
{
ModelForView2 model = new ModelForView2();
return View(model);
}
public ActionResult View3()
{
ModelForView3 model = new ModelForView3();
return View(model);
}
Shortly speaking if layout view doesn`t require model, specific method for View is invoked,
model is created, passed to view and everything is ok. But now layout requires model as well so it crashes.
The question is: how do I elegantly resolve this problem?
Desired workflow is:
View1 is requested
Method in controller for this view is called, model instance created, passed to view
Some method for layout is called, model for layout created, passed to layout.
Is it possible to make things work somehow like this?
Thanks.
Create a base model type and have your specific view models extend it. This base model can have a property of type UserNavigationModel. The layout can accept the base model and use the new property as the model for the navigation menu.
public abstract class ModelBase
{
public UserNavigationModel NavigationModel { get; set; }
}
public class ModelForView1 : ModelBase { ... }
public class ModelForView2 : ModelBase { ... }
public class ModelForView3 : ModelBase { ... }
View1:
#model ModelForView1
Layout:
#model ModelBase
#* use Model.NavigationModel for nav bar *#

How to render a partial view that deals with its own controller within another view?

I'm new to MVC and I wanna render a partial view in a popup within a view, but I wondering how to provide the partial view's model.
<div id="enumsDialog" class="dialog">
#Html.Partial("~/Views/Enumeration/List.cshtml", [it needs a modal here])
</div>
In another words, I want the partial view to deal with its own controller.
How to implement that?
If you are wanting to render a partial view whose action/data comes from a different controller, then you will probably want to use the RenderAction method:
#Html.Action("MyAction", "MyController")
If you are wanting to render a partial view with data from your current viewmodel (without having to make another request of your application), then use the RenderPartial method:
#Html.Partial("NameOfView", Model.WhateverYouArePassing)
The easiest way is this one:
public class EnumerationController : Controller {
// other actions...
public ActionResult List(){
// TODO: var model = retrieve-your-model-here
return PartialView(model);
// using "PartialView" instead of "View" method ensures this action isn't
// responsible from direct requests
// Also you can use "PartialViewResult" class as a return-type instead
// of "ActionResult" in method, like "List2()" method below:
}
public PartialViewResult List2(){
// TODO: var model = retrieve-your-model-here
return PartialView(model);
}
}
and in .cshtml file:
#{Html.RenderAction("List", "Enumeration");}
// or:
#Html.Action("List", "Enumeration")

Categories