Помимо задач рендеринга, шаблоны могут выполнять задачи на стороне сервера. Данный функционал реализован при помощи обработчика на стороне сервера, предоставляемым файлами *.receive.js. Вызов обработчика выполняется вызовом метода fcf.NClient.Wrapper.send. Его сигнатура имеет следующий вид:
fcf.Actions->mixed fcf.NClient.Wrapper.send(object a_data, array[FileList|DomElement{input type=file}] a_files = undefined).
Метод вызывает на стороне сервера обработчик и асинхронно возвращает результат его исполнения. Используя данный метод можно передавать, как данные, так и файлы в виде массива объектов FileList или input элементов имеющих тип file.
Обработчик шаблона на стороне сервера [TEMPLATE_NAME].receive.js имеет следующий вид.
fcf.module({
name: "templates/blocks/moving-containers.receive.js",
dependencies: [],
lazy: [],
module: function() {
return class Handler {
receive(a_data, a_files){
}
}
}
});
Как видно из выше приведенного кода, модуль поставляет класс обработчика с одним обязательным методом receive. Который принимает два аргумента, соответствующих аргументам метода fcf.NClient.Wrapper.send.
Аргументы метода receive:
-
mixed a_data - Данные переданные методом fcf.NClient.Wrapper.send
-
array[object] a_files - Массив объектов, хранящих информацию о переданных файлах. Каждый объект имеет следующие поля:
-
integer size - Размер файла.
-
string path - Путь к файлу.
-
string name - Имя файла на стороне клиента.
-
string type - MIME тип.
-
object attributes - Объект со значениями атрибутов input элемента на стороне клиента.
Результат исполнения метода receive возвращает метод fcf.NClient.Wrapper.send.
Теперь допишем в наше приложение возможность сохранения конфигурации на стороне сервера.
Добавим кнопку для сохранения конфигурации.
Файл :templates/blocks/moving-containers.tmpl
...
//~TEMPLATE
<div class="moving-containers-view" style="background-image: url(@{{args._file}}@);">
<div class="moving-containers-view-info">
Rebound counter: <span name="rebound_counter">#{{args._reboundCounter}}#</span>
</div>
%{{
for(let i = 0; i < args._strings.length; ++i) {
}}%
@{{
render.template(
"+view-item",
{
string: fcf.argRef(`_strings[${i}]`),
fcfEventRebound: "parent.onReboundString(event)"
})}}@
%{{
}
}}%
</div>
<fieldset>
<legend>Editor</legend>
@{{
render.template("@controls:tabs", {
items: fcf.argVal({
strings: {
title: "Strings",
data: fcf.argTmpl("+strings", {strings: fcf.argRef("_strings")})
},
settings: {
title: "Settings",
data: fcf.argTmpl("+settings")
}
})
});
}}@
@{{ render.template("@controls:button", {title: fcf.t("Save"), fcfEventClick: "parent.onSave()"}); }}@
</fieldset>
...
И добавим обработчик события onSave в код основного враппера.
Файл :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();
}
onFile() {
let self = this;
let fileReader = new FileReader();
fileReader.onload = () => {
self.setArg("_file", fileReader.result);
self.update();
}
fileReader.readAsDataURL(this.select("input[type=file]")[0].files[0]);
}
onSave() {
this.send({strings: this.getArg("_strings")}, [this.select("input[type='file']")[0]]);
}
};
}
});
Осталось только сохранить данные на стороне сервера
Файл templates/blocks/moving-containers.receive.js
const libFS = require('fs');
const libUtil = require('util');
fcf.module({
name: "templates/blocks/moving-containers.receive.js",
dependencies: [],
lazy: [],
module: function() {
return class Handler {
async receive(a_data, a_files){
if (a_data.strings) {
await fcf.application.setSystemVariable("application:strings", a_data.strings);
}
if (a_files[0]) {
await libUtil.promisify(libFS.copyFile)(a_files[0].path, fcf.getPath(":files/background.jpg"));
}
}
}
}
});
Теперь можно посмотреть, как все это работает