среда, 6 марта 2013 г.

Способы кастомизации форм списков в SharePoint 2013

Я уже писал про формы списков и про то, как их можно кастомизировать, однако в свете новых возможностей SharePoint 2013, а также накопившегося с того времени опыта (прошло уже 1.5 года, как время летит!), хочется еще раз пробежаться по этой теме.

Формы InfoPath


Еще год назад, услышав задачу про кастомизацию формы списка в SharePoint, я бы скорее всего порекомендовал использовать InfoPath. Работая в основном с SharePoint Foundation, я не имел много опыта с InfoPath, однако как сторонник "нэйтивных" путей в SharePoint, сама идея InfoPath (то что мол бизнес-пользователь может сам менять формы, и то что формы печатные) мне всегда нравилась. Что ж, не в первый раз я убедился, что нельзя доверять статьям на MSDN, которые говорят о том что в SharePoint все здорово и работает :)
  1. Формы InfoPath слишком сложны чтобы бизнес-пользователь мог там что-то поправить
  2. InfoPath-формы печально известны своими проблемами с рендерингом: иногда что-нибудь не рендерится как положено и это ну никак не удается поправить, и заканчивается все пересозданием формы с нуля и неделей выкинутого времени
  3. InfoPath-формы полностью лишены современных интерактивных элементов пользовательского интерфейса - вкладки, аккордеоны, слайдеры, красивые кнопочки и т.д.
  4. Также, с InfoPath-формами много проблем с их развертыванием и эксплуатацией
  5. В SharePoint 2013 InfoPath-формы уже не могут использоваться в рабочих процессах
Ходят даже слухи, что Microsoft (неофициально) совсем не рекомендует использовать InfoPath-формы...

RenderingTemplate и ListFieldIterator


Этот подход я описывал полтора года назад в этом самом блоге. Это хороший подход, надежный, проверенный, с использованием серверного C# кода. С помощью этого подхода хорошо решаются задачи изменения расположения полей на форме, группировки полей, распределения полей по вкладкам, и даже создания многошаговых форм (собственно списки Survey в SharePoint реализованы как раз кастомным ListFieldIterator'ом).

К сожалению, этот подход тоже не без минусов. Самая большая проблема - это то, что RenderingTemplate должен быть задеплоен в CONTROLTEMPLATES, а следовательно этот вариант просто никак нельзя использовать в SharePoint 2013 Apps. Кроме того, по большому счету ListFieldIterator решает напрямую только одну задачу: размещение/группировка полей и общий внешний вид формы. Чтобы кастомизировать сами поля, или их взаимодействие, приходится использовать javascript-хаки.

DataFormWebPart


Подход, который для меня является, пожалуй, в настоящий момент приоритетным при работе с SharePoint 2010 (но не SP2013).

Идея в том, что SharePoint Designer 2010 позволяет представить формы списков в виде DataFormWebPart, отображение которых легко кастомизировать XSL-преобразованием (а XSLT-преобразования можно очень быстро генерить в том же SPD). Дополнительные данные подтягиваются в DataFormWebPart через ParameterBindings.

По сравнению с ListFieldIterator, возможности и варианты использования почти такие же, но зато этот подход будет работать в SharePoint 2013 Apps. К тому же, DataFormWebPart хорошо интегрируется с другими компонентами SharePoint.

Но есть и недостатки:
  1. SharePoint Designer 2013 был лишен "Design"-режима, поэтому генерить XSLT для DFWP теперь сложнее.
  2. SharePoint Designer 2010 генерит формы с фиксированным набором полей. Если в список добавляются дополнительные поля, в форму их надо добавлять вручную или перегенерировать XSLT.
  3. Подход с DFWP имеет такой же недостаток, как ListFieldIterator: мы можем группировать поля, размещать их, но не можем кастомизировать отображение самих полей и взаимодействие между ними. Проблема тут в том, что за отображение редакторов полей отвечают серверные контролы, т.е. фактически для каждого поля мы получаем что-то типа такого кода:
    <td width="190px" valign="top" class="ms-formlabel">
        <H3 class="ms-standardheader">
            <nobr>Title<span class="ms-formvalidation"> *</span>
            </nobr>
        </H3>
    </td>
    <td width="400px" valign="top" class="ms-formbody">
        <SharePoint:FormField runat="server" id="FormField1" ControlMode="New" FieldName="Title"/>
        <SharePoint:FieldDescription runat="server" id="FieldDescription1" FieldName="Title" ControlMode="New"/>
    </td>
    
    Серверный контрол SharePoint:FormField как раз отвечает за отображение редактора поля. Все что мы можем с этим контролом сделать, это переключить его в режим ControlMode.Display, сделав нередактируемым, - но и только. Дальнейшие кастомизации возможны уже только с помощью javascript.

Custom Field Types


Безусловно, для изменения отображения полей можно использовать Custom Field Types. Однако для этого подхода недостатки очень сильно перевешивают достоинства:
  1. Custom Field Types недоступны в SharePoint 2013 Apps
  2. Нельзя создать Lookup-поле, которое бы ссылалось на поле Custom Field Type (!!)
  3. Кастомные поля не могут участвовать в формулах - т.е. в Calculated полях и в формулах валидации списка.
  4. Custom Field Types отображаются как текстовые readonly значения в Document Information Panel, что практически убивает целесообразность их использования при работе с библиотеками документов.
  5. Custom Field Types также неверно отображаются в многих других случаях, к примеру при работе с оффлайн-файлами, при экспорте в Excel и Access, в Datasheet view в SP2010 и т.д.

В общем, на мой взгляд, Custom Field Types - одна из самых неудачных возможностей в SharePoint, несмотря на всю свою востребованность. Если бы они везде работали - цены бы им не было. А так, я не создал ни одного Custom Field Type начиная с января 2010го года...

Update: Александр Плетнев в комментариях рассказал о том, что оказывается некоторые из перечисленных ограничений (пп. 2, 3, частично 4) можно обойти, если в классе Custom Field Type переопределить (через new) свойство TypeAsString, чтобы оно всегда возвращало "Text". Это, между прочим, значительно улучшает положение Custom Field Types и делает возможным их применение в некоторых случаях.

JavaScript/jQuery Hacks


Этот способ заключается в том, что на страницу с формой списка деплоится Content Editor WebPart с javascript-кодом, или же javascript вставляется непосредственно в код страницы в SPD. Дальше как правило используется jQuery, чтобы найти нужные поля и как-то их изменить.

Этот способ может использоваться для любых кастомизаций форм списков - и для группировки полей, и для ковыряния в их внутренностях, и для организации взаимодействия между ними.

Есть целые библиотеки, как например общеизвестная SPServices, которая позволяет быстро наворачивать полезные javascript хаки на ваши формы.

Однако, недостатки у этого подхода очень неприятные:
  1. Стандартные формы SharePoint не содержат надежных идентификаторов для индивидуальных полей в разметке страницы. Разные типы полей рендерятся по-разному (и соответственно Custom Field Types вообще могут рендериться по особому). Формы кастомизированные в SPD опять же будут иметь другую разметку, и т.п. Это приводит к тому, что решение получается очень хрупким: кто-нибудь переименовал поле или же на портале задеплоили какое-нибудь стороннее решение делающее что-то с формами, - и все начинает разваливаться по швам.
  2. Как правило javascript-код отрабатывает довольно долго. Т.е. если вы разносите поля по вкладкам и полей у вас много, то велика вероятность что пользователь успеет увидеть форму в исходном виде (без вкладок) прежде чем ваш javascript-хак приведет ее к нужному виду.
  3. Javascript-хаки работают полностью на стороне клиента, поэтому если вы делаете поле readonly через javascript-хак, вы должны понимать, что любой более-менее продвинутый пользователь легко обойдет это ваше "ограничение".
Безусловно, в некоторых конкретных случаях такой подход работает более-менее без нареканий и его можно использовать, особенно когда иначе никак.

Client Side Rendering


Новая возможность, которая появилась в SP2013 - это Client Side Rendering. По большому счету это тот же самый javascript/jQuery хак, но официально поддерживаемый MS :) По сравнению с предыдущим подходом, эта возможность устраняет как минимум первый недостаток - т.е. мы можем быть уверены, что именно нужные нам поля будут изменены. Кроме того, Client Side Rendering предоставляет очень полезную дополнительную контекстную информацию, т.е. чаще всего не придется делать никаких лишних запросов, чтобы изменить форму так как нам надо.

Немаловажно также, что теперь у XSLT веб-частей SharePoint появилось свойство IsClientRender, которое позволяет отключить серверный рендеринг (что позволяет улучшить производительность на стороне сервера, т.к. традиционно ресурсоемкое XSL-преобразование не запускается), а на клиента срендерить только js-массивы со всеми нужными данными.

Однако, использовать Client Side Rendering нужно все же с некоторой осторожностью:
  1. Javascript всегда сравнительно медленный. Большие формы и вьюшки, полностью сделанные на javascript с кучей эффектов и т.д., будут тормозить и глючить.
  2. Client Side Rendering не препятствует загрузке данных на страницу. Он работает с данными, которые уже лежат на странице (данные рендерятся в код страницы в виде javascript-объектов). Например, если вы хотите изменить отображение Lookup-поля, отфильтровав значения этого поля, вы должны понимать что все значения поля все равно будут загружены на страницу, и т.д.
В целом, Client Side Rendering представляется наиболее перспективным подходом для изменения форм списков на сегодняшний день.

Заключение


В SharePoint существует много возможностей для кастомизации форм списков. К сожалению, ни одна из них не идеальна.

Чтобы понять, что лучше использовать в каждом из возможных случаев, нужно тщательно изучить все рассмотренные подходы и понять их преимущества и недостатки (и очень желательно - попробовать поковыряться с ними). Именно достоинства и недостатки я старался выделить в этой статье. Надеюсь, эта информация позволит сделать ваши решения на SharePoint более оптимальными. Удачи!

15 комментариев:

  1. Скажу и я свое словечко:
    1) Формы InfoPath - не так уж они и плохи!, и за частую это самый быстрый и дешевый способ кастомизировать форму списка, в рамках возможностей конечно. А возможностей для "не навороченной" формы достаточно. Ограничений конечно еще больше, но я крутых конструкторов форм, кроме VS, еще не видал. Больше всего меня убивает, то что они только в Enterprise, и отсутствие диалога выбора из DataSuorce
    , с возможностью поиска и фильтрации. А так как раз таки один из клиентов, далеко не разраб, сам формочки клипает без проблем. А Классическая форма InfoPath в своем сегменте просто не заменима + проигрывание в веб. известный мне аналог это только pdf формы. код в формах мы не используем - это геморрой, в случае чего нибуть такого эдакого разработка веб-сервиса.
    2) Custom Field Types - полностью согласен с "одна из самых неудачных возможностей в SharePoint". Очень редко разрабатываем CustomField, последний раз это был Custom field для ЭЦП. здесь он пригодился как нельзя лучше, и форма настроек, и отображение в списке, и хранение статуса.
    А по поводу ограничений ReadOnly, CalculatedFiled и т.п., надо в переопределении SPField.TypeDisplayName (могу ошибаться что именно это свойство) возвращать слово "Text", ибо у индусов в коде жесткий хардкод. Если "Text", то аллилуя, если нет, то давай до свиданья. в 2007 прокатывало.
    3) Возлагаем большие надежды на DataFormWebPart + Client Side Rendering , мне кажется совмещая оба подхода XSLT (+ с использованием серверных контролов) + JavaScript/jQuery Hacks , можно и движочек небольшой забабахать - замену InfoPath! :)

    ОтветитьУдалить
    Ответы
    1. Александр, спасибо за комментарий, очень конструктивно!

      Насчет InfoPath скажу еще раз: много с ним не работал, поэтому утверждать что InfoPath-формы плохие не буду. Собственно, у меня был только один реальный проект с InfoPath - уже здесь, в Финляндии. И к сожалению, опыт этот был неудачным: после выгрузки в облако InfoPath форма работать отказалась, и после нескольких недель борьбы с этим - с привлечением техподдержки Microsoft, - я за два дня переписал все решение на DataFormWebPart и оно мгновенно заработало. И проблемы с рендерингом тоже ощутил, довольно весомые. И с Windows Phone формы InfoPath тоже отказались работать. Но самое главное, это то что InfoPath нельзя использовать в 2013 Workflow, и второе - действительно ходят слухи что MS намекает на то, что не надо бы мол InfoPath-то использовать. В свете всего этого и получились выводы, озвученные в статье. Хотя повторюсь, сама идея InfoPath-форм мне очень нравится...

      Про Custom Field Types и свойство TypeAsString, спасибо за информацию, я не был знаком с этим хаком. Возможности открываются очень интересные - т.е. можно будет использовать Custom Field Type и в Lookup'ах, и в Calculated полях - что очень ценно, хотя надо будет обдумать как следует какие могут быть последствия, ведь это свойство даже не virtual... Но все равно спасибо, интересно, я поправлю статью комментарием об этом.

      Удалить
  2. Насчет InfoPath-форм - тоже не большой любитель, несколько раз крупно подводили, отнимали много времени на пустую разработку.
    Но при должном старании можно сделать их и с вкладками, и много ещё с чем...
    Как это делается, не знаю - специально обученные люди этим занимаются.

    ОтветитьУдалить
    Ответы
    1. Вкладки делаются путем добавления таблицы с ячейками разного цвета. Кликаешь ячейку - переключается на другой View, на котором такая же таблица только "текущим" выделена другая ячейка. Способ жутковатый: поддерживать такое сложно. Если 5 вкладок и надо подправить какой-нибудь общий элемент для них - делаешь на всех 5 вкладках по очереди.

      Да, при должном старании там много что можно сделать, но далеко не все. По сравнению с HTML5, CSS3 и jQuery UI, InfoPath формы очень статичные и очень устаревшие. А современные пользователи лазают в интернетах и прекрасно понимают, когда интерфейс клевый, и когда интерфейс - какашка...

      Удалить
  3. InfoPath хороши для пользователей, так как можно форму с "логикой" слепить быстро. Но очень плохи для программистов из-за ограничений. Поэтому InfoPath был в узкой нише, а в 2013 еще сильнее подвинется из-за Access Services и HTML5\CSR.

    Будущее UI в шарике это CSR. Все представления списков и формы сделаны на нем (без xsl). XSLT только для унаследованных решений и, быть может, для публичных сайтов.

    ОтветитьУдалить
    Ответы
    1. Про CSR согласен, наиболее перспективный подход в 2013. Планирую какую-то из следующих статей посвятить целиком этой теме - как оно работает, примеры и т.п.

      Удалить
    2. Немного подправил пост в части Client Side Rendering, включил немного подробностей и расписал некоторые плюсы.

      Удалить
    3. Будет очень хорошо!! А то я сам 13 еще не ставил и не щупал, но вопрос CSR сjвсем немножко "курил". У меня возникло ощущение что CSR то да, но это как обработчики событий , а база то все равно осталось XSLT. Вывод сей я сделал исходя из того что в основе веб-части формы и списка все теже XSLT*WebPart.
      Или я жестко ошибаюсь? подтвердите ссылкой пжл.

      Удалить
    4. В основе XSLT-вебчасти, да, но насколько я понимаю, в случае IsClientRender=true XSL-преобразование не запускается вообще. В коде страницы данные рендерятся только в виде javascript-массивов. Ссылки нет, но есть скриншот.

      Удалить
    5. Может просто дополнительно генерирует массивы? как раз таки для CSR обработчиков. вот тут немножко даже кода есть: http://msdn.microsoft.com/en-us/library/jj220061.aspx

      Удалить
    6. Нет, не дополнительно. Это единственное совпадение поиска в коде страницы по данным. Приду домой, посмотрю еще в рефлекторе как это сделано, действительно ли там вообще не запускается XSLT, но то что на страницу данные выводятся только в виде массивов - это 100%.

      Удалить
    7. XSL в новых веб-частях не запускается вообще.

      Удалить
    8. Веб части там старые, XsltListWebPart и ListFormWebPart для форм, но у них появились свойства ClientRender, ServerRender и IsClientRender. Вот эти три свойства определяют что выводит веб часть. Насколько я понял, ServerRender=FALSE отключает серверный рендеринг, а ClientRender=TRUE включает вывод двух объектов в Js: данных и схемы списка.

      Удалить
  4. Про JS, второй пункт. На моей практике часто такое было. Пользователь успевал видеть html, до его обработки.
    Я все заранее в див помещал, спрятанный. А код, который форму обрабатывал - делал его видимым, в самом конце своей работы.

    ОтветитьУдалить
  5. Интересная статья. Спасибо.

    ОтветитьУдалить

Внимание! Реклама и прочий спам будут беспощадно удаляться.