1

I'm trying to write a helper that will return an array of objects that can then be looped through. Here's what I have now:

Handlebars.registerHelper('testHelper', () => {
  return [
    { slug: 'Test', title: 'This is it!' },
    { slug: 'Test 2', title: 'This is the second it!' },
  ];
});

Using it like:

{{#entries this}}
  <a href="/{{slug}}">{{title}}</a>
{{/entries}}

And I'm receiving [object, Object] for each object in the array instead of the individual values. Please help :)

Thanks!

5
  • The purpose of a helper is to return a string of markup, not to return data. Why do you want your helper to store your data? Commented Feb 18, 2017 at 22:58
  • @76484 to reduce the amount of data I'm compiling with, to make the server-side compiling faster, I'd like to be able to only use data that I need. Commented Feb 18, 2017 at 23:59
  • You don't compile the data. You compile the template. Commented Feb 19, 2017 at 0:01
  • I understand, but in the helper I would make a query to the database that I wouldn't have to make when I compile the template Commented Feb 19, 2017 at 0:48
  • The compilation is of a string into a function. This has nothing to do with querying a database. If you are rendering your views on the server, then it looks like what you are trying to achieve is to move a database query from the controller layer to the view layer. This saves nothing and makes the code a lot harder to understand. Commented Feb 19, 2017 at 1:08

1 Answer 1

7

The way helpers in Handlebars work is a bit tricky. Instead of passing data from the helper to the main template body, you pass the portion of the template body related to the helper to the helper.

So, for example, when you do this:

{{#entries this}}
  <a href="/{{slug}}">{{title}}</a>
{{/entries}}

You are providing two things to the entries helper: 1) the current context (this) 2) some template logic to apply

Here's how the helper gets these items:

Handlebars.registerHelper('entries', (data, options) => {
  // data is whatever was provided as a parameter from caller
  // options is an object provided by handlebars that includes a function 'fn'
  //   that we can invoke to apply the template enclosed between 
  //   #entries and /entries from the main template
   :
   :
});

So, to do what you want to do:

Handlebars.registerHelper('testHelper', (ignore, opt) => {
  var data = [
    { slug: 'Test', title: 'This is it!' },
    { slug: 'Test 2', title: 'This is the second it!' },
  ];
  var results = '';
  data.forEach( (item) => {
    results += opt.fn(item);
  });
  return results;
});

The opt.fn(item) applies this portion of template:

<a href="/{{slug}}">{{title}}</a>

and the idea is to create a string (a portion of your html) that is then returned and placed into the string being formulated by your main template.

Here's a sample to show this working.

<!DOCTYPE html>
<html>
<head>
    <title>Page Title</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.js"></script>
</head>
<body>

<script id="t" type="text/x-handlebars">
    {{#testHelper this}}
    <a href="/{{slug}}">{{title}}</a>
    {{/testHelper}}
</script>

<script>
    Handlebars.registerHelper('testHelper', (ignore, opt) => {
        var data = [
            { slug: 'Test', title: 'This is it!' },
            { slug: 'Test 2', title: 'This is the second it!' },
        ];
        var results = '';
        data.forEach((item) => {
            results += opt.fn(item);
        });
        return results;
    });

    var t = Handlebars.compile($('#t').html());
    $('body').append(t({}));
</script>
</body>
</html>

Let me also echo what others have been trying to tell you. It doesn't make a lot of sense to try to populate data within your templates. This should be passed as context for your templates to act on. Otherwise, you are mixing your business logic with your template logic (view) and this complicates things needlessly.

Here's a simple change you can make in the same snippet, passing the data to your templates:

<!DOCTYPE html>
<html>
<head>
    <title>Page Title</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.js"></script>
</head>
<body>

<script id="t" type="text/x-handlebars">
    {{#testHelper this}}
    <a href="/{{slug}}">{{title}}</a>
    {{/testHelper}}
</script>

<script>
    Handlebars.registerHelper('testHelper', (ignore, opt) => {
        var results = '';
        data.forEach((item) => {
            results += opt.fn(item);
        });
        return results;
    });

    var data = [
        { slug: 'Test', title: 'This is it!' },
        { slug: 'Test 2', title: 'This is the second it!' },
    ];
    var t = Handlebars.compile($('#t').html());
    $('body').append(t(data));
</script>
</body>
</html>

This way you can retrieve your data in your javascript and keep the templates for what they were intended - formulating html.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.