Razor Pages ASP.NET unobtrusive ajax Post partial update - asp.net-core

I'm trying to update only part of my page from a partial View.
It works perfectly fine if i use this
Click heeeeeeeere
But this is a simple get and i'd like to actually post some data and do something with it. I wrote a form, set its method to post like this.
<form method="post" data-ajax="true" data-ajax-method="post" data-ajax-complete="completed" data-ajax-update="#panel" >
<div class="row">
id : #Html.TextBoxFor(x => x.customer.ID)
</div>
<div class="row">
Name : #Html.TextBoxFor(x => x.customer.Name)
</div>
<div class="row">
<input type="submit" value="send data" />
</div>
</form>
BUT this updates my entire page so my entire page is just the little partial view thats supposed to be updated.

a first observation, it seems you are missing the data-ajax-url from the second form .
Saying that, then in your Razor view you should include on the top of the page
#page "{handler?}"
This will allow you to pass additional information to your handler, then in your form you can simply include something like
<input type="hidden" name="id" value="send value"/>
where value is the value you want to pass and name is how the handler will identify what property to bind this to, then in your .cshtml.cs page your handler should look something like this
public IActionResult OnPostPartial(string id) {...//do something here id == "send value"}
hope this helps

Related

ASP.NET CORE Razor Pages: How to avoid executing page handler 2nd time on page refresh?

When using method handlers to execute OnGet or OnPost methods, &handler=[action] query string gets added.
Problem is if user manually refreshes the page afterwards by hitting browser's refresh button, the same action will get executed for the 2nd time unintentionally.
What is the recommended approach to avoid this?
Problem is if user manually refreshes the page afterwards, same action
will get executed for the 2nd time.
For the browser refresh button click event, we can't prevent it. But, as a workaround, you could defined a TriggerCount property in the page model, and use a hidden field to store the value in the form, then in the handler method, get the hidden field value and based on the count to do something. Code as below:
code in the .cshtml.cs page:
public void OnPostDelete()
{
if (Request.Form["TriggerCount"].Count > 0)
{
TriggerCount = Convert.ToInt32(Request.Form["TriggerCount"]);
TriggerCount++;
}
if (TriggerCount < 2)
{
// do something.
Message = "Delete handler fired, Count:" + TriggerCount;
}
else
{
Message = "Over 2 times";
}
}
Code in the .cshtml page:
#page
#model RazorPageSample.Pages.HandlerPageModel
#{
}
<div class="row">
<div class="col-lg-1">
<form asp-page-handler="edit" method="post">
<button class="btn btn-default">Edit</button>
</form>
</div>
<div class="col-lg-1">
<form asp-page-handler="delete" method="post">
<input type="hidden" asp-for="TriggerCount" />
<button id="btndelete" disabled="#(Model.TriggerCount>=1?true:false)" class="btn btn-default">
Delete
</button>
</form>
</div>
</div>
<h3 class="clearfix">#Model.Message</h3>
the screenshot as below:

Scaffolded Identity Razor Pages Forms don't submit unless there is a route parameter

This happens on all the Scaffolded Identity Razor Pages that do not include a route parameter in the Form definition, but let's take the ForgotPassword one as an example:
Here is the form as defined on the scaffolded page:
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
When I click the submit button, nothing happens. The browser dev tools confirm nothing happens. The page does not submit to the server. (and yes, I have entered a valid email address; if I don't, then the jquery validation correctly displays the appropriate error messages.)
But I've found that if I change the form definition to include a route parameter, as follows:
<form asp-route-unusedArg="SomeValue" method="post">
then the submit button works fine, and the form is submitted to the server. (Even though the OnPostAsync method defined by the server code does not expect a parameter at all; it is defined as public async Task<IActionResult> OnPostAsync()). The way I figured this out was to compare the form definition with that of the Login form, which was working fine, and had a route parameter for returnUrl.
What is going on here? Why is the form not submitting unless I add a route parameter?

Checkbox Click Form Submit in Razor Pages AspNetCore 2.2

I am trying to (non-Ajax) get a checkbox to resubmit form in Razor Pages and reload the page / and catch the result in my OnPost method.
I have in my index.cshtml
#page "{id:int?}"
#model IndexModel
<div class="text-center">
<form action="post" name="form1">
<strong>Filter:</strong> Hide Single Runs <input onclick="document.form1.submit()" asp-for="#Model.HideSingle" />
<hr />
And in my PageModel
public class IndexModel : PageModel
{
public bool HideSingle { get; set; } = true;
public async Task OnPost(int? id, bool hideSingle)
{
for say a starting page URL:
http://localhost:5000/TestRuns/1
The form submits on the checkbox click, but it ends up with a Url:
http://localhost:5000/TestRuns/post?HideSingle=false
Which obviously fails to resolve as I am expecting a route of http://localhost:5000/TestRuns/1.
For Asp.net Core form, the default method is Get which means for your current code, it send request with Get instead of post. You could specify the method with post like
<div class="text-center">
<form method="post" name="form1">
<strong>Filter:</strong> Hide Single Runs <input onclick="document.form1.submit()" asp-for="#Model.HideSingle" />
<hr />
</form>
</div>
For another way, you could explictly set the handler like
<div class="text-center">
<form asp-page-handler="post" name="form2">
<strong>Filter:</strong> Hide Single Runs <input onclick="document.form2.submit()" asp-for="#Model.HideSingle" />
<hr />
</form>
</div>
<input onclick="document.form1.submit()" />
is using JavaScript to submit the form. Do you want the page to post back, since you're not using Ajax (from your question)?
You want the call from your page to end up being http://localhost:5000/TestRuns/post?id=1&HideSingle=false. Is id required for your post to work (it's unclear with OnPost(int? id..)?
I order to get both values, you'll need to have hidden form values or multiple <input asp-for="Email" class="form-control" />. You need 1 for hideSingle and on for the id.

.NET Core Razor Pages Data Binding with partial views on form submission

I've got a an object with an Address object as a property which is used several places in my application. I made a partial view which looks like
#model Address
<div>
<div class="form-group">
<label asp-for="#Model.AddressLine1"></label>
<input asp-for="#Model.AddressLine1" />
</div>
....
I use the form like
<form method="post">
<div class="form-group">
<input asp-for="#Model.Request.Business.Owners[0].FirstName" />
<input asp-for="#Model.Request.Business.Owners[0].LastName" />
</div>
<input type="submit"/>
</form>
the data renders fine on the entire form, including the partial Address view. when I submit the form, everyone I reference the Address partial I get back as null when I submit the form, while the other data is data bound correctly. Is it possible to bind data to a partial view and have it submit with a form post correctly?
Likely, you're not providing context to the partial, so that it can generate the names properly. As-is, the partial is going to want to generate names like AddressLine1, when what you actually need for proper binding is Request.Business.Owners[0].Address.AddressLine1. I'm assuming you're doing something like:
<partial name="_Address" model="Request.Business.Owners[0].Address" />
Instead, you need to use the for attribute:
<partial name="_Address" for="Request.Business.Owners[0].Address" />

MVC 4 Model not returned to controller

I have three cascading dropdowns using Ajax to populate themselves on a view. Also in the view I call a partial view that iterates a container in the model and for each item calls another partial to display for editing the model properties appropriate according to the selected items in the dropdowns. I have built a complex model that contains the dropdown choices as well as the properties to be edited, and pass the portion of the model to the lowest level partial necessary to display the properties to be edited.
I want to update the db when the user clicks the submit button, through a normal Html.BeginForm, not by using Ajax. So I must wrap only the partials that display the properties in the form so that the existing Ajax functionality does not post to the controller. The problem is that although I can build this all, and the submit button connects to the controller as expected, the model returns to the controller null.
Does the model not come back from the partials up through the path that built them? Or more correctly stated, does the model not persist on the page even if it is built using partials?
I am sure someone is going to suggest that I post back using Ajax but that is not a best option for me. Or someone might ask what the html looks like on the page. Oddly, I can only see the complete html using browser developer tools, it does not show in a View Source selection.
Any ideas?
Moving this to where it belongs:
I'm not certain I understand. I get that the returning model needs to match the expected model but I don't get the explanation above. You say "So if your controller looks like this:", and of course it does, then what? That's wrong?
I have to look in dev tools for Chrome to see the actual html output and I see this in one case:
<input class="text-box single-line" id="status_Comments" name="status.Comments" type="text" value="Last Install to be completed this weekend">
So if the 'name' tag needs to look proper, I think it does. Am I wrong?
My date fields look like this:
<input type="text" value="8/19/2014" class="datepicker hasDatepicker" id="dp1391795955619">
So there's no 'name' tag but does have an id. Do I need to add a name?
Here's the code that generates the above:
#foreach (Status status in Model) {
string date = status.Date.HasValue ? status.Date.Value.ToShortDateString() : string.Empty;
<tr>
<td style="width: 175px;">#Html.DisplayFor(model => status.Name)</td>
<td style="width: 75px;">#Html.DisplayDropdownColorFor(model => status.StatusValue)</td>
<td style="width: 80px;"><input type="text" value="#date" class="datepicker" /></td>
<td style="width: 80px;"><input type="text" value="" class="datepicker" /></td>
<td style="width: 375px;">#Html.EditorFor(model => status.Comments)</td>
</tr>
}`
Geez, I sound like a desperate moron.
Thanks for your help.
Let's say you had a couple of classes like this:
public class SomeClass1
{
public string Name { get; set; }
public SomeClass2 class2Value { get; set; }
}
public class SomeClass2
{
public string Description { get; set; }
}
And you have a form in a Razor view:
#model SomeClass1
#using (Html.BeginForm()) {
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.class2Value.Description)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.class2Value.Description)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
The actual HTML code is going to look something like this:
<form id="form1" action="/pageurl" method="POST">
<fieldset>
<div class="editor-label">
Name
</div>
<div class="editor-field">
<input type="text" name="Name" id="Name" value="Name val 1" />
</div>
<div class="editor-label">
Description
</div>
<div class="editor-field">
#Html.EditorFor(model => model.class2Value.Description)
<input type="text" name="class2Value.Description" id="class2Value_Description" value="Desc val 2" />
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
</form>
If you notice, the name attribute of the form fields are set by MVC to follow the model. In the case of the nested object, it uses the underscore in the ID and period in the name to keep track of the where in the post model the values should go.
So if your controller looks like this:
[HttpPost]
protected ActionResult Save(SomeClass1 model)
{
//Then
// SomeClass1.Name == "Name val 1"
// and
// SomeClass1.class2Value.Description == "Desc val 2"
}
That's what #David was getting at in the comments... No matter how complex your page, when the <form> posts back to the server, the name attributes of the fields you are returning have to line up with the model that the controller action is expecting... or you will get null values.
You can even create a custom class for the post model, and as long as the name attributes line up with the model, and even though it was not the same as the #model at the top of the Razor view, MVC will use the key/value pairs and populate the data in your post model.
UPDATE
I updated the above to reflect a correction. The underscore is the delimiter in the ID attribute, and the period is used in the NAME attribute.
#leemid: An HTML form only posts fields with a name attribute. Only the name'ed inputs, selects, textareas, etc. will be passed back to the server when the form is submitted. And those names have to be inline with the model that is expected by the controller. For the sake of sounding redundant, that date input field you showed the example for, does not have a name attibute, and if the name were the same as the id, there would have to be a property in your model like
public string dp1391795955619 { get; set; }
for it to show up in your in controller action. You can set the name manually, as long as it's named the way MVC is expecting so that it can pass it into the model when posting.
In my example, the parts I was trying to highlight was the relationship between the class and property names, versus the name attributes MVC writes to the HTML document, and how it keeps all that stuff straight. In simple examples, you don't have to think about it. But in your complex scenario, it looks like you're going to have to manage some of the naming conventions manually so that MVC can understand what you're trying to do.