While working on a new ASP.NET MVC project, I had a simple objective: add a new record and refresh my View. After sifting through several sites I found several resources that lead me to accomplish my goal.
I’ve compiled my result into a new sample MVC Application that I created in Visual Studio that you can download here. I’ll explain what I did using that as my reference. It’s a trimmed down version of what I did on the actual project, but it will get the point across.
Let’s assume we want to have a View that lists some People from our data source. I started out by creating a Person data model:
public class Person { public Guid Id { get; set; } public String FirstName { get; set; } public String LastName { get; set; } public Person() { Id = Guid.NewGuid(); } }
Next, I created some ViewModels so that I can work with strongly typed Views:
public class PersonIndexViewModel { public PersonListViewModel PersonListModel { get; set; } public AddPersonViewModel AddPersonModel { get; set; } } public class PersonListViewModel { public List PersonList { get; set; } } public class AddPersonViewModel { public String FirstName { get; set; } public String LastName { get; set; } }
Next, I added a People folder in my Views folder and created a strongly typed Index View on my PersonIndexViewModel. I started out with building a table and doing a foreach to display each Person object. I moved that into a PartialView (.ascx) by creating a ParitialView in my Views/Shared folder (This blog post is very helpful for showing you how to use PartialViews). I called that PersonList.ascx and that is a strongly typed partial view on my PersonListViewModel.
Now, I can update my View to display that PartialView with this code:
<% Html.RenderPartial("PersonList", Model.PersonListModel); %>
Next, I want to be able to perform a delete action to remove a Person from the DB. You’ll notice I’m using an Ajax.ActionLink in my PersonList PartialView so that I can perform the delete with Ajax.
<%= Ajax.ActionLink("delete", "JsonDelete", "People", new { Id = person.Id }, new AjaxOptions { Confirm = "Are you sure you want to Delete this Person? This action cannot be undone.", HttpMethod = "Delete", OnComplete = "JsonDelete_OnComplete" })%>
In the ActionLink, I specify the Action I want to call, pass the Person.Id and in the AjaxOptions I defined a JavaScript method that should be called on complete. In my People Controller I can now add the JsonDelete method:
[AcceptVerbs(HttpVerbs.Delete)] public JsonResult JsonDelete(Guid Id) { // call your Repository to delete the Person bool result = _personList.Remove(toDelete); // return a Json Object, you could define a new class return Json(new { Success = result, Message = result ? "The person has been deleted!" : "Error!" }); }
You would call your Repository to delete that Person and then return a new Json Object. What I did was define a couple of properties that I will reference from the JavaScript function to give feedback to the user. Here is the JavaScript function:
function JsonDelete_OnComplete(context) { var JsonDelete = context.get_response().get_object(); if (JsonDelete.Success) { $(this).parents('tr.item').remove(); } $("#message").html(JsonDelete.Message); }
I found this link that showed me how to use “context.get_response().get_object();” to get the Json Object in JavaScript.
Now that I can delete, the next logical step would be the ability to add a new Person. I’ll start out by creating a new form that uses my AddPersonViewModel Model:
<% using (Ajax.BeginForm("JsonAdd", "People", new AjaxOptions { OnComplete = "JsonAdd_OnComplete" })) {%> <fieldset> <legend>Add a Person</legend> <%= Html.LabelFor(model => model.AddPersonModel.FirstName)%>: <%= Html.TextBoxFor(model => model.AddPersonModel.FirstName, new { @class = "firstname" })%> <%= Html.ValidationMessageFor(model => model.AddPersonModel.FirstName)%> <%= Html.LabelFor(model => model.AddPersonModel.LastName)%>: <%= Html.TextBoxFor(model => model.AddPersonModel.LastName, new { @class = "lastname" })%> <%= Html.ValidationMessageFor(model => model.AddPersonModel.LastName)%> <input id="AddBtn" name="AddBtn" type="submit" value="Add" /> </fieldset> <% } %>
Again, I use the Ajax.BeginForm to set the Action to call and define a JavaScript function to call on complete. To my Controller I add:
public JsonResult JsonAdd(AddPersonViewModel AddPersonModel) { ... Person newPerson = new Person { FirstName = AddPersonModel.FirstName, LastName = AddPersonModel.LastName }; // call your Repository to add the new Person _personList.Add(newPerson); // return a Json Object, you could define a new class return Json(new { Success = true, Message = "The person has been added!", PartialViewHtml = RenderPartialViewToString("PersonList", new PersonListViewModel {PersonList = _personList}) }); }
One important thing here is the method “RenderPartialViewToString”. I ran across this which was a tremendous resource in solving my problem here, which was returning a Json Object with a rendered PartialView so that I could use JavaScript/jQuery to update the page.
The post I referenced above showed where you needed to create a base Controller to inherit from and that Controller defines the methods which will return a PartialView as an HTML string:
public abstract class BaseController : Controller { protected string RenderPartialViewToString() { return RenderPartialViewToString(null, null); } protected string RenderPartialViewToString(string viewName) { return RenderPartialViewToString(viewName, null); } protected string RenderPartialViewToString(object model) { return RenderPartialViewToString(null, model); } protected string RenderPartialViewToString(string viewName, object model) { if (string.IsNullOrEmpty(viewName)) viewName = ControllerContext.RouteData.GetRequiredString("action"); ViewData.Model = model; using (StringWriter sw = new StringWriter()) { ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName); ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw); viewResult.View.Render(viewContext, sw); return sw.GetStringBuilder().ToString(); } } }
Now with my JavaScript function is called, I can again reference my Json Object and then update the page:
function JsonAdd_OnComplete(context) { var JsonAdd = context.get_response().get_object(); if (JsonAdd.Success) { $("#PersonList").html(JsonAdd.PartialViewHtml); } $("#message").html(JsonAdd.Message); }
With this line:
$("#PersonList").html(JsonAdd.PartialViewHtml);
I have a div tag that surrounds my Html.RenderPartial call and I can use jQuery to just replace the HTML. Remember JsonAdd.PartialViewHtml contains the entire HTML of the newly rendered PartialView that we called from the Controller:
return Json(new { Success = true, Message = "The person has been added!", PartialViewHtml = RenderPartialViewToString("PersonList", new PersonListViewModel {PersonList = _personList}) });
That just about sums it up how to use Ajax, jQuery, Json, and PartialViews in an effective manor in an ASP.NET MVC application.