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


#1

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


#2

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))
    }
  }
}

#3

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


#4

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.


#5

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


#6

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