How to update DIV in _Layout.cshtml with message after Ajax form is submitted? - ajax

Currently I have a Razor View like this:
TotalPaymentsByMonthYear.cshtml
#model MyApp.Web.ViewModels.MyViewModel
#using (#Ajax.BeginForm("TotalPaymentsByMonthYear",
new { reportName = "CreateTotalPaymentsByMonthYearChart" },
new AjaxOptions { UpdateTargetId = "chartimage"}))
{
<div class="report">
// MyViewModel fields and validation messages...
<input type="submit" value="Generate" />
</div>
}
<div id="chartimage">
#Html.Partial("ValidationSummary")
</div>
I then display a PartialView that has a #Html.ValidationSummary() in case of validation errors.
ReportController.cs
public PartialViewResult TotalPaymentsByMonthYear(MyViewModel model,
string reportName)
{
if (!ModelState.IsValid)
{
return PartialView("ValidationSummary", model);
}
model.ReportName = reportName;
return PartialView("Chart", model);
}
What I'd like to do is: instead of displaying validation errors within this PartialView, I'm looking for a way of sending this validation error message to a DIV element that I have defined within the _Layout.cshtml file.
_Layout.cshtml
<div id="message">
</div>
#RenderBody()
I'd like to fill the content of this DIV asynchronously. Is this possible? How can I do that?

Personally I would throw Ajax.* helpers away and do it like this:
#model MyApp.Web.ViewModels.MyViewModel
<div id="message"></div>
#using (Html.BeginForm("TotalPaymentsByMonthYear", new { reportName = "CreateTotalPaymentsByMonthYearChart" }))
{
...
}
<div id="chartimage">
#Html.Partial("ValidationSummary")
</div>
Then I would use a custom HTTP response header to indicate that an error occurred:
public ActionResult TotalPaymentsByMonthYear(
MyViewModel model,
string reportName
)
{
if (!ModelState.IsValid)
{
Response.AppendHeader("error", "true");
return PartialView("ValidationSummary", model);
}
model.ReportName = reportName;
return PartialView("Chart", model);
}
and finally in a separate javascript file I would unobtrusively AJAXify this form and in the success callback based on the presence of this custom HTTP header I would inject the result in one part or another:
$('form').submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result, textStatus, jqXHR) {
var error = jqXHR.getResponseHeader('error');
if (error != null) {
$('#message').html(result);
} else {
$('#chartimage').html(result);
}
}
});
return false;
});

Related

MVC Core ajax and return result is a view

MVC Core, NET 5, not razor pages.
On a view I have three select components (bootstrap-select). I populate them via ViewModel.
"Get request -> Controller -> return View(viewModel);"
What I want...
When I changed value in any select component I do a post request (ajax) to the same controller (other method) and return view with repopulated data.
"'Post request -> Controller -> return View(changedModel);"
As I understood when I did ajax request I should handle it result in success and other cases.
What I should to do to reload page with new data?
Is it possible to achive this with this approach?
Yes, this is possible and you do not need to reload the page, just append the returned html to wherever you want it.
$.ajax({
type: "POST",
url: {your_url},
dataType: "html",
success: function (html) {
$("#someDiv").html(html);
}
});
What I should to do to reload page with new data?
If the post action return the same view as the get action and you want to reload the whole page, I think there is no need to use ajax. You can just redirect to post action with a form submission. If the view returned by the post action is a partialview you want render to the current view, you can use it like that in #cwalvoort answer.
Based on advices of cwalvoort and mj1313
I did:
Render main page with partials. ViewModel transfered to a partial as a parameter
On main page I added eventListners to controls with JS.
When control changes - ajax request to backend happens Controller/GetPartialView
Result from ajax replace html in partial section
Programmatically show needed components, re-add eventListners
PS Really need to learn Blazor or UI Framework :)
Code samples:
// JS
document.addEventListener("DOMContentLoaded", function (event) {
BindSelectActions();
});
function BindSelectActions() {
$('#selectGroups').on('hidden.bs.select', DoPartialUpdate);
$('#selectCompanies').on('hidden.bs.select', DoPartialUpdate);
$('#selectPeriods').on('hidden.bs.select', DoPartialUpdate);
}
function DoPartialUpdate(e, clickedIndex, isSelected, previousValue) {
// ToDo: Implement common script with "CallBackend" function
$.ajax({
type: "POST",
url: 'https://localhost:44352/TestController/TestGetPartial',
// no data its a stub at the moment
// data: $('#form').serialize(),
success: function (data, textStatus) {
$("#testControls").html(data);
$('#selectGroups').selectpicker('show');
$('#selectCompanies').selectpicker('show');
$('#selectPeriods').selectpicker('show');
BindSelectActions();
}
});
}
// Controllers
[HttpGet]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public async Task<IActionResult> Main()
{
// ViewModel = _helper -> _mediator -> query -> context
return await Task.Run(() => View(new TestViewModel()));
}
[HttpPost]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public IActionResult TestGetPartial(TestViewModel model)
{
// ViewModel = _helper -> _mediator -> query -> context
var result = new TestViewModel();
result.IsPageReload = "yes";
result.TestCollection = new string[] { "A", "B", "C" };
result.Companies = new List<SelectListItem> { new SelectListItem { Value = "999",
Text = "Test" } };
// ModelState.Clear();
return PartialView("_TestPartial", result);
}
// Main and partial views
#model TestViewModel
#{
ViewData["Title"] = "Test";
}
<div id="testControls">
#await Html.PartialAsync("_TestPartial", Model)
</div>
#section Scripts {
<script type="text/javascript" src="~/js/test.js" asp-append-version="true">
</script>
}
#model TestViewModel
<form>
<div class="d-flex flex-row justify-content-between mt-4">
<div><select id="selectGroups" asp-for="Groups" asp-items="Model.Groups"
class="selectpicker" data-live-search="true" data-style="btn-outline-dark"
title="Group"></select></div>
<div><select id="selectCompanies" asp-for="Companies" asp-items="Model.Companies"
class="selectpicker" data-live-search="true" data-style="btn-outline-dark"
title="Company"></select></div>
<div><select id="selectPeriods" asp-for="Periods" asp-items="Model.Periods"
class="selectpicker" data-live-search="true" data-style="btn-outline-dark"
title="Period"></select></div>
<div><button type="button" class="btn btn-outline-dark">Import</button></div>
</div>
</form>
<div>
#{
if (null != Model.TestCollection)
{
foreach (var item in Model.TestCollection)
{
<p>#item</p>
<br>
}
}
}
</div>

how to pass a model to view in ajax-based request in mvc4

I'm creating an ajax-based Quiz in MVC. Below is the Question view. When the form is submitted I save the user selection in the controller then need to send the next question to the view without reloading the page. Is it possible to send/update the model from the controller in the ajax request
#model DataAccess.Question
#{
ViewBag.Title = "Survey";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Ajax.BeginForm("Survey", "Tools", new AjaxOptions { UpdateTargetId = "QuestionContent", HttpMethod = "Post", InsertionMode = InsertionMode.Replace }, new { QuestionId = Model.QuestionId }))
{
<div id="QuestionContent">
<h2>Welcome To Quiz</h2>
<fieldset>
<p>
Question
#Model.QuestionId of #ViewBag.QuestionCount:
</p>
<p>
#Model.Description.
</p>
<ul style="list-style:none;">
#foreach (var item in Model.Answers)
{
<li> #Html.RadioButton("ChoiceList", item.score) #item.AnswerDesc</li>
}
</ul>
<input type="submit" value="Next" id="submitButton" />
</fieldset>
</div>
}
It's much easier to implement an AJAX POST using jQuery and return a JSON object that contains all the next Q&A info. Use js/jQuery to set <div>'s or any other html element. Posting back and reloading is such a pain and becoming an outdated approach.
For example you could have this ViewModel class:
public class Answer
{
public int QuestionId {get; set;}
public string Answer {get; set;}
}
Build a view that has a div & input control for the Q & A.
Implement the Answer Button to POST via AJAX:
$.ajax({
type: "POST",
url: "/Exam/HandleAnswer" ,
data: { QuestionId: _questionId, Answer: $("#txt_answer").val() },
success: function (resp) {
if (resp.Success) {
$("#div_Question").text( resp.NextQuestionMessage);
_questionId = resp.NextQuestionId,
$("#txt_answer").val(''"); //clear
}
else {
alert(resp.Message);
}
}
});
In your ExamController:
[HttpPost]
public ActionResult HandleAnswer(Answer qa)
{
//use qa.QuestionId to load the question from DB...
//compare the qa.Answer to what the DB says...
//if good answer get next Question and send as JSON or send failure message..
if (goodAnswer)
{
return Json(new { Success = true, NextQuestionMessage = "What is the capital of of Texas", NextQuestionId = 123});
}
else{
return Json(new { Success = false, Message = "Invalid response.."});
}
}

Returning ValidationSummary in Partial View to Ajax error handler - nothing else getting rendered

I'm calling a method from Ajax that updates a some content in the page by loading up a partial view & setting a contained property appropriately.
This all works ok but I'm having problems with server side validation using the ValidationSummary control. I'm using an approach suggested elsewhere to stick the ValidationSummary in a partial view. (I've actually got client side validation turned off & I'm trying to get server side validation to work using the same validation summary that client side validation uses)
When there is a validation error, ajax error handler should update the contents of a div with the partial view that is returned.
All this works ok in the sense that the validation summary is rendered with the expected error messages except for the fact that the nothing else is getting displayed on the page other than the partial view containing the validation summary and its containing elements. i.e. no siblings / of the div are getting rendered
I reaslly haven't got a clue what's going on. Intersetingly, if I comment out the line in the error handler that updates the div with the partial, I see the form in its entirety again.
I'm using an ExceptionFilter:
public class ValidationErrorAttribute : FilterAttribute, IExceptionFilter
{
public virtual void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.Exception != null)
{
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode =
(int)System.Net.HttpStatusCode.BadRequest;
//return the partial view containing the validationsummary and set the ViewData
//so that it displays the validation error messages
filterContext.Result = new PartialViewResult { ViewName = "validation",
ViewData = filterContext.Controller.ViewData };
}
}
}
when there's a validation error, throw a validationexception which will
trigger the OnException method of the filter
//Controller
[HttpPost]
[ValidationErrorAttribute]
public ActionResult Save(ConsumptionData consumptionData)
{
if (ModelState.IsValid)
{
var dto = Mapper.Map<ConsumptionData, ConsumptionDataDto>(consumptionData);
var refId = _personalProjectionRepository.AddPersonalProjectionRecord(dto);
consumptionData.ReferenceId = refId;
return PartialView("Reference", consumptionData);
}
else
{
throw new ValidationException();
}
}
The validation partial view:
#model PersonalProjection.Web.Models.ConsumptionData
#Html.ValidationSummary()
and the jquery:
$('form').submit(function () {
var form = $(this);
if (form.valid())
{
$.ajax({
url: form.attr("action"),
type: "POST",
dataType: "html",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({
SiteId: $('#siteid').val(),
ElectricityConsumption: $('#electricityconsumption').val(),
ElectricitySpend: $('#electricityspend').val(),
GasConsumption: $('#gasconsumption').val(),
GasSpend: $('#gasspend').val()
}),
success: function (result) {
$('#rightPanelSection').html(result);
},
error: function (jqXHR) {
$('#validationSummary').html(jqXHR.responseText);
}
});
}
return false;
});
and the index.cshtml markup
<div class="panel panel-outcome">
#using (Html.BeginForm("Save", "Home", FormMethod.Post, (object)new { ID =
"projectionForm" }))
{
<div id="validationSummary"/>
<div id="topPanelSection" class="panel-section">
#Html.LabelFor(m => m.SiteId, new { id = "siteidlabel" })
#Html.TextBoxFor(m => m.SiteId, ViewBag.TextBoxDisabled ? (object)new { id =
"siteid", disabled = true, maxlength = 9, #class = "input-disabled", #Value =
ViewBag.SiteId } : new { id = "siteid", maxlength = 9 })
<span id="errorText" class="error-text" />
</div>
<div id="leftPanelSection" class="panel-section">
<div class="column-heading">
<span class="column-heading-left">Total Cost</span>
<span class="column-heading-right">Total Consumption</span>
</div>
<div class="panel-section-row">
#Html.LabelFor(m => m.ElectricitySpend, new { id = "electricitylabel" })
<span>£</span>
#Html.TextBoxFor(m => m.ElectricitySpend, new { #class = "textbox-right-
margin", id = "electricityspend" })
#Html.TextBoxFor(m => m.ElectricityConsumption, new { #class = "textbox-
left-margin", id = "electricityconsumption" })
<span>kWhs</span>
</div>
<div class="panel-section-row">
#Html.LabelFor(m => m.GasSpend, new { id = "gaslabel" })
<span>£</span>
#Html.TextBoxFor(m => m.GasSpend, new { #class = "textbox-right-margin", id =
"gasspend" })
#Html.TextBoxFor(m => m.GasConsumption, new { #class = "textbox-left-margin",
id = "gasconsumption" })
<span>kWhs</span>
</div>
<div class="panel-section-row">
<button type="reset" id="resetbutton">Reset Form</button>
<button type="submit">Generate Reference Id</button>
</div>
</div>
<div id="rightPanelSection" class="panel-section">
#Html.Partial("Reference", Model)
</div>
}
I'm fairly new to Ajax so this is probably a schoolboy error but here goes:
It's because when I use this jquery
$('#validationSummary').html(jqXHR.responseText);
To update this
<div id="validationSummary"/>
it doesn't like the closing tag & it messes up
Change to this
<div id="validationSummary">
</div>
And it works ok.

MVC Knockout validation display and

I am using knockout for the first time and I am struggling to get my head around a problem.
I have a page with multiple sections and want to be able to edit a section and submit to the controller, then display the saved details.
Each section is a partial view which contains the display information and the form. They are shown and hidden as required. I have the code working for submitting, but the problem is when the ModelState is not valid. I need to return to the form with the validation message displayed
How can I display the form again when the server validation fails? When the validation fails it currently goes back to the display section.
Also I have noticed the validation message does not display.
I am sure this must be a common problem with a simple fix. I know there are knockout validation tools, but will need to do more complex business logic validation later on and need to get the technique working.
ViewModel:
[Required]
public DateTime? InterviewDate { get; set; }
View:
<div data-bind="if: showAdminInterviewDisplay" id="Display">
<div>
<button data-bind="click: showInterviewForm" id="EditButton">Edit</button>
</div>
<div>
#Html.Label("Inteview Date") :
<label data-bind="text: interviewDate"></label>
</div>
</div>
<div data-bind="if: showAdminInterviewForm" id="Form">
<div>
#Html.Label("Interview Date")
<input data-bind="value: interviewDate" id="interviewDatePicker" />
#Html.ValidationMessageFor(m => m.InterviewDate)
</div>
<div>
<button data-bind="click: saveInterviewDate">Submit</button>
</div>
Knockout ViewModel:
function InterviewViewModel() {
//Data
var self = this;
var jsonDate = #Html.Raw(Json.Encode(#Model.InterviewDate));
var date = new Date(parseInt(jsonDate.substr(6)));
self.interviewDate = ko.observable(dateFormat(date, "dd/mm/yyyy"));
self.showAdminInterviewDisplay = ko.observable(true);
self.showAdminInterviewForm = ko.observable();
self.showInterviewForm = function () {
self.showAdminInterviewDisplay(false);
self.showAdminInterviewForm(true);
$("#interviewDatePicker").datepicker({dateFormat: 'dd/mm/yy'});
};
//Operations
self.saveInterviewDate = function() {
$.ajax("#Url.Action("SaveInterview")", {
data: ko.toJSON(self),
type: "post",
contentType: "application/json",
success: function(data) {
self.showAdminInterviewDisplay(true);
self.showAdminInterviewForm(false);
}
});
};
};
ko.applyBindings(new InterviewViewModel());
Controller:
public ActionResult SaveInterview(KnockoutViewModel model)
{
if (ModelState.IsValid)
{
return Json(model);
}
return PartialView("_AdminInterview", model);
}
Instead of returning a Partial View from your Action Method, return a serialised error model to the success function in the AJAX call. The error model will contain all the errors in the ModelState.
See this post on how to get and consume the errors from Model State:
ASP.NET MVC How to convert ModelState errors to json (JK's answer)
So you would have something like:
Error Model:
public class JsonErrorModel
{
public JsonErrorModel()
{
HasFailed = true;
}
public bool HasFailed { get; set; }
public IEnumerable ErrorList { get; set; }
}
Controller:
if(ModelState.IsValid)
{
//Do whatever here
return Json(new { model });
}
return Json(new JsonErrorModel {ErrorList = ModelState.Errors()});
Success function of AJAX call:
success: function (result) {
if(result.HasFailed) {
self.showAdminInterviewDisplay(false);
self.showAdminInterviewForm(true);
DisplayErrors(result.ErrorList);
}
else {
self.showAdminInterviewDisplay(true);
self.showAdminInterviewForm(false);
}
}
So now, if the server side validation failed, the view will show the form and the validation errors.

Jquery Accordion Validation Not working when rendering Partial View through $.ajax call

Hi friends,I am working on MVC 4 Razor and I am stuck in a situation
where Employee Personal Details form is to be filled in
steps(wizard)..for which i used jquery accordion control..for every
step i put an accordion..The html in each accordion section is
rendered from partial view through ajax call on every click of
respective accordion (i.e. <h3></h3> tag)..
On page load first/top accordion is active by default. My problem is
to restrict the user to click on next accordion until he/she fills the
presently active accordion correctly..
Here is my full code:
View:
#model XXX.ViewModels.PersonalDetailsViewModel
#{
ViewBag.Title = "PersonalDetails";
Layout = "~/Views/Shared/Template.cshtml";
}
#using (Html.BeginForm("Lifestyle", "Apply", FormMethod.Post, new { id = "personalDetailForm" }))
{
<div class="centerdiv margin_top20">
<div class="row">
#Html.ValidationSummary(true, "Please Correct the following errors:")
</div>
<div style="width: 1000px;">
<div id="Personalaccordion" class="acordion_div" style="padding: 10px; float: left;">
<h3 class="acordion_div_h3" onclick="javascript:PersonalModule.GetRenderingView('Apply/GetBasicDetailsView','personalDetailForm','BasicDetailsDiv');">
<p>
Basic Details<span id="BasicDetailsDivExp"></span>
</p>
</h3>
<div id="BasicDetailsDiv">
</div>
<h3 class="acordion_div_h3" onclick="javascript:PersonalModule.GetRenderingView('Apply/GetPersonalAddressView','personalDetailForm','PersonalAddressDiv');">
<p>
Address<span id="PersonalAddressDivExp"></span></p>
</h3>
<div id="PersonalAddressDiv">
</div>
</div>
<ul id="conlitue_ul" style="margin-top: 20px;">
<li style="margin-left: 140px;">
<input type="submit" class="compareBtn float_lt" value="Continue Buying >" id="continue" /></li>
</ul>
</div>
</div>
}
#Scripts.Render("~/bundles/PersonalDetails")
<script type="text/javascript">
PersonalModule.GetRenderingView('Apply/GetBasicDetailsView', '', 'BasicDetailsDiv');
</script>
My Controller:
public ActionResult PersonalDetails(int leadId)
{
var personalDetailsViewModel = LeadHelper.GetPersonalDetails(leadId);
return View(personalDetailsViewModel);
}
public ActionResult GetBasicDetailsView(PersonalDetailsViewModel personalDetailsViewModel)
{
if (personalDetailsViewModel.BasicDetails == null)
{
ModelInitializerHelper.InitilaizeBasicDetailsVModel(personalDetailsViewModel);
}
ModelInitializerHelper.InitializeBasicLookup(personalDetailsViewModel);
return PartialView("Personal/BasicDetails", personalDetailsViewModel);
}
public ActionResult GetPersonalAddressView(PersonalDetailsViewModel personalDetailsViewModel)
{
if (personalDetailsViewModel.PersonalAddressDetails == null)
{
ModelInitializerHelper.IntializePersonalAddressVModel(personalDetailsViewModel);
}
ModelInitializerHelper.InitializePersonalAddressLookup(personalDetailsViewModel);
return PartialView("Personal/PersonalAddress", personalDetailsViewModel);
}
My JS :
var PersonalModule = {
GetRenderingView: function (url, formId, containerID) {
var applicationurl = ApplicationRoot + '/' + url;
var objects = $('#BasicDetailsDivExp , #PersonalAddressDivExp' );
viewDivID = containerID;
GetAccordionView(applicationurl, formId, objects, containerID, 'accordion_plus', 'accordion_minus');
}
}
GetAccordionView: function (url, formId, objects, containerID, accordion_plus, accordion_minus) {
var formObjectData = null;
if (formId != undefined) {
formObjectData = $("#" + formId).serialize();
}
var renderView = function (data) {
$('#' + containerID).innerHtml = data;
}
ExpandAccordion(objects, containerID, accordion_plus, accordion_minus);
DoServerRequest(url, formObjectData, renderView);
}
ExpandAccordion: function (objects, spanIconID, accordion_plus, accordion_minus) {
var Objects = objects;
Objects.removeClass(accordion_minus);
Objects.addClass(accordion_plus);
$('#' + spanIconID + 'Exp').removeClass(accordion_plus).addClass(accordion_minus);
if (Browser.ie7) {
Objects.css("margin-top", "-22px");
}
}
DoServerRequest: function (url, data, funSuccess) {
$.ajax({
type: "POST",
url: url,
data: data,
async: false,
dataType: "json",
success: funSuccess,
error: function (errorResponse) {
if (errorResponse.readyState == 4 && errorResponse.status == 200) {
renderCurrentView(errorResponse.responseText)
}
else {
alert(errorResponse.responseText);
}
}
});
}
Please somebody help..I have heard lots of good thing about this forum
and this is my first Question...Thanks in advance..
I have removed my jquery validation attempt as it made the code
garbage thing Now I dont know what to write and where to write
If you are trying to validate data that has been added to form via Ajax after page load then you will need to use the rules method and add rules for these new elements. Jquery Validate has no way of knowing about them otherwise.
Example
Once you have loaded your new content via Ajax you need to find each element and add the necessary rules to them.
$('#yourDiv').find(".newElements").rules("add", {
required: true,
messages: {
required: "Bacon is required"
}
});
If you are using unobtrusive validate you may need to add your new elements to that also. See this SO question for more details.
Validating the Form
To check if the fields are valid, you will need to validate the form on click. This can be done using .validate(). You can then check if the form validated using .valid()
Example
$('#yourForm').validate();
if(!$('#yourForm').valid()) {
alert('Bacon is required');
}

Resources