This section will explain how templating and rendering work in Nodea applications. It will also explain the organization of the app / views folder in which all the .dust files are stored.
In fact, above all, the rendering tool used with Nodea is Dust.js. For more information => Dust.js by Linkedin
The views/ folder contains all of the application's .dust files. This file is structured in a certain way which requires some explanation:
Folder root:
Entity folder:
Here is a simplified diagram to illustrate the structure of the views/ folder.
In order to explain how our "block" dust structure is organized, it is necessary to understand the basic principle of templating. For this, do not hesitate to inquire at this address:
In a standard route the dust files called for rendering are: create.dust, update.dust, list.dust, show.dust
These files will then call the layout of the module which itself will call the main layout. These files will also include their corresponding _fields (for example for the create.dust it is create_fields.dust)
Here is a simple diagram showing the structure of the rendering process:
Simplified schema of the rendering process
The declared dust blocks are, all these blocks are declared in the main_layout.dust but are filled in the file specified between parenthesis:
Here is an image representing the location of the blocks on the GUI:
Comments: It is important not to make a comment starting with // in javascript in a dust file. This will result in crashing the rendering dust and the error displayed will not be particularly clear to understand that it comes from the js comment.
Semicolon: It is also very important to not forget the semicolon at the end of js lines, otherwise it will also crash the Dust.js rendering.
So to not waste your time unnecessarily, take into account the following code:
{<custom_js}
<script>
// <- Comment that will explode your rendering, don't do that in .dust files
/* Gorgeous comment that won't cause dust.js any problem */
console.log('Not ok')
console.log('Ok'); // Notice the semicolon
</script>
{/custom_js}
In summary comment with /* */ and not with //.
Custom CSS and custom JS can be included into create.dust, update.dust, show.dust and list.dust files. You have to place your specific codes in the corresponding blocks {custom_css} and {custom_js}.
It is important to never write custom CSS or JS in the dust files ending by “_fields.dust”! These files are only there to store the fields to display, so the only things that you can modify in these files are the HTML attributes.
By default Dust.js allows you to dump the entire current context via the following helper:
{@contextDump /}
This will displays directly in your HTML rendering a text block representing all the data sent by the server.
Be careful depending on where you place this helper : the result will be different, in fact it is a context dump so:
{#myContext1}
{@contextDump/} {! Will display all the contents of myContext1, including myContext2 !}
{#myContext2}
{@contextDump/} {! Will display all content from myContext2, so myContext1 will be excluded !}
{/myContext2}
{/myContext1}
Advice:
In order to make your context dump more readable, it is advisable to do this:
<script>console.log({@contextDump/});</script>
This way you will have a rendering of your dump directly in your client's console, and you will be able to browse objects and values much more easily.
Nodea provides a dedicated helper function allowing you to dump all the context present on the page, should help you too:
<script>console.log({@contextUpperDump/});</script>
See: Nodea helpers
Certain Nodea instructions allow you to add tabs within your entity, these tabs work entirely in ajax and this part aims to explain the native functioning of these tabs.
The following instructions allow you to add tabbed ajax content in the display (show.dust and show_fields.dust) of the targeted entity:
You can observe the behavior that a tab of type has one (for example) could have and how the execution of a creation form would be carried out natively. Subentity means the target entity.
Here are the steps of the detailed diagram:
For this section we will start from the following script:
add entity Source
entity Source has one Target
select entity Target
add field My Field
In this case you will find yourself with a source entity with a has one tab to an entity called Target.
Therefore you will have in the views folder this: e_source/r_target/tab.dust
It is in this tab.dust file that you will be able to customize the behavior of your tab.
To begin with, here is the native behavior:
e_source/r_target/tab.dust
{>"tabs/has_one" defaultJS="true" /}
<script class="tab-script" type="text/javascript" src="/core/js/tabs/has_one.js"></script>
In this file is first included the file _core/views/tabs/has_one.dust:
{#isEmpty}
{#__ key="message.empty" /}<br>
{! create btn !}
<a data-href="/{subentity}/create_form?{associationHref}&ajax=true" class="ajax btn btn-success">
<i class="fa fa-plus fa-md"> </i>
<span>{#__ key="button.create"/}</span>
</a>
{:else}
{#data}
{! fields !}
{>"{e_subentity}/show_fields" ajaxDisplay="true" /}
{! update btn !}
<a style="margin-right:8px;float:left;" data-href="/{subentity}/update_form?id={id}&{associationHref}&ajax=true" href="#" class="ajax btn btn-warning">
<i class="fas fa-edit"> </i>
<span>{#__ key="button.update"/}</span>
</a>
{! delete btn !}
<form action="/{subentity}/delete" style="float:left;" class="ajax" method="post">
<button class="btn btn-danger btn-confirm">
<i class="fas fa-trash"> </i>
<span>{#__ key="button.delete" /}</span>
</button>
<input name="id" value="{id}" type="hidden">
</form>
{/data}
{/isEmpty}
Then is included the javascript file necessary for the proper functioning of the forms in the tab:
$(function() {
const tab = NodeaTabs.current.tab;
NodeaForms(tab);
});
You have just seen the default behavior of a specific has one tab in a well-defined entity. If you want to modify this behavior, you will have to modify your tab.dust file.
Reminder: Never modify the contents of the _core/ folder! If you want to change the native behavior then you need to duplicate the _core/ files in your app/ folder and then integrate them into your tab.dust.
With dust.js you have access to a set of tools to help you generate your dust templates. These tools take the form of helpers, locales, and filters in Dust.js. I invite you to consult the Dust.js documentation to learn more:
In addition to the default elements of Dust.js we natively add the Dust.js exta module: Dust.js Extra
Finally Nodea also includes several helpers, locales and filters in its core that we will define here:
ifTrue → Check that the given value is equal to true, “true” or even 1
{@ifTrue key=myBoolean}
{:else}
{/ifTrue}
inArray → Checks the value of a key in a given array
{@inArray key=myKey value=myValue array=myArray}
{:else}
{/inArray}
in → Checks for the presence of a value in a comma-separated list of values
{@in value="myValue1" values="myValue,myValue1,myValue2"}
{:else}
{/in}
notIn → Checks for non-presence of a value in a comma-separated list of values
{@notIn value="myValue1" values="myValue,myValue1,myValue2"}
{:else}
{/notIn}
contextUpperDump → Improvement of the standard contextDump, go back in all context to display them all:
<script>console.log({@contextUpperDump/});</script>
__ → Allows you to return a translation according to the given key and the language of the user in session
{#__ key="message.success" /}
M_ → Allows you to return a translation according to the given key and the language of the user in session, and capitalizes the 1st letter
{#M_ key="message.success" /}
haveGroup → Check that the connected user owns the given group
{#haveGroup group="admin"}
{/haveGroup}
moduleAccess → Checks that the connected user can access the specified module
{#moduleAccess module="home"}
{/moduleAccess}
entityAccess → Checks that the connected user can access the specified entity
{#entityAccess entity="user"}
{/entityAccess}
actionAccess → Checks that the connected user can perform the action on a given entity
{#actionAccess entity="user" action="read"}
{/actionAccess}
checkStatusPermission → Check that the logged in user has access to the following status
statusObj must include its r_accepted_group relation
{#checkStatusPermission status=statusObj}
{/checkStatusPermission}
date → Convert a given date (moment format)
{myDate|date} {! Moment obj convert to YYYY-MM-DD (or DD/MM/YYYY for FR-fr lang)
datetime → Convert a given datetime (moment format)
{myDatetime|datetime} {! Moment obj convert to YYYY-MM-DD HH:mm (or DD/MM/YYYY HH:mm for FR-fr lang)
dateTZ → Convert a given date based on application timezone (moment format)
{myDate|dateTZ}
datetimeTZ → Convert a given datetime based on application timezone (moment format)
{myDatetime|datetimeTZ}
time → Remove seconds from time
{myTime|time} {! Convert 12:54:26 -> 12:54 !}
filename → Remove automatic uuid prefix from filename
{myFilename|filename} {! Convert 654258432489461384548654654654421856-my_file.txt -> my_file.txt !}
urlencode → Apply encodeURIComponent on given url
{myUrl|urlencode}
If you want to add your own function to dust.js just go to the app/utils/dust.js
file and you will find everything you need to add your own Helpers, Locals and Filter
File content:
// ----------- Custom Dust Locals | Helper | Filters ----------- //
// Example:
// {@myHelper} for global helpers
// {#myHelper} for context helpers
// {variable|myFilter} for context filters
// Add your custom Dust Locals | Helper | Filters here =>
module.exports = {
locals: function(locals, req, language, block_access) {
// Use example: {#logCurrentUser param="myParam"}{/logCurrentUser}
locals.logCurrentUser = function(chunk, context, bodies, params) {
console.log(params.param);
console.log(req.user);
}
return locals;
},
helpers: function(dust) {
// Use example: {@myDustHelper param="myParam"}{/myDustHelper}
dust.helpers.myDustHelper = function(chunk, context, bodies, params) {
console.log(params.param);
}
return dust;
},
filters: function(dust, lang) {
// Use example: {myDate|convertToDateFormat}
dust.filters.convertToDateFormat = function(value) {
console.log('Converting date: ', value);
}
return dust;
}
};