Saving data on the server and *.receive.js files

In addition to rendering tasks, templates can perform tasks on the server side. This functionality is implemented using a server-side handler provided by *.receive.js files. The handler is called by calling the fcf.NClient.Wrapper.send method. Its signature is as follows:

fcf.Actions->mixed fcf.NClient.Wrapper.send(object a_data, array[FileList|DomElement{input type=file}] a_files = undefined).

The method calls a handler on the server side and asynchronously returns the result of its execution. Using this method, you can transfer both data and files in the form of an array of FileList objects or input elements of the file type.

The server side template handler [TEMPLATE_NAME].receive.js is as follows.

fcf.module({ name: "templates/blocks/moving-containers.receive.js", dependencies: [], lazy: [], module: function() { return class Handler { receive(a_data, a_files){ } } } });

As you can see from the above code, the module provides a handler class with one obligatory receive method. Which takes two arguments corresponding to the arguments of the fcf.NClient.Wrapper.send method. Receive method arguments:

  • mixed a_data - Data transmitted by the fcf.NClient.Wrapper.send method
  • array[object] a_files - An array of objects that store information about uploaded files. Each object has the following fields:
    • integer size - File size.
    • string path - File path.
    • string name - The file name on the client side.
    • string type - MIME type.
    • object attributes - An object with client-side input element attribute values.

The result of the receive method execution is returned by the fcf.NClient.Wrapper.send method.

Now let's add to our application the ability to save the configuration on the server side.

Let's add a button to save the configuration.

File :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> ...

And add an onSave event handler to the main wrapper code.

File :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]]); } }; } });

It remains only to save the data on the server side

File 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")); } } } } });

Now you can see how it all works