First function is overriding all instances in Mustache template [Solved]

Hi all,

I’m using the Mustache template which renders the JSON onto the page. Same set up for those who helped with previous threads, thanks in advance.

For an example

name: Luke Skywalker

Now when I create a function and change this, everything works

{{#formatString}}{{name}}{{/formatString}}

luke-skywalker

Now if I call/display the name again without the function, the name is still showing as luke-skywalker. I think it has something to do with this.name though could be mistaken.

Code

function formatString(str) {
  return str.replace(/\s+/g, '-').toLowerCase();
}
function formatStringTwo(str) {
  return str.replace(/\s+/g, '+').toLowerCase();
}
var renderFuncs = {
  'formatString': function () {
    return function (text, render) {
      this.name = formatString(this.name);
      return render(text);
    };
  },
  'formatStringTwo': function () {
    return function (text, render) {
      this.name = formatStringTwo(this.name);
      return render(text);
    };
  }
}
<ul>
    <li>{{#formatString}}{{name}}{{/formatString}}</li>
    <li>{{name}}</li>
    <li>{{#formatStringTwo}}{{name}}{{/formatStringTwo}}</li>
    <li>{{name}}</li>
</ul>

The above prints

luke-skywalker
luke-skywalker
luke-skywalker
luke-skywalker

It should print

luke-skywalker
Luke Skywalker
luke+skywalker
Luke Skywalker

Any ideas, advice what might be causing this?

Codepen

Barry

You’re modifying the original data here. After this line

$.extend(data, renderFuncs)

your data object looks something like this

{
  name: 'Luke Skywalker',
  // ...
  formatString: function () {
    // This function gets called first...
    return function (text, render) {
      // this.name is "Luke Skywalker"
      this.name = formatString(this.name)
      // this.name is now "luke-skywalker"
      return render(text)
    }
  },
  formatStringTwo: function () {
    // Then this function gets called...
    return function (text, render) {
      // this.name is still "luke-skywalker",
      // so /\s+/ has no match
      this.name = formatStringTwo(this.name)
      return render(text)
    }
  }
}

Instead you’ll just have to return the formatted text value without mutating anything:

var renderFuncs = {
  formatString: function () {
    return function (text, render) {
      return formatString(render(text))
    }
  },
  formatStringTwo: function () {
    return function (text, render) {
      return formatStringTwo(render(text))
    }
  }
}
1 Like

Thanks @m3g4p0p

I’m sure I did try this :upside_down:

Initially, what you suggest is what I was using some time back. On an older thread I was trying to format a date object which would only work by adding, what we have just removed (below snippet), this fixed the issue after much trail and error. I carried on using this technique ever since.

Maybe this was just need to format the date.

   formatDate: function () {
	  var format = "DD-MM-YYYY";
	  return function (text, render) {
      this.submitted = dateConvert(new Date(this.submitted), format);
      return '<b>' + render(text) + '</b>';
    };
  }

That’s the history behind me using this anyhow :nerd:

What you suggest is working perfect now :grinning:
I’ll have to run a few more tests and add the date into the mix and see what happens.

Updated Codepen

Cool, thanks for helping and detailed explanation.
Barry

Well no, that solution is not quite right either I’m afraid… it may work in this case but again, it modifies the data. So it should actually like this:

Mustache.render(template, {
  submitted: '2018-03-06T12:01:00',
  formatDate: function () {
    return function (text, render) {
      var format = 'DD-MM-YYYY'
      var rendered = render(text).trim()
      var date = new Date(rendered)
      
      return dateConvert(date, format)
    }
  }
})

If you do want to modify the data, you’d do so beforehand so you get no surprises later on. For example, $.enxtend()ing it with render functions is okay because the data object gets discarded after the render process anyway. You could also explicitly prepare the data like so:

data.submitted = dateConvert(new Date(data.submitted), format)
Mustache.render(template, data)

But a render function itself should never do this (be it in mustache, react, vue or what have you); given the same data, it should always yield the same output no matter how often or at which point it gets called.

1 Like

Thanks a lot!
Share the knowledge :grinning:

Some useful information, might refactor some of my code :nerd:

…it should always yield the same output no matter how often or at which point it gets called

My problem initially.
And nice to know, React is on my wish list :slight_smile:

Cheers,
Barry

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.