Rendering a template Template rendering in the FCF framework can be performed from different parts of the WEB application: from the template body, from the template argument, from the template hook, from the browser and from the server code. All of these five cases are different.

In this section, we will get acquainted with the following types of rendering:

Rendering from a template

Most often, when building a WEB application, this kind of rendering is used, it can be performed both in the constructions of the %{{}}% code, and in the constructions of the output @{{}}@.

The template is rendered using the render.template() method of the internal template object render. The first parameter of this method is the path to the template, and the second is the template arguments.

Here is an example of rendering a template based on an application form ($ fcfmngr create project test). File :templates/pages/main-page.tmpl:

//~OPTIONS { // Basic inheritance template extends: ":templates/super/page.tmpl", } //~ARGUMENTS body { } //~TEMPLATE body %{{ let textEdit = await render.template("@controls:text-edit", { value: "text", width: "100%"}); }}% <p> @{{textEdit}}@ </p> <p> @{{ render.template("@controls:textarea", { value: "some text", width: "100%"}); }}@ </p>

In this example, we render two templates from the fcfControls package by their aliases: "@controls:text-edit" is an input field and "@controls:textarea" is a multiline text input field.

Rendering is done in the code construct %{{}}% and in the output construct @{{}}@. But in the construction of the code, waiting for the completion of the operation is performed through the await operator, after which the result is written to the textEdit variable, which is output to the template through the output operation @{{}}@.

For output operations, the await operator is not required, the template engine independently determines that the output is directed to the Promise or fcf.Actions object

The template path can be specified either as a direct path to the template, or as an alias that begins with the @ symbol to simplify access to a frequently used resource. Aliases are specified in the aliases configuration parameter. This is how the fcfControls package defines aliases for input fields:

File fcfControls:package.config

{ ... aliases: { ... "controls:text": "fcfControls:templates/text.tmpl", ... "controls:textarea": "fcfControls:templates/textarea.tmpl", ... } ... }

The rendering result is shown below.

Rendering from a template argument

There are times when it is more convenient to render a template simply by declaring a template argument; for this, the template argument created by the fcf.argTmpl() function is used

This type of arguments is expanded into the template when assembling the arguments of the parent template.

The first parameter of the fcf.argTmpl() function is the path/name of the template, and the second is its arguments.

As an example, let's show the output of a dialog with a button created by a template argument. The dialog template from the fcfControls package, alias @controls:dialog, has a buttons argument, which must be an array of strings containing HTML elements.

Example of a page file :templates/pages/main-page.tmpl created on the basis of a blank application ($ fcfmngr create project test).

//~OPTIONS { // Basic inheritance template extends: ":templates/super/page.tmpl", } //~ARGUMENTS body { } //~TEMPLATE body @{{ render.template( "@controls:dialog", { content: "Hello to this beautiful world!", buttons: fcf.argVal([ "***", fcf.argTmpl("@controls:button", {title: "Close dialog", fcfEventClick: "parent.close()"}), "***", ]) }); }}@

Note that the fcf.argTmpl()function is called inside the argument created by the fcf.argVal() function, this is necessary because when processing template arguments, simple values (objects and arrays) are not parsed for nested arguments and no tokenization is performed on them. In order for the template builder to process the nested argument with our button, we used the template argument created by calling fcf.argVal().

Rendering result:

Rendering from a hook handler

Rendering from the hook handler is performed by the fcf.NRender.TaskInfo.render() method. Let's give a small example based on a basic project created by the command: $ fcfmngr create project

Page template :templates/pages/main-page.tmpl: //~OPTIONS { // Basic inheritance template extends: ":templates/super/page.tmpl", } //~OPTIONS body { autoUpdate: true, } //~ARGUMENTS body { templateName: "+block", block: fcf.argProg(), } //~TEMPLATE body <a fcfEventClick="wrapper.setArg('templateName', '+block')">Show first block</a> | <a fcfEventClick="wrapper.setArg('templateName', '@controls:button')">Show button</a> | <a fcfEventClick="wrapper.setArg('templateName', '@controls:text-edit')">Show text-edit</a> <hr> @{{args.block}}@ //~TEMPLATE block <h3>Example rendering from hooks</h3>

Hook Handlers File :templates/pages/main-page+body.hooks.js

fcf.module({ name: "templates/pages/main-page+body.hooks.js", dependencies: [], module: function(){ return { // // Object of hooks for programmatically populated arguments // hooksProgrammableArgument: { // // Hook of the assembly of a programmatically populated argument created by the fcf.argProg() method with the name ARG_NAME // @result Returns the value of an argument or a Promise object // block: (a_taskInfo) => { return a_taskInfo.render(a_taskInfo.args.templateName); }, }, }; } });

As you can see from the code of the files, when you click on the links, the template will be redrawn. the autoUpdate parameter is true. The template path is set when the link is clicked using the Wrapper.setArg method. The template itself is rendered in the file :templates/pages/main-page+body.hooks.js into the variable block, which is output in its pure form to the main template:

Rendering a template outside of the server-side templating engine

There are times when it is necessary to render outside of the standard template engine mechanisms, for example, in a custom controller, when sending a letter, or in another case. Then the method is used fcf.application.render()

Here is an example of rendering a button template using the fcf.application.render() method:

let template = await fcf.application.render({ template: "@controls:button", args: { title: "Hello World!" } }); console.warn("", template.content);

Result:

<div fcftemplate="fcfControls:templates/button.tmpl" id='_4e508c3cd14a3989363d57084f2a4605' class=' fcfwrapper ' ><button autocomplete="off" style="width:auto" class="fcf-button ">Hello World!</button></div>

Rendering a template outside of the template engine on the client side

On the client side, rendering is performed by the fcf.NClient.Application.render() function of the fcf.application object

Here is an example of displaying a dialog when clicking on a link. Let's create an example based on the application form created by the command:

$ fcfmngr create project test

Page body file :templates/pages/main-page.tmpl будет иметь вид:

//~OPTIONS { // Basic inheritance template extends: ":templates/super/page.tmpl", } //~TEMPLATE body <a fcfEventClick="wrapper.onClick()">Show dialog</a>

We will render the dialog in the onClick method of the template wrapper :templates/pages/main-page+body.wrapper.js

fcf.module({ name: "templates/pages/main-page+body.wrapper.js", dependencies: [":templates/super/page+body.wrapper.js"], module: function(Wrapper){ return class WrapperEx extends Wrapper{ constructor(a_initializeOptions){ super(a_initializeOptions); } onClick(){ fcf.application.render({ template: "@controls:dialog", owner: document.body, args: { content: "Hello world!" } }) } }; } });

As you can see from the example, the template is placed in document.body as specified in the owner parameter.

Now, if we run the application, open the http: localhost:8080 address in the browser.

$ fcfserver ./server.json

After clicking on the link, we will see the output of our dialog.