Dariel Dato-on
Dariel Dato-on

Reputation: 225

How to be explicit with data context?

Beginner question here: I'm working through the Discover Meteor book, Chapter 3 on Templates. This line of the template gives me pause:

<h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3>

What's going on here is the {{domain}} expression is coming from the postItem template helper and the {{url}} and {{title}} expressions are coming from the postsList template helper. However, by just looking at the template, there is no way to tell which expression is handled by which template helper. I understand you can make the template more explicit by using the this keyword in the template. However, instead of doing this, is there a way I can explicitly create a helper in the postItem template? For example, I tried doing this, but it did not work:

Template.postItem.helpers({
  title: this.title,
  url: this.url,
  domain: function() {
    var a = document.createElement('a');
    a.href = this.url;
    return a.hostname;
  }
});

And I don't understand why this would not work. I understand this may make the code more verbose, but personally, I would prefer the code to be more explicit than leaving it ambiguous.

Thanks for your help!

Upvotes: 0

Views: 77

Answers (2)

looshi
looshi

Reputation: 1236

This is my preferred way to be explicit about data context using Blaze in Meteor.

You can see a working example here :

http://meteorpad.com/pad/rkybNCiKBpzEdcfHc/Template%20Parameters

I provide each child template an explicit list of parameters like this :

{{> player name=this.name score=this.score}}

In this example, each list item will only need to display a user's name and their score. So, the child template gets only two variables as its data context, name and score, and nothing else.
This helps me debug issues and define exactly which fields a template needs.

//   Parent Template
Template.leaderboard.helpers({
  players: function () {
    return Players.find({}, { sort: { score: -1, name: 1 } });
  }
});

<template name="leaderboard">
  {{#each players}}
    {{> player name=this.name score=this.score}}
  {{/each}}
</template>


//   Child Template
<template name="player">
  <div>{{userName}} : {{userScore}}</div>
</template>

Template.player.helpers({
  userName: function() {
    return this.name;
  },
  userScore: function(){
    return this.score;
  }
});

Inside the template it can also be a good idea to throw errors if the data context fields are not available or the wrong type.

When I started with Meteor I had the very same question and it seemed awkward to me to have this idea of implied data context.

Upvotes: 1

Michel Floyd
Michel Floyd

Reputation: 20256

Your description of what's going on is a bit off. Let me attempt to clarify what's going on.

The two templates are:

<template name="postsList">
  <div class="posts">
    {{#each posts}}
      {{> postItem}}
    {{/each}}
  </div>
</template>

<template name="postItem">
  <div class="post">
    <div class="post-content">
      <h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3>
    </div>
  </div>
</template>
  1. The postList template includes an iterator {{#each posts}}
  2. This iterator gets its data from the posts helper of the postItem template
  3. The helper returns a cursor which is the result of performing a find() on a collection. Alternatively this helper could return an array of objects.
  4. A cursor is reactive meaning that items can be added, removed or changed at any time without you needing to write code to update the DOM. Magic.
  5. Inside the iterator the postItem template is included.
  6. For each iteration of the iterator a data context is created which is a single object.
  7. In javascript the current data context will be in this
  8. In the spacebars you can access any field in the object simply by putting that field's name in double curly braces ex: {{title}}
  9. If you wanted to be more explicit you could also use {{this.title}}

Sometimes the object doesn't have all the data you need to display. You might need to reformat existing fields, combine them someway, convert them (ex: UTC to local time, $ to £ etc...), or lookup related data from a different collection. In those cases you need a helper to return the extra piece of data.

Directly manipulating the DOM in a helper in the way you're showing is unadvisable. Since helpers can be called repeatedly even with the same data context you would likely end up with many more a elements than you expected.

In practice the distinction between a field value from the current data context and a helper value doesn't really matter to the presentation layer. It's not something that gets in your way of developing in Meteor. The common thing that crops up is that you insert a {{mything}} in the markup but forget to define a helper for it and then it shows up as blank. Or you believe that your object has a key that it doesn't have and you also get a blank.

Upvotes: 1

Related Questions