Query on Block Helpers in Handlebars.js - javascript

I have a product block in a template like this :
<script type="text/x-handlebars-template" id="tmpl-person">
<div class="product">
<!-- Product details here -->
</div>
</script>
What I want to do is if from the array of person I get as data , after every three persons , I want to insert a container called <div class="row-fluid"></div> and three persons inside it.. then a row-fluid container and three persons inside it. How can I achieve this using helpers ? Thanks for help.

You could use something like this
Handlebars.registerHelper('each', function(context, block) {
var ret = "";
for(var i=0, j=context.length; i<j; i++) {
ret = ret + "<li>" + block(context[i]) + "</li>";
}
if( i % 3 == 0)
ret = ret + <div class="row-fluid"></div>
return ret;
});
And you could define your custom iterator like following
<script type="text/x-handlebars-template" id="tmpl-person">
{{#each productInfo}}
<div class="product">
<!-- Product details here -->
</div>
{{/each}}
</script>

Related

Loops in Handlebars

I have an array of tracks coming from a database that I want to display in a div.
More specifically I need to put every two of them in a bootstrap row. I can easily do it in the controller JS file by first collecting all in a fragment element and then use a loop to put them into the rows and then in the target div but I am wondering if it would be possible to do it directly while producing them in handlebars?
Here is the handlebars template:
{{#if result}}
{{#each result}}
<div class="playlist-item col-xs-4">
<a href="#/user/{{username}}/playlist/{{id}}" class="no-style">
<h3 class="result-title">{{title}}</h3>
<p class="result-description">{{description}}</p>
<img class="result-image img-circle" src="{{img}}">
</a>
<br>
<a type="button" id="{{id.videoId}}" class="btn btn-default btn-remove"
href="#/user/{{username}}/playlist/{{id}}/remove-from-playlist">Remove from
playlist</a>
</div>
{{/each}}
{{else}}
<h4>You currently have no tracks in your playlist</h4>
{{/if}}
Here is the JS:
showPlaylist() {
return Promise.all([
userController.loadPlaylist(),
templates.loadTemplate('playlist'),
])
.then(([tracks, template]) => {
let fragment = document.createDocumentFragment()
let div = document.createElement('DIV');
div.innerHTML = template(tracks);
div = [...div.children];
let len = div.length
while(div.length > 0) {
let row = document.createElement('div')
row.className = 'row'
let col = div.splice(0,2)
row.append(col[0])
if(col[1]) {
row.append(col[1])
}
len -= 2;
fragment.append(row)
}
$('#container').html(fragment)
})
}
It is possible to group your items into rows, but you would need to use a custom helper function to do it.
We will need to create a block helper that takes an array of items, breaks the array into rows of specified number of columns, and then applies the block "row" template to each row. If we were to call our block helper "eachRow", the resulting template might look like the following:
{{#eachRow result 2}}
<div class="row">
{{#each columns}}
<div class="playlist-item col-xs-4">
{{!-- TODO: Rest of item template goes here. --}}
</div>
{{/each}}
</div>
{{/eachRow}}
Notice that we still use the item template within a regular Handlebars #each block. Except now the #each is wrapped within a "row" template block. The 2 is a parameter that will be passed to our helper that is to be the number of columns in each row.
Next, we will write our helper:
Handlebars.registerHelper('eachRow', function (items, numColumns, options) {
var result = '';
for (var i = 0; i < items.length; i += numColumns) {
result += options.fn({
columns: items.slice(i, i + numColumns)
});
}
return result;
});
This helper simply iterates over our source array in increments of numColumns and for each iteration applies our "row" block template, passing the array of items (columns) that are to render in that row. The helper concatenates the rows and returns the result.
I have created a fiddle for reference.

Output key from array?

How can I output the peoples names below? e.g. Martin and Tabitha?
people:
- martin:
job: Developer
skills:
- python
- perl
- pascal
- tabitha:
job: Developer
skills:
- lisp
- fortran
- erlang
Here's the loop:
{{#each people}}
{{ this }}
{{/each}}
I've found the answer to be:
{{#each people}}
{{#key}}: {{this}}
{{/each}}
You can create a block helper list to iterate over people and use the current index as a private variable to obtain the name which is the first key in the object: Object.keys(context[i])[0].
Code:
var people = [{"martin": {"job": "Developer","skills": ["python","perl","pascal"]}}, {"tabitha": {"job": "Developer","skills": ["lisp","fortran","erlang"]}}];
// Register list helper
Handlebars.registerHelper('list', function (context, options) {
var out = '<ul>',
data;
if (options.data) {
data = Handlebars.createFrame(options.data);
}
for (var i = 0, l = context.length; i < l; i++) {
if (data) {
data.index = Object.keys(context[i])[0];
}
out += '<li>' + options.fn(context[i], {
data: data
}) + '</li>';
}
out += '</ul>';
return out;
});
// The main template
var main = Handlebars.compile($('#main').html());
// Register the list partial that 'main' uses
Handlebars.registerPartial('list', $('#list').html());
// Render the list
$('#output').html(main({
people: people
}));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js"></script>
<script id="list" type="x-handlebars-template">
{{#list people}}
{{#index}}
{{/list}}
</script>
<script id="main" type="x-handlebars-template">
{{> list}}
</script>
<div id="output"></div>
Note: I have converted the yaml to an array for the code example.

Send array to function in handlebars?

I have the following piece of code...
var mainArray =
{
nestedArrays:
{
array1:[... items ...],
array2:[... items ...]
}
};
var source =
'{{#nestedArrays}}' +
'{{#each this}}' +
'<div class="listItem" onclick="populateSecondMenu({{SEND THIS ARRAY!}});">' +
'<div class="leftSide">' +
'<div class="listTitle">Indicator : {{this.length}} </div>' +
'</div>' +
'</div>' +
'{{/each}}' +
'{{/items}}';
var template = Handlebars.compile(source);
$('.list').html(template(mainArray));
As you can already see here, I am able to iterate over this structure, and place the length of both "array1" and "array2" inside list items on the UI.
However, What I also want to be able to do - is to be able to pass the current item to a the function when inside the "#each" tags - see that function call, "populateSecondMenu"? I want to put the array I am at now - so that I can pass that in, How might I do that?
Thanks in advance!
try this. I have used arguments
<script type="text/javascript">
function populateSecondMenu(item) {
console.log(arguments);
}
</script>
<div class="entry">
<h1></h1>
<div class="body">
{{#nestedArrays}}
{{#each this}}
<div class="listItem" onclick="populateSecondMenu({{this}})">
<div class="leftSide">
<div class="listTitle">Indicator : {{this.length}} </div>
</div>
</div>
{{/each}}
{{/nestedArrays}}
</div>
</div>

Getting row data in dynamically created listview

Hi i'm creating list view dynamically. I want to get the data of particular row on click, to proceed to further steps.
my code is as below
function getList(tx, results){
$('#DeliveryList').empty();
var len = results.rows.length;
for(var i=0; i <len; i++)
{
var deliveryItems = results.rows.item(i);
var html = '<li data-role="list-divider">'+deliveryItems.DeliveryName+ ' | ' + deliveryItems.PrimaryName+' <span class="ui-li-count">Copay ='+deliveryItems.Total+'</span> </li><li><a><img src="Pending"/><h3>'+deliveryItems.Name1+'</h3><p>'+deliveryItems.Address+'</p><p>'+deliveryItems.City+' <b></b></p><p><strong>'+deliveryItems.ContactNumber+'</strong></p><a href="#PrescriptionPage" class="cls_btn" id="btn_list" onclick = "Prescription()" >Delivary Details</a></a></li>';
$('#DeliveryList').append(html).trigger('create');
}
$('ul').listview('refresh');
}
My html file looks like
<div data-role="page" id="page3" >
<div data-role="header">
Back
<h1>Heading</h1>
Home
</div><!-- /header -->
<ul data-role="listview" id="DeliveryList" data-inset="true" data-theme="d" data-divider-theme="b"> </ul>
</div>
can any one help me to achieve the result. Thanks in Advance.
It worked for me with below code
$('ul').children('li').on('click', function () {
alert('Selected Name=' + $(this).text());
});
You're using jQuery already, why not use it to create these elements aswell? I asume getList() is bound to an event.
// Create the element
var li = $('<li></li>');
// Add your class
li.addClass('list-divider');
// Change the innerHTML
li.html('Your content');
// Then append it to the list
$('#DeliveryList').append(li);
Simply alter this code to your need. This example just adds one <li> element with a class and some content.
Good luck.

Adding get() to line causes "is not a function" error

This is the start of an inventory system I am working on. Basically it takes an array with items and quantities in a compressed form and outputs the items into an item div.
Running the below produces no error:
$('.item_amount').html(collection[itemName].amo);
Adding the get() method after the selector like so:
$('.item_amount').get(i).html(collection[itemName].amo);
produces "$(".item_amount").get(i).html is not a function".
This is what the line is altering:
<div class="item">
<img src="" class="item_image"/>
<div class="item_amount"></div>
</div>
The line that is causing the error is located in a for loop that loops through all the keys in an array. Then outputs the item quantity from the array in the item_amount div based on the index stored in the variable "i". The for loop also creates an object for each item in the array and puts in the a collection object.
Full code below:
<body>
<div class="item">
<img src="" class="item_image"/>
<div class="item_amount"></div>
</div>
<div class="item">
<img src="" class="item_image"/>
<div class="item_amount"></div>
</div>
<div class="item">
<img src="" class="item_image"/>
<div class="item_amount"></div>
</div>
<script type="text/javascript">
var collection = new Object();
function makeItem(itemName, id, amo) {
collection[itemName] = new item(id, amo);
}
function item(id, amo) {
this.id = id;
this.amo = amo;
}
var inventoryCom = "368.9,366.15,384.32"; //compressed inventory
var inventoryArr = inventoryCom.split(',');
for(var i=0; i < inventoryArr.length; i++) {
var itemName = 'item' + (i + 1); //unique name for each item
var itemArr = inventoryArr[i].split('.');
makeItem(itemName, itemArr[0], itemArr[1]);
$('.item_amount').get(i).html(collection[itemName].amo);
}
</script>
</body>
.get(i) returns DOM element, which doesn't have .html() method - that's what js engine wants to say to you.
You need to use .eq(i) instead. Like
$('.item_amount').eq(i).html(collection[itemName].amo);
or
$('.item_amount:eq(' + i + ')').html(collection[itemName].amo);
This line may be a problem
var itemName = 'item' + (i + 1); //
This may increment the array count out of the upper bound. check the itemName value.
Also try to add an alert for this
collection[itemName].amo

Categories