Pages

Wednesday, 29 May 2013

How I wrote my SharePoint 2013 Bill Tracker App


I am writing this post to help myself and other fellows who are developers like me and are interested in developing SharePoint 2013 Apps.

In this post I am going to explain how I developed my BillTracker App for SharePoint 2013 using Javascript/CSOM for accessing SharePoint List Items using Repository and Knockout JS for displaying bills using MVVM patterns.
I am starting by showing high level architecture so it will make it easier to understand at first glance then project structure and code snippets.

Below is high level architecture:
 
And MVVM with the help of Knockout JS as shown below:

And here is my project structure:

and here is repository implementation in javascript using CSOM:

window.Masood = window.Masood || {};
window.Masood.BillTracker = window.Masood.BillTracker || {};
window.Masood.BillTracker.Repositories = window.Masood.BillTracker.Repositories || {};

window.Masood.BillTracker.Repositories.SharePointRepository = function (context, web, listName) {

    //properties
    this.context = context;
    this.web = web;
    this.listName = listName;
    var listItems;
    var listItem;

    var dateFormat = 'dd-MMM-yyyy';
    //collection for getAllItems
    this.Results = [];
    this.ErrorMessage;

    //result for getBill method
    this.Result;

    var clientSuccessCallback;
    var clientFailureCallback;

    this.getAllItems = function (sucCallback, failCallback) {

        //save the client's callback to variables 
        //that will be used in getAllItemsSuccessCallback event
        clientSuccessCallback = sucCallback;
        clientFailureCallback = failCallback;

        //use CSOM to grab SharePoint items
        listItems = null;
        var oList = web.get_lists().getByTitle(listName);
        var camlQuery = new SP.CamlQuery();
        //ste your CAML to filter items

        listItems = oList.getItems(camlQuery);
        context.load(listItems);
        context.executeQueryAsync(Function.createDelegate(this, getAllItemsSuccessCallback),
                                                                    Function.createDelegate(this, failureCallback));
     
    };

function getAllItemsSuccessCallback() {
        var listItemInfo = '';
        var listItemEnumerator = listItems.getEnumerator();

        this.Result = null;
        this.Results = [];

        paymentDate = '';
        while (listItemEnumerator.moveNext()) {
            var oListItem = listItemEnumerator.get_current();

            //push your Item/Model to Results array
        }

        //call client's success callback    
        clientSuccessCallback();
    }
}
and here is the ViewModel that calls the repository to get all/filtered items:

window.Masood = window.Masood || {};
window.Masood.BillTracker = window.Masood.BillTracker || {};
window.Masood.BillTracker.ViewModels = window.Masood.BillTracker.ViewModels || {};

window.Masood.BillTracker.ViewModels.AllBillsViewModel = function (context, web) {

    //private variables
    var self = this;
    var repository = new window.Masood.BillTracker.Repositories.SharePointRepository(context, web, 'MyBills');
    helper = new Helper();
    var _totalAmount = 0;

    //observable properties those are bind to UI elements
    self.Items = ko.observableArray();
    self.message = ko.observable("");
    self.totalAmount = ko.observable("");


    self.load = function () {
        self.message("Please wait, loading all your bills...");
        repository.getAllItems(onSuccess, onFailure);
    };

    function onSuccess() {
        //clear all items from observable collection
        self.Items.removeAll();
        var allBills = repository.Results;
        
        //populate observable collection
        _totalAmount = 0;
        for (i = 0; i < allBills.length; i++) {
            var item = allBills[i];
            self.Items.push(item);

            _totalAmount += parseFloat(item.Amount);
        }

        self.totalAmount(_totalAmount.toFixed(2));
        self.message("");
    }

    function onFailure() {
        alert('Something went wrong while fetching all your bills. Please contact administrator or  email at mmasood@gmail.com');
        self.message("Something went wrong while fetching all your bills. Please contact administrator or  email at mmasood@gmail.com");
    }
}
 
If you look above code, it does not update UI elements rather it just updates observable collections and property those are bind to UI elements as shown below:


 
 

And each .aspx or .html file has got its codebehind .js file that glue ViewModel instance as shown below:
var context;
var web;

//this function is called when page is loaded and DOM
//is ready
function sharePointReady() {
    context = new SP.ClientContext.get_current();
    web = context.get_web();
    
    //create instance of AllBills ViewModel 
    //and rest is taken care by KnockoutJs

    var viewModel = new window.Masood.BillTracker.ViewModels.AllBillsViewModel(context, web);
  
    //call load to fetch items from SharePoint
    //and populate all observable property and collection
    viewModel.load();
    ko.applyBindings(viewModel);
}


One last thing, before packaging, you need to minify all .js files using Micrsoft Ajax Minifier tool.
That’s it. Hope it would help you to understand and will help you to write better apps.