AnApprentice
AnApprentice

Reputation: 110950

How to handle an IF STATEMENT in a Mustache template?

I'm using mustache. I'm generating a list of notifications. A notification JSON object looks like:

[
  {
    "id": 1364,
    "read": true,
    "author_id": 30,
    "author_name": "Mr A",
    "author_photo": "image.jpg",
    "story": "wants to connect",
    "notified_type": "Friendship",
    "action": "create"
  }
]

With mustache, how can I do a if statement or case statement based on the notified_type & action...

If notified_type == "Friendship" render ......

If notified_type == "Other && action == "invite" render.....

How does that work?

Upvotes: 103

Views: 171086

Answers (5)

Meloman
Meloman

Reputation: 3712

Based on @françois-dispaux solution, I wrote a recursive variant called prepMustache browsing multi-level objects and adding key=value keys for all child but also :

  • hasTimexxx for variable timexxx greater than 00:00:00
  • var123>0 for Integer variable var123 is greater than 0
  • key[i]=value for sub-arrays

And I also put this function in global scope to use it everywhere in my Node-Red project.

const prepMustache = (function (object) {

    for (const key in object) {

        const value       = object[key];
        const keyWithVal  = `${key}=${value}`;

        // IS STRING, INT or BOOL
        if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
            
            object[keyWithVal] = true;

            // INT greater than zero
            if (typeof value === 'number' && Number.isInteger(value) && value > 0) {
                const intKeyWithValue = `${key}>0`;
                object[intKeyWithValue] = true;
            }

            // TIME (format 00:00:00) greater than 00:00:00
            else if (typeof value === 'string' && /^([0-9]{2}:){2}[0-9]{2}$/.test(value)) {
                const timeArray = value.split(':');
                const hours = parseInt(timeArray[0]);
                const minutes = parseInt(timeArray[1]);
                const seconds = parseInt(timeArray[2]);
                if (hours > 0 || minutes > 0 || seconds > 0) {
                    const timeKey = `has${key.charAt(0).toUpperCase() + key.slice(1)}: true`;
                    object[timeKey] = true;
                }
            }
        }

        // IS SUB-ARRAY
        else if (Array.isArray(value)) {
            value.forEach((val, i) => {
                if (typeof val === 'object') {
                    prepMustache(val);
                } else {
                    const arrKeyWithVal = `${key}[${i}]=${val}`;
                    object[arrKeyWithVal] = true;
                }
            });
        }

        // IS SUB-OBJECT
        else if (typeof value === 'object') {
            prepMustache(value);
        }
    }

    return object;
});

// Add this for NodeRed global scope only
global.set('prepMustache', prepMustache);

use :

vars = prepMustache(vars);

use (Node-Red) :

msg.payload = global.get('prepMustache')(msg.payload)

Upvotes: 0

mu is too short
mu is too short

Reputation: 434615

Mustache templates are, by design, very simple; the homepage even says:

Logic-less templates.

So the general approach is to do your logic in JavaScript and set a bunch of flags:

if(notified_type == "Friendship")
    data.type_friendship = true;
else if(notified_type == "Other" && action == "invite")
    data.type_other_invite = true;
//...

and then in your template:

{{#type_friendship}}
    friendship...
{{/type_friendship}}
{{#type_other_invite}}
    invite...
{{/type_other_invite}}

If you want some more advanced functionality but want to maintain most of Mustache's simplicity, you could look at Handlebars:

Handlebars provides the power necessary to let you build semantic templates effectively with no frustration.

Mustache templates are compatible with Handlebars, so you can take a Mustache template, import it into Handlebars, and start taking advantage of the extra Handlebars features.

Upvotes: 98

Lucas
Lucas

Reputation: 3471

Just took a look over the mustache docs and they support "inverted sections" in which they state

they (inverted sections) will be rendered if the key doesn't exist, is false, or is an empty list

http://mustache.github.io/mustache.5.html#Inverted-Sections

{{#value}}
  value is true
{{/value}}
{{^value}}
  value is false
{{/value}}

Upvotes: 156

François Dispaux
François Dispaux

Reputation: 746

I have a simple and generic hack to perform key/value if statement instead of boolean-only in mustache (and in an extremely readable fashion!) :

function buildOptions (object) {
    var validTypes = ['string', 'number', 'boolean'];
    var value;
    var key;
    for (key in object) {
        value = object[key];
        if (object.hasOwnProperty(key) && validTypes.indexOf(typeof value) !== -1) {
            object[key + '=' + value] = true;
        }
    }
    return object;
}

With this hack, an object like this:

var contact = {
  "id": 1364,
  "author_name": "Mr Nobody",
  "notified_type": "friendship",
  "action": "create"
};

Will look like this before transformation:

var contact = {
  "id": 1364,
  "id=1364": true,
  "author_name": "Mr Nobody",
  "author_name=Mr Nobody": true,
  "notified_type": "friendship",
  "notified_type=friendship": true,
  "action": "create",
  "action=create": true
};

And your mustache template will look like this:

{{#notified_type=friendship}}
    friendship…
{{/notified_type=friendship}}

{{#notified_type=invite}}
    invite…
{{/notified_type=invite}}

Upvotes: 22

Dave Newton
Dave Newton

Reputation: 160181

In general, you use the # syntax:

{{#a_boolean}}
  I only show up if the boolean was true.
{{/a_boolean}}

The goal is to move as much logic as possible out of the template (which makes sense).

Upvotes: 36

Related Questions