Ajax call returning old ASP.NET MVC partial view instead of updated view - ajax

I have an Ajax call triggered by a button, that calls a controller to update a partial view. The controller returns an updated partial view, but the partial view received in the success function of the Ajax call is the original view, not the updated view.
I created a sample ASP.NET MVC program to reproduce the problem. The program displays a list of customers in a table as follows.
Snapshot of UI
The text boxes are rendered in the partial view _Status. When the Toggle Status button is clicked, controller is called via Ajax to toggle the customer's status between true and false, and refresh the partial view of the corresponding text box. The problem is that the status never changes. Why is that?
UPDATE
I just added the following line of code in the Status action of the controller, and now, the Ajax success function correctly receives the updated partial view!
this.ModelState.Clear();
Can someone explain why?
Here is the Index.cshtml view that displays the initial view.
#model IEnumerable<ComboModel.Models.CustomerSummary>
<script type="text/javascript">
function updatePartialView(id) {
debugger;
$.ajax({
url: "/CustomerSummary/Status",
data: $('#' + id + ' :input').serialize(),
dataType: "HTML",
type: "POST",
success: function (partialView) {
// Here the received partial view is not the one created in
// the controller, but the original view. Why is that?
debugger;
$('#' + id).replaceWith(partialView);
},
error: function (err) {
debugger;
},
failure: function (err) {
debugger;
}
});
}
</script>
<h2>Customer Summary</h2>
<table>
<tr>
<th>Name</th>
<th>Active?</th>
<th>Toggle</th>
</tr>
#foreach (var summary in Model)
{
<tr>
<td>#summary.FirstName #summary.LastName</td>
#Html.Partial("_Status", summary.Input)
<td><button type="button" name="#("S" + summary.Input.Number)" onclick="updatePartialView(this.name)">Toggle Status</button></td>
</tr>
}
</table>
The _Status.cshtml partial view.
#model ComboModel.Models.CustomerSummary.CustomerSummaryInput
<td id="#("S" + Model.Number)">
#Html.TextBoxFor(model => model.Active)
<input type="hidden" value="#Model.Number" name="Number" />
</td>
The CustomerSummaryController.cs.
using System.Collections.Generic;
using System.Web.Mvc;
using ComboModel.Models;
namespace ComboModel.Controllers
{
public class CustomerSummaryController : Controller
{
private readonly CustomerSummaries _customerSummaries = new CustomerSummaries();
public ViewResult Index()
{
IEnumerable<CustomerSummary> summaries = _customerSummaries.GetAll();
return View(summaries);
}
public PartialViewResult Status(CustomerSummary.CustomerSummaryInput input)
{
this.ModelState.Clear(); // If I add this, things work. Why?
input.Active = input.Active == "true" ? "false" : "true";
return PartialView("_Status", input);
}
}
public class CustomerSummaries
{
public IEnumerable<CustomerSummary> GetAll()
{
return new[]
{
new CustomerSummary
{
Input = new CustomerSummary.CustomerSummaryInput {Active = "true", Number = 0},
FirstName = "John",
LastName = "Smith"
},
new CustomerSummary
{
Input = new CustomerSummary.CustomerSummaryInput {Active = "false", Number = 1},
FirstName = "Susan",
LastName = "Power"
},
new CustomerSummary
{
Input = new CustomerSummary.CustomerSummaryInput {Active = "true", Number = 2},
FirstName = "Jim",
LastName = "Doe"
},
};
}
}
}
And finally, the CustomerSummary.cs model.
namespace ComboModel.Models
{
public class CustomerSummary
{
public string FirstName { get; set; }
public string LastName { get; set; }
public CustomerSummaryInput Input { get; set; }
public class CustomerSummaryInput
{
public int Number { get; set; }
public string Active { get; set; }
}
}
}
Thanks!

This is a duplicate of Asp.net MVC ModelState.Clear.
In the current scenario, because there is no validation of the status field, it is ok to clear the ModelState. However, the MVC correct way would be to pass a status value (true or false) to a GET action in the controller, toggle the value, return the result string, and update the text box on the page.

Related

Reload partial view via Ajax: controls in partial are renamed

I'm following this standard pattern for using Ajax to reload a partial view. However, when the partial view is reloaded, the controls in the view have different IDs. They lose the name of the parent model. This means that when the form is posted back, model binding won't work.
So in the example below, when the page is first loaded, the checkbox id is "PenguinEnclosure_IsEnoughPenguins" but after the partial is reloaded, the checkbox id is "IsEnoughPenguins" The ID must be "PenguinEnclosure_IsEnoughPenguins" for model binding to correctly bind this to the PenguinEnclosure property of the VM.
Model:
public class ZooViewModel
{
public string Name { get; set; }
public PenguinEnclosureVM PenguinEnclosure { get; set; }
}
public class PenguinEnclosureVM
{
public int PenguinCount { get; set; }
[Display(Name = "Is that enough penguins for you?")]
public bool IsEnoughPenguins { get; set; }
}
Controller:
public ActionResult Index()
{
var vm = new ZooViewModel
{
Name = "Chester Zoo",
PenguinEnclosure = new PenguinEnclosureVM { PenguinCount = 7 }
};
return View(vm);
}
public ActionResult UpdatePenguinEnclosure(int penguinFactor)
{
return PartialView("DisplayTemplates/PenguinEnclosureVM", new PenguinEnclosureVM { PenguinCount = penguinFactor * 10 });
}
View:
#model PartialProblem.Models.ZooViewModel
#Scripts.Render("~/bundles/jquery")
<p>
Welcome to #Model.Name !
</p>
<p>
<div id="penguin">
#Html.DisplayFor(m => m.PenguinEnclosure)
</div>
</p>
<button id="refresh">Refresh</button>
<script>
$(document).ready(function () {
$("#refresh").on("click", function () {
$.ajax({
url: "/Home/UpdatePenguinEnclosure",
type: "GET",
data: { penguinFactor: 42 }
})
.done(function (partialViewResult) {
$("#penguin").html(partialViewResult);
});
});
});
</script>
Partial View:
#model PartialProblem.Models.PenguinEnclosureVM
<p>
We have #Model.PenguinCount penguins
</p>
<p>
#Html.LabelFor(m => m.IsEnoughPenguins)
#Html.CheckBoxFor(m => m.IsEnoughPenguins)
</p>
An approach I have used is to set the "ViewData.TemplateInfo.HtmlFieldPrefix" property in the action that responds to your ajax call (UpdatePenguinEnclosure). This tells Razor to prefix your controls names and/or Ids.
You can choose whether to hard code the HtmlFieldPrefix, or pass it to the action in the ajax call. I tend to do the latter. For example, add a hidden input on the page with its value:
<input type="hidden" id="FieldPrefix" value="#ViewData.TemplateInfo.HtmlFieldPrefix" />
Access this in your ajax call:
$.ajax({
url: "/Home/UpdatePenguinEnclosure",
type: "GET",
data: { penguinFactor: 42, fieldPrefix: $("#FieldPrefix").val() }
})
Then in your action:
public ActionResult UpdatePenguinEnclosure(int penguinFactor, string fieldPrefix)
{
ViewData.TemplateInfo.HtmlFieldPrefix = fieldPrefix;
return PartialView("DisplayTemplates/PenguinEnclosureVM", new PenguinEnclosureVM { PenguinCount = penguinFactor * 10 });
}
Try this:
Controller:
public ActionResult UpdatePenguinEnclosure(int penguinFactor)
{
PenguinEnclosureVM pg = new PenguinEnclosureVM { PenguinCount = penguinFactor * 10 };
return PartialView("DisplayTemplates/UpdatePenguinEnclosure", new ZooViewModel { PenguinEnclosure = pg });
}
Your Partial:
#model PartialProblem.Models.ZooViewModel
<p>
We have #Model.PenguinEnclosure.PenguinCount penguins
</p>
<p>
#Html.LabelFor(m => m.PenguinEnclosure.IsEnoughPenguins)
#Html.CheckBoxFor(m => m.PenguinEnclosure.IsEnoughPenguins)
</p>
I Think this will do the trick

How to update View using Ajax in asp.net mvc?

I have a simple form with drop down list with people names(exhibitors). After I choose one of them and click link “Get Exhibitor data” I want to update only a part of my Home site and show data of chosen exhibitor.
Structure of my folders in project:
My home controller looks like follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace WebApplication5.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
public PartialViewResult GetExhibitorDataById(int? Id)
{
List<Exhibitor> exhibitors = new List<Exhibitor>()
{
new Exhibitor()
{
Id=1,
Name= "Tom",
Surname="Cruise"
},
new Exhibitor()
{
Id=2,
Name= "Jennifer",
Surname="Lopez"
},
};
if (Id == 1)
{
//return PartialView("_Exhibitor", exhibitors[0]);
Session["ExhibitorData"] = exhibitors[0];
return PartialView("_Exhibitor");
}
else if(Id==2)
{
//return PartialView("_Exhibitor", exhibitors[1]);
Session["ExhibitorData"] = exhibitors[1];
return PartialView("_Exhibitor");
}
else
{
//return PartialView("_Exhibitor", new Exhibitor());
Session["ExhibitorData"] = new Exhibitor();
return PartialView("_Exhibitor");
}
}
public class Exhibitor
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
}
}
My Index View code from Home folder looks like this:
#using WebApplication5.Controllers
<h2>Exhibitors</h2>
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
#Html.DropDownList("ExhibitorsList", new List<SelectListItem>
{
new SelectListItem {Text ="Tom Cruise", Value = "1" },
new SelectListItem {Text ="Jennifer Lopez", Value = "2" },
}, "Select Exhibitor" )
#Ajax.ActionLink("Get Exhibitor data", "GetExhibitorDataById", new { Id = 1 }, new AjaxOptions()
{
HttpMethod = "GET",
UpdateTargetId = "divExhibitors", // ID of the HTML element to update
InsertionMode = InsertionMode.Replace // Replace the existing contents
})
<div id="divExhibitors">
</div>
But I would like to set parameter Id of Ajax.ActionLink, to value from DropDownList called “ExhibitorsList” and I don’t know how to do that.
Partial View code “_Exhibitor” looks like this:
#using WebApplication5.Controllers
<table>
#if (Session["ExhibitorData"] != null)
{
<tr>
<td>Id</td>
#*#{HomeController.Exhibitor exhibitor = ((HomeController.Exhibitor)(#Session["ExhibitorData"]))};*#
#*<td>#exhibitor.Id</td>*#
<td>#((HomeController.Exhibitor)(#Session["ExhibitorData"])).Id</td>
</tr>
<tr>
<td>Name</td>
<td>#((HomeController.Exhibitor)(#Session["ExhibitorData"])).Name</td>
</tr>
<tr>
<td>surname</td>
<td>#((HomeController.Exhibitor)(#Session["ExhibitorData"])).Surname</td>
</tr>
}
</table>
When I tried to run my app and then click Aajax.ActionLink something gone wrong because after I clicked Ajax.ActionLink I go to different url.
Home View:
After Clicked Ajax.ActionLink
What I want is to choose person name from dropdownlist, then click Ajax.ActionLink and get data of chosen person(exhibitor) without any redirect or refresh the site as I wrote at the beginning. Im also curious if it possible to do that only with one View- "Index" without using partialview.
As Stephen Muecke said you should change your controller ina View to pass model and show it:
Controller:
public PartialViewResult GetExhibitorDataById(int? Id)
{
List<Exhibitor> exhibitors = new List<Exhibitor>()
{
new Exhibitor()
{
Id=1,
Name= "Tom",
Surname="Cruise"
},
new Exhibitor()
{
Id=2,
Name= "Jennifer",
Surname="Lopez"
},
};
//here how you pass model to View
ViewData.Model = exhibitors.FirstOrDefault(x => x.Id == Id);
return PartialView("_Exhibitor");
}
Your View will be:
#model WebApplication5.Controllers.Exhibitor
#using WebApplication5.Controllers
<table>
#if (Model != null)
{
<tr>
<td>Id</td>
<td>#Model.Id</td>
</tr>
<tr>
<td>Name</td>
<td>#Model.Name</td>
</tr>
<tr>
<td>surname</td>
<td>#Model.Surname</td>
</tr>
}
</table>
What i did is make your View strongly typed with #model WebApplication5.Controllers.Exhibitor and then use your model properties to show data.
Finally I found the solution. "The problem was due to the corrupted unobtrusive-ajax.min.js file." I found post asp.net mvc partialview #Ajax.ActionLink doesn't work and I had the same problem. I uninstalled unobtrusive-ajax.min.js and intalled it once again. It was good solution in my case and now ajax works properly.

Ajax call breaks binding to object children

Without the ajax call, My main view binds to my parent class and the partial view on main view, binds to a child member of the parent class
parent...
public class Client
{
[ScaffoldColumn(false)]
public int Id { get; set; }
[DisplayName("Name")]
[Required]
[StringLength(120)]
public string Name { get; set; }
// etc...
public virtual Address Address { get; set; }
}
child of parent...
public class Address
{
[ScaffoldColumn(false)]
public int AddressId { get; set; }
[DisplayName("Address")]
[Required]
[StringLength(200)]
public string Street { get; set; }
// etc...
[ForeignKey("Client")]
public int? Id { get; set; }
public virtual Client Client { get; set; }
}
the main view
#using (Html.BeginForm("Create", "Client", FormMethod.Post, new Dictionary<string, object> { { "data-htci-target", "addressData" } }))
{
#Html.AntiForgeryToken()
<div class="row">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-sm-4 col-md-4 col-lg-4">
#Html.Kendo().AutoCompleteFor(model => model.Name).HtmlAttributes(new { style = "width:100%" })
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
#{ var vdd = new ViewDataDictionary(ViewData) { TemplateInfo = new TemplateInfo() { HtmlFieldPrefix = "Address" } };}
#Html.Partial("_AddressPartial", Model.Address, #vdd)
// yada yada...you can imagine the rest of the very standard view
The partial view's model is Address and all hooks up.
When I post back to the server the Address member is properly filled with entered data from the partial view...
So now...in my partial view, I now load the js to call the async routine to load the IP GEO data for the user - so it pre-fills the city, province, country
Any example of an ajax call will suffice...mine calls an AddressControl, returns a partialview result and replaces a div named addressData with the updated partialview :
$(function() {
var urlGeoIeoip = "http://ip-api.com/json/?callback=?";
$.ajax({
url: urlGeoIeoip,
type: "GET",
dataType: "json",
timeout: 5000,
success: function (geoipdata) {
$.ajax({
url: "/getlocationdata/" + geoipdata.country + "/" + geoipdata.regionName + "/" + geoipdata.city,
type: "GET",
timeout: 5000,
success: function (data) {
//alert(data);
//var $form = $(this);
// var $target = $($form.attr("data-htci-target"));
var $newHtml = $(data);
//alert($target);
$("#addressData").replaceWith($newHtml);
$("#City").data("kendoComboBox").value(geoipdata.city);
$("#State").data("kendoComboBox").value(geoipdata.regionName);
$("#Country").data("kendoComboBox").value(geoipdata.country);
}
});
}
}).fail(function(xhr, status) {
if (status === "timeout") {
// log timeout here
}
});
});
All works great!
BUT
Now, when I post back to the user via the submit button, the Address child member of the parent class is null....
How do I get it to rebind the Address member of the parent class after return of the ajax call?
By generating your input fields in the partial view, the HTML helpers are unaware that your Address model is a property of your initial Client model, so it's generating HTML inputs like:
<input type="text" id="City" name="City" />
<input type="text" id="State" name="State" />
If your POST action method is accepting a Client model then the model binder will look for the properties City and State of the Client model, which don't exist.
You need your HTML input to look like:
<input type="text" id="Address_City" name="Address.City" />
<input type="text" id="Address_State" name="Address.State" />
Instead of using a partial for your Address fields, you should use an Editor Template which will then preserve the parent property as you need in this case.
#Html.EditorFor(x => x.Address)

Updating only the partial view contained in a mvc 3 page?

I have a MVC 3 page that returns a list of user responses with a partial view called "memo" (which displays/add memos) for each response. When I add a memo to a response, it should update the db and the list of memos for that response. It should be partial page update via ajax, which effects only the partial view "memo".
The view Response.chtml that contains "memo":
#using (Html.BeginForm("Response", "User", FormMethod.Post, new { id = "UserResponse" }))
{
.... code removed ....
#foreach (var response in Model)
{
<div class="qna"><input type="text" id=#response.responseId value="#response.ResponseText" />
<div>#Html.Partial("_memo", response.responseId)</div>
}
.....
The partial page "_memo.chtml":
<div>add memo</div>
<ul id="memos">
#foreach (var memo in Model) {
<li>#memo.Text</li>
}
</ul>
<form method="post" id="memoForm"
action="#Url.Action("AddMemo")">
#Html.TextArea("Memo", new { rows = 5, cols = 50 })
<br />
<input type="submit" value="Add" />
</form>
Controller for view User/Response:
[HttpGet]
public ActionResult Response(id)
{
.....
return View(responses);
I just started with the code above, need help filling the blanks.
If I pass the response Id to the partial view, how do I pull the list of memos for that response? Will it involve ajax? (instead of ..Partial("_memo", response.memos))
How do I update the partial view via ajax call. What is ajax call (sample code) on the client side and how would the controller look? When the ajax call is successful, how do I update the list memos div="memos" to reflect the new memo?
Will the form action from Response conflict with form action of the partial view Memo?
Answers to Questions:
You shouldn't pass the responseId to the partial, you should pass the memo collection from your response object and make your partial view strongly typed to that collection.
See full code example below.
You don't need the form in the partial since you're making a simple ajax call to add the new memo. See full code example below.
This is a modified example from a project I am currently working on:
There is a bit of code to follow, so here goes:
This is my model. There are several sections on a career planning form, one of which is a section to select and update competencies. The SelectCompetencies model has a collection of competencies within it. The user will have the ability to add competencies. When they do, it will be added to the database and will update the list of competencies in the partial.
public class CareerPlanningFormViewModel
{
// code removed ...
public SelectCompetenciesModel SelectCompetencies { get; set; }
// code removed ...
}
public class SelectCompetenciesModel
{
public int CareerPlanningFormID { get; set; }
public IList<CompetencyModel> Competencies { get; set; }
public byte MaximumCompetenciesAllowed { get; set; }
}
public class CompetencyModel
{
public int CompetencyID { get; set; }
public int? CompetencyOptionID { get; set; }
public string ActionPlan { get; set; }
public IDictionary<int, string> CompetencyOptions { get; set; }
}
The main view of the career planning form: /Views/CPF/CareerPlanningForm.cshtml
#model MyNamespace.Models.CareerPlanningForm.CareerPlanningFormViewModel
<link rel="stylesheet" href="#Url.Content("~/Content/CreateCPF.css")" />
#using (Html.BeginForm())
{
// other sections loaded here...
// code removed for brevity...
#Html.Partial("SelectCompetencies", Model.SelectCompetencies)
// other sections loaded here...
// code removed for brevity...
}
The SelectCompetencies partial: /Views/CPF/SelectCompetencies.cshtml
The user will fill in the new action plan text and click the add competency button.
That will post via ajax to CPFController/NewCompetencyTemplate
#model MyNamespace.Models.CareerPlanningForm.SelectCompetenciesModel
#Html.HiddenFor(m => m.CareerPlanningFormID)
<h3>Select Competencies</h3>
<p class="guidance">
Select up to #Model.MaximumCompetenciesAllowed competencies to focus on improving.
</p>
<table id="CompetenciesTable">
<thead>
<tr>
<th>Competency</th>
<th>Action Plan:</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Competencies.Count(); i++)
{
#Html.EditorFor(m => m.Competencies[i])
}
</tbody>
<tfoot id="CompetenciesTableFooter" class="#(Model.Competencies.Count() < Model.MaximumCompetenciesAllowed ? "" : "hidden")">
<tr>
<td colspan="2">
#Html.TextArea("NewActionPlanText")
#Html.Button(ButtonType.Button, "Add Another Competency", "add", new { id = "AddCompetencyButton" })
</td>
</tr>
</tfoot>
</table>
#section script
{
<script>
jQuery(document).ready(function ($) {
var competenciesTableBody = $('#CompetenciesTable tbody'),
competenciesTableFooter = $('#CompetenciesTableFooter'),
addCompetencyButton = $('#AddCompetencyButton'),
newCompetencyTemplateUrl = '#Url.Content("~/CPF/NewCompetencyTemplate")',
count = competenciesTableBody.find('tr').length,
newActionPlanText = $('#NewActionPlanText'),
careerPlanningFormID = $('#CareerPlanningFormID');
addCompetencyButton.click(function () {
$.ajax({
url: newCompetencyTemplateUrl(),
type: 'POST',
data: {
careerPlanningFormID: careerPlanningFormID,
actionPlan: newActionPlanText,
itemCount: count
},
dataType: 'html',
success: function (data) {
var elements = $(data);
// other code removed here...
competenciesTableBody.append(elements);
// other code removed here...
}
});
});
});
</script>
}
Views/CPF/EditorTemplates/CompetencyModel.cshtml
#model MyNamespace.Models.CareerPlanningForm.CompetencyModel
<tr class="competency">
<td>
#Html.DropDownListFor(m => m.CompetencyOptionID, new SelectList(Model.CompetencyOptions, "Key", "Value"), "Select competency...")
</td>
<td>
#Html.TextAreaFor(m => m.ActionPlan, new { #class = "competencyActionPlan" })
#Html.HiddenFor(m => m.CompetencyID)
</td>
</tr>
The controller containing the action to add the new competency: /Controllers/CPFController.cs
This will call the CareerPlanningFormService to add the new competency and will return a partial view for NewCompetencyTemplate that will render out the new competency
public class CPFController : Controller
{
private readonly ICareerPlanningFormService careerPlanningFormService;
public CPFController(ICareerPlanningFormService careerPlanningFormService)
{
this.careerPlanningFormService = careerPlanningFormService;
}
[HttpPost]
public PartialViewResult NewCompetencyTemplate(int careerPlanningFormID, int itemCount, string newActionPlanText)
{
var count = itemCount + 1;
// Even though we're only rendering a single item template, we use a list
// to trick MVC into generating fields with correctly indexed name attributes
// i.e. Competencies[1].ActionPlan
var model = new SelectCompetenciesModel
{
Competencies = Enumerable.Repeat<CompetencyModel>(null, count).ToList()
};
model.Competencies[count - 1] = this.careerPlanningFormService.BuildNewCompetencyModel(careerPlanningFormID, newActionPlanText);
return this.PartialView(model);
}
}
My service class: CareerPlanningFormService.cs
This handles the business logic and makes the calls to the repository to add the item to the database and returns a new CompetencyModel
public class CareerPlanningFormService : ICareerPlanningFormService
{
private readonly IMyRenamedRepository repository;
private readonly IPrincipal currentUser;
public CareerPlanningFormService(
IMyRenamedRepository repository,
IPrincipal currentUser)
{
this.repository = repository;
this.currentUser = currentUser;
}
public CompetencyModel BuildNewCompetencyModel(int careerPlanningFormID, string newActionPlanText)
{
var competency = new Competency
{
CareerPlanningFormID = careerPlanningFormID,
CompetencyOptionID = null,
ActionPlan = newActionPlanText
};
this.repository.Add(competency);
this.repository.Commit();
return new CompetencyModel
{
CompetencyID = competency.CompetencyID,
CompetencyOptionID = competency.CompetencyOptionID,
ActionPlan = competency.ActionPlan,
CompetencyOptions = this.GetCompetencyOptionsForCareerPlanningFormID(careerPlanningFormID)
};
}
}
Now, the partial for NewCompetencyTemplate: Views/CPF/NewCompetencyTemplate.cshtml
This is very simple, it simply renders the same editor template as above, for the last competency in the collection (which we just added)
#model MyNamespace.Models.CareerPlanningForm.SelectCompetenciesViewModel
#Html.EditorFor(m => m.Competencies[Model.Competencies.Count() - 1])
When the ajax call succeeds, it will receive this partial back from the controller action method it called. It then takes the partial and appends it to the competencies table body
// snippet from ajax call above
competenciesTableBody.append(elements);
I hope this helps. Let me know if you have any additional questions.
While you're correct that you can do it just by returning a partial view containing the updated content, you may also consider using jQuery's load method.
Look here, in particular at the "loading page fragments" section. Basically you can just get the original page again and jQuery will "extract" the content you want as long as it can be targetted by a selector (such as a div id).
Note, this solution is not suitable in all cases as there will be redundant markup in the response from the server because you will be discarding the rest of the page content and just using the updated part.

jquery client side validation only works at certain field (doesnt work at Required field)

I am implementing client side validation in mvc3.
I got my form showing via jquery dialog, and submit via ajax post
I am not sure is it necessary, but i created a partial class in my Model to customize the validation:
[MetadataType(typeof (FoodMetaData))]
public partial class FOOD
{
[Bind(Exclude="FoodID")]
public class FoodMetaData
{
[ScaffoldColumn(false)]
public object FoodID { get; set; }
[Required(AllowEmptyStrings = false, ErrorMessage = "Please enter a name")]
public object FoodName { get; set; }
[Range(1, 200, ErrorMessage = "Please enter a valid amount")]
public object FoodAmount { get; set; }
public object StorageDate { get; set; }
public object ExpiryDate { get; set; }
Currently I only get the validation shown at the amount field if i enter a string or a number out of the range. However, If i empty the Name field, nothing happen.
This is my first try on client side validation and got no idea what is happening. Can anyone please give me some advice??
Appreciate any help, thanks...
Here's an example of how you could implement a partial form with jQuery dialog.
As always start with a view model:
public class MyViewModel
{
[Required]
public string SomeProperty { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Edit()
{
return PartialView(new MyViewModel());
}
[HttpPost]
public ActionResult Edit(MyViewModel model)
{
if (!ModelState.IsValid)
{
return PartialView(model);
}
// TODO: validation passed => process the model and return a JSON success
return Json(true);
}
}
and then a ~/Views/Home/Index.cshtml view which will contain only a link to the dialog:
#Html.ActionLink("click me for dialog", "edit", null, new { id = "showDialog" })
<div id="dialog"></div>
and a ~/Views/Home/Edit.cstml partial which will contain the form that we want to be shown in the dialog:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.SomeProperty)
#Html.EditorFor(x => x.SomeProperty)
#Html.ValidationMessageFor(x => x.SomeProperty)
<button type="submit">Save</button>
}
All that is left now is to wire up. So we import the necessary scripts:
<script src="#Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
and then write our own to make the dialog live:
$(function () {
$('#showDialog').click(function () {
$('#dialog').dialog().load(this.href, function (result) {
ajaxify(this);
});
return false;
});
});
function ajaxify(dialog) {
// we need to parse client validation rules
// because the form was injected into the DOM later as
// part of the dialog. It was not present initially
// See here for more info: http://weblogs.asp.net/imranbaloch/archive/2011/03/05/unobtrusive-client-side-validation-with-dynamic-contents-in-asp-net-mvc.aspx
$.validator.unobtrusive.parse($(dialog));
// AJAXify the form
$('form', dialog).submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
if (result === true) {
// The controller action returned a JSON result
// inidicating the success
alert('thank you for submitting');
$(dialog).dialog('close');
} else {
// there was a validation error => we refresh the dialog
// and reajaxify it as we have now modified the DOM
dialog.html(result);
ajaxify(dialog);
}
}
});
return false;
});
}
Now you could adapt this to any view model you want with any editor templates and validation rules.
I just found out that jquery client side validation is only triggered after 1st form submission after i gone through the example here: http://weblogs.asp.net/imranbaloch/archive/2011/04/30/eagerly-performing-asp-net-mvc-3-unobtrusive-client-side-validation.aspx
A great one! It helps to solve my weird problem by editing the jquery.validate.unobtrusive(.min).js file by this:
options: { // options structure passed to jQuery Validate's validate() method
errorClass: "input-validation-error",
errorElement: "span",
errorPlacement: $.proxy(onError, form),
invalidHandler: $.proxy(onErrors, form),
messages: {},
rules: {},
success: $.proxy(onSuccess, form),
onfocusout: function (element) { $(element).valid(); }
}
Thanks for every help!

Resources