Earlier this week we introduced newer Mobile Packs, Mobile SDK 2.0, Mobile Templates and Mobile App Gallery — all designed to make it easier and faster for you to create mobile apps on the Salesforce Platform. One of the newer mobile packs is based on Knockout (aka KnockoutJS). In this blog I’m going to walk you through how it works. If you’d like to get it up and running and try it out, follow the step-by-step instructions in this ”Getting Started.”

What is Knockout and Why Knockout?

Knockout is one of the more popular JavaScript frameworks that aims to simplify and organize JavaScript app development by using a pattern/architecture they call Model-View-ViewModel (“MVVM”). The main idea is to separate Models and Views by taking care of all the bindings b/w Models and Views within the framework itself so that the we-the-developers don’t have to (i.e. write JQuery code to bind to DOM and listen to events and call JS functions etc). It also comes with several other features like templates, dependency tracking etc to help easily build modular apps.
To understand it better, let’s take a look at the below picture from their tutorial (click on it to see it in full screen).


How Knockout Works (High Level):

1. Let’s say you have some text-labels, edit fields, or buttons in your HTML that are supposed to receive or send data from/to a JavaScript function(which in turn talks to server), you simply  ’describe‘ that information (i.e. click should call some function, edit field is waiting for some text etc) using data-bind attribute. Such HTML called as “View” in Knockout. For example:

data-bind="text:firstName" //for a text-label to display data 
or data-bind="click:callSomeFunction" //for a button to call some function 
or data-bind="value:lastName" // edit-field to send or receive data

.
2. Then create a JS class and make model’s properties (like firstName, lastName etc), instance properties of this JS class (like this.firstName, this.lastName, this.someButtonFunction etc). In KO, this class called a ViewModel class. For Example:

function AppViewModel() {
    this.firstName = ko.observable("Bert"); //Bounce any change to this model data back to view

    this.capitalizeLastName = function() {
        var currentVal = this.lastName();        // Read the current value
        this.lastName(currentVal.toUpperCase()); // Write back a modified value
    };    
}

3. Finally, ask KO bind an object of ViewModel class with the actual View by calling:

 ko.applyBindings(new AppViewModel());

After step #3, Knockout will take care of updating views when model data changes and updating the models when there is change in the views without us having to do all those things. That’s the gist but for more please go through their excellent tutorials here.

Mobile Pack for Knockout

Mobile pack for Knockout is essentially a set of example ‘Contacts’ apps that shows how to use our Mobile SDK and KnockoutJS framework and easily talk to Salesforce backend. They are ‘single-page’ apps but with multiple views (list view, details view and edit view). They use Twitter Bootstrap as a UI framework and come in various versions depending on deployment and code locations (Heroku, Visualforce and Cordova-Hybrid).

Architecture

To understand where KnockoutJS, Twitter Bootstrap etc fits in, let’s take a look at the overall architecture.

As you can see from the above pictures, you can create 2 kinds of apps (with at least 6 different variations) using the Mobile Pack:
1. A mobile web app that runs in the browser.

1.1 Hosted on Heroku (using Node.js server)
1.2 Hosted on Visualforce.

2. A hybrid app using Mobile Packs (this is excluding pure-native Mobile SDKs).

2.1 iOS cordova hybrid app with files locally stored (on-the-device)
2.2 iOS cordova hybrid app with files served from Visualforce
2.1 Android cordova hybrid app with files locally stored (on-the-device)
2.2 Android cordova hybrid app with files served from Visualforce.

 

Javascript Libraries and Frameworks Used by Knockout Mobile Pack Apps

Irrespective of what kind of Mobile Pack app, they all more or less use the following set of files:
OSS  libraries: 

  1. boostrap*.cssTwitter Bootstrap as a UI framework (for look and feel)
  2. knockout-2.3.0.jsKnockout as a JavaScript framework (code organization and modularity)
  3. sammy-latest-min.jsSammy.js (as routing library to make it single-page app; Knockout doesn’t natively support it)
  4. jquery.min.jsJQuery for dom manipulation via Knockout
  5. underscore.jsUnderscore.js for JavaScript utilities.
  6. cordova-2.3.0.js (Hybrid only) - Apache Cordova JS library that bridges native and JavaScript

 

Salesforce OSS libraries:

  1. knockout-force.js – A bridge between your app and smartsync.js to perform CRUD operations
  2. smartsync.js – A library thats a wrapper to mobilesdk.forcetk.js and helps sync data between app and Salesforce.
  3. mobilesdk.forcetk.js – A popular Salesforce REST api library that actually does the AJAX calls to Salesforce.
  4. cordova.force.js (Hybrid only) – A Cordova extension to help with OAuth and Salesforce smart-store.
  5.  forcetk.ui.js (mobile web only) — An authentication library that helps with OAuth, login and logout of mobile-web apps.

 

App’s own JS files:

  1. init.js - Bootstraps and initializes the app
  2. app.js - Main app where all the main app business logics and ViewModels reside.

 

Creating a ‘single-page’ Contacts App

Before we go ahead let’s remember that our example Contacts app (Mobile Web – Heroku Bootrtrap Node.js)  is designed as a ‘single-page’ app. This means once the app’s index page is loaded, it’s never reloaded but instead different ‘views’ (list view, details view etc) are simply added and removed to existing DOM by simply changing hash or anchor part of the url.

To achieve this we use Sammy.js’s routing and some tricks that can be described in 3 parts:
1. First, we create a div container to hold views like below:

<!– This is the main container where different views will be inserted (by SammyJS) –>

<div id=”mainContainer” class=”container”></div>

2. Then, we create ‘routes’ in SammyJS for each views like below:

function sammyRoutes(koApp) {
    var sammyApp = Sammy(function () {
        // if "#/" route, render home.html 
        // and set new ViewModel for '/'(LoginViewModel)
        this.get('/', function () {
                this.render('/partials/home.html').replace('#mainContainer'); 
                koApp.setViewModelByRoute("/");
            }
        });

        // if "#/contacts" route, render list.html & set ContactListViewModel
        this.get('/contacts', function () {
              //replace and render contacts list view
        });

      //so on
}

3. And finally, reapply Knockout binding after a new-view is injected (so that Knockout now works for this new view).

      //Note: bind to 'changed' event and reapply bindings if mainContainer has changed
        //This is required to essentially wait until new view is swapped before applying bindings.
        this.bind('changed', function () {
            var mainContainer = document.getElementById('mainContainer');
            if (mainContainer && mainContainer.childNodes.length > 0) {
                ko.cleanNode(mainContainer); //clean previous bindings
                ko.applyBindings(koApp.currentViewModel, mainContainer); //reapply new view's binding
            }
        });

Defining a Salesforce Object

One of the first things you want to do when playing around with mobile packs is to describe a Salesforce object so that you can perform CRUD operations on it. Thankfully knockout-force.js library provides KnockoutForceObjectFactory to make this trivial. And further help perform CRUD and search operations on top of that object. For example, in our contacts app we can create a Contact-class by setting fields, type, orderBy etc. like below:

/************************************
 * Create a Contact "CLASS" from KnockoutForceObjectFactory by setting SOQL parameters.
 ***********************************/
var Contact = (function () {
    var objDesc = {
        type: 'Contact',
        fields: ['FirstName', 'LastName', 'Title', 'Phone', 'Email', 'Id', 'Account.Name'],
        where: '',
        orderBy: 'LastName',
        limit: 20
    };
    return KnockoutForceObjectFactory(objDesc, SFConfig);
})();

And you can then perform CRUD operations like:

 Contact.query(soql) or Contact.save() 
or Contact.get({id:myContactId}) etc

Contacts App Work Flow – Bringing it All Together

Now that you are familiar with the architecture and various parts of the app, let’s take a quick look at the work-flow to understand how it all comes together.

Step 1: When the app loads, the below JavaScript snippet gets populated with Salesforce client_id (consumer key) and app_url (app’s Heroku url) by Node.js server.

    //Get configuration from environment variables (via heroku or localhost env)
    var configFromEnv = {};
    configFromEnv.client_id = '';
    configFromEnv.app_url = '';

Step 2: Index.html page is rendered (in Cordova version, then native Salesforce OAuth is displayed).

Step 3: Once the user signs in using OAuth, after the callback, it ultimately calls ‘initApp’ function in init.js. This simply initializes everything with real Salesforce user’s authentication information and redirects to ‘/contacts’ route.

    this.get('/callback:cbInfo', function () {
            koApp.knockoutForce.oauthCallback(document.location.href);
            location.hash = '/contacts';
        });

Step 4: Once /contacts route is triggered, the below code sets the list view and also sets the ViewModel via koApp.setViewModelByRoute(‘/contacts’)

       this.get('/contacts', function () {
            if (!koApp.knockoutForce.authenticated()) {
                location.hash = '/login';
            } else {
                koApp.setViewModelByRoute("/contacts");
                this.render('/partials/contact/list.html').replace('#mainContainer');
            }
        });

Step 5: The change in route also triggers ‘changed’ event in Sammy.js and calls ko.applyBindings on currentViewModel

        //Note: bind to 'changed' event and reapply bindings if mainContainer has changed
        //This is required to essentially wait until new view is swapped before applying bindings.
        this.bind('changed', function () {

            var mainContainer = document.getElementById('mainContainer');
            var logoutDiv = document.getElementById('logoutDiv');

            if (mainContainer && mainContainer.childNodes.length > 0) {
                ko.cleanNode(mainContainer);
                ko.applyBindings(koApp.currentViewModel, mainContainer);

            }
        });

Step 6: By this step, the view is already changed to ‘list’ view and all the bindings are applied. But if you take a look at the ContactListViewModel, you will see that the minute it became ‘current’ viewModel (due to step 4), it makes an AJAX call to Salesforce to get list of contacts. And once those Contacts are returned by the server, Knockout will do its magic of automatically updating the views.

function ContactListViewModel() {   
    ... 
    ...
    Contact.query(function (data) {
        self.formatAndSetContacts(data.records);

    }, errCB);
    ...
}

Step 7: After this point steps 2 through 6 is repeated with different ViewModels and routes for other views and user actions.

Next Steps

If you have read till this point hopefully you have gotten an understanding of how to build Knockout based single-page mobile apps. Now if you haven’t already, I highly encourage you to go through Knockout’s tutorials and take a look at source code for other variations (3 of them) in Knockout Mobile Pack samples github repo.

 

-  Raja Rao DV (@rajaraodv)

tagged , , , , , , , Bookmark the permalink. Trackbacks are closed, but you can post a comment.
  • mailtoharshit

    Brilliant, I loved it testing my hand here and working on pretty much designing part, these libraries work wonder for me