Find URL submit path of a Razor inherited page controller method - c#

Usually, I send data to a controller with a URL using the class name followed by the method name /Parent?handler=InitData or /Parent/InitData if the #page "{handler?}" is defined in a page.
However, how can this be done with a child controller than inherits the main parent controller ?
What I am looking for is to call a method inside a child controller with a syntax that is similar to this /Parent/Child/Test. Note that the call is made within a partial page embedded inside the main parent page.
Any idea on how this can be achieved ? (I know it has something to do with routing but I just don't understand how to do it)
Here is what I have so far
Partial page
#using App.Models.PagesViews.PartialPage
#model PartialPage // <-- partial parent model has access to PartialPage_Add and PartialPage_Delete children
<script type="module">
$.ajax({
url: "/Parent/Child/Test", <-- should call child controller method but is not working!!!
type: "post",
contentType: "application/json; charset=utf-8",
dataType: "json",
...
});
</script>
<div>
<!--... html code...-->
<div>
Parent page
#page "{handler?}"
#model App.Pages.ParentModel
#section head {
...
}
<div>
<partial name="_PartialPage" model="Model.partialPage_Add"
...
<partial name="_PartialPage" model="Model.partialPage_Delete"
</div>
Parent page controller
public class ParentModel : PageModel
{
private readonly DatabaseContext _dbContext;
public readonly PartialPage_Add partialPage_Add; // <-- PartialPage_Add is a child of PartialPage
public readonly PartialPage_Delete partialPage_Delete; // <-- PartialPage_Delete is a child of PartialPage
public MainModel(DatabaseContext _dbContext)
{
this._dbContext = _dbContext;
}
public IActionResult OnGet() => Page();
}
Child page controller
public class ChildModel : ParentModel
{
public ChildModel(DatabaseContext _dbContext) : base(_dbContext)
{
}
public void OnPostTest() // <-- Access this method from partial page
{
Log.Debug($"Successfully called method Test() in class ChildModel");
....
}
}

I think you may misunderstand the usage of Razor Pages and MVC Controller.
Razor Pages should contains .cshtml file declared with #Page and .cshtml.cs file with same name of the .cshtml file. Also the .cshtml.cs file should inherits PageModel or the inheritance class of the PageModel.
Your ChildModel extends the ParentModel which is the inheritance class of PageModel, if your project does not contain corresponding .cshtml, the ChildModel is just a simple class and cannot be called by ajax.
So you need be sure your project contains a Child.cshtml and declare #page "{handler?}" in it. And then the correct url should be /Child/Test.
If you want to use the parent as the suffix, change the page route template to #Page "/Parent/Child/{handler?}".
Besides, you must send the AntiForgeryToken together with the data you want to the Post method if you use ajax in Razor Pages.
Reference:
How to take result value of a jQuery script into a C# code in the same razor page in ASP.NET Core MVC?
For MVC controller, no matter your project contains the .cshtml file or not, you can also hit the action, because you can configure the route template in the Startup.cs/Program.cs or just use [Route] attribute.

Related

Creating model within partial razor page itself

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?

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.

c# - Returning multiple views in one

I am currently trying to display one of my views (partial view) inside of another one. Inside of my view, I have been using the following code: #{ Html.RenderPartial("_Pending"); }.
My partial view is inside of my Shared folder. My goal is to pull in two different views using logic from two different controllers. Given my two views (Transaction Index and Pending Transactions (partial)), I essentially want both of these to appear on the same page. The issue is that currently, I am essentially getting the same view twice.
In short, how do I display a partial view inside of another view, with the partial view returning results from a different controller?
If I understand correctly, what you want is getting result of same partial view but executed by two different controller.
Have you tried #Html.Action?
essentialy it is the same concept as calling Partial but with controller action involved.
to use it, just create two Action then call #Html.Action in your view, such as:
in your controller:
public ActionResult Action1() {
return PartialView("__Pending");
}
public ActionResult Action2() {
return PartialView("__Pending");
}
in your view - you can also call it within your partial view
#Html.Action("Action1")
#Html.Action("Action2")
More info abount Partial and Action difference check out
MVC Html.Partial or Html.Action
more info about hwo to use Html.Action
How can I use Html.Action?
The partial is always included in the main view. The only time you would return the partial on its own would be if you were updating via AJAX. Presumably you would use a partial to display a list of clients. You would, perhaps, use a foreach loop in your view to iterate over the lists (contained in the view model), passing each one to the partial as its model.
Find an example to use many partial views inside one view.
In aspx page:-
<asp:content id="content" contentplaceholderid="CenterContentPlaceHolder" runat="server">
<form id="frmCheckout" name="frmCheckout" method="post">
<div class="rzccartcont">
<%this.Html.RenderPartial("CheckoutProduct", Model);%>
<div class="rzcchkpmnt rzcchkpmnt-bg" id="divChkbottomArea">
<% this.Html.RenderPartial("CheckoutCartProfileInformation", Model); %>
<%this.Html.RenderPartial("CheckoutCartPaymentDetails", Model); %>
</div>
</div>
</form>
</asp:content>
Reloading part of a page via AJAX (note partial is rendered inline in initial page load)
<script type="text/javascript">
$(function() {
$('#someButton').click( function() {
$.ajax({
url: '/controller/action',
data: ...some data for action...,
dataType: 'html',
success: function(data) {
$('#partial').html(data);
},
...
});
});
});
</script>
Controller for AJAX:-
public ActionResult Action(...)
{
var model = ...
...
if (Request.IsAjaxRequest())
{
return PartialView( "Partial", model.PartialModel );
}
else
{
return View( model );
}
}
Hope it helps you.

Retrieve data from partial view on post request via jQuery

I have a view that dynamically updates depending on user input - here's how it works:
View is rendered with initial data from controller populated with a partial view
When something is clicked, I use retrieve a partial view from an ActionResult from the controller and replace existing data with jQuery
I need a string from the controller for the main view, so I make another call to the controller and retrieve a string.
I want to know if there's a way to get the string I need with the partial view instead of making a separate controller call.
Initial Data:
<!-- This needs to change when the partial is replaced, but it's heavier html
and I don't want to include it in the partial to minimize server calls/data -->
<div class="Location"> </div>
<div class="myContainingDiv">
<!-- This content gets updated -->
#Html.Partial("_MyPartial");
</div>
Partial View:
<div class="something">Model.something</div>
Controller Method (Partial View):
[HttpGet]
public ActionResult _MyPartial(string param1)
{
MyModel model = new MyModel();
// This is dynamic and what I need to get in my main view from the partial view
model.something = "Hi";
return PartialView(model);
}
Controller Method (Return desired string):
public string GetHiString(string param1)
{
return "Hi";
}
JavaScript (Update partial view):
$.post('/Home/_MyPartial', { 'param1': 'Hi' }, function (result) {
$('.something').replaceWith(result);
getOtherThing('Hi');
});
JavaScript (getOtherThing):
$.post('/Home/_GetHiString', { 'param1': 'Hi' }, function (result) {
$('.Location').replaceWith(result);
});
OK, i think that's everything.
Basically I only want to hit the controller once per call. Including the div in the partial view is not an ideal option.
I hit the controller the second time from my main view to get data that can easily be created from an object within the first method and sent down to the partial view.
So, besides including <div class="Location"> in my partial view, is there a way to send the extra information I need to the partial view and retrieve it dynamically from my main view (via JavaScript). Or is there a way to return additional data when I call _MyPartial from my JavaScript?
This is really the last simplification I want to make and i'd really appreciate the help!
You could modify your partial view _MyPartial and it's model to include something hidden within it that you could extract using jQuery:
Partial View
<div class="something">Model.something
<input class="somethingElse" type="hidden" value="#(Model.somethingElse)" />
</div>
Controller Method (Partial View):
[HttpGet]
public ActionResult _MyPartial(string param1)
{
MyModel model = new MyModel();
// This is dynamic and what I need to get in my main view from the partial view
model.something = "Hi";
model.somethingElse = GetHiString(param1);
return PartialView(model);
}
JavaScript (Update partial view)
$.post('/Home/_MyPartial', { 'param1': 'Hi' }, function (result) {
$('.something').replaceWith(result);
$('.Location').html($('.somethingElse').val());
});

Categories