Access ReactiveVar from helper (indirectly)


#1

How can I access ReactiveVar in the construction like below?

Template.profile.helpers({
    user: {
        ...
        gender: () => Template.instance().genderFlag.get(), //returns Uncaught TypeError: Cannot read property 'genderFlag' of null
        ...
    },
});

Returns error because Template.instance() inside gender: is null

This is how I create ReactiveVar:

Template.profile.onCreated( function() {
  this.genderFlag = new ReactiveVar(0);
});

#2

Why do you have an object labeled with user ? Try directly

Template.profile.helpers({
   gender: () => Template.instance().genderFlag.get()
})

#3

Because I use {{#with}}. I can access it directly, but how can I access it indirectly?


#4

The usage of {{#with}} is unrelated. Can you show us your html template code ?


#5
{{#with user}}
...
<button type="button" name="Male" class="{{#if gender '1'}}gender--selected{{/if}}">Male</button>
<button type="button" name="Female" class="{{#if gender '2'}}gender--selected{{/if}}">Female</button>
...
{{/with}}

and in .js:

Template.profile.helpers({
    user: {
        ...
        gender: (g) => g === Template.instance().genderFlag.get(),
        ...
    },
});

#6

Answered here: http://stackoverflow.com/questions/37410040/access-reactivevar-template-instance-from-helper-indirectly


#7

Is there any reason you don’t want to flatten those helpers ?

With your version you save a couple of calls to the reactive variable but you are building functions on the fly which is probably just equally expensive.

<button type="button" name="Male" class="{{isMale}}">Male</button> 
<button type="button" name="Female" class="{{isFemale}}">Female</button>

Template.profile.helpers({
  gender: () => Template.instance().gender.get(),
  isMale: () => (Template.instance().gender.get() == 1) ? "male" : "",
  isFemale: () => (Template.instance().gender.get() == 2) ? "female" : ""
})

The flat code is probably more idiomatic. The only case when I build functions on the fly in this setting is when the common computation is expensive (crunching data) and it’s hard to cache - otherwise you just compute it on the first call and just read the cache on subsequent calls.


#8

@diegoolivier Thank you for your help. I am relatively new to Meteor.

What do you mean by:

So in those situations it is better to make more helpers, than to make helper which accepts args?

I.e. Better to make this:

.html

<select class="" name="language">
    <option value="en" selected="{{langIsEn}}">en</option>
    <option value="sp" selected="{{langIsSP}}">sp</option>
</select>

<input type="radio" name="gender" value="1" checked="{{isMale}}" id="male"><label for="male">Male</label>
<input type="radio" name="gender" value="2" checked="{{isFemale}}" id="female"><label for="female">Female</label>

.js

Template.profile.helpers({
   langIsSP: () => 'sp' === Template.instance().language.get()
   langIsEn: () => 'en' === Template.instance().language.get()
   isMale: () => '1' === Template.instance().genderFlag.get()
   isFemale: () => '2' === Template.instance().genderFlag.get()
});

instead of this:

.html

<select class="" name="language">
    <option value="en" selected="{{lang 'en'}}">en</option>
    <option value="sp" selected="{{lang 'sp'}}">sp</option>
</select>

<input type="radio" name="gender" value="1" checked="{{gender '1'}}" id="male"><label for="male">Male</label>
<input type="radio" name="gender" value="2" checked="{{gender '2'}}" id="female"><label for="female">Female</label>

.js

Template.profile.helpers({
   lang: (lang) => lang === Template.instance().language.get()
   gender: (gender) => gender === Template.instance().genderFlag.get()
});

??


#9

Your second example (2 helpers) is better than the first one (4 helpers).

What you should avoid (unless you really know what you are doing) is to create functions on the fly like you were trying to do with the very first example

Template.profile.helpers({
   avoid_unless_you_know_what_you_are_doing : () => {
      const c = Template.instance().gender.get()
      return {
         f1 : v => (c == v) ? "equal" : "nonequal",
         f2 : () => c
      }       
   }
})

#10

Thank you. Now it becomes more clear, but I still not yet fully understood…

This is what I have now:

Template.profile.helpers({
    user() {
        return {
              email: () => bar
            , isEmailConfirmed: () => 1 === foo
            , phoneNumber: () => bar
            , phoneCode: (phoneCode) => phoneCode === foo
            , nameSurname: () => {
                let nameSurname = foo + ' ' + bar;
                return nameSurname.trim();
            }
            , lang: (lang) => lang === foo
            , gender: (gender) => gender === bar
        };
    }
});

Everything is under user() because I want to pass user context and use it with {{#with user}} in my spacebars .html. Am I doing it wrong? Is use of {{#with}} inexpedient? Are these computations expensive in regards to the performance?


#11

Do what works for you. What matters is that you easily understand and are able to maintain your code.

That said, I would have written it this way

Template.profile.helpers({
   user : () => ({ // Just return data
      username : "myname", 
      id : Template.instance().id.get(), 
      gender : "male" 
   }),
   isMale : g => (g == "male"), // use data as parameter
 })

<template name="profile">
{{#with user}}  <!-- gets the data -->
<p>name : {{username}}</p> <!-- uses the data -->
<p>is male ? : {{isMale gender}}</p> <!-- passes the data to a helper -->
{{/with}}
</template>