Applying the MVVM pattern to create SharePoint list-driven interactive tools using Knockout

MVC, MVP, MVVM. They’re common acronyms that any developer worth their salt would have at least a vague understanding of what they mean and entail. I remember doing an MVC hands-on lab at Tech-Ed 2-3 years ago in ASP.NET for my own curiosity, see what all the fuss was about. Having been in the SharePoint realm for the past 3 and a half years though it’s something that I never thought would have had much application in my day to day job. Surely it was just left to the ASP.NET crowd, nothing to do with SharePoint. How wrong I was.

To be honest this article could go on forever with explanations and definitions, examples and possibilities. What I’ve since found when researching for this post however is that the information available on the net is actually pretty comprehensive. Unsure what the difference is between the various view-model design patterns? Take a look at Niraj Bhatt’s post on MVC vs. MVP vs. MVVM. In fact somewhat to my surprise there are even a number of quality SharePoint related posts in regards to implementing MVVM. The majority of them relate to MVVM in Silverlight hosted in SharePoint – you can read up on a great article series by Andrew Connell regarding Silverlight, MVVM & SharePoint. There’s a quality post by Shailen Sukul regarding Applying MVVM Principles to SharePoint 2010 Development. There’s even a post on Using MVVM with Office365 and SharePoint 2010 REST API by Sahil Malik.

Sahil’s post is somewhat similar to my own. The reason being, we’ve both implemented the MVVM pattern using Knockout – a library to ‘Simplify dynamic JavaScript UIs by applying the Model-View-View Model (MVVM) Pattern’. There is a difference however – where Sahil had the luxury of using the SharePoint 2010 REST APIs which go hand in hand perfectly for this purpose, my task was to implement it in MOSS 2007.

The tools created using this method are a part of the suite of tools that exist on the Career Centre Portal including the Resume Builder, Slider tools, Choices and Hands On, Career Possibility Generator, Card Sort tools and multiple forms throughout the site. A number of developers have played a large part in the creation of these tools and forms including Paul Maddox, Elliot Wood, Lay Hooi Tan and myself. Some are SharePoint list-driven and others are CRM-driven. For the purpose of this article I’ll focus on the Career Possibility Generator.

So how does Knockout work? It could really do with a post of its own. I’d suggest reading the documentation for a full explanation of how it works and its capabilities. Trying to keep it simple however, essentially you use data-binds to map UI elements to values stored in a data model. If you define the value as ‘observable’ you ensure that updates to the UI are reflected in the model and vice-versa. Knockout also harnesses the jQuery.tmpl template engine to render repeatable blocks of code.

It’s driven really well by using a JSON object as the underlying data model. This is why it ties in so nicely with the SharePoint 2010 REST services – you can quite easily pull back a JSON object instead of an Atom response via those services and use it within the Knockout framework. To get around this missing functionality an ASHX handler was created to return the JSON object for us. This is quite easy to do – the generic shell of the handler would look something like that shown below:

public class MyHandler : IHttpHandler
{
    #region IHttpHandler Members

    public bool IsReusable
    {
        get { return true; }
    }

    public void ProcessRequest(HttpContext context)
    {
        List<CloudListItem> items = new List<CloudListItem>();
        // Retrieve items from SharePoint and populate into the generic list
        JavaScriptSerializer ser = new JavaScriptSerializer();
        string jsonObject = ser.Serialize(items);
        context.Response.Clear();
        context.Response.ContentType = "application/json";
        context.Response.Write(jsonObject);
    }

    #endregion
}

public class CloudListItem
{
    // Various properties to return in the JSON object
}

The next step is to bind the returned JSON object to Knockout and in turn allow Knockout to bind the values to the UI using applyBindings.

<script id="Occupations_Cloud_Tmpl" type="text/x-jquery-tmpl">
    <li class="cloud${Count}"><a target='_blank' href="${PageUrl}">
      <label data-bind="text:Title"></label>
    </a></li>
</script>

<div id="tag-cloud">
    <ul id="occupations-cloud"
        data-bind="template: {name: 'Occupations_Cloud_Tmpl', foreach: viewModel.Cloud}">
    </ul>
</div>

<script type="text/javascript">
    $.getJSON('/_controltemplates/ccosdportal/handlers
               /ListToJsonHandler.ashx?ListName=NormalisedKSA
               &Web=occupations', function(data){
        //viewModel.Cloud = ko.mapping.fromJS(data);
        viewModel.Cloud = data;
        $.each(viewModel.Cloud, function(index, value) {
            value.Count = ko.observable(0);
        });
        ko.applyBindings(viewModel.Cloud);
    });
</script>

Essentially what the above code will result in is an unordered list of Occupation profile links with a distinct class applied. I’ve left in the comment above to demonstrate one of the gotchyas I encountered using Knockout. By using ko.mapping.fromJS() you’re essentially ensuring that all properties within the JSON object are observable. This causes some memory issues if the object is too large and can result in the following warning in Internet Explorer browsers:

The only thing left to explain here is how the ‘observable’ functionality works. The tool itself includes a number of checkboxes which users can select which in turn drives how the cloud looks on the screen. The checkboxes are mapped in SharePoint lists to various occupations and thus when a checkbox is selected that maps to a given occupation, the class applied to the relevant list item is modified to reflect that link. As you may have noticed, the only observable property in the object is the ‘Count’ which is used in the list item class=”cloud${Count}”. By incrementing the Count value in the model, the UI is updated to reflect the new value and hence changes the class of the list item, changing the way it displays on the screen.

function AdjustCloud(checkbox) {
    var addValue = -1;
    if (checkbox.checked) addValue = 1;

    $.getJSON('/_controltemplates/ccosdportal/handlers
               /ListToJsonHandler.ashx?ListName=OccupationKSA
               &Web=occupations&KSAID=' + checkbox.value, function(data){
    if (data.length > 0)
    {
        $.each(data, function(ind, dtwd) {
            var dtwdVal = dtwd.DTWDANZSCO;
            $.each(viewModel.Cloud, function(index, value) {
              var clouddtwdVal = value.DTWDANZSCO;
              if (clouddtwdVal == dtwdVal)
                if (((viewModel.Cloud[index].Count() + addValue) >= 0) &&
                   ((viewModel.Cloud[index].Count() + addValue) <= 9))
                       viewModel.Cloud[index].Count(viewModel.Cloud[index].Count() + addValue);
            });
        });

         HideItems();
    }
    });
}

A visual example of the tool can be seen below (click for the full-sized image).

If you take the time to look at the Career Possibility Generator and all the tools throughout the Career Centre Portal you’ll get an appreciation of how powerful it is to leverage the MVVM pattern via Knockout to create highly interactive applications within SharePoint.