Using MVVM with Office365 and SharePoint 2010 REST API

Posted on 5/1/2011 @ 9:42 AM in #SharePoint by | Feedback | 3083 views

I love JavaScript – people had pronounced this language dead a long time ago. But just like a chicken – which you eat before it’s born and after it’s dead, JavaScript – is being eaten all over the technical world, long after it’s dead! How nice! The coolest thing about JavaScript is that,

  • There is no need for separate ActiveX controls, it is part of HTML/Browser
  • It can interact with other DOM elements very very naturally
  • It’s safe.
  • And  it’s backwards and future compliant.

It is no surprise thus that a number of libraries have emerged helping us work with JavaScript.

But, JavaScript is not like C#. Notably, it has some biggies missing. For instance,

  • No support for events – well, it’s there but it’s icky! You end up writing a lot of spaghetti code.
  • You have to worry about browser incompatibilities.
  • String concatenation, and version concatenation hell.
  • IP issues, minification seems to go against every rule of good higher level programming/architecture you’ve learnt.
  • No good support for design patterns, OO etc.

Well each of these, has solutions in JavaScript, so neither of the above is true! :)
I won’t address each of these in a single article – that’s for a book. If you attend my trainings, you will find many of these concepts covered.

ESPECIALLY, the fact that design patterns, such as MVVM cannot be applied to JavaScript.
Because HTML doesn’t have dependency properties like XAML does. So it can’t do WPF like databinding – which is what makes MVVM possible.

Untrue.

They can be applied, using www.knockoutjs.com . But, inside SharePoint 2010, where every single list is exposed over a REST API, this becomes really really compelling. So, I am going to describe to you, step by step, how to do MVVM with JavaScript in SharePoint 2010, while using the REST API. Here goes.

  • Create a new custom list as shown below, call it “Songs”

 

  • Next, create a new Empty SharePoint project running as a Sandbox solution called RESTAPIMVVM.
  • Go ahead and add an empty module here called Knockout
  • Next, go to www.knockoutjs.com, and go to http://github.com/SteveSanderson/knockout/downloads, and download knockout-1.2.0.js. Drag drop this into the Knockout module.
  • Then go to jquery.org, and download jquery-1.5.2.min.js. Also add that into the Knockout module.
  • Finally, download jquery template from http://api.jquery.com/jquery.tmpl/
  • In the ELements.xml for Knockout module, add the following -
      1: <?xml version="1.0" encoding="utf-8"?>
    
      2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    
      3:   <Module Name="Knockout" Url="SitePages">
    
      4:     <File Path="Knockout\jquery-1.5.2.min.js" Url="jquery-1.5.2.min.js" />
    
      5:     <File Path="Knockout\knockout-1.2.0.js" Url="knockout-1.2.0.js" />
    
      6:     <File Path="Knockout\MVVMPage.aspx" Url="MVVMPage.aspx" />
    
      7:   <File Path="Knockout\jquery.tmpl.min.js" Url="jquery.tmpl.min.js" />
    
      8: </Module>
    
      9: </Elements>
  • In the MVVMPage.aspx, start by adding the following
  1: <%@ Page Language="C#" MasterPageFile="~masterurl/default.master" %>
  2: <%@ Import Namespace="Microsoft.SharePoint" %>
  3: <%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
  4: <%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
  5: <%@ Register TagPrefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
  6: <%@ Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
  7: <asp:Content ID="Content" ContentPlaceHolderID="PlaceHolderMain" runat="server">
  8:     <script src="jquery-1.5.2.min.js" type="text/javascript"></script>
  9:     <script src="jquery.tmpl.min.js" type="text/javascript"></script>
 10:     <script src="knockout-1.2.0.js" type="text/javascript"></script>
 11:     <script language="javascript" type="text/javascript">
 12:                // we're going to write more code here #1
 13:     </script>
 14:                // we're going to write more code here #2
 15: </asp:Content>
 16: 

 

Now, where it says, we’re going to write more code here #1, I am going to execute a JSON call to SharePoint REST API, and craft up my ViewModel -

  1:     <script language="javascript" type="text/javascript">
  2:         $(function () {
  3:             InitForm();
  4:         });
  5:         var viewModel;
  6:         function InitForm() {
  7:             $.ajax({
  8:                 type: "GET",
  9:                 contentType: "application/json;",
 10:                 url: "/_vti_bin/ListData.svc/Songs",
 11:                 processData: false,
 12:                 dataType: "json",
 13:                 success: function (data) {
 14:                     viewModel = {
 15:                         songs: ko.observableArray(data.d.results),
 16:                         addSong: function () {
 17:                             this.songs.push({ Id: "", Title: "" });
 18:                         },
 19:                         removeSong: function (song) {
 20:                             alert(song.Id);
 21:                         },
 22:                         sortByName: function () {
 23:                             this.items.sort(function (a, b) { return a.Title < b.Title ? -1 : 1; });}
 24:                     };
 25:                     ko.applyBindings(viewModel);
 26:                 }
 27:             });            
 28:         }
 29:     </script>

 

As you can see, my ViewModel is pretty smart – it has behaviors and methods on the business object. Yes this is still javaScript.
I haven’t written save/update/delete implementations – those are just REST calls, I’ve omitted them to keep my code small and understandable – this is a blogpost aferall.

Next, I am going to use jQuery template library to declare my rendering template for an individual song row, as shown below,

  1:     <script id='songRowTemplate' type='text/html'>
  2:         <tr>
  3:             <td><span data-bind='text:Id, uniqueName:true' /></td>
  4:             <td><input data-bind='value:Title, uniqueName:true' /></td>
  5:             <td><a href='#' data-bind='text:Id?"Delete":"Save", click: function() { viewModel.removeSong($data) }'></a></td>
  6:         </tr>
  7:     </script>

Wow, very interesting! As you can see, I am using standard higher level XAML/C# like concepts such as databinding. Not only that, this is conditional databinding – for instance, in TD#3, I have put a logic that if text Id is blank, the label should say “save”, otherwise “delete” – makes sense! But again, I did this with no spaghetti code, no wiring up logic that eventually becomes unmaintainable in a complex enough form.

Finally, lets go ahead and use this rendering template -

  1:     <p>You have <span data-bind='text: songs().length'>&nbsp;</span> song(s)</p>
  2:     <table data-bind='visible: songs().length > 0'>
  3:         <thead>
  4:             <tr>
  5:                 <th align=left>Song ID</th>
  6:                 <th align=left>Song Name</th>
  7:                 <th />
  8:             </tr>
  9:         </thead>
 10:         <tbody data-bind='template: { name: "songRowTemplate", foreach: songs }' />
 11:     </table>
 12:     <button data-bind='click: addSong'>Add song</button>
 13:     <button data-bind='click: sortByName, enable: songs().length > 0' type='submit'>Sort</button>

As you can see, I don’t do songs.length calculation – it’s done for me! By the business object, like a dependency property in XAML.

That’s it. Build, deploy, and show in browser -

This is truly an incredible way to write JS code, and it especially makes sense for SharePoint.

And yes, this will work in Sandbox solutions and Office365 too.

Sound off but keep it civil:

Older comments..


On 5/1/2011 9:50:56 AM Sahil Malik said ..
.. and I want to add 1 more thing. I'm not a huge fan of MVVM in Silverlight in SharePoint 2010. In fact, it's downright icky. More on that later, if I ever get time to write about it.


On 5/2/2011 4:45:31 AM Daniel Fisher said ..
Hi Sahil,

You could also use $.getJSON (http://api.jquery.com/jQuery.getJSON/) and shorten your code :-)

--Daniel


On 5/2/2011 2:54:01 PM Sahil Malik said ..
Aha! Yes in this case I could. I'm so used to using $.ajax in SharePoint though because of many SharePoint specific reasons - but yes here I could shorten the code with $.getJSON.


On 9/8/2011 4:53:06 PM Brad said ..
Would you happen to have any examples using this same technique for create, update, and delete using knockout and REST? I have found lots of read examples but nothing about the others. Thanks


On 10/22/2011 9:42:51 AM Andreas Aschauer said ..

On 3/26/2012 10:16:45 PM Dan Kline said ..
With the aparent demise of jQuery.Templ, what do you see as the direction for Knockout in SharePoint 2010?


On 3/27/2012 10:39:20 AM Sahil Malik said ..
Dan, Good question - I don't know the answer. I think we are all waiting with bated breath because there was plenty of goodness in jquery templates.

S


On 3/11/2013 12:26:12 PM Dan Kline said ..
DO you have a code sample for this. I'm trying to convert it to use KO Templates


On 4/7/2013 10:08:48 AM krishna said ..
Can the same be achieved in a pure Client Object model, where one doesn't need to go by a Sandbox or Farm solution, but a purely ECMAScript-JQuery-Knockout solution? Is it possible or any extra things that has to be taken care of?