Append templates programatically


#1

Hey everyone

I was trying to do this, but it didn’t work

for (i = 0; i < noplayers; i++)
{
$(’#playerslotrow’).append( ‘{{> playercard}}’ );
}

and I want to avoid outright typing out the whole template in html within the javascript
I tried to lookup some handlebars syntax, and I got something like this:

for (i = 0; i < noplayers; i++)
{
$(’#playerslotrow’).append(template({name: addplayer}));
}

which gives a lot of ‘template is not defined’ errors
but I feel like im close.
How can i call templates I’ve already built in javascript and append them to a div in my html?
preferably using jquery.


#2

Is there any reason you can’t just have the iteration done directly in Blaze?

Ie

{{#each players}}
  {{> player }}
{{/each }}

If players has nothing in it, it won’t render anything. You just need the players helper to reactively update at the appropriate time and it will add the template for each record.


#3

Well as far as i know, #each only works with helpers. Im changing the DOM after every change event on a particular select element.


#4

In general when working with Blaze it’s a bad idea to change the DOM but sometimes it’s necessary. For example you see that with maps implementations.

Here is the advised way of working with libraries:

http://blazejs.org/guide.html#onrendered-for-libs

And then, to render the template with the data in it use: http://blazejs.org/blaze.html#Blaze-toHTMLWithData, so that will give something like (untested):

$('#playerslotrow').append(Blaze.toHTMLWithData('playerCard', {name: 'John Doe'}));

#5

OK, but I’m still not clear why you couldn’t be couldn’t be updating some reactive data source in the events that would then automatically trigger the refresh of the Dom by fetching new values from your helper.

Just to be clear, I’m not insisting the above is 100% workable for your particular scenario, just trying to find a solution that lets Blaze do all the heavy lifting and leaves you with a more Blaze-like way of solving the problem. It will usually lead to cleaner, less-verbose code.


#6

Agree with that @peter.roehlen if blaze can handle it that’s in general the best solution.


#7

Thanks guys.

I should note though that there is no data to manipulate; the ‘select’ element literally just has options 1-6, which corressponds to the amount of times that template is supposed to render on the page.


#8

This might be one way of doing it. You can determine better if it works for you or not:

Templates

<head>
  <title>template-times</title>
</head>

<body>
  {{> main}}
</body>

<template name="main">
  <!--Select number of players-->
  <label>Number of players:</label>
  <select id="playerCount">
    <option value="0">None</option>
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
  </select>

  <!--Render players-->
  {{#each players}}
    {{> player}}
  {{/each}}
</template>

<template name="player">
  <p>Hello player {{playerNumber}}</p>
</template>

Helpers/event handlers

import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';

import './main.html';

Template.main.onCreated(function mainOnCreated() {
  // Player count starts at 0
  this.playerCount = new ReactiveVar(0);
});

Template.main.helpers({
  players() {
    let players = [];
    const playerCount = Template.instance().playerCount.get();
    for (var index = 0; index < playerCount; index++) {
      players.push({
        playerNumber: index + 1
      });
    }
    return players;
  },
});

Template.main.events({
  'change #playerCount'(event, instance) {
    // set number of players to selection
    instance.playerCount.set(parseInt(event.currentTarget.value));
  },
});

##Output

There’s no need to have the array elements contain anything like I’m doing in the above - ie it could just be an array of nulls I think. But it might be useful to have some sort of index property in each element.


#9

Yeah I changed these lines and it still works. You just don’t get any way of identifying players this way:

    for (var index = 0; index < playerCount; index++) {
      players.push(null);
    }