Spacebars #if, else

My below code is crashing the browser, even though I included the line in question as the example in the docs shows under the title “Helpers that take arguments”

Error: No such function: elementIs

element is a field and has a value of “input” in the collection document.

Thanks

server.js
[
{element: ‘input’, action: ‘myAction’, name: ‘plate’, placeholder: ‘Enter your number’}
]`

<template name="content">
  <div class="container">
    <div class="row">
      <section class="col-xs-12">
        <ul class="list-group">
          {{#each this.items}}
            {{#if elementIs "input"}}  <<<<<<<<<<< THIS   <<<<<<<<<<<
              <li>
                <input class="list-group-item basic-vertical-spacing col-xs-12" type="text"
                   name={{name}} placeholder={{placeholder}} value={{value}}>
              </li>
            {{/if}}
          {{/each}}
        </ul>
      </section>
    </div>
  </div>
</template>

Can you show us the JS part so we can check it? I’d especially like the elementIs helper which you use here and your code claims it doesn’t exist. And if it’s always checking against the word “input”, I’d hardcode this word into the helper.

Anyways, I think I see another thing. In your {{#each}} loop you use elementIs helper to check if the value of element is equal to “input”. But you don’t pass the value of the element field into the helper. Your elementIs helper exists in the data context of the whole template, not in the data context of the item from {{#each}} loop, so it doesn’t know what’s the value of element for the current item. It doesn’t even know that element exists.

I recommend you to either rewrite it so that you pass the element field value instead of “input” or to make the whole content of {{#if}} a subtemplate, where the item’s content is the main data context of the template.

This last option is the best practice for Blaze.

1 Like

I removed the {{#if condition from the file and I used the field element instead of one of the other helpers and I can verify that the value for the field element is showing in the browser fine.
Which tells me that the data context and the value is present but the if condition is broken.

The element field value is “input”.

This is a static template with dynamic data so there is no js file for a helper.

If there is no js file for a helper, where do you get elementIs at all? It has no right to work if it’s not defined.

Your error says that there’s no elementIs, because in fact there is no elementIs helper. You didn’t make such a thing, but for some reason you try to use it.

The data context exists, but only for what’s inside of {{#each}} loop. Your helpers live outside of this data context, they can see only the data context of the template. That’s why you can see the element field when you show it in your {{#each}} loop but you can’t use it in your helpers without passing the data.

Anyways, your applications tries to locate the elementIs helper and it doesn’t exist, because you didn’t write it.

Also, if elementIs is actually what you refer to as element field in the document, you cannot use it this way with passing the “input” value to it to make a comparison. {{#if}} doesn’t take care of comparison and it’s looking for a helper called elementIs in order to make it.

here is the js files

<template name="index">
  {{#if (session 'showMainMenu')}}
    {{> mainMenu}}
  {{else}}
    {{> Template.dynamic template="content"     data=taskItems}}
  {{/if}}
</template>

'use strict';
Template.index.helpers({
    taskItems: function () {
      var data = InputsCol.find({});
      return {items: data};
  } 
});

and then the code in my original post which is content.html

1 Like

Like I said, the elementIs helper is missing. And even if it existed, it wouldn’t work the way you try to use it in your {{#if}} because of the data context, so you need to pass the element field value into the helper.

I highly recommend you to read the docs again (or read it for the first time if you didn’t do it yet), because you’re missing some basics here which are very important to learn.

Since the data is dynamic in “this”, how will I reference it in a template helper.js given that each time the #each loops the value of element changes? Without loosing the dynamic advantage of the code.

The best way is to move the inside of {{#each}} into a subtemplate with its separate data context. This way everything lives only in the main data context of the template.

It’s one of the best guidelines for using Blaze that exist imho. It saved me a lot of problems.

But for the simple comparison use case, it’s good to declare a global helper that takes two arguments, compares them and returns true or false for the {{#if}}.

Something like (put it f.e. in globalHelpers.js file in your client folder):

Template.registerHelper( 'equals', ( a1, a2 ) => {
  return a1 === a2;
});

And you can use it like:

{{#if equals element "input"}} (or elementIs if that's the name of your field)

You can read more about global helpers here:
https://themeteorchef.com/snippets/using-global-template-helpers

The reason for my attempt is that I can dynamically change from a html input to another type of element when the field “element” in the data being referenced by “this” gets checked in the {{# if conditional.

I am not able to get my head around how the subTemplate with its own data context will achieve the desired effect. In that the order of the documents matter because it arranges which html element comes after which, I will keep thinking about it though.
Thank you for your much attention. :slight_smile:

They’re all questions that are answered in the docs ( http://docs.meteor.com ), the guide ( http://guide.meteor.com ) and the official tutorial. If you want to become a good Meteor developer, I highly recommend you to take a look. Without it, you will have to ask questions such as that (and other questions that you asked last few days) and wait for answers instead of writing your applications. It’s counter productive. :slight_smile:

Anyways, here’s how it looks like, when it works:


Working example without subtemplates:

if (Meteor.isClient) {

  Template.body.helpers({
    isEqual: function (a, b) {
      return a === b;
    },
    girls: [
        {name: 'Alice', element: 'input'},
        {name: 'Betty', element: 'textarea'},
        {name: 'Charlotte', element: 'input'},
        {name: 'Diana', element: 'form'},
        {name: 'Emily', element: 'input'},
        {name: 'Florence', element: 'div'},
        {name: 'Ginny', element: 'input'}
    ]
  });

<head>
  <title>AICA - an Awesome Input Checking App</title>
</head>

<body>
  <h1>Welcome to AICA - an Awesome Input Checking App!</h1>

  <p>Now let's test our Girls for inputs:</p>
  <br />
  {{#each girls}}
    <h4>Girl: {{name}}</h4>
    {{#if isEqual element "input"}}
      <p>Good job, {{name}}, input confirmed.</p>
    {{else}}
      <p>Are you stupid, {{name}}? I wanted an input, not a {{element}}!</p>
    {{/if}}
  {{/each}}
</body>

Working example with subtemplates (recommended):

if (Meteor.isClient) {

  Template.body.helpers({
    girls: [
        {name: 'Alice', element: 'input'},
        {name: 'Betty', element: 'textarea'},
        {name: 'Charlotte', element: 'input'},
        {name: 'Diana', element: 'form'},
        {name: 'Emily', element: 'input'},
        {name: 'Florence', element: 'div'},
        {name: 'Ginny', element: 'input'}
    ]
  });

  Template.subTemplate.helpers({
    isEqual: function (a, b) {
      return a === b;
    }
  });
}

<head>
  <title>AICA - an Awesome Input Checking App</title>
</head>

<body>
  <h1>Welcome to AICA - an Awesome Input Checking App!</h1>

  <p>Now let's test our Girls for inputs:</p>
  <br />
  {{#each girls}}
    {{> subTemplate}}
  {{/each}}
</body>

<template name="subTemplate">
  <h4>Girl: {{name}}</h4>
  {{#if isEqual element "input"}}
    <p>Good job, {{name}}, input confirmed.</p>
  {{else}}
    <p>Are you stupid, {{name}}? I wanted an input, not a {{element}}!</p>
  {{/if}}
</template>

Working example with global helper:

if (Meteor.isClient) {

  Template.body.helpers({
    girls: [
        {name: 'Alice', element: 'input'},
        {name: 'Betty', element: 'textarea'},
        {name: 'Charlotte', element: 'input'},
        {name: 'Diana', element: 'form'},
        {name: 'Emily', element: 'input'},
        {name: 'Florence', element: 'div'},
        {name: 'Ginny', element: 'input'}
    ]
  });

  Template.registerHelper('isEqual', function (a, b) {
    return a === b;
  });
}

<head>
  <title>AICA - an Awesome Input Checking App</title>
</head>

<body>
  <h1>Welcome to AICA - an Awesome Input Checking App!</h1>

  <p>Now let's test our Girls for inputs:</p>
  <br />
  {{#each girls}}
    <h4>Girl: {{name}}</h4>
    {{#if isEqual element "input"}}
      <p>Good job, {{name}}, input confirmed.</p>
    {{else}}
      <p>Are you stupid, {{name}}? I wanted an input, not a {{element}}!</p>
    {{/if}}
  {{/each}}
</body>

@fredj So if you got the basics of Blaze and you wanna stay at it, I can recommend you to take a look at http://viewmodel.meteor.com

This makes things easier, especially dividing your app into more templates/components and handling context issues.

1 Like

Yes, being just few months old in javascript and Meteor, with C++ background, I have a lot of readying and learning to do. Thanks for your help and patience. :slight_smile:

deleted for not being necessary.

I’ll answer anyways - it’s doable with Jade, but not with HTML.