Validation on Ajax loaded form - presenting back the validation summary - c#

I am trying to validate a submitted form that is loaded in isolation to the rest of the page. I use validation all the time with my normal non ajax loaded content. I go on this principle:
Submit the form
pass the request from my controller to my service
Validate the object in the service
pass back to the controller a bool depending on the validation state
Present the original form back with validation summary if there are validation errors
Present a success page if there are no validation errors
That works fine on non-ajax content.
Now lets consider my issue. I have this kind of structure:
<div id="mainContent">
<div id="leftContent"></div>
<div id="rightContent"></div>
</div>
<script>
$.ajax({
url: baseUrl + "home/newApplicationForm/",
type: "GET",
success: function (data) {
$("#rightContent").html(data);
},
error: function (xhr, ajaxOptions, thrownError) {
alert("Error displaying content");
}
});
</script>
This puts my blank application form on the right hand side of the page.. everything else on the page is left unchanged by that ajax.
So home/newapplicationform now displays the form that is wrapped with:
#model homelessRentals.Model.Application
#{
AjaxOptions options = new AjaxOptions{
HttpMethod = "Post",
UpdateTargetId = "AddApplication"
};
}
#using (Ajax.BeginForm(options)) {
#Html.ValidationSummary(true)
<div class="editor-field">
#Html.EditorFor(model => model.YourName)
#Html.ValidationMessageFor(model => model.YourName)
</div>
<input type="submit" value="Add Application" id="saveMe"/>
}
This form now pings back to my controller:
[HttpPost]
public ActionResult AddApplication(Application app)
{
bool validated = _service.AddApplication(app);
if(validated)
{
return PartialView("SuccessApp");
}
return PartialView(app);
}
This will either return me to my form with validation errors shown or route me to my success page. This works to the extent that the logic is right, BUT I get presented the partial view in replacement of the whole page - it is not presented back in the 'rightContent' div.
I have tried submitting the form and catching the submission in jquery, then running something like this but I get the same behaviour:
$.ajax({
url: baseUrl + "home/AddApplication/",
data: "{'app':" + JSON.stringify(newApp) + "}",
type: "POST",
success: function (data) {
$("#rightContent").html(data);
},
error: function (xhr, ajaxOptions, thrownError) {
alert("Error displaying content");
}
});
Can anyone help me with a better way to achieve this validation?
Many thanks

The UpdateTargetId is incorrect, this needs to point to rightContent rather than AddApplication.
#{
AjaxOptions options = new AjaxOptions{
HttpMethod = "Post",
UpdateTargetId = "rightContent"
};
There is no dom element with the id of AddApplication.

Related

Why does my ajax post to my handler method fail with using POST method, but not when using GET method?

Not for the first time, I'm seeing that my Razor Pages seem to be handling GET/POST actions strangely when posting with ajax.
The latest example looks like this:
#inject IAntiforgery antiForgery
#{
ViewData["Title"] = "Disclaimers";
Layout = "~/Pages/Shared/_Blade.cshtml";
var token = antiForgery.GetAndStoreTokens(HttpContext).RequestToken;
}
$(".save-button").on("click", function(e) {
e.preventDefault;
const body = document.querySelector(".editor").innerText;
let disclaimer = {
clientid: parseInt($("#Disclaimer_Client_Id").val()),
description: $("#Disclaimer_Description").val(),
type: $("#Disclaimer_Type").val(),
markup: body
};
$.ajax({
method: "GET",
url: "./Create?handler=Create",
headers: {
"RequestValidationToken": "#token"
},
data: disclaimer,
dataType: "application/json",
success: function (data) {
console.log(data);
},
error: function (data) {
console.log(data);
}
});
});
I've done it this way because I'm using quilljs which employs a div for it's rich text editor. I can't use asp-for bindings on the div to bind it to the model.
public async Task<IActionResult> OnGetCreate(CreateDisclaimerViewmodel model)
{
var disclaimer = new Disclaimer
{
Created = DateTime.Now,
CreatedBy = User.Identity.Name,
Description = model.Description,
Markup = model.Markup,
Type = model.Type
};
if (model.ClientId > 0)
{
disclaimer.Client = await context.Clients.FindAsync(model.ClientId);
}
context.Disclaimers.Add(disclaimer);
await context.SaveChangesAsync();
return Redirect("/Disclaimers/Index");
}
With the code set up as using a GET method, it all works, but in this case, it should clearly be a POST.
Change it to a POST however and an empty response is returned with HTTP 400...
$(".save-button").on("click", function(e) {
e.preventDefault;
const body = document.querySelector(".editor").innerText;
let disclaimer = {
clientid: parseInt($("#Disclaimer_Client_Id").val()),
description: $("#Disclaimer_Description").val(),
type: $("#Disclaimer_Type").val(),
markup: body
};
$.ajax({
// Only the method changes here, everything else is above.
method: "POST",
url: "./Create?handler=Create",
headers: {
"RequestValidationToken": "#token"
},
data: disclaimer,
dataType: "application/json",
success: function (data) {
console.log(data);
},
error: function (data) {
console.log(data);
}
});
});
And the page model:
// Only the method changes (OnGetCreate becomes OnPostCreate).
public async Task<IActionResult> OnPostCreate(CreateDisclaimerViewmodel model)
{
var disclaimer = new Disclaimer
{
Created = DateTime.Now,
CreatedBy = User.Identity.Name,
Description = model.Description,
Markup = model.Markup,
Type = model.Type
};
if (model.ClientId > 0)
{
disclaimer.Client = await context.Clients.FindAsync(model.ClientId);
}
context.Disclaimers.Add(disclaimer);
await context.SaveChangesAsync();
return Redirect("/Disclaimers/Index");
}
This clearly should be a POST request but it simply won't work when using POST.
So what am I missing or misunderstanding? And since the solution can't be to use GET, what's the solution?
Shorten answer:
You use the wrong header name, it should be RequestVerificationToken:
headers: {
"RequestVerificationToken": "#token"
},
You are getting a 400 (Bad Request) response because the framework expects the RequestVerificationToken as part of the posted request. Be sure you have send it correctly.
Common way use ajax post in Razor Pages
Be sure your form tag does not have action attribute and then it will dynamically add a hidden input for token (You can F12 in browser to check whether your html contains input named __RequestVerificationToken, Ctrl+F and search for __RequestVerificationToken):
<form method="post">
//other elements...
<input class="save-button" type="button" value="CHANGE"/>
</form>
#section Scripts
{
<script>
$(".save-button").on("click", function(e) {
e.preventDefault;
//...
$.ajax({
// Only the method changes here, everything else is above.
method: "POST",
url: "?handler=Location",
headers: {
RequestVerificationToken: $('input:hidden[name="__RequestVerificationToken"]').val()
},
data: disclaimer,
dataType: "application/json",
//...
});
});
</script>
}
Otherwise, you will need manually add it by using #Html.AntiForgeryToken() in the form:
<form method="post">
//other elements...
#Html.AntiForgeryToken()
<input class="save-button" type="button" value="CHANGE"/>
</form>

Using ajax to update fields in a view based off of dropdown onchange with a method in the DAL

I have a view that has a dropdown that is generated by a linq statement when the view is populated.
<div class="form-group">
#Html.LabelFor(m => m.OrderID, "Order ID")
<div>
#Html.DropDownListFor(m => m.OrderID, Model.Orders, "Select Order ID",
new { #id = "orderDropdown" })
</div>
</div>
I have 2 other fields in the view that need to be updated with the data retrieved from the database.
<div class="form-group">
#Html.Label("Final Weight")
<div id="finalWeight">
#Html.DisplayFor(m => m.FinalWeight)
</div>
</div>
<div class="form-group">
#Html.Label("Initial Weight")
<div id="initialWeight">
#Html.DisplayFor(m => m.InitialWeight)
</div>
</div>
This is the ajax that I got from the link below:
<script>
$("#orderDropdown").change(function (event) {
$.ajax({
url:"???" + $(this).val(),/* not sure what to use*/
data: { id: $(this).val()},
cache: false,
type: "GET",
dataType: "html",
success: function (data, textStatus, XMLHttpRequest) {
$("#divinitialWeight").html(data);
}
});
});
Here is the controller code
[HttpGet]
[OpenAction]
public async Task<float> GetInitialWeight(int sid)
{
var initialWeight = await Manager.FindInitialFilterWeightBySID(sid);
return initialWeight.MeanWeight;
}
The method is in a Data Access Layer that is only referenced in the main project. The way I would call it in the controller would be like below and pass it the orderId :
Entities.Manager.FindInitialWeight(orderId);
Entities.Manager.FindFinalWeight(orderId);
I came across this SO question and it's close to what I need but the only issue is the url: because the data retrieval method is not in my controller...it is in the data access layer that is only a reference. Also the orderId is being passed as a parameter.
How can I call that method from ajax and pass it the orderId from the url: of the ajax call?
You can call the controller method from JQuery like this
if the JQuery code is within the razor view
url: '#Url.Action("GetInitialWeight")',
data: { sid: $(this).val()},
I actually had to create a GET method in my controller that contacted the DAL with the passed parameter from the javascript
[HttpGet]
[OpenAction]
public async Task<ActionResult> GetInitialWeight(int sid)
{
var initialWeight = await Manager.FindInitialFilterWeightBySID(sid);
return Json(new { initialWeight.MeanWeight }, JsonRequestBehavior.AllowGet);
}
Then change the javascript as such
<script>
$("#sidDropdown").change(function (event) {
var url = "/Controller/GetInitialWeight/";
var data = { sid: $(this).val() };
var dataType = "json";
$.get(
url,
data,
function (response) {
$("div#initialWeight").text(response.MeanWeight)
console.log(response.MeanWeight);
}, dataType);
});

ValidateInput(true) not working accepting html tags with ajax post call

I want to block HTML tags. I'm passing HTML tags to action and it is accepting it. I have used [ValidateInput(true)] but still its accepting HTML. By default, validation is enabled but in this case, it is not working
Im using ajax call to send data :
$.ajax({
method: "Post",
url: "/Home/MyAction",
contentType: 'application/json',
data: JSON.stringify({ htm: "<span>abc</span>"}),
success: function (d) {
UnBlockUI();
if ($.type(d) == "string")
AccessDenied();
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
UnBlockUI();
ErrorMessage("Something went wrong, please try again");
}
});
The code:
[ValidateInput(true)]
public ActionResult MyAction(string htm)
{
return View(htm);
}
any solution to get rid of this problem
Thanks :)
#Biby Augustine is right....
Simply pass the Object and it validates donot do JSON.stringify() as it converts the object to valid json (string) which is not validated by ValidateInput annotation
$.ajax({
method: "Post",
url: "/Home/MyAction",
data: dataObject,
success: function (d) {
UnBlockUI();
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
UnBlockUI();
ErrorMessage("Something went wrong, please try again");
}
});
ValidateInput validates if there any suspicious requests coming on Form submission.
Form submission means do post back of the entire form by click on a submit button.
For example
HTML:
<body>
<form id="frmDemo" method="post" action="/Home/Demo">
<input type="hidden" id="hdnText" value="<span>Testing</span>"/>
<button type="submit" form="frmDemo" value="Submit">Submit</button>
</form>
</body>
In ActionResult
[HttpPost,ValidateInput(false)]
public ActionResult Demo(FormCollection frm)
{
frm["hdnText"].ToString(); //this will give you the result
}
In case any html tag encountered while posting it will be blocked.

Sending Data from View to Different Controller without QueryString

I need to pass data from View to Controller(From TestView1 to TestController2)
#Html.ActionLink("Text", "Index", "Sample", new { testId = test }, null)
Currently this is sending Data in QueryString. But i need to avoid this and pass data to Controller without Query string ?
How do i achieve without Querystring ?
I searched and most of them were by using Query string. If i missed out on solutions please redirect to correct path.
Thanks
Have you tried to post your data to the controller, if possible? Keeping a form and hidden fields..
e.g http://www.asp.net/ajaxlibrary/jquery_posting_to.ashx
You can send data using Ajax call back. try this method on your link/button
#* Your button/link *#
<input type="button" onclick='Link1()'" value="Submit" />
<script type="text/javascript">
function Link1() {
var Id = $("#txt").val();
$.ajax({
url: '#Url.Content("~/Test2/Actionname")',
type: 'post',
async: true,
data: { text: Id },
success: function (data) {
alert("Success");//Ajax request success
alert(data);//data from Test/yourAction
},
error: function (err) {
alert("fail");//Ajax request fail
alert(err.responseText);//error will displayed here
}
});
}
</script>

How could I set UpdateTargetId on jQuery ajax

I want to render the partial view using ajax.
When I submit the button, the partial view must be rendered.
I could implemented it using ActionLink but I want to call the Action by JavaScript.
But the following code doesn't work. The Action is called but the partial view does not be rendered.
View
#section script{
<script type="text/javascript">
function test() {
$.ajax(
{
type: "POST",
url: "Test",
data: "",
success: function (result) { alert("OK!!"); },
error: function (req, status, error) {
alert("Damn!!");}
});
}
</script>
}
<input type="submit" onclick="test()" />
<div id="Container">
Controller
public ActionResult Test()
{
if (Request.IsAjaxRequest())
{
return PartialView("ViewUserControl1");
}
return View();
}
Partial View ViewUserControl1
Hello world
This works but is not what I want to do
#Ajax.ActionLink("click me", "Test", new AjaxOptions { UpdateTargetId = "Container" })
Your url may not be correct. It dependent on your routing settings
defined in Global.asax . But usually your url should look like
"/Home/Test" if your action is in HomeController. It's better to use
url helper for getting actions' urls:
...
url: '#Url.Action("Test", "Home")'
...
You can render your partial in next way:
...
success: function (result) { $('#elementId').html(result) },
...
Also if you want to update block, don't forget to clear it first and if it contains form with unobtrusive validation - parse form.

Categories

Resources