Monday, November 1, 2010

How to return HTML in JSON Response using ASP.NET and jQuery

During the development of my recent project (Delight), I ran into a challenge that normally I wouldn’t believe will take me any time more than what's required to code it straight up.

Because of the heavy use of ajax in the application, I wrote a generic jquery function to handle all ajax request triggered by a particular group of controls or elements, it was very useful and a time saver for me. In most cases, the JSON is returned in this format:

{Successful: "true", Message : "response message", Data  : "object or html"}

The interesting part of this response is the "Data" part, it can be of any type, simple or complex object type or (escaped) html.

What I’m going to show you in this post is how to return html content in json response type. The useful thing about this technique is that you get to write your html once in a partial view, which you can then render using full page or ajax request, you can also use the same ajax request with different unrelated url that return different types. This saves you from writing too many codes for different kind of request and reduces view duplication between server and client side.

To demonstrate the trick I explained above, I will be using a mocking framework (Moq) which you can download here and reference in your project. You will also need the jquery library which you can download here.


The screenshot





The working

Start by create a class and name it “Person”. Add the following code below.

public class Person
    {
        public int PersonId { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string JobTitle { get; set; }

        public string Company { get; set; }

        public string ProfilePic { get; set; }

        public List<Email> Emails { get; set; }

        public List<Phone> Phones { get; set; }

        public List<Address> Addresses { get; set; }
    }

    public class Email
    {
        public string Address { get; set; }

        public string Name { get; set; }
    }

    public class Phone
    {
        public string Number { get; set; }

        public string Name { get; set; }
    }

    public class Address
    {
        public string AddressLine { get; set; }

        public string City { get; set; }

        public string State { get; set; }

        public string Country { get; set; }

        public string Name { get; set; }
    }

Now let’s create a service class for our contacts. Add a new class name it "ContactService" and add the following code


public class ContactService
    {
        public List<Person> GetContacts()
        { 
//Normally we would have called the contacts list from a database, but this will do for
//demonstration purpose. 
List<Person> contacts = new List<Person>();

            contacts.Add(new Person
                             {
                                 PersonId = 1,
                                 FirstName = "Emeka",
                                 LastName = "Emego",
                                 JobTitle = "Web Developer",
                                 Company = "SymaCord",
                                 ProfilePic = "~/content/images/emeka-emego.jpg",
                                 Emails = new List<Email>
                                              {
                                                  new Email
                                                      {
                                                          Address = "symacord@gmail.com",
                                                          Name = "Personal"
                                                      },
                                                  new Email {Address = "info@symacord.com", Name = "Professional"}
                                              },
                                 Phones = new List<Phone>
                                              {
                                                  new Phone {Number = "(234)803 780 8732", Name = "Mobile"},
                                                  new Phone {Number = "(234)838 333 3333", Name = "Personal"}
                                              },
                                 Addresses = new List<Address>
                                                 {
                                                     new Address
                                                         {
                                                             Name = "Home",
                                                             AddressLine = "Plot 123 Nowhere",
                                                             City = "SomeCity",
                                                             Country = "Nigeria",
                                                             State = "Lagos"
                                                         }, 
                                                         new Address {
                                                             Name="Office",
                                                             AddressLine = "Second address 273",
                                                             City = "Ikeja",
                                                             Country = "Nigeria",
                                                             State = "Lagos" } }
                             });

            contacts.Add(new Person
                             {
                                 PersonId = 2,
                                 FirstName = "Chris",
                                 LastName = "Ikani",
                                 JobTitle = "COO",
                                 Company = "Santoch Lotto",
                                 ProfilePic = "~/content/images/no-photo.gif",
                                 Emails =
                                     new List<Email> {new Email {Address = "info@nowheremail.com", Name = "Personal"}},
                                 Phones = new List<Phone>
                                              {
                                                  new Phone {Number = "(234)138 333 3333", Name = "Personal"}
                                              },
                                 Addresses = new List<Address>
                                                 {
                                                     new Address
                                                         {
                                                             Name = "Home",
                                                             AddressLine = "Plot 123 Some place",
                                                             City = "City place",
                                                             Country = "Nigeria",
                                                             State = "Delta"
                                                         } }
                             });

            contacts.Add(new Person
            {
                PersonId = 3,
                FirstName = "Dave",
                LastName = "Obiuwevbi",
                JobTitle = "CEO",
                Company = "Santoch Lotto",
                ProfilePic = "~/content/images/no-photo.gif",
                Emails = new List<Email> { new Email { Address = "info@nowheremail.com", Name = "Personal" } },
                Phones = new List<Phone>(),
                Addresses = new List<Address>()
            });

            contacts.Add(new Person
            {
                PersonId = 4,
                FirstName = "Tunde",
                LastName = "Oduyoye",
                JobTitle = "DGM",
                Company = "Protea Hotel Leadway",
                ProfilePic = "~/content/images/no-photo.gif",
                Emails = new List<Email>(),
                Phones = new List<Phone>(),
                Addresses = new List<Address>()
            });

            contacts.Add(new Person
            {
                PersonId = 5,
                FirstName = "Biodu",
                LastName = "Bameke",
                JobTitle = "Building Contractor",
                Company = "Jobams Group",
                ProfilePic = "~/content/images/no-photo.gif",
                Emails = new List<Email>(),
                Phones=new List<Phone>(),
                Addresses= new List<Address>()
            });

            contacts.Add(new Person
            {
                PersonId = 6,
                FirstName = "Isaac",
                LastName = "Okwu",
                JobTitle = "President",
                Company = "Africa Farming Project",
                ProfilePic = "~/content/images/no-photo.gif",
                Emails = new List<Email>(),
                Phones = new List<Phone>(),
                Addresses = new List<Address>()
            });

            contacts.Add(new Person
            {
                PersonId = 7,
                FirstName = "Johnson",
                LastName = "Clark",
                JobTitle = "Graphic Designer",
                Company = "Design Vision Inc",
                ProfilePic = "~/content/images/no-photo.gif",
                Emails = new List<Email>(),
                Phones = new List<Phone>(),
                Addresses = new List<Address>()
            });

            return contacts.OrderBy(x=>x.FirstName).ToList();
        }

        public Person GetContact(int personId)
        {
            return GetContacts().Where(x => x.PersonId == personId).FirstOrDefault();
        }
    }

Here is the action method to get the list of contacts.

public ActionResult Contacts()
        {
            List<Person> contacts = _contactService.GetContacts();

            return View(contacts);
        }

In other to be able to return our view in a json response, we need to mock the request and pass our partial view as the return view. The code below demonstrates that. Add this action method in the same controller as above.

public ActionResult Contact(int id)
        {
            //Get the requested person object
            Person contact = _contactService.GetContact(id);

            try
            {
                var writer = new StringWriter();
                var moqContext = new Mock<HttpContextBase>();
                var response = new Mock<HttpResponseBase>();
                moqContext.Setup(ctx => ctx.Response).Returns(response.Object);
                response.Setup(res => res.Output).Returns(writer);

                var requestContext = ControllerContext.HttpContext;
                ControllerContext.HttpContext = moqContext.Object;

                var partialView = PartialView("PartialPersonDetails", contact);
                partialView.ExecuteResult(ControllerContext);
                var resultHtml = writer.ToString();

                ControllerContext.HttpContext = requestContext;

                return Json(new {Successful = true, Data = resultHtml},JsonRequestBehavior.AllowGet);

            }catch(Exception ex)
            {
                return Json(new { Successful = false, Message = ex.Message }, JsonRequestBehavior.AllowGet);
            }
        }

Now let’s look into our partial view. Create a partial view and name it "PartialPersonDetails".
Add the below code to the view.

<div style="background:#F3F3F3; padding:10px; overflow:auto;">
<img src="<%=Url.Content(Model.ProfilePic) %>" style="float:left; border:1px solid #cccccc;" />

<div style="margin-left:60px;">
<b><%=Model.FirstName+ " "+Model.LastName %></b><br />
<%=Model.JobTitle %> at <%=Model.Company %>
</div>
</div>

<div class="contact-detail">
<%if (Model.Addresses.Count > 0){%>
<fieldset>
<legend>Address</legend>

<%foreach (var x in Model.Addresses){%>
<dl>
<dt><%=x.Name %></dt>
<dd>
<%:Html.Encode(x.AddressLine) %> <%:Html.Encode(x.City) %><br />
<%=x.State %>, <%=x.Country %>.
</dd>
</dl>
<%} %>
</fieldset>
<%} %>

<%if (Model.Emails.Count > 0)
  {%>
<fieldset>
<legend>Email</legend>

<% foreach (var x in Model.Emails)
      {%>
<dl>
<dt><%=x.Name%></dt>
<dd><%:Html.Encode(x.Address)%>
</dd>
</dl>
<% }%>
</fieldset>
<% }%>

<%if (Model.Addresses.Count > 0)
  {%>
<fieldset class="last">
<legend>Phone</legend>

<%
      foreach (var x in Model.Phones)
      {%>
<dl>
<dt><%=x.Name%></dt>
<dd><%:Html.Encode(x.Number)%>
</dd>
</dl>
<%
      }%>
</fieldset>

<%
  }%>
</div>

Now create a new view called "Contacts" for the contacts' action method and add the code below

<h1>Contacts</h1>

<div class="contact-wrapper">
<ul class="contact-list">
<%foreach(var c in Model) {%>
<li>
<div>
<a href="<%=Url.Action("contact",new{id=c.PersonId}) %>"><%=c.FirstName + " " + c.LastName%></a>
<br />
<span><%=c.Company %></span>
</div>
</li>
<%} %>
</ul>

<div id="details">

</div>
</div>

Here is the ajax call to get and process the response. Add this to the "Contacts" view.

<script type="text/javascript">
    $(document).ready(function() {
        $('ul.contact-list li a').click(function() {
        var $this = $(this);
            
            $.ajax({
            url: $this.attr("href"),
            type: "GET",
                dataType: "json",
                error: function(xmlHttpRequest, textStatus, errorThrow) {
                    alert("Woops! " + textStatus);
                },
                success: function(result) {

                    if (result.Successful) {
                        $('#details').html(result.Data);
                    }
                    else {
                        alert(result.Message);
                    }
                }
            });
            return false;
        });
    });
</script>


Note that you need to download and reference the jquery library. Download jquery library here http://jquery.com/

That's all we need to get things to run properly. Note that I omitted the inclusion of css in this post to keep things in point. However, you can download the source code here to see this in action.


Somehow this example failed to work in .NET 3.5, but worked fine in 4.0. I hope you find this post useful.



0 comments:

Post a Comment