Different context for `#each collection` and `#each item in collection`


#1

@sashko or anyone else who might know…

Why does the context (this) for #each collection is different than #each item in collection?

Using #each people

<body>
  {{#each people}}
    Name: {{contextName}}
  {{/each}}
</body>
Template.registerHelper('contextName', function() {
  return this.name; // this = { name: "Alan" }
});

Template.body.viewmodel({
  people: function() {
    return [ { name: "Alan" } ]
  }
});

Using #each person in people

<body>
  {{#each person in people}}
    Name: {{contextName}}
  {{/each}}
</body>
Template.registerHelper('contextName', function() {
  return this.name; // this = { }
});

Template.body.viewmodel({
  people: function() {
    return [ { name: "Alan" } ]
  }
});

I would have expected both to return { name: "Alan" }


#2

when you use #each person in people there needs to be a person key in people

so in your first (and only loop) of people, there is still an item, just not a person


#3

Seems to be working as designed albeit not expected: https://github.com/meteor/meteor/tree/devel/packages/spacebars#each

The newer variant of #each doesn’t change the data context but introduces a new variable that can be used in the body to refer to the current item.

So how do you get the person object in a helper?


#4

Yes, but how do you get a hold of the person item?


#5

{ person: {name: “Alan”} }

consider this array

[
{ person: {name: “Alan”}, role: {title: ‘Caretaker’, fte: ‘part-time’}}
{ person: {name: “Clare”}, role: {title: ‘Advisors’, fte: ‘part-time’},
{ person: {name: “Manuel”}, role: {title: ‘Developer’, fte: ‘full-time’}
]

When you ask for person that is what this so this.name will work as long as there is a person to extract


#6

Take 2: What do you put instead of **???** so the helper returns the name?

<body>
  {{#each person in people}}
    Name: {{contextName}}
  {{/each}}
</body>

Template.registerHelper('contextName', function() {
  return **???**.name;
});

Template.body.helpers({
  people: function() {
    return [ { name: "Alan" } ]
  }
});

#7

consider this array

[
 { person: {name: "Alan"}, role: {title:  'Caretaker', fte: 'part-time'}}
  { person: {name: "Clare"}, role: {title:  'Advisors', fte: 'part-time'},
  { person: {name: "Manuel"}, role: {title:  'Developer', fte: 'full-time'}
]

When you ask for person that is what this becomes so this.name will work as long as there is a person to extract from people

it remains in the data context but abstracts the key for you, to save you some work

for example take two same blocks

{{#each people}}
   <li> {{role.title}} - {{role.fte}} </li>
{{/each}}


{{#each role in people}}
   <li> {{title}} - {{fte}} </li>
{{/each}}

#8

You misunderstand how #each ... in ... works. Here’s your example:

<body>
  {{#each role in people}}
    <li> {{title}} - {{fte}} </li>
  {{/each}}
</body>
Template.body.viewmodel({
  people: function() {
    return [
      { person: {name: "Alan"}, role: {title:  'Caretaker', fte: 'part-time'}},
      { person: {name: "Clare"}, role: {title:  'Advisors', fte: 'part-time'}},
      { person: {name: "Manuel"}, role: {title:  'Developer', fte: 'full-time'}}
    ]
  }
});

That outputs:


#9

Yes, sorry. I got that upside-down… late night here after a long week!

And you’re also right that this is unexpectedly empty

apologies for the goose chase


#10

in both cases you acces it as {{ role.title }}
difference is that data context in case of simple #each would be just that 1 item it is iterating over
but in #each role in, data context would be still whole array.

you can check by for example {{consoleLog this}} if u add helper to console log function parameter.


#11

You can’t, but here is one workaround: https://github.com/meteor/meteor/issues/5280#issuecomment-145321706

(and feel free to add your opinion to issue discussion)