Привязка события и рендеринг из аргумента шаблона.

Обработка событий из аргументов шаблона

Некоторые шаблоны могут предоставлять DOM события. В частности шаблон @controls:button вызывает событие click при нажатии на кнопку, которое может быть обработано.

К DOM событиям, вызываемым на DOM элементе шаблона можно привязать обработчик события, используя аргумент шаблона. Аргументы шаблона начинающиеся с префикса fcfEvent, после которого следует имя события с большой буквы, считаются строковой Javascript инструкцией обработчика события. Внутри каждого обработчика события доступны следующие переменные:

  • fcf.NClient.Wrapper wrapper - объект враппера, для которого вызвано событие
  • fcf.NClient.Wrapper parent - объект владельца враппера
  • Event event - объект события

Рендеринг из аргумента шаблона

Нам нужно будет разместить панель редактирования свойств строк, но пока без сохранения состояния на стороне сервера. Мы будем использовать панель вкладок tab (шаблон @controls:tabs), которая будет состоять из двух вкладок. В первой вкладке мы разместим редактор строк. Вторая вкладка будет менять картинку фона, но реализовывать мы это будем на следующем этапе.

Шаблон вкладок @controls:tabs имеет аргумент шаблона items - перечень вкладок, элемент которого должен содержать поле content, которое в свою очередь должно являться строковым содержимым вкладки.

В нашем случае в качестве контента будет выступать шаблон, который можно разрендерить используя шаблонный аргумент, создаваемый функцией fcf.argTmpl(). Данная функция может принимать 2-а аргумента: путь к шаблону и не обязательный параметр - объект с аргументами шаблона. В результате ее выполнения возвращается объект с информацией о правилах сборки аргумента шаблона. Шаблонный аргумент при сборке аргументов шаблона раскрывается в строку содержащую результат рендеринга шаблона и может быть использована в конечном рендеринге.

Реализация редактора строк

Добавим вкладки для редактирования конфигурации. В качестве содержимого вкладки редактирования строк мы создадим подшаблон strings, в котором будет располагаться редактор строк с возможностью добавления и удаления отдельной строки. Каждая строка будет редактироваться шаблоном редактирования строки @controls:text-edit, а добавление и удаление строк будет привязано к кнопкам @controls:button.

Файл :templates/blocks/moving-containers.tmpl

//~OPTIONS
{
// Basic inheritance template
// Default: undefined
// extends:"",

// An array of roles that have permission to access the template
// Default: ["*"]
// access: ["*"],

// Automatic template update mode when the argument changes.
// Acceptable values:
// true|"all" - The update is performed on any change
// "external" - The update is performed only if the external template was the initiator of the change.
// false - The template is not being updated
// This option can be overridden by the fcfAutoUpdate template argument.
// Default: false
//autoUpdate: false,

// If true, the rendering is performed on the client side.
// Acceptable values:
// true|"all" - Rendering is done on the client, when done on the browser side
// "update" - The first render is done on the server side and the update is on the client side
// "update_np" - The first render is done on the server side and the update is on the client side.
// Parameters of the programmable type are not recalculated.
// false - Rendering is always done on the server side
// This option can be overridden by the fcfClientRendering template argument, but only if the option is true.
// Default: false
//clientRendering: false,

// Additional JS & CSS files to connect (JS files are also connected on the server side)
// Default: []
include: ["moving-containers.css"],

// Plug-in additional JS & CSS files on the client side
// Default: []
//clientInclude: [],

// If the parameter is false, the template is not wrapped in a container,
// a wrapper is not created for it, and its arguments are not available on the client.
// Default: true
//wrapper: true,

// DOM elements merge flag.
// If true, then existing items are not replaced when updated, but updated.
// Default: false
//merge: false,

// If set to true, the DOM elements are not changed by default when the template is updated.
// To update DOM elements on update, you need to call the update|reload|refresh methods with the updateStatic flag.
// Default: false
//static: false,

// Saving the initial values of children template arguments.
// This option can be overridden by the fcfInitialStorageOfChildren template argument.
// Default: false
//initialStorageOfChildren: false,

// The template is displayed when the template is locked or false,
// then the lock is performed only by the transparent container.
// If the option is true, then @controls:lock is used.
// This option can be overridden by the fcfLockTemplate template argument.
// Default: true
//lockTemplate: true
}
//~ARGUMENTS { _strings: fcf.argProg(), } //~TEMPLATE <div class="moving-containers-view"> %{{ for(let i = 0; i < args._strings.length; ++i) { }}% @{{ render.template( "+view-item", { string: fcf.argRef(`_strings[${i}]`), })}}@ %{{ } }}% </div> <fieldset> <legend>Editor</legend> @{{ render.template("@controls:tabs", { items: fcf.argVal({ strings: { title: "Strings", content: fcf.argTmpl("+strings", {strings: fcf.argRef("_strings")}) }, settings: { title: "Settings", content: "", } }) }); }}@ </fieldset> //~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~ SUBTEMPLATE view-item //~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~OPTIONS view-item { autoUpdate: true, clientRendering: true, } //~ARGUMENTS view-item { fcfClassInner: "moving-containers-view-item", string: "", } //~TEMPLATE view-item @{{args.string}}@ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~ SUBTEMPLATE strings //~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~TEMPLATE strings %{{ for(let i = 0; i < args.strings.length; ++i) { }}% <p> <table width="100%"><tr> <td width="100%"> @{{ render.template("@controls:text-edit", {value: fcf.argRef(`strings[${i}]`), width: "100%"}); }}@ </td> <td> @{{ render.template("@controls:button", {title: fcf.t("Remove"), fcfEventClick: `parent.getParent().getParent().onRemoveString(${i})`}); }}@ </td> </tr></table> </p> %{{ } }}% @{{ render.template("@controls:button", {title: fcf.t("Add record"), fcfEventClick: "parent.getParent().getParent().onAddString()"}); }}@

И добавим код враппера основного шаблона, в котором будет реализовано добавление и удаление строки в массиве _strings

Файл :templates/blocks/moving-containers.wrapper.js

fcf.module({ name: "templates/blocks/moving-containers.wrapper.js", dependencies: ["fcf:NClient/Wrapper.js"], module: function(Wrapper){ return class WrapperImpl extends Wrapper{ constructor(a_initializeOptions){ super(a_initializeOptions); } onRemoveString(a_index){ let strings = this.getArg("_strings"); strings.splice(a_index, 1); this.setArg("_strings", strings); this.update(); } onAddString() { let strings = this.getArg("_strings"); strings.push("") this.setArg("_strings", strings); this.update(); } }; } });

В приведенном коде обратим внимание на три нюанса.

Рендеринг шаблона вкладок использует вложенный аргумент шаблона fcf.argTmpl() в аргумент шаблона "items", созданный функцией fcf.argVal(). Это сделано потому, что если аргумент шаблона представлен значением, то его содержимое не анализируется на вложенные данные и содержащиеся в нем аргументы не раскрываются, а так же не выполняется токенизация. Но данные, предоставленные через функцию fcf.argVal(), обрабатываются, поэтому мы можем использовать вложенный шаблон.

Так же обратите внимание на то, как выполняется вызов методов onRemoveString() и onAddString() основного шаблона. Обращение к данным метода выполняется через метод getParent(). То есть, обращение происходит по цепочке: подшаблон strings; контрол @controls:tabs; основной шаблон.

В коде враппера внутри методов onRemoveString() и onAddString() выполняется вызов метода update, для обновления шаблона. Это сделано потому, что основной шаблон и подшаблон strings имеют значение опции шаблона autoUpdate равным false и при изменении значения внутри аргументов шаблонов обновления автоматически выполнятся не будет. Данный способ выбран потому, что при редактировании отдельной строки перерисовывать весь шаблон нет необходимости, особенно учитывая, что его рендеринг выполняется на стороне сервера.

Теперь откройте приложение в окне браузера и увидите результат.