5 replies
Jun '16

comerc

Each and With There are also two Spacebars built-in helpers, {{#each}}, and {{#with}}, which we do not recommend using (see use each-in below). These block helpers change the data context within a template, which can be difficult to reason about.

How about this case? (we care about <ul>):

      +with games
        ul
          +each game in this
            li {{game.name}}
      else
        | Not Found

or we have strange code without {{#with}} :slight_smile:

      +let tmp_games=games
        +if tmp_games
          ul
            +each game in tmp_games
              li {{game.name}}
        else
          | Not Found
 
Jun '16

veered

One Blaze pattern that I am very fond of is ditching helpers and storing everything on the template instance. In onCreated:

Template.templateName.onCreated(function() {
  let tpl = this;

  _.extend(tpl, {

    someState: new ReactiveVar([], EJSON.equals),

    initialize(params) {
      // do whatever initialization e.g.:
      tpl.listenForChanges();
    },

    listenForChanges() {
      tpl.autorun(comp => ...);
    },

    useThisInsteadOfHelper(arg1, arg2) {

     },

  }).initialize(tpl.data);
});

Then if you declare a global helper:

Template.registerHelper('$tpl', function () {
  return Template.instance();
});

You can do {{$tpl.useThisInsteadOfHelper arg1 arg2}} or {{$tpl.someState.get}} inside of the templateName html file. This means that your functions can call each other, store state on the template instance without calling Template.instance() everywhere, and aren’t run everytime anything in the data context changes (if you want the function to rerun whenever anything in the data context changes you have to explicitly call Template.currentData()). This gives waaaay more flexibility.

1 reply
Jun '16

comerc

I have elegant bicycle for store state:

You have clicked the button {{state.counter}} times.
<button>click</button>
Template2 'hello',
  states: counter: 0
  events: 'click button': ->
    # increment the counter when button is clicked
    @state.counter += 1
Jun '16

solarc

You still need {{#with ... }} to switch the context to the looped item for accessing it from an event and don’t want to write another template for it:

{{#each user in users}}
    {{ user.name }}
    {{#with user=user }}
        <span class="remove">X</span>
    {{/with}}
{{/each}}

Template.users.events({
    'click .remove': function(event) {
        console.log(this);
    }
});

Using the {{#with }} wrapper console.log will return {user: {name: ... }}; without it it will return {users: [{name: ...}]} and you won’t know which user from the list you clicked on.

Is there a way to add the user to the context with {{#let }} instead? The other way I found to do it is add a data attribute to the span an retrieve it from the event.target.dataset but then it turns into a string and feels wrong. Sometimes I still need access to other objects from the outer context and then it turns into a monstrous {{#with user=user users=users foo=foo }}.

1 reply
Jun '16

comerc

Thanks! It is better. But we need define new variable:

      +with tmp_games=games
        ul
          +each game in tmp_games
            li {{game.name}}
      else
        | Not Found

Because helper games called twice in this case:

      +with games=games
        ul
          +each game in games
            li {{game.name}}
      else
        | Not Found