12

I have started using mustache.js and so far I am very impressed. Although two things puzzle me. The first leads on to the second so bear with me.

My JSON

{"goalsCollection": [
    {
        "Id": "d5dce10e-513c-449d-8e34-8fe771fa464a",
        "Description": "Multum",
        "TargetAmount": 2935.9,
        "TargetDate": "/Date(1558998000000)/"
    },
    {
        "Id": "eac65501-21f5-f831-fb07-dcfead50d1d9",
        "Description": "quad nomen",
        "TargetAmount": 6976.12,
        "TargetDate": "/Date(1606953600000)/"
    }
]};

My handling function

function renderInvestmentGoals(collection) {
    var tpl = '{{#goalsCollection}}<tr><td>{{Description}}</td><td>{{TargetAmount}}</td><td>{{TargetDate}}</td></tr>{{/goalsCollection}}';
    $('#tblGoals tbody').html('').html(Mustache.to_html(tpl, collection));
}

Q1 As you can see my 'TargetDate' needs parsing but I am unsure of how to do that within my current function.

Q2 Say I wanted to perform some function or formatting on one or more of my objects before rendering, what is the best way of doing it?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Dooie
  • 1,649
  • 7
  • 30
  • 47

7 Answers7

21

You can use "Lambdas" from mustache(5)

"TargetDate": "/Date(1606953600000)/",
"FormatDate": function() {
    return function(rawDate) {
        return rawDate.toString();
    }
}, ...

Then in the markup:

<td>
{{#FormatDate}}
    {{TargetDate}}
{{/FormatDate}}
</td>

From the link:

When the value is a callable object, such as a function or lambda, the object will be invoked and passed the block of text. The text passed is the literal block, unrendered.

Tranzium
  • 89
  • 2
  • 10
McGarnagle
  • 101,349
  • 31
  • 229
  • 260
  • 1
    Ok, i get how this would work but how do I construct my JSON to include the 'FormatDate' function, as my result is a serialized dictionary? – Dooie Apr 08 '12 at 18:29
  • 1
    You would have to use JSONP, ie. return an executable script instead of pure data... which kinda defeats data/presentation/logic separation. – Eric Grange Oct 14 '14 at 15:39
  • 1
    You could also use something like : Mustache.to_html(tpl, $.extend({}, DefaultTplFunctions, collection}) and then the object DefautTplFunctions contains FormatDate fucntion (and other functions similar ot this one) – Christophe Blin Mar 24 '15 at 12:40
  • The link for `lambda` is broken – ccpizza Apr 14 '22 at 20:27
10

I have created a small extension for Mustache.js which enables the use of formatters inside of expressions, like {{expression | formatter}}

You would anyway need to create a function that parses your date value like this:


      Mustache.Formatters = {
        date: function( str) {
          var dt = new Date( parseInt( str.substr(6, str.length-8), 10));
          return (dt.getDate() + "/" + (dt.getMonth() + 1) + "/" + dt.getFullYear());
        }
      };

And then just add the formatter to your expressions:

{{TargetDate | date}}

You can grab the code from here: http://jvitela.github.io/mustache-wax/

JVitela
  • 2,472
  • 2
  • 22
  • 20
  • Took me a bit to realize that mustache-wax.js does not work when pulling a #template from the html where it will be rendered, or is there another method of using it? I currently wrap things on a – Ricardo Alves Mar 07 '17 at 22:37
  • Could you provide an example? Normally you load first the templates into a string and then pass it to Mustache.render. – JVitela Mar 09 '17 at 08:34
7

It's a long time ago but got on this looking for exactly the same. Mustachejs (now) allows you to call functions of the passed data and not only that; in the function the value of this is whatever value is true in a section.

If my template is like this:

{{#names}}
    <p>Name is:{{name}}</p>
    <!-- Comment will be removed by compileTemplates.sh
         #lastLogin is an if statement if lastLogin it'll do this 
         ^lastLogin will execute if there is not lastLogin      
    -->
    {{#lastLogin}}
    <!-- 
      formatLogin is a method to format last Login 
      the function has to be part of the data sent 
      to the template
    -->
    <p>Last Login:{{formatLogin}}</p>
    {{/lastLogin}}
    {{^lastLogin}}
    not logged in yet
    {{/lastLogin}}
    {{#name}}
     passing name to it now:{{formatLogin}}
    {{/name}}
{{/names}}

And Data like this:

var data={
    names:[
        {name:"Willy",lastLogin:new Date()}
    ],
    formatLogin:function(){
          //this is the lastDate used or name based on the block
                  //{{#name}}{{formatLogin}}{{/name}}:this is name
                  //{{#lastLogin}}{{formatLogin}}{{/lastLogin}}:this is lastLogin
          if(!/Date\]$/.test(Object.prototype.toString.call(this))){
              return "Invalid Date:"+this;
          }
          return this.getFullYear()
            +"-"+this.getMonth()+1
            +"-"+this.getDate();
    }
};
var output = Mustache.render(templates.test, data);
console.log(output);
HMR
  • 37,593
  • 24
  • 91
  • 160
1

You can get the timestamp using simple String methods:

goalsCollection.targetDate = goalsCollection.targetDate.substring(6,18);

Of course, this depends on your timestamp being the same length each time. Another option is:

goalsCollection.targetDate = 
  goalsCollection.targetDate.substring(6, goalsCollection.targetDate.length - 1);

These techniques aren't specific to Mustache and can be used to manipulate data for any library. See the Mozilla Developer Center Documentation on substring for more details.

jamesmortensen
  • 33,636
  • 11
  • 99
  • 120
1

To declare a function within a json you can always do this.

var json = '{"RESULTS": true, "count": 1, "targetdate" : "/Date(1606953600000)/"}'

var obj = JSON.parse(json);
obj.newFunc = function (x) {
  return x;
}

//OUTPUT
alert(obj.newFunc(123));
jimmyo
  • 151
  • 3
0

Working example of a 'lambda' function for parsing an ISO-8601 date and formatting as UTC:

var data = [
  {
    "name": "Start",
    "date": "2020-04-11T00:32:00.000-04:00"
  },
  {
    "name": "End",
    "date": "2022-04-11T00:32:00.000-04:00"
  },
]

var template = `
{{#items}}
<h1>{{name}}</h1>
  {{#dateFormat}}
     {{date}}
  {{/dateFormat}}
{{/items}}
`;


var html = Mustache.render(template, {
  items: data,
  dateFormat: function () {
    return function (timestamp, render) {
      return new Date(render(timestamp).trim()).toUTCString();
    };
  }
});

document.getElementById("main").innerHTML = html;
<script src="https://unpkg.com/mustache@4.2.0/mustache.min.js"></script>

<div id="main"></div>

If you want fancier date formatting you could use for example something like:

new Date().toLocaleDateString('en-GB', {
    day : 'numeric',
    month : 'short',
    year : 'numeric', hour: 'numeric', minute: 'numeric'
})
// outputs '14 Apr 2022, 11:11'
ccpizza
  • 28,968
  • 18
  • 162
  • 169
-2

I've been using Mustache for my projects as well, due to its ability to be shared across client/server. What I ended up doing was formatting all values (dates, currency) to strings server-side, so I don't have to rely on helper Javascript functions. This may not work well for you though, if you're doing logic against these values client-side.

You might also want to look into using handlebars.js, which is essentially Mustache, but with extensions that may help with client-side formatting (and more). The loss here is that you will probably not be able to find a server-side implementation of handlebars, if that matters to you.

mystictheory
  • 110
  • 1
  • 7
  • 12
    This is a bad answer. The formatting of numbers is presentation logic - not business or application logic. It does not belong in the server. Rather, it should be part of the template language or gui somehow. – egervari Sep 17 '12 at 04:14
  • I agree with previous comment : the answer "use a lamdba" is the correct one – Christophe Blin Mar 24 '15 at 12:43
  • "It's presentation logic" is too shallow. How would one reuse a complex formatting function in Mustache? Copying+pasting lambdas to follow such a dogmatic view of things is worse than adding strings to server data. Creating a string representation isn't a presentation function unless toString() is. If how to format something is object-dependent, it sure belongs in that object. Otherwise you get duplicated formatting code all over a project and differences arise. Almost every company makes this costly mistake. Get away from dogma and be pragmatic. – grantwparks Feb 10 '18 at 20:09