Toggle navigation

ASP.NET MVC: Using Ajax, Json and PartialViews

Josh Smith May 24, 2010

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.

  • http://twitter.com/danielrcarneiro Daniel

    First of all, awesome post =)

    Now for some personal ranting: Just spent the full week trying to implement something similar and when I finally reach the add part on Friday i found your post =P

    You could use the jQuery UI dialog to add a Person. a quick example:

    On the View that call the add Person View put the following script:

    $(“#dialog-form”).dialog({
    autoOpen: false,
    modal: true,
    title: ‘Add Person’,
    buttons: {
    Cancel: function() {
    $(this).dialog(‘close’);
    },
    ‘Ok’: function() {
    $(this).dialog(‘close’);
    $.ajax({
    type: “POST”,
    url: “/Person/JsonAdd”,
    data: “FirstName =” + $(‘#FirstName ‘).val() + “&LastName =” + $(‘#LastName ‘).val(),
    success: function(result) {
    $(“#contactlistform”).html(result.PartialViewHtml);
    }
    });
    }
    },
    });

    and place the AddPerson partial view inside a div with id=dialog-form.
    This should do the trick =)

  • Josh

    Daniel,

    In the actual project that I was working on, I did use jQuery dialog Modal to pop up and then add the person. I did the same for the “Edit” as well.

  • mazhar

    was a great help

  • Shawn

    How do you unit test the JsonAdd method? For me the RenderPartialViewToString method blows up. I’ve been trying to use mocks etc, to work around it, but so far no luck.

  • Josh

    Shawn,

    That’s a good question. Maybe you could ask the original poster at http://craftycodeblog.com/2010/05/15/asp-net-mvc-render-partial-view-to-string/.

    When you say it “blows up,” what does it do? Are you getting a particular exception? (are you using MVC 2?)

  • Bryan

    Hi Josh – great post. I downloaded the code and its exactly what I’m looking for. However, trying to incorporate into my project, I want to use an actual database to populate the Person list. How do I do that? What is the best way to do that. I see you use a List – however how would I do this using Linq? Sorry I’m pretty new to MVC and C#. I’ve tried many different ways Cast….ToList()….but I can’t seem to get it to work. I get messages such as “Cannot implicitly convert type ‘System.Collections.Generic.List’ to ‘System.Collections.Generic.List”, or Cannot implicitly convert type ‘System.Collections.Generic.List’ to ‘System.Collections.Generic.List’. Any help would be greatly appreciated.

    Thanks in advance.
    -Bryan

  • Demetrios

    First of all … great post, Josh! Thank you.

    Bryan, you have a lot of reading to do … when starting from scratch, asking novice, incremental questions will take too long (for everyone involved). There are many examples out there for MVC, AJAX, jQuery, etc. Granted, not quite as polished as this one! :)

    To (hopefully) answer your question, you need to define a Model. You can relatively quickly add a new model based on an existing database by using the wizard (right-click Models (folder), add New item, Data category, ADO.NET Entity Data Model). If that’s not an option, create your own model classes and look up how to create repositories (aka, your data layer).

    Using LINQ (whether against your ADO.NET model or your repository data), generate a data set like this:
    var results = from rec in (model name)
    where rec.ColumnName.Equals(someArgument)
    select rec;
    return Json(results.ToList());

    I hope this helps you along. Seriously, hit the books and the online sample code (both from MS and the community).

    DCC

  • Jason

    So as far as I know, I have pretty much followed your advice here and it seems that when the JsonResult is called to add the “Person”, in the result method the wrapped ViewModel’s field are all null, or default. So can you think of anything I have done wrong or some pitfalls that I may have fallen into? I cannot seem to hash out the issue here.

  • Pingback: Gibson Les Paul Supreme Reviews

  • Nicolas

    How can I add a new line in the confirmation message?

    Is it even possible?

    Thanks

  • Pingback: lexington Taxi

  • Mike

    Hi there.
    Great post:)
    I have a question:what if I need to show the grid after a select some parameter? I mean if there are some list of groups,when I select a group(by clicking on anchor) I would like to send a group_id to the action of controller and then the grid would fill by those people who belong to that specific group only.I want all this data to be on the same view,don’t want to go to another view.

  • http://www.yeorwned.com Yeorwned

    Great post. Thanks for taking the time to share it Josh.

  • Félix

    Hello there. I tried your solution and it didn’t work for me. Could you please tell me if there is any conflict between unobtrusive-ajax and jquery. Because it never reaches the json object returned to the view by Action.

    Uncaught TypeError: Object # has no method ‘get_response’
    fnCheckStatus
    a.extend.complete jquery.unobtrusive-ajax.min.js:5
    o jquery-1.7.2.min.js:2
    p.fireWith jquery-1.7.2.min.js:2
    w jquery-1.7.2.min.js:4
    d