Разработка приложений SharePoint 2013 с помощью TypeScript

от автора

Прошлый раз я описывал преимущества использования TypeScript для разработки приложений.

В этом посте я покажу как TypeScript поможет разрабатывать приложения для SharePoint 2013. В SharePoint 2013 были улучшены возможности разработки клиентских приложений на JavaScript. Это касается не только API, доступных на клиенте, но и механизмов поставки и развертывания приложений, инструментов разработчика. Более того, многие функции самого SharePoint 2013 реализованы и могу быть кастомизированы с помощью JavaScript.

SharePoint 2013 предлагает два вида API для использования на клиентской стороне: Client-Side Object Model (CSOM) и REST API. REST API позволяет манипулировать объектами на сервере используя REST (OData) веб-сервис. CSOM представляет из себя набор классов, семантически эквивалентных серверной объектной модели SharePoint. CSOM доступна как для JavaScript (также называют JSOM – JavaScript Object Model), так и для .NET. Но в JavaScript, в отличие от .NET, недоступны метаданные и типизация. В этой статье будет рассмотрено именно применение JSOM.

TypeScript позволяет описать типы для JSOM и использовать статическую проверку типов и intellisense при разработке приложений. К сожалению готовых определений типов для SharePoint 2013 в открытом доступе нет.

Я и Андрей Маркеев создали проект на CodePlex, в котором сделали определения типов и кучу примеров приложений на TypeScript для SharePoint 2013. Ссылка на проект — http://sptypescript.codeplex.com/

Пример приложения

Для примера создам приложение, позволяющее отслеживать время на рабочем месте.

Подготовка

Для начала необходимо:

Для того чтобы при сборке проекта выполнялась компиляция TypeScript необходимо добавить в .csproj файл следующие элементы:

<PropertyGroup>     <TypeScriptTarget>ES3</TypeScriptTarget>     <TypeScriptIncludeComments>true</TypeScriptIncludeComments>     <TypeScriptSourceMap>true</TypeScriptSourceMap> </PropertyGroup> <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" />   
Библиотеки и определения

Визуальный интерфейс будет создан с помощью библиотеки knockoutjs с расширением koLite.

Для того чтобы использовать эти библиотеки в проекте необходимо добавить следующие NuGet пакеты:

  • KoLite (knockoutjs добавится автоматически)
  • jquery.TypeScript.DefinitelyTyped
  • knockout.TypeScript.DefinitelyTyped
  • kolite.TypeScript.DefinitelyTyped

Последние три пакета представляют из себя .d.ts файлы, которые описывают типы для TypeScript.

Для работы с JSOM в TypeScript надо добавить в проект файл SharePoint.d.ts, который можно найти по ссылке. NuGet пакет будет доступен в ближайшее время.

Загрузка скриптов по требованию
В SharePoint есть свой загрузчик скриптов по требованию в классе SP.SOD. Подробное описание можно найти в этом посте.

Код загрузчика скриптов приложения:

///<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);     }); }); 
Модель представления

Разметка страницы приложения:

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

Используется плагин koLite для асинхронных команд.

Код модели представления:

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;          //...     } } 

Все типы описаны в .d.ts файлах и проверяются при компиляции.Инициализация модели
JSOM при выполнении формирует очередь команд, отправляемых на сервер функцией SP.ClientContext.executeQueryAsync. executeQueryAsync принимает два коллбека, первый вызывается в случае успешного завершения, второй в случае неудачи. Внимание, указатель this портится внутри коллбеков функции executeQueryAsync, но если указывать коллбеки в виде лямбд, то TS заботливо генерирует код, который сохраняет указатель this.

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())); }; 

Запрос множества элементов в JSOM возвращает не массив, а коллекцию объектов, реализующую интерфейс IEnumerable, хотя внутри объекта лежит массив. Это все вызвано тем, что большая часть клиентской объектной модели сгенерирована из серверной объектной модели, и все коллекции требуют специальный паттерн для обхода. Он 100% соответствует коду .NET для обработки IEnumerable коллекций.

Обработка результатов запроса:

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;             }         }     } } 

В коде выше также показано как выполнять приведение типов. TypeScript доверяет всем операциям приведения типов, поэтому вам необходимо следить, чтобы они были корректными.

Обработка команд
В зависимости от текущего состояния модели выполняется Check-In или Check-Out

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

Операция Check-In заключается в создании нового элемента в списке SharePoint, без указания времени завершения.

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();         }); } 

Противоположная операция – Check-Out – заполняет значения времени завершения и продолжительности в часах.

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();         }); } 

В обоих случаях используется один и тот же “паттерн”. Сначала формируется пакет команд для отправки на сервер, а после успешного применения изменения отражаются в модели.

Заключение

Полный код примера вы можете скачать по ссылке. Также рекомендую посмотреть код проекта и примеры использования определений TypeScript для SharePoint (source code), найдете много интересного.

Кстати сам код примера будет работать и в SharePoint 2010, но придется создать другой проект и по-другому развертывать артефакты решения, чтобы все вместе заработало.

А в следующий раз я расскажу как можно кастомизировать формы и представления списков в SharePoint 2013, и тоже с помощью TypeScript.

ссылка на оригинал статьи http://habrahabr.ru/post/174099/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *