Прошлый раз я описывал преимущества использования 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/
Пример приложения
Для примера создам приложение, позволяющее отслеживать время на рабочем месте.
Подготовка
Для начала необходимо:
- Установить средства разработки приложений для SharePoint 2013.
- Установить Web Essentials 2012.
- Установить TypeScript.
- Если у вас еще нет Office365, то можете создать 90-дневный trial для отладки и тестирования.
- Создать проект SharePoint Hosted приложения.
Для того чтобы при сборке проекта выполнялась компиляция TypeScript необходимо добавить в .csproj файл следующие элементы:
<PropertyGroup>
<TypeScriptTarget>ES3</TypeScriptTarget>
<TypeScriptIncludeComments>true</TypeScriptIncludeComments>
<TypeScriptSourceMap>true</TypeScriptSourceMap>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath32)MicrosoftVisualStudiov$(VisualStudioVersion)TypeScriptMicrosoft.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.
Автор: gandjustas