Post partial view form - c#

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.

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?

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.

Child model is null on post

I have seen similar questions here regarding my question but I am unable to find a suitable solution for my purpose. So I started working with the MVC and had to create a simple wizard. I created the Parent View model as,
public class WizardSteps
{
public int CurrentStepIndex { get; set; }
public WizardStep1 Step1 { get; set; }
public WizardStep2 Step2 { get; set; }
public WizardStep3 Step3 { get; set; }
public WizardSteps()
{
CurrentStepIndex = 1;
Step1 = new WizardStep1();
Step2 = new WizardStep2();
Step3 = new WizardStep3();
}
}
Now I have created three partial views for each of my wizard step. For every view I used the parent model,
#model eFormation_MVC.Models.WizardSteps
For each of the fields on each of the Partial Views I used controls like this inside forms,
#Html.LabelFor(m => m.Step1.FirstName, new { #class = "col-sm-3 control-label" })
#Html.LabelFor(m => m.Step2.Address, new { #class = "col-sm-3 control-label" })
This is brief code for just giving the idea what I have done so far. I have different controller methods for each of the Partial View. I pass the Parent Model WizardSteps to the controller and pass it back to next Partial View. SO for all Partial View single model is used which has child model. Now when I post the model from Second Partial View then all the values in WizardSteps.Step1 become null. I also know that this is the actual behavior but my question is how to persist the values. What can be the proper way of doing this so that the values for every Step is not lost from the Parent model.
Edit:
Each partial view has its own form and I am sending the parent model WizardSteps to the controller on form submit of each partial view. The problem is that when I submit Partial View for Step 2 then the values for Step 1 in the WizardSteps becomes null. I know this happens because there are no fields for Step 1 on Step 2 partial view and controller think values as null and does not persists it.
On some related question it is suggested to Serialize the models and store it on the View and send it back to controller and de-serialize it. Some has use Sessions or TempData for storing the models but I want something which is done with models. There is a question for using MVCWizard at MVCWizard.Wizard - Using Complex Models but the answer to that question is so vague that I am unable to grasp it. I want something similar to the MVCWizard with the use of Merge functionality but couldn't find any example of doing so. Need help with this.

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());
});

set value for a textbox in partial view razor mvc

I have following scenario:
My Index page, uses a layout which has a partial View ebbeded in it. the partial view contains a search text box.
For a particular scenario, i need to set the text of the search box with my viewdata[] for index page.
is it somehow poosiblein mvc3, asp.net 2010 to set the value of textbox in partial view from the viewpage?
You could make your partial strongly typed to some view model:
#model SearchViewModel
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.Keywords)
#Html.EditorFor(x => x.Keywords)
<button type="submit">OK</button>
}
and then when inserting the partial you could pass this view model:
#Html.Partial("_Search", new SearchViewModel { Keywords = "some initial value" })
or even better the view model of your main view will already have a property of type SearchViewModel and you will be able to call the partial like this:
#Html.Partial("_Search", Model.Search)
Now obviously in your Index action you no longer need to use any ViewData, but you could directly work with your strongly typed view model:
public ActionResult Index()
{
var model = new MyViewModel
{
Search = new SearchViewModel
{
Keywords = "some initial value"
}
};
return View(model);
}
You can always make the partial view strongly typed (even if the model is just a string) and pass the value you need.
public class MyModel
{
public int ValueForView {get;set;}
public string TextBoxValue {get;set;}
}
-Index.cshtml
#model MyModel
#{ Html.RenderPartial("PartialView", Model.TextBoxValue); }
-PartialView.cshtml
#model string
#Html.TextBoxFor(m => Model)
As I understand your issue, the partial view is in your layout and you need to get data into it.
In this case layouts are processed last but passing data to it your options are somewhat limited. You can use an ActinFilter or ViewData.
ViewData is the easiest, and also the messiest so I don't recommend it.
ActionFilters would work, but you could just process your partial by simply calling in your layout:
#Html.RenderAction("PartialViewAction", "PartialViewController")
Unless I'm missing something I don't believe the other answers addressed that this is in a layout, hence a different issue.

Categories