📜 ⬆️ ⬇️

SharePoint 2013 Application Development with TypeScript

The last time I described the benefits of using TypeScript for application development.

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


SharePoint 2013 offers two kinds of APIs for use on the client side: the Client-Side Object Model (CSOM) and the REST API. The REST API allows you to manipulate objects on the server using a REST (OData) web service. CSOM is a set of classes, semantically equivalent to the SharePoint server object model. CSOM is available for both JavaScript (also called JSOM - JavaScript Object Model), and .NET. But in JavaScript, unlike .NET, metadata and typing are not available. In this article, the application of JSOM will be considered.
')
TypeScript allows you to describe types for JSOM and use static type checking and intellisense when developing applications. Unfortunately, there are no ready-made type definitions for SharePoint 2013.

I and Andrey Markeev created a project on CodePlex, in which we made type definitions and a bunch of sample applications on TypeScript for SharePoint 2013. The link to the project is http://sptypescript.codeplex.com/

Sample application

For example, create an application that allows you to track time in the workplace.



Training

First you need to:


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

<PropertyGroup> <TypeScriptTarget>ES3</TypeScriptTarget> <TypeScriptIncludeComments>true</TypeScriptIncludeComments> <TypeScriptSourceMap>true</TypeScriptSourceMap> </PropertyGroup> <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" /> 


Libraries and definitions

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

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


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

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

Download scripts on demand
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:

 ///<reference path="typings/SharePoint.d.ts" /> ///<reference path="typings/jquery/jquery.d.ts" /> ///<reference path="typings/knockout/knockout.d.ts" /> /// <reference path="ViewModel.ts" /> $(() => { 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

Layout of the application page:

 <div> <p data-bind="text:message"></p> <button data-bind="text:buttonText, command: checkInOut, visible:isLoaded" style="display:none;"/> </div> 


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 checked during compilation. Model initialization
JSOM, when executed, forms a queue of commands sent to the server by the SP.ClientContext.executeQueryAsync function. executeQueryAsync accepts two callbacks, the first is called upon successful completion, the second in case of failure. Attention, this pointer is spoiled inside the function executeQueryAsync callbacks , but if you specify callbacks in the form of lambda, then 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 of 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 traversal pattern. 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 = <SP.ListItem>enumerator.get_current(); var author = <SP.FieldUserValue>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 cast. TypeScript trusts all cast operations, so you need to ensure that they are correct.

Command processing
Depending on the current state of the model, 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 values ​​of completion time and duration in hours.

 private checkOut(complete: () => void ) { var startedDate = <Date>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 created for sending to the server, and after successful application, changes are reflected in the model.

Conclusion


The full code of the sample can be downloaded from the link . I also recommend to look 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 differently so that everything works together.

And next time I will tell you how to customize forms and list views in SharePoint 2013, and also with TypeScript.

Source: https://habr.com/ru/post/174099/


All Articles