Develop SharePoint 2013 applications with TypeScript

  • Tutorial
Last time I described the benefits of using TypeScript for application development.

In this post, I’ll show how TypeScript helps you develop applications for SharePoint 2013. In SharePoint 2013, JavaScript client development capabilities have been improved. This applies not only to the APIs available on the client, but also to the delivery and deployment mechanisms of applications and developer tools. Moreover, many features of SharePoint 2013 itself are implemented and can be customized using JavaScript.


SharePoint 2013 offers two types of APIs for use on the client side: Client-Side Object Model (CSOM) and REST API. The REST API allows you to manipulate objects on the server using the REST (OData) web service. CSOM is a set of classes that are semantically equivalent to the SharePoint server object model. CSOM is available for both JavaScript (also called JSOM - JavaScript Object Model) and for .NET. But in JavaScript, unlike .NET, metadata and typing are not available. This article will focus on the use of JSOM.

TypeScript allows you to describe types for JSOM and use static type checking and intellisense when developing applications. Unfortunately, ready-made type definitions for SharePoint 2013 are not publicly available.

Me and Andrey Markeevcreated a CodePlex project in which we made type definitions and a bunch of sample TypeScript applications for SharePoint 2013. Link to the project - http://sptypescript.codeplex.com/

Application example

For example, we’ll create an application that allows you to track time at the workplace.



Training

First you need:


In order to compile TypeScript when building the project, you need to add the following elements to the .csproj file:

ES3truetrue


Libraries and Definitions

The visual interface will be created using the knockoutjs library with the extension koLite.

In order to use these libraries in the project, you need to add the following NuGet packages:

  • KoLite (knockoutjs will be added automatically)
  • jquery.TypeScript.DefinitelyTyped
  • knockout.TypeScript.DefinitelyTyped
  • kolite.TypeScript.DefinitelyTyped

The last three packages are .d.ts files that describe types for TypeScript.

To work with JSOM in TypeScript, you need to add the SharePoint.d.ts file to the project, which can be found at the link . NuGet package will be available soon.

On-demand script loading
SharePoint has its own on-demand script loader in the SP.SOD class. A detailed description can be found in this post .

Application Script Loader Code:

///
///
///
/// 
$(() => {
    SP.SOD.registerSod('ViewModels', _spPageContextInfo.webServerRelativeUrl + '/Scripts/ViewModel.js');
    SP.SOD.registerSodDep('ViewModels', 'sp.js');
    SP.SOD.executeFunc('ViewModels', null, () => {
        var vm = new ViewModels.Model(SP.ClientContext.get_current());
        ko.applyBindings(vm);
    });
});


Presentation model

Application page layout:



The koLite plugin is used for asynchronous commands.

View Model Code:

module ViewModels {
    export class Model {
        constructor(public context: SP.ClientContext) {
            this.isLoaded = ko.observable(false);
            this.message = ko.observable('');
            this.buttonText = ko.observable('');
            this.checkInOut = ko.asyncCommand({
                canExecute: (isExecuting) => !isExecuting && this.isLoaded(),
                execute: this.executeCheckInOut
            });
            this.init();
        }
        public message: KnockoutObservableString;
        public buttonText: KnockoutObservableString;
        public checkInOut: KoliteCommand;
        public isLoaded: KnockoutObservableBool;
        //...
    }
}


All types are described in .d.ts files and are checked at compilation.
Initialization of the JSOM model during execution forms a queue of commands sent to the server by the SP.ClientContext.executeQueryAsync function . executeQueryAsync accepts two callbacks, the first is called on success, the second on failure. Attention, the this pointer spoils inside the callbacks of the executeQueryAsync function , but if you specify the callbacks as lambdas, TS carefully generates code that stores the this pointer.

private init() {
    this.list = this.context.get_web().get_lists().getByTitle('Log');
    var items = this.list.getItems(SP.CamlQuery.createAllItemsQuery());
    this.context.load(items);
    this.context.executeQueryAsync(
        () => {
            this.processItems(items);
            this.setData();
            this.isLoaded(true);
        },
        (sender, args) => alert(args.get_message()));
};

A query for a set of elements in JSOM does not return an array, but a collection of objects that implements the IEnumerable interface, although an array lies inside the object. This is all due to the fact that most of the client object model is generated from the server object model, and all collections require a special pattern for traversal. It is 100% compliant with .NET code for handling IEnumerable collections.

Processing query results:

private processItems(items: SP.ListItemCollection) {
    this.hoursSubmitted = 0;
    var enumerator = items.getEnumerator();
    while (enumerator.moveNext()) {
        var item = enumerator.get_current();
        var author = item.get_item('Author');
        //Filter by current user
        if (author.get_lookupId() == _spPageContextInfo.userId) {
            var dateCompleted = item.get_item('DateCompleted');
            if (dateCompleted) {
                this.hoursSubmitted += item.get_item('DurationInHours');
            } else {
                this.curentItem = item;
            }
        }
    }
}

The code above also shows how to perform type casting. TypeScript trusts all type casting operations, so you need to make sure that they are correct.

Command Processing
Depending on the current state of the model, a Check-In or Check-Out is performed.

private executeCheckInOut(complete: () => void ) {
    if (this.curentItem) {
        this.checkOut(complete);
    } else {
        this.checkIn(complete);
    }
};


The check-in operation is to create a new item in the SharePoint list, without specifying the completion time.

private checkIn(complete: () => void ) {
    var item = this.list.addItem(new SP.ListItemCreationInformation());
    item.set_item('StartDate', new Date());
    item.update();
    this.context.executeQueryAsync(
        () => {
            this.curentItem = item;
            this.setData();
            complete();
        },
        (sender, args) => {
            alert(args.get_message());
            complete();
        });
}


The opposite operation, Check-Out, fills in the completion time and duration in hours.

private checkOut(complete: () => void ) {
    var startedDate = this.curentItem.get_item('StartDate');
    var dateCompleted = new Date();
    var hours = (dateCompleted.getTime() - startedDate.getTime()) / (1000 * 60 * 60);
    this.curentItem.set_item('DateCompleted', dateCompleted);
    this.curentItem.set_item('DurationInHours', hours);
    this.curentItem.update();
    this.context.executeQueryAsync(
        () => {
            this.curentItem = null;
            this.hoursSubmitted += hours;
            this.setData();
            complete();
        },
        (sender, args) => {
            alert(args.get_message());
            complete();
        });
}

In both cases, the same “pattern” is used. First, a package of commands is generated for sending to the server, and after successful application, the changes are reflected in the model.

Conclusion


You can download the full example code from the link . I also recommend looking at the project code and examples of using TypeScript definitions for SharePoint ( source code ), you will find a lot of interesting things.

By the way, the sample code itself will work in SharePoint 2010, but you will have to create another project and deploy solution artifacts in a different way so that everything works together.

And next time I’ll talk about how you can customize forms and list views in SharePoint 2013, and also using TypeScript.

Also popular now: