Geeks With Blogs
David Jacobus SharePoint Consultant

I was playing with SharePoint 2013 on my Office 365 site and asked myself what is the most important skill I need to acquire as a developer for SharePoint 2013?  The same skill, as I have needed from the very beginning as a SharePoint developer:  Acquire SharePoint list data and customize the look and feel to meet customer requirements!  Not needing the whole CRUD stack, just read, except maybe for External lists.  I looked at a few blogs and decided to jump into the fray and see how hard this would be to do for a SharePoint 2013 App.  The biggest hurdle, I would need to cross is that I have been primarily a Server side developer using C# and JQuery!  It appears to me that a SharePoint 2013 skills matrix is going to push this skill down the heap as client side scripting is being pushed up!   I looked at a post by Sahil Malik on templates and knockout for SharePoint.  Cool and easy to understand, however, it appear like templates are old school and knockout provides WPF/Silverlight data binding experience for html.  So I started with his blog,  but soon found that he was using an old version of knockout.  so I am using his songs list as the basis for this post!  My thinking is that this a a do it once and use many times over, as this would be the basis project for all my 2013 pseudo web parts. Here is my Office 365 Developer Site:

image

Just an OOTB team site without any custom styling!  The best part of having this is that I don’t need SharePoint installed locally to develop an App for SharePoint.  In addition, most of the apps I have looked at are using the List Data service of the new O Data api’s to retrieve list data.  I will be using CSOM JavaScript to retrieve the list data.   Here is my list of songs (Actually, Sahil’s songs)

image

The end result is going to look almost exactly like this!  Except for the fact that it will be in an SharePoint hosted App looking into SharePoint from an application page.  SharePoint walls off the App from the SharePoint site by creating another web site for the App.  Thus there is some rethinking needed when building these applications. Different Url’s are being used by these Apps; Let’s look at one to see what is happening:

 

https://jacobusconsulting99-3e8d06205ce145.sharepoint.com/ListKO/Pages/Default.aspx
?SPHostUrl=https%3A%2F%2Fjacobusconsulting99%2Esharepoint%2Ecom
&SPLanguage=en%2DUS
&SPClientTag=0&SPProductNumber=16%2E0%2E2308%2E1221
&SPAppWebUrl=https%3A%2F%2FJacobusConsulting99%2D3e8d06205ce145%2Esharepoint%2Ecom%2FListKO

I added line feeds at the query string parameters for readability. 

SPHostUrl = https://jacobusconsulting99.sharepoint.com

SPAppWebUrl = https://jacobusConsulting99-3e8d06205ce145.sharepoint.com/ListKO

 

The SPHostUrl is the actual SharePoint Site and the SPAppWebUrl is the hosted site which contains my App. 

 

As soon as I saw this, I remembered that I had been doing this years ago (SharePoint 2007), when I had a vanilla ASP.Net site added to my SharePoint Development Sites in Visual Studio for testing web parts.  I used a standard ASP.net web part page, added my web parts, and changed the using statement to open the site by Url.  It made testing very quick as I didn’t need to install a WSP, In those days it was much harder and time consuming, I did this even though I was developing on the server.  Okay back from reminiscing about the good old daysSmile.    Knockout, is an easy framework to understand and the site has some very easy tutorials on using it!   Another aspect of Knockout is for the purist whom wants a MVVM solution.  I am not an MVVM specialist so I won’t try and describe its benefits!  Except to say, that I have heard good things about it for use in large projects for testing purposes! 

When creating an APP with Visual Studio you get the basic Hello World app which installs and displays your context name in the browser. 

 

image

 

 

image

 

 

If we deploy the app as is, this is what we get!

 

image

 

Not scintillating but actually pretty cool, cause all the piping, etc. is all there!  Okay, I want to expose list data on this page using CSOM.  Lets look at how the context name is displayed on the page.

 

'use strict';
 
var context = SP.ClientContext.get_current();
var user = context.get_web().get_currentUser();
 
// This code runs when the DOM is ready and creates a context object which is needed to use the SharePoint object model
$(document).ready(function () {
    getUserName();
});
 
// This function prepares, loads, and then executes a SharePoint query to get the current users information
function getUserName() {
    context.load(user);
    context.executeQueryAsync(onGetUserNameSuccess, onGetUserNameFail);
}
 
// This function is executed if the above call is successful
// It replaces the contents of the 'message' element with the user name
function onGetUserNameSuccess() {
    $('#message').text('Hello ' + user.get_title());
}
 
// This function is executed if the above call fails
function onGetUserNameFail(sender, args) {
    alert('Failed to get user name. Error:' + args.get_message());

 

It uses the Async call backs to get the data from the site.  One thing to point out is the context being used is the App context which id different than the host site context.  We will be using the same process to retrieve list data except we will need to get the host site context.  okay knockout uses  data binding   to

abstract the movement of data from the source onto the page.  How it does that is very simple and understandable.  Here is my html markup to display the songs from my songs list:

 

   <div class='liveExample'> 
       <ul data-bind="foreach: Songs">
           <li data-bind="text: Title"></li>
       </ul>
    </div> 

 

Basically, what this few lines of markup is doing is saying:  For each song in the songs collection display its title in a list!  Behind the scenes in app.js is the JavaScript code to make this happen.  Lets look at it:

 

   1:  'use strict';
   2:   
   3:  var context = SP.ClientContext.get_current();
   4:  var user = context.get_web().get_currentUser();
   5:  var collListItem;
   6:  // This code runs when the DOM is ready and creates a context object which is needed to use the SharePoint object model
   7:  $(document).ready(function () {
   8:      getUserName();
   9:    
  10:   
  11:      retrieveListItems();
  12:  });
  13:   
  14:  // This function prepares, loads, and then executes a SharePoint query to get the current users information
  15:  function getUserName() {
  16:      context.load(user);
  17:      context.executeQueryAsync(onGetUserNameSuccess, onGetUserNameFail);
  18:  }
  19:   
  20:  // This function is executed if the above call is successful
  21:  // It replaces the contents of the 'message' element with the user name
  22:  function onGetUserNameSuccess() {
  23:      $('#message').text('Hello ' + user.get_title());
  24:  }
  25:   
  26:  // This function is executed if the above call fails
  27:  function onGetUserNameFail(sender, args) {
  28:      alert('Failed to get user name. Error:' + args.get_message());
  29:  }
  30:  //retieve the list items from the host web
  31:  function retrieveListItems() {
  32:      var hostWebUrl = decodeURIComponent(getQueryStringParameter('SPHostUrl')); 
  33:      var hostcontext = new SP.AppContextSite(context, hostWebUrl);
  34:        
  35:      var oList = hostcontext.get_web().get_lists().getByTitle('Songs');
  36:   
  37:      var camlQuery = new SP.CamlQuery();
  38:      camlQuery.set_viewXml(
  39:          '<View><Query><Where><Geq><FieldRef Name=\'ID\'/>' +
  40:          '<Value Type=\'Number\'>1</Value></Geq></Where></Query>' +
  41:          '<RowLimit>10</RowLimit></View>'
  42:      );
  43:      collListItem = oList.getItems(camlQuery);
  44:   
  45:      context.load(collListItem);
  46:      context.executeQueryAsync(onQuerySucceeded,onQueryFailed);
  47:       
  48:  }
  49:  //business object
  50:  var song = function (title) {
  51:      var self = this;
  52:      self.Title = ko.observable(title)
  53:  }
  54:  //business collection
  55:  var songs = new SongsViewModel();
  56:   
  57:      function SongsViewModel() {
  58:          var self = this;
  59:         
  60:          self.Songs = ko.observableArray([]);
  61:   
  62:          
  63:      }
  64:   
  65:  function onQuerySucceeded(sender, args) {
  66:      var listItemEnumerator = collListItem.getEnumerator();
  67:      while (listItemEnumerator.moveNext()) {
  68:          var oListItem = listItemEnumerator.get_current();
  69:          songs.Songs.push(new song( oListItem.get_item('Title')));
  70:      }
  71:      ko.applyBindings( songs);
  72:    
  73:  }
  74:   
  75:  function onQueryFailed(sender, args) {
  76:      alert('Request failed. ' + args.get_message() +
  77:          '\n' + args.get_stackTrace());
  78:  }
  79:   
  80:  function getQueryStringParameter(paramToRetrieve) {
  81:      var params = document.URL.split("?").length > 1 ?
  82:          document.URL.split("?")[1].split("&") : [];
  83:      var strParams = "";
  84:      for (var i = 0; i < params.length; i = i + 1) {
  85:          var singleParam = params[i].split("=");
  86:          if (singleParam[0] == paramToRetrieve)
  87:              return singleParam[1];
  88:      }
  89:   
  90:   
  91:  }

Okay!  I highlighted one line of code, I think is most important as there is very little documentation on this.  We need the host web context as the list is in the host we not the app web.  The main Idea I am trying to convey here is that this same code can get any list in the site.  So all we need to change are the two Business Objects on lines 50 and 57.  I have a single song object, on line 50 and a songs collection on 57.  knockout expects a ko.observableArray([]) for its collection and the object needs to have its entities use ko.observable.  My thinking here is that most of what we do will have complex objects that will have more than just a title to display.  We just add more properties like:  self.Title = ko.observable(title).   The rest of the list code can be the same! 

 

Okay, I deployed the app and here is what the user see’s: 

 

image

What is going on here is SharePoint is asking you do you trust this app to look at a specific list.  If you click the trust it button you will see the songs displayed if you choose the songs list.  When devloping the App, you tell SharePoint what permissions the App needs in the App Manifest file:

image

Here is the out put!  Not styled, just a simple rendering.  But all the piping is there!

image

Here is the code for this post:  Code

 

I thought I would extend this post to add something more complicated:  I would use the OOTB announcements list which has ID, Title, Body, and Expires as the list columns.  In addition, I wanted to use an HTML gridview to display the data.  I would use the above project as a basis for Announcements App.   The code just needs to be changed slightly to accommodate this:

 

//business object
var Announcement = function (id,title,body,expires) {
    var self = this;
    self.ID = ko.observable(id)
    self.Title = ko.observable(title)
    self.Body = ko.observable(body)
    self.Expires = ko.observable(expires)
}
//business collection
var announcements = new AnnouncementsViewModel();
function AnnouncementsViewModel() {
    var self = this;
    self.Announcements = ko.observableArray([]);
}
 
 
function onQuerySucceeded(sender, args) {
    var listItemEnumerator = collListItem.getEnumerator();
    while (listItemEnumerator.moveNext()) {
        var oListItem = listItemEnumerator.get_current();
        announcements.Announcements.push(new Announcement(oListItem.get_item('ID'), oListItem.get_item('Title'),  remove_tags(oListItem.get_item('Body')), oListItem.get_item('Expires')));
    }
     ko.applyBindings(announcements);
   
}
//announcements have a rich text element (Body)
function remove_tags(html) {
    return jQuery(html).text();
}
 
function onQueryFailed(sender, args) {
    alert('Request failed. ' + args.get_message() +
        '\n' + args.get_stackTrace());
}

As you can see I just added more ko.observable’s in the business object and added it into the collection.   easy, just a little refactoring of the code.  In addition, I had to remove the HTML form the Body Rich text field.  However,  this just prints out an un-styled list:

image

Notice the link at the bottom which is a link to the grid display below:

image

Which is a knockout control which can be down loaded and added to the project.  What I had to do is create another view model to take advantage of the grid view:

//retieve the list items from the host web
function retrieveListItems() {
    var hostWebUrl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));
    var hostcontext = new SP.AppContextSite(context, hostWebUrl);
    var oList = hostcontext.get_web().get_lists().getByTitle('Announcements');
    var camlQuery = new SP.CamlQuery();
    camlQuery.set_viewXml(
        '<View><Query><Where><Geq><FieldRef Name=\'ID\'/>' +
        '<Value Type=\'Number\'>1</Value></Geq></Where></Query>' +
        '<RowLimit>10</RowLimit></View>'
    );
    collListItem = oList.getItems(camlQuery);
    context.load(collListItem);
    context.executeQueryAsync(onQuerySucceeded, onQueryFailed);
}
//business object
var Announcement = function (id,title,body,expires) {
    var self = this;
    self.ID = ko.observable(id)
    self.Title = ko.observable(title)
    self.Body = ko.observable(body)
    self.Expires = ko.observable(expires)
}
//business collection
 
 
var ann = new PagedGridModel()
 
function PagedGridModel() {
    var self = this;
    self.items = ko.observableArray([]);
    self.gridViewModel = new ko.simpleGrid.viewModel({
        data: self.items,
        columns: [
            { headerText: "ID", rowText: "ID" },
            { headerText: "Title", rowText: "Title" },
            { headerText: "Description", rowText: "Body" },
            { headerText: "Expires", rowText: "Expires" }
        ],
        pageSize: 4
    });
};
 
function onQuerySucceeded(sender, args) {
    var listItemEnumerator = collListItem.getEnumerator();
    while (listItemEnumerator.moveNext()) {
        var oListItem = listItemEnumerator.get_current();
        ann.items.push(new Announcement(oListItem.get_item('ID'), oListItem.get_item('Title'), remove_tags(oListItem.get_item('Body')), oListItem.get_item('Expires')));
    }
   
     ko.applyBindings(ann);
}
//announcements have a rich text element (Body)
function remove_tags(html) {
    return jQuery(html).text();
}

 

Okay!, I think I proved my point, this can be the basis project for most of your apps in SharePoint to retrieve list information and present it to the user.  Here is the source for this project:  Code, I might add, I think this is a wonderful way to expose list data in SharePoint 2010 and SharePoint 2013 on premise.  Very easy, and with only a few lines of code change.  I see, a SharePoint 2010 app designed this way:

1.  Create and aspx page from a template file

2.  Add an All Users Web Part in the elements file for the template page:  Content Editor

3.  create an HTML file link the CSS and JavaScript in the file

2.  Link the Content Editor to this file.

Here is a SharePoint 2010 site with this same script being used:

image

It is the way I will try and develop in the future!!SmileSmile  So I will try and not consider a web part but an aspx page linking to an html page as an App!  I like it a lot!  with a few lines of code a “2010 App” can be upgraded to 2013!

Posted on Thursday, December 19, 2013 7:11 AM SharePoint | Back to top


Comments on this post: SharePoint 2013 CSOM with Knockout and JQuery

# re: SharePoint 2013 CSOM with Knockout and JQuery
Requesting Gravatar...
great article))
thx you very much :*
Left by janex on Dec 30, 2013 10:43 PM

# re: SharePoint 2013 CSOM with Knockout and JQuery
Requesting Gravatar...
Excellent post, keep up the good work!
Left by John Coltrane on Jan 01, 2014 2:43 PM

# re: SharePoint 2013 CSOM with Knockout and JQuery
Requesting Gravatar...
This is a great example. Thank you!
Left by Tristan S on May 21, 2014 7:53 PM

# Knockout + SharePoint = Shockout SP Forms
Requesting Gravatar...
I wrote a comprehensive framework that combines the power of Knockout JS data binding with SharePoint REST services for truly modern and dynamic SharePoint forms without the headache of InfoPath or XSLT. I call it "Shockout" and you can download it from Git Hub - https://github.com/jbonfardeci/ShockoutForms
Left by John B on Oct 09, 2015 10:18 AM

# re: SharePoint 2013 CSOM with Knockout and JQuery
Requesting Gravatar...
Awesome article!! Really helped me get started!
Left by Evans on Nov 01, 2016 1:37 PM

Your comment:
 (will show your gravatar)


Copyright © David Jacobus | Powered by: GeeksWithBlogs.net