Search code examples
javascripthtmlnode.jshandlebars.js

Handlebars {{each}} inside of {{#times}}


                    <form action="/ayarlar/league/edit-{{this}}/{{../league._id}}?_method=PUT" method="POST" class="boxInfo">
                        <div class="grupDiv"
                            style="width:150px;display:inline-block;border:1px solid black;padding:10px">
                            <h2>Grup {{this}}</h2>
                            <select name="groupNo{{this}}" id="" multiple>
                                {{#each team}}
                                <option value="{{_id}}">{{teamName}}</option>
                                {{/each}}
                            </select>
                            <input type="hidden" name="_method" value="PUT">
                            <button type="submit" class="redButton">Kaydet</button>

                            <p><span style="color:red">Takımlar : </span>
                            </p>
                        </div>
                    </form>
                    {{/times}}

When I try to run this code it doesn't work. However, when I delete the {{#times}} block, the code is correctly working. Also I defined the times helper like this :

const hbs = exphbs.create({
    helpers: {
        times: function(n, block) {
            let accum = '';
            for (let i = 1; i <= n; ++i)
                accum += block.fn(i);
            return accum;
        },
        eq: function (a, b) {
            return a === b;
        }
    }
});

I want to have each teamName rendered as an option, but none are.


Solution

  • The issue is that when you are inside the block wrapped by {{#times league.groupNumber}} {{/times}}, the context - ie., the this - is current value passed to block.fn in your times helper, which is a Number. (This is why <h2>Grup {{this}}</h2> results in something like <h2>Grup 1</h2>)

    Assuming your data is shaped like:

    {
      league: {
        groupNumber: 2
      },
      team: [
        {
          _id: 'TeamA',
          teamName: 'Team A'
        },
        {
          _id: 'TeamB',
          teamName: 'Team B'
        },
        {
          _id: 'TeamC',
          teamName: 'Team C'
        }
      ]
    }
    

    Then you need to "step-up" a context-level within the #times block in order to get team from the parent context with ../team. The template becomes:

    {{#times league.groupNumber}}
      <div class="grupDiv" style="width:150px;display:inline-block;border:1px solid black;padding:10px">
        <h2>Grup {{this}}</h2>
        <select name="" id="" multiple>
          {{#each ../team}}
            <option value="{{_id}}">{{teamName}}</option>
          {{/each}}
        </select>
       </div>
    {{/times}}
    

    const template = Handlebars.compile(
    `
    {{#times league.groupNumber}}
      <div class="grupDiv" style="width:150px;display:inline-block;border:1px solid black;padding:10px">
        <h2>Grup {{this}}</h2>
        <select name="" id="" multiple>
          {{#each ../team}}
            <option value="{{_id}}">{{teamName}}</option>
          {{/each}}
        </select>
       </div>
    {{/times}}
    `);
    
    Handlebars.registerHelper('times', function (n, block) {
      let accum = '';
      for (let i = 1; i <= n; ++i)
        accum += block.fn(i);
      return accum;
    });
    
    const data = {
      league: {
        groupNumber: 2
      },
      team: [
        {
          _id: 'TeamA',
          teamName: 'Team A'
        },
        {
          _id: 'TeamB',
          teamName: 'Team B'
        },
        {
          _id: 'TeamC',
          teamName: 'Team C'
        }
      ]
    };
    
    const output = template(data);
    
    document.body.innerHTML = output;
    <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"></script>

    And here is the same code in a fiddle for reference.