KnockoutJS with SharePoint: Fixing ‘Error: You cannot apply bindings multiple times to the same element.’

KnockoutJS LogoI’ve been using KnockoutJS to build functionality into SharePoint pages for a while now. (In case you’ve ever wondered, yes, it’s the same thing people mean when they simply say “Knockout” or “KO”.)

Setting everything up with KnockoutJS can take a bit longer than hard-wiring things with jQuery – depending on what you are trying to accomplish and what your background may be – but once it’s all in place, changes to functionality become much easier. Some people naturally think in the Model-View-View Model (MVVM) pattern, but it took some time for me to get my head around the idea. Early on it felt like not just a separation of church and state, but also separation of the apse from the nave and Arkansas from middle, lower Arkansas.

I’ve probably run into every newbie error in the book, and this one has gotten me multiple times.

Error: You cannot apply bindings multiple times to the same element.

Error: You cannot apply bindings multiple times to the same element.

The screen snippet above is from my favorite debugger, Firebug. The error message itself doesn’t give you much to go on. It makes basic sense, since multiple bindings on the same element would seem to be a bad idea, but it doesn’t tell you anything about why it’s happening or what you’ve done to trigger the error. It just kills the page load.

Let’s take a simple example. In one of the sets of pages I’m working on, we want to have a header section driven by one view model and a body section driven by another. The two bindings are in separate .js files because the header logic is reused across pages, while the body section is unique to individual pages. (We’re using SharePoint 2010 more as a back-end repository and identity management provider in this application and replacing virtually all the UI with custom pages.) Each of the .js files performs the binding for which it is responsible.

The markup – at a high level – looks something like this:

<div id="abc-page-header">
...
</div>
<div id="abc-page-body">
...
</div>

and the bindings occur in the JavaScript looking something like this:

ko.applyBindings(new HeaderViewModel(), document.getElementById("abc-page-header"));
...
ko.applyBindings(new BodyViewModel(), document.getElementById("abc-page-body"));

As you can see, each binding is scoped to the container for which it has responsibility. Using document.getElementById is straighforward: we should get a JavaScript object reference to the containing element with that id.

All well and good, right? The problem in this case comes in when you have no second argument, or even worse, a typo in the id. For instance, I ran myself silly with document.getElementById("ab-page-body"). Because the document.getElementById function returned null (the id wasn’t in the page), the binding occurred on the entire document, and therein arises the problem. Elements with data-bind now become bound twice, throwing the error.

It would be much better if the error told us a little more about what was happening, of course. For instance, the error might mention which binding is currently applied to the particular element where the issue arises. Even better would be some sort of warning when you attempt to apply bindings with a null parameter. Alas, this is not the case.

The simple fix is to find the place in your code where a ko.applyBindings is applied incorrectly. It’s not so hard if you know that’s the right thing to do. If you don’t, you may end up tearing down your code and starting afresh, which is rarely productive.

See Creating view models with observables for the [rather sparse] documentation on ko.applyBindings. Note that the second parameter must be a JavaScript DOM node, not a jQuery object. If you try to get fancy like this:

ko.applyBindings(new HeaderViewModel(), $("#abc-page-header"));

You’ll get a very clear error:

rror: ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node

Error: ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node

Moral of the story: always scope your bindings carefully!

SPServices Stories #16: Beginning SharePoint Development with KoSp – Knockout for SharePoint, REST API and SPServices

Introduction

There were a few tweets from Ashok Raja today that caught my eye.

KoSpAshok has released a new Codeplex project called KoSp which helps with the bindings between SharePoint data and Knockout. Knockout has become a very popular library to use client side to manage data and Ashok’s tool should come in handy for anyone who decides to use it.

KoSp, Knockout for SharePoint 2013 and SharePoint 2010 is a knockout binder extensions for SharePoint REST API and SPServices. KoSp provides custom knockout binding handlers that enables easy binding of json data from SharePoint lists retrieved via REST API with oData queries and  SP Services with CAML queries with client side controls.

Now, I’ll admit that I haven’t had time to try this out. After all, Ashok just released it today. I did take a quick look through the JavaScript file, though, and it looks good.

Ashok’s posts give examples of how to use KoSp with both REST calls and SOAP calls using SPServices. Which you decide to use is really up to you and which version of SharePoint you’re using. If you’re on SharePoint 2007, SPServices is definitely the ticket, on SharePoint 2010 and 2013, you have a choice of REST or SOAP. What you choose should be determined by your requirements and your available skill sets.

I wanted to reproduce the “Beginning” post from Ashok’s blog here to help give him some publicity for his efforts on KoSp. I know how it can be frustrating to do great work, put it on Codeplex, and then just hear the crickets. Given that Ashok has just released the project, I’m sure he’d love to have some downloads, but even better, some feedback on how it works for others and what they would like to see next. If you’re interested in using Knockout with SharePoint, give it a look.

As with all of the posts written by others in the SPServices Stories series, I’ve done some minor editing and added a few notes, but otherwise, the content is all Ashok’s. Enjoy.

Beginning SharePoint development with KoSp – Knockout for SharePoint , REST API and SPServices

Follow the below steps to build your first KoSp-based SharePoint application. The first method explains the REST API mode and the next method explains the SPServices mode.

Method 1: Data via Rest API

Step 1

Download ko.sp.min.Ex.js, knockout.js, jquery.js from the respective project sites.

Step 2

Upload the downloaded JavaScript libraries to the Style Library of a SharePoint site.

imageStep 3

Create a new SharePoint List named “Employees” with columns Title (Default), Skills (Choice Field With Multi-select option with values “Asp.Net”, “C#”, “SharePoint”, “VB.Net”)

Step 4

Add some records to this list so that we can display those data in our application

Step 5

Create a new SharePoint page or edit an existing page. Add a script editor web part (located under Media and Content Category) to the page. [Or use a Content Editor Web Part pointing to a Content Link]

imageStep 6

Click the “EDIT SNIPPET” link of script editor web part and copy paste the below code into it (Change the site URL in the script paths pointing to your style library or where ever you have placed the code)

<script src="http://srv2:902/ko/Style Library/kosp/jquery-1.8.3.min.js"></script>
<script src="http://srv2:902/ko/Style Library/kosp/knockout-3.0.0beta.js"></script>
<script src="http://srv2:902/ko/Style Library/kosp/ko.sp-1.0.min.Ex.js"></script>

<div>
  <table width="600px">
    <thead>
      <tr>
        <th>Employee Name</th>
        <th>Skill Set</th>
        <th>Created Date</th>
        <th>Created By</th>
      </tr>
    </thead>
    <tbody data-bind="template: { name: 'Emp', foreach: Employees }" />
  </table>
</div>

<script type="text/html" id="Emp">
  <tr>
    <td data-bind="text:Title"></td>
    <td data-bind="spChoice:Skills,multi:true"></td>
    <td data-bind="spDate:Modified,dataFormat:'DD-MMM-YYYY, hh:mm:ss a'"></td>
    <td data-bind="spUser:ModifiedBy"></td>
  </tr>
</script>

<script type="text/javascript">

function EmployeeModal() {
  var self = this;
  self.Employees = ko.observableArray([]);
  $.getJSON(_spPageContextInfo.webAbsoluteUrl + "/_vti_bin/listdata.svc/Employees?$expand=Skills,ModifiedBy",
    function (data) {
      if (data.d.results) {
        self.Employees(ko.toJS(data.d.results));
    }
  });
}

$(document).ready(function () {
  ko.applyBindings(new EmployeeModal());
});
</script>

Check in and save the page.

That’s it. Now the data will be rendered in a tabular format as shown below.

imageWell, let’s see what’s there in the above code. If you notice the code, except for spDate, spUser and spChoice in the data-bind attribute, all the remaining code is related to typical Knockout view and View Modal. The data binding handlers starting with “sp” are related to KoSp and it takes care of parsing and formatting the data.

If the default text binder of Knockout is used instead of KoSp, the below would be the expected output.
image

Template code with default text binder
<script type="text/html" id="Emp">
  <tr>
    <td data-bind="text:Title"></td>
    <td data-bind="text:Skills"></td>
    <td data-bind="text:Modified"></td>
    <td data-bind="text:ModifiedBy"></td>
  </tr>
</script>

Note: Subsequent post explains in detail about each and every Knockout binding handlers available in KoSp

Method 2 : Data via SPServices

Step 1

Download SPServices from Codeplex along with all the JavaScript libraries mentioned in Step 1 of Method 1

Step 2

Follow steps from 2 to 6 in Method 1 and replace the code with the below code.

<script src="http://srv2:902/ko/Style Library/kosp/jquery-1.8.3.min.js"></script>
<script src="http://srv2:902/ko/Style Library/kosp/jquery.SPServices-2013.01.min.js"></script>
<script src="http://srv2:902/ko/Style Library/kosp/knockout-3.0.0beta.js"></script>
<script src="http://srv2:902/ko/Style Library/kosp/ko.sp-1.0.min.Ex.js"></script>

<div>
  <table width="600px">
    <thead>
      <tr>
        <th>Employee Name</th>
        <th>Skill Set</th>
        <th>Created Date</th>
        <th>Created By</th>
      </tr>
    </thead>
    <tbody data-bind="template: { name: 'Emp', foreach: Employees }" />
  </table>
</div>

<script type="text/html" id="Emp">
  <tr>
   <td data-bind="text:Title"></td>
   <td data-bind="spChoice:Skills,src:'sps'"></td>
   <td data-bind="spDate:Modified,src:'sps',dataFormat:'DD-MMM-YYYY, hh:mm:ss a'"></td>
   <td data-bind="spUser:ModifiedBy,src:'sps'"></td>
  </tr>
</script>

<script type="text/javascript">

function Employee(data) {

  this.Title = ko.observable(data.Title);
  this.Skills = ko.observable(data.Skills);
  this.Modified = ko.observable(data.Modified);
  this.ModifiedBy = ko.observable(data.ModifiedBy);

}

function EmployeeModal() {

  var self = this;
  self.Employees = ko.observableArray([]);

  $().SPServices({
    operation: "GetListItems",
    async: false,
    listName: "Employees",
    CAMLViewFields: "<ViewFields Properties='True' />",
    CAMLQuery: "<Query></Query>",
    completefunc: function (xData, Status) {
      var spsData = $(xData.responseXML).SPFilterNode("z:row").SPXmlToJson({ includeAllAttrs: true, removeOws: true });
      if (spsData) {
        $.each(spsData, function (k, l) {
          self.Employees.push(new Employee({
            Title: l.Title,
            Skills: l.Skills,
            Modified: l.Modified,
            ModifiedBy: l.Editor
          }))
        });
      }
    }
  });
}

$(document).ready(function () {
  ko.applyBindings(new EmployeeModal());
});

</script>

[As I pointed out in a comment on Ashok’s post, note that as of SPServices 2013.01, you can use jQuery promises (.Deferred() objects) This eliminates the need to do synchronous calls and can really improve performance, especially in IE. See my blog post about Comparing SPServices 2013.01 Calls with Async vs. Promises Methods.]

Save and publish the page. You can expect the same output as shown in Step 6 of Method 1

If the default text binder of Knockout is used instead of KoSp, the below would be the output you can expect.

imageHope this might have provided you a clear understanding of how to begin with KoSp for SharePoint.

Knockout for SharePoint at Codeplex