(даже если вы уже смотрели доклад, эта статья все равно может оказаться полезной для вас, т.к. некоторые моменты здесь рассмотрены намного более тщательно, и приводится гораздо больше примеров)
CSR - это способ отображения данных в SharePoint 2013, пришедший на смену
скоропостижно скончавшемуся XSLT. С помощью CSR отображаются и представления списков, и формы списков, и результаты поиска. Хотя бы поэтому нужно знать, что такое CSR и как он работает (о чем, собственно, я и намерен рассказать в этой статье).
Ну и конечно немаловажно, что у CSR есть API, и его можно использовать для создания кастомизаций тех самых представлений списков, их форм, и результатов поиска. И об этом мы тоже поговорим чуть ниже.
Как устроен CSR
CSR - это подсистема отображения данных, главным отличием которой является тот факт, что она клиентская, т.е. реализована целиком на JavaScript. Иными словами, на стороне сервера рендерятся только данные в Json-формате, которые затем с помощью неких JavaScript-функций превращаются в HTML и затем этот HTML вставляется в DOM страницы.
Т.о. CSR представляет собой своеобразный конвертер, который принимает входящие данные, и возвращает HTML-строку на основе этих данных.
Для удобства CSR реализован не в виде одной огромной функции, а в виде множества javascript-функций, каждая из которых рендерит свой собственный кусочек HTML. Эти функции как правило вызывают одна другую, и таким образом как бы вложены друг в друга.
Исследовав исходный код CSR в файле /_layouts/15/clienttemplates.debug.js, я составил полную схему, которая демонстрирует порядок вызова этих функций:
OnPrePrender и OnPostRender отличаются от Render*-функций тем, что являются событиями, срабатывающими соответственно перед процессом генерации HTML и сразу после завершения этого процесса. Эти события служат в основном для расширений, в то время как Render*-функции выполняют основную работу по отображению.
Если взять в качестве примера представление произвольного списка, можно отметить области, которые были срендерены каждой из этих Render*-функций:
Ну что же, пока что довольно просто и логично, неправда ли?
Используем CSR API
Есть два основных способа кастомизации CSR:
- Подписывание на события OnPreRender и OnPostRender
- Подмена (override) функций-шаблонов Render*.
Технически оба способа доступны через единое API, а именно через функцию
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(options)
Функция RegisterTemplateOverrides принимает в качестве единственного параметра объект, содержащий всю необходимую информацию для кастомизации и имеющий следующую структуру:
- OnPreRender
- Templates
- View
- Header
- Body
- Footer
- Group
- Item
- Field
- OnPostRender
В полях OnPreRender и OnPostRender можно указать одну функцию или же массив функций. Пример:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
OnPreRender: function() { console.log('CSR OnPreRender'); },
OnPostRender: [
function() { console.log('CSR OnPostRender'); },
function() { alert('CSR OnPostRender'); }
]
});
Для полей внутри составного поля Templates можно указывать функцию или строку. В случае строки внутри можно использовать токены, ссылающиеся на JavaScript-контекст:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
Templates: {
Footer: "Hello world from <#= ctx.ListTitle #>!"
}
});
Результат работы такой функции будет следующий:
Как видите, в Footer представления списка добавилась наша строка, и токен был заменен значением, соответствующим заголовку списка.
Вместо строки-шаблона можно использовать функцию, вот как это выглядит:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
Templates: {
Footer: function(ctx) {
return "Hello world from " + ctx.ListTitle + "!";
}
}
});
При генерации HTML-строк в CSR очень удобно использовать функцию String.format, которая автоматически уже есть на любой странице SharePoint'а и позволяет делать то же самое, что серверный C#-овский
String.Format.
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
Templates: {
Footer: function(ctx) {
return String.format("Hello world from {0}!", ctx.ListTitle);
}
}
});
Обратите внимание, что обработчик должен принимать в качестве параметра объект ctx - это тот самый JSON-объект, который генерируется на стороне сервера. И естественно, возвращает функция обработчика HTML-строку.
Замечание: при регистрации override'а, исходный шаблон будет
ЗАМЕНЕН, т.е. больше он вызываться не будет, а вместо него будет запускаться ваш шаблон. Поэтому, если в предыдущем примере заменить Footer на Body, то результат будет такой:
Как видите, записи списка более не отображаются. Т.о. важно понимать, что если вы хотите заменить шаблон не полностью, а только одну малую его часть, вам все равно придется генерировать каким-то образом все остальное.
Чтобы этого избежать, можно попробовать вместо замены шаблона использовать событие OnPostRender, и заменять/дополнять нужный вам элемент с помощью jQuery или селекторов.
Вызов других шаблонов
Как я уже упоминал, шаблоны CSR как бы вложены друг в друга, потому что друг друга вызывают. Поскольку вам придется подменять эти шаблоны в процессе кастомизаций, возникает естественный вопрос: а как вызывать низлежащие шаблоны?
Оказывается, когда JSON-объект сгенерированный на стороне сервера начинает использоваться в качестве контекстного объекта в CSR, этот объект дополняется всякими полезными при обработке полями и методами. И в частности, в нем можно найти все Render*-функции:
Таким образом, вызов низлежащих функций не представляет никаких проблем. Пример:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
Templates: {
View: function(ctx) {
return ctx.RenderHeader() + ctx.RenderBody() + ctx.RenderFooter();
}
}
});
Подключение к представлению
Куда же нужно положить JavaScript-код с вызовом RegisterTemplateOverrides, чтобы кастомизация заработала? Очень просто: нужно положить этот код на страницу сразу после представления списка.
Представленный выше пример я сделал следующим образом: перешел в режим редактирования страницы, добавил веб-часть Script Editor, и добавил туда вышеозначенный код:
Чтобы код работал из любого места страницы, необходимо обернуть его в широко известный SP.SOD.executeFunc:
SP.SOD.executeFunc("clienttemplates.js", "SPClientTemplates", function() {
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
// ...
});
});
В этом случае можно подключать хоть в header, и делать это любым способом (через masterpage, через page layout, через script link и т.п.) - все сработает нормально.
Теперь давайте посмотрим, как сделать так, чтобы кастомизировался только нужный нам view (если на странице несколько view разных списков). Для этого в объекте опций предусмотрены 3 дополнительных поля: ViewStyle, ListTemplateType и BaseViewID.
Используя эти три поля, можно "нацелить" кастомизацию строго на нужный список, чтобы никакие другие списки на странице не были затронуты.
Про JSLink
Я с удивлением замечаю, что JSLink для многих является практически синонимом CSR. Многие статьи про CSR имеют заголовок из серии "Новая фича шарепойнта - JSLink", и дальше повествование идет о CSR... Я считаю своим долгом рассеять эти заблуждения.
Правда заключается в том, что JSLink не имеет никакого отношения к CSR вообще. Они
не связаны. Они могут использоваться вместе, но это совершенно необязательно. CSR прекрасно работает без всяких JSLink, как мы видели выше. А JSLink прекрасно работает без CSR-скриптов.
JSLink - это просто способ прицепить некий javascript к некоему объекту SharePoint. Свойство JSLink присутствует у многих объектов SharePoint:
- SPContentType
- SPField
- SPForm (кстати, по непонятным причинам, мне не удалось заставить работать именно SPForm.JSLink, хотя согласно моим исследованиям через dotPeek, все должно прекрасно работать - не знаю в чем было дело, может быть у вас получится?...)
- SPView
- XsltListViewWebPart
- ListFormWebPart
- ...
Как только мы проставляем это свойство у некоего объекта SharePoint, везде где он появляется на портале, везде будут добавлены соответствующие js-файлы.
Фактически эти js-файлы добавляются через контрол ScriptLink, что кстати говоря гарантирует автоматическое решение проблемы дубликатов - если кто не знал, контрол ScriptLink не позволяет загрузить на страницу дважды файл с одинаковым путем. Т.е. если поместить 2 ScriptLink-контрола на одну страницу и в обоих прописать один и тот же файл, то будет загружен лишь один файл (но сильно не обольщайтесь, дубликаты отслеживаются только по url, а не по контенту).
Внутри свойства JSLink можно использовать некоторые токены и особый синтаксис:
- Во-первых, можно загружать несколько файлов сразу, используя в качестве разделителя вертикальную черту ("|").
- Во-вторых, можно использовать токены "~site" и "~sitecollection", которые будут заменены на соответствующие URL.
- В-третьих, можно использовать "(d)" на конце, чтобы грузить файлы в режиме SOD (Script-On-Demand). Т.е. вместо того, чтобы быть загруженным на страницу сразу, скрипт будет зарегистрирован в SOD системе и будет действительно загружен только в том случае, если кто-то к нему обратиться, используя "SP.SOD.executeFunc" или подобный метод. Внимание! Только скрипты, поддерживающие SOD, могут быть загружены таким способом.
В общем, JSLink - это очень удобный способ загрузки скриптов в SharePoint'е, и безусловно этот способ можно и нужно использовать вместе с CSR. Однако, важно понимать, что JSLink никак не влияет на CSR, и например можно подцепить CSR-скрипт к объекту списка А, и этим скриптом применять кастомизации к совершенно другому списку В....
Также, надеюсь понятно, что через CSR можно грузить далеко не только CSR-скрипты, но и любые сторонние файлы - например, jQuery, knockout.js и т.п.
Заключение
На мой взгляд CSR - это отличная замена XSLT. Огромным преимуществом CSR является то, что CSR базируется на JavaScript. Сейчас столько всего происходит вокруг JavaScript! Буквально каждую неделю появляются какие-нибудь новые классные библиотеки и фреймворки, создаются инструменты, и т.д. Мне кажется, CSR - это отличный шанс привнести современные достижения web-технологий в SharePoint, и начать создавать по-настоящему удобные и качественные решения.
Конечно, как и везде в SharePoint, у CSR есть свои тараканы, и следует быть крайне осторожными в оценке трудозатрат, особенно если вы пока еще мало работали с CSR.