Aaron
Aaron

Reputation: 1061

Handlebars Helper returning array but rendering comma delimited string

I have an array of objects that I am trying to display in categories using bootstrap panels. To get all of the different categories in the array I created a helper that loops through the array and returns an array of strings which contains all the different distinct categories.

Here are my two helpers:

Handlebars.registerHelper("GetFamilies", function (array) {
    var families = [];

    for (var i = 0; i < array.length; i++) {
        var item = array[i];

        if (families.indexOf(item.Family) <= -1) {
            families.push(item.Family);
        }
    }
    console.log("====================Families");
    console.log(families);
    return families;
});

Handlebars.registerHelper("GetFamiliyPieces", function (array, familyName) {
    var result = _.filter(array, function (obj) {
        // return true for every valid entry!
        return obj.Family == familyName;
    });

    console.log("====================Family Pieces");
    console.log(result);
    return result;
});

Here is the html:

  <div class="panel-group" id="toolbox-accordion">
        {{#GetFamilies pieces}}
        <div class="panel panel-default">
            <div class="panel-heading">
                <h4 class="panel-title">
                    <a class="accordion-toggle" data-toggle="collapse" data-parent="#toolbox-accordion" href="#{{this}}">
                        {{this}}
                    </a>
                </h4>
            </div>
            <div id="{{this}}" class="panel-collapse collapse in">
                <div class="panel-body">
                    {{#GetFamilyPieces pieces this}}

                    <div class="toolbox_item" data-type="{{TypeName}}" data-type-id="{{TypeID}}" title="{{TypeDescription}}" data-input-count="{{Length Inputs}}" data-output-count="{{Length Outputs}}">
                        <span id="line"></span>
                        <div class='typename'>{{ShortName TypeName}}</div>
                    </div>

                    {{/GetFamilyPieces}}
                </div>
            </div>
        </div>
        {{/GetFamilies}}
    </div>

In the Console I am hitting the GetFamilies Helper:

enter image description here

But not hitting the GetFamilyPieces helper.

My rendered HTML looks like this:

<div id="toolbox-container">

    <div class="panel-group" id="toolbox-accordion">
Circuit,Component,Conductor    </div>


</div>

As you can see it is just rendering a comma delimited string of what the array contains. Do you know why the full html is not being rendered?

Upvotes: 1

Views: 2788

Answers (1)

76484
76484

Reputation: 9003

You are getting the comma-delimited string because that is the result when an array in JavaScript is .toStringed.

From MDN:

For Array objects, the toString method joins the array and returns one string containing each array element separated by commas.

This means that ["Circuit", "Component", "Conductor"].toString() results in "Circuit,Component,Conductor".

The reason why the rest of your template is not rendered after calling your helper is because your helper is not written properly. Your Block Helper should return a string containing the HTML that is to be rendered. Because your helper returns an array, the toString() of that array is rendered. In order to access the inner template of a Block Helper, Handlebars provides a function, options.fn, to which you pass context data object. Your helpers must pass each item in the array argument to the options.fn function and join the results. A working implementation would look something like the following:

Handlebars.registerHelper("GetFamilies", function (array, options) {
    return array
        .reduce((memo, el) => {
            if (memo.indexOf(el.Family) === -1) {
                memo.push(el.Family);
            }
            return memo;
        }, [])
        .reduce((memo, family) => memo += options.fn(family), '');
});

Handlebars.registerHelper("GetFamilyPieces", function (array, familyName, options) {
    return array
        .filter(el => el.Family === familyName)
        .reduce((memo, el) => memo += options.fn(el), '');
});

Please note that within your #GetFamilies Block Helper, pieces does not necessarily exist within your context data. You must tell Handlebars that to step up a context frame to find pieces with a path: ../pieces.

An alternative solution, and, judging by your helpers, the one it looks like you were aiming for, would be to use a standard (non-Block) Handlebars Helper. This would allow you to return the arrays from your helpers as you were doing, and then pass the resulting array to the existing #each Block Helper. Your template would need to be changed to the following:

{{#each (GetFamilies pieces)}}
    {{#each (GetFamilyPieces ../pieces this)}}

    {{/each}}
{{/each}}

Upvotes: 6

Related Questions