In the last post, we successfully implemented a webpart framework that renders simple static Views and allows the user to change the layout.

However, one must wonder what needs to be done to make our web parts richer. At the moment, I’m not passing any data to these webparts so they simply render a string:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<p>THIS IS THE AUTHORS WEB PART</p>


If we want to display meaningful information we have to find a way to pass it down to our view. Usually, when we deal with “traditional” Partial Views, we can simply instruct our action methods to include that information as part of our Model or add it to the ViewData dictionary.

In this example, that would mean something like this:

public ActionResult Index()

{

    ViewData["books"] = BooksHelper.GetBooks();

    var webPartList = PersonalizationInfo.GetListOfWebParts();

    return View(webPartList);       

}


We also need to change the booksWebPart.ascx Partial View to render the information:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<table>

    <tr>

        <th>ISBN</th>

        <th>Name</th>

        <th>PublishDate</th>

    </tr>

    <% foreach (var item in ViewData["books"] as List<MyDashboard.Models.Book>)

       { %>

    <tr>

        <td><%= Html.Encode(item.ISBN) %></td>

        <td><%= Html.Encode(item.Name) %></td>

        <td><%= Html.Encode(String.Format("{0:g}", item.PublishDate)) %></td>

    </tr>

    <% } %>

</table>


And now, we’ll get this output:

webpart with ViewData information

We could now add information for the Authors List and Chapters List web parts but this is not really a good approach. Even though we didn’t add functionality to remove web parts in our framework, it just seems silly to send all information inside the Index action method. To be honest, as we develop the Index action method, we have no clue of the web parts the user has selected in his personalization info, so we can’t safely know that the information we’re injecting in ViewData is necessary, or if it’s enough. Plus, we would have to do this for all action methods that make use of our web part framework.

Small parenthesis here: if you read this quick tip, you saw me extending the base controller class in order to pass data to the masterpage. We could think of extending our web part framework model to include information about the query that needs to be done in each web part and then add some logic in our base controller class to iterate through the currently displayed web parts and add the required information to the ViewData. It’s ugly, but doable…and it’s still a better alternative than to blindly add information to the ViewData.

In this type of implementation, it’s much better to add some AJAX functionality to our Partial Views. One easy way to do this is using JQuery and adding an action method that can return JSON.

Let’s start by creating the action method:

public JsonResult BookList()

{

    var bookList = BooksHelper.GetBooks();

    return this.Json(bookList,JsonRequestBehavior.AllowGet);

}


Notice that instead of using ActionResult as the return type, we’re using JsonResult. The ASP.NET MVC allows us to easily translate data into this format as you can see from the code above.

The BooksHelper class is simply a static class I created (but this could come from a database just as easily):

public class Book

{

    public string ISBN { get; set; }

    public string Name { get; set; }

    public DateTime PublishDate { get; set; }

}

 

public static class BooksHelper

{

    private static List<Book> bookList;

 

    private static void InitializeBookList()

    {

        bookList = new List<Book>();

 

        Book b1 = new Book();

        b1.ISBN = "123";

        b1.Name = "ASP.NET MVC Quick Tips";

        b1.PublishDate = System.DateTime.Now;

        bookList.Add(b1);

 

        Book b2 = new Book();

        b2.ISBN = "456";

        b2.Name = "VS 2010 Team System Quick Tips";

        b2.PublishDate = System.DateTime.Now;

        bookList.Add(b2);

 

    }

 

    public static List<Book> GetBooks()

    {

        if (bookList == null)

        {

            InitializeBookList();

        }

        return bookList;

    }

 

}


So now we have to change our Partial View in order to call this action method and render the results. First, and because we’ll be using JQuery to make the asynchronous call, I’m going to add a script reference to my masterpage:

<head runat="server">

    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>

    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />

    <script src="/Scripts/jquery-1.3.2.js" type="text/javascript"></script>

</head>


Back on my booksWebPart Partial View, I will change it to this:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<script type="text/javascript">

 

    $.fn.addRows = function (data) {

        return this.each(function () {

            var table = $(this);

            $(table).find("tr:gt(0)").remove();

 

            $.each(data, function (index, itemData) {

 

                var tds = "<tr>";

                tds += "<td>" + itemData.ISBN + "</td>";

                tds += "<td>" + itemData.Name + "</td>";

                tds += "<td>" + itemData.PublishDate +"</td>";

                tds += "</tr>";

 

                $('tbody', table).append(tds);

            });

        });

    };

 

    $.getJSON("/Home/BookList", null, function (data) {

        $("#bookList").addRows(data);

    });

 

</script>

    

<table id="bookList">

    <th>ISBN</th>

    <th>Name</th>

    <th>Publish Date</th>

</table>

 

Let’s review this code carefully:

·        In the HTML world, I’m adding a simple table “booklist”, only specifying the header

·        The $.getJSON method will call my action method, retrieve the results as JSON data and will call the addRows function (using some JQuery magic to associate it with our table)

·        The addRows function will clear all rows except the first (the header) and then adds new rows to the table, using the JSON data we retrieved from the action method

We clear the rows to avoid duplication if the user refreshes the page. We can verify this is indeed working if we start the application:

webpart retrieving information through AJAX and JQuery

Notice, however, that the Publish Date field has an incorrect format. That is unfortunately a slight problem with JSON formatting, so we have to add some scripting magic to turn this into a readable date.

Here is the booksWebPart Partial View after applying these changes:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<script src="/Scripts/jquery.dateFormat-1.0.js" type="text/javascript"></script>

<script type="text/javascript">

 

    function formatDate(date, format) {

        var value = new Date(parseInt(date.replace("/Date(", "").replace(")/", ""), 10));

        return $.format.date(value.toString(), format);

    }

 

    $.fn.addRows = function (data) {

        return this.each(function () {

            var table = $(this);

            $(table).find("tr:gt(0)").remove();

 

            $.each(data, function (index, itemData) {

 

                var tds = "<tr>";

                tds += "<td>" + itemData.ISBN + "</td>";

                tds += "<td>" + itemData.Name + "</td>";

                tds += "<td>" + formatDate(itemData.PublishDate,"dd/MM/yyyy") +"</td>";

                tds += "</tr>";

 

                $('tbody', table).append(tds);

            });

        });

    };

 

    $.getJSON("/Home/BookList", null, function (data) {

        $("#bookList").addRows(data);

    });

 

</script>

   

<table id="bookList">

    <th>ISBN</th>

    <th>Name</th>

    <th>Publish Date</th>

</table>

 

Notice I am adding a JQuery plugin (jquery.dateFormat) to write the date in the format I want, but the work needed to convert the JSON result into an actual, readable date is done in the first line of the formatDate function.

Now, our output is much nicer:

webpart with correct date format

To sum it up, and placing it out of the context of a web part framework, there are 3 ways you can pass data to a partial view:

1.      Pass data in the second parameter of the Html.RenderPartial method (which we used in the first post to pass the WebPartInfo object to the WebPart Partial View. However, the data has to be present in the “main” view in the first place.

2.      Add it to the ViewData dictionary and have your Partial View retrieve the information from it

3.      Add some AJAX magic to your partial view and make it retrieve the information from the server directly

The JQuery code here might look a bit tricky if you are not used to it. With ASP.NET 4 AJAX coming out soon, we do have other alternatives. I’ll see if I can write a post using the new client-side binding templates of AJAX 4 which would simplify this code a lot.

Until then, have fun!