В этом посте я покажу как 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)\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/
Добавить комментарий