I'm trying to create a table that populates each table cell with an object from a JSON file. My handlebars template just adds a with the data for each object. What I'm trying accomplish is for every 5th item a new row is created and then it continues populating out the table cells until the 10th item then it creates a new row etc.
I've been reading up on #index. Is there some function that does something like {{#if #index / 5 == 0}} ? Otherwise is there something handlebars offers that could achieve the functionality I'm trying to do? I'm not confined to use a table I just figured that was the best option to put the data.
My current template. Thanks for any help! I edited this below using a handlebars helper. But the information still doesn't render. There is additional code that compiles the template after the end of this but it includes a very long json array in the local file for testing.
<script type = "text/x-handlebars-template" id="itemTemplate">
<table class="tableStyle">
<tr>
{{#each all_coupons}}
{{#ifPos}}
<tr>
<td>
<div class="wrapper">
<div class="header">{{coupon_title}}</div>
<div class="column_wrapper">
<div class="two-col">
<div class="product_image"><img src="{{coupon_thumb}}" alt="Apple" height="110" width="110"></div>
<div class="description">{{coupon_description}}</div>
</div>
</div>
<div class="expiration">Valid From: {{valid_from}} to {{valid_to}}</div>
</div>
</td>
</tr>
{{else}}
<td>
<div class="wrapper">
<div class="header">{{coupon_title}}</div>
<div class="column_wrapper">
<div class="two-col">
<div class="product_image"><img src="{{coupon_thumb}}" alt="Apple" height="110" width="110"></div>
<div class="description">{{coupon_description}}</div>
</div>
</div>
<div class="expiration">Valid From: {{valid_from}} to {{valid_to}}</div>
</div>
</td>
{{/ifPos}}
{{/each}}
</tr>
<table>
</script>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/handlebars.js"></script>
<script type="text/javascript" src-"js/handlebars.runtime-v1.3.0.js"></script>
<script type="text/javascript">
Handlebars.registerHelper('ifPos', function (context, options) {
var pos = false;
for (var i = 0, j = context.length; i < j; i++) {
if (context.length/5 == 0)
{
pos = true;
}
else {
pos = false;
}
}
console.log(pos);
return pos;
});
context.length/5 == 0
will not give you the value you want every 5th element. As 5/5 is 1, better to use modulus(%) which gives you the remainder, this way when it equals 0 you know it has gone into it whole.
Also when wanting to do your own if/else block handle bars provides you with options.fn and options.inverse. Return options.fn(//whatever you want to pass to the if block) or options.inverse(//what ever to provide to the else block) from your helper to go into the relevant part of the block.
Here is a code pen showing a quick example of how you could get the index position of the element you are iterating over and apply a styling based on that.
The helper functions will go to the true part of the if block when index % 3 is 0 (the first, because it is a 0 based index, and then every 3rd element. All other times it will go to the else
Helper
Handlebars.registerHelper('ifThird', function (index, options) {
if(index%3 == 0){
return options.fn(this);
} else {
return options.inverse(this);
}
});
Template
<script id="template" type="text/x-handlebars-template">
{{#each this}}
<p class="{{#ifThird #index}}
red
{{else}}
blue
{{/ifThird}}">{{#index}} - {{name}}</p>
{{/each}}
</script>
Related
I am new to handlebars and trying to get the first letter from a looped string i.e
{{#each message}}
<div class='name'>{{this.user}}</div>
<div class='img'><h1 class="abbr">{{this.user.charAt(0)}}</h1></div>
<div class='text'>
<div>
{{this.text}}
</div>
</div>
{{/each}}
I have tried {{this.text[0]}} and {{this.text.charAt(0)}}
You can use Preparation-Script to achieve this : HandleBars Documents
must register helper and use it:
Handlebars.registerHelper('returnOnlyZeroindex', function () {
return this.user[0]
})
and use like this:
<p>{{returnOnlyZeroindex}}</p>
I'm having a problem to insert a template after and not before a node. For example:
//Html looks like this
<div class="questions">
<div class="question"></div>
<div class="question"></div>
<div class="question"></div>
</div>
<template name="question">
<div class="question"></div>
</div>
<template name="questionExtraInfo">
<div class="extra"></div>
</template>
I'm trying to get the following:
<div class="questions">
<div class="question"></div>
<div class="extra"></div>
<div class="question"></div>
<div class="question"></div>
</div>
Calling blaze render inside question event
Template.question.events({
'click .more-details': function () {
var instance = Template.instance();
Blaze.renderWithData(Template.questionExtraInfo, {}, document.querySelector('.questions'), instance.find('.question')));
});
I can only figure out how render it before or inside how about after?
<div class="extra"></div>
<div class="question"></div>
<div class="question"><div class="extra"></div></div>
I think a better approach would be to take advantage of reactivity:
Change your questions template to:
<template name="question">
<div class="question"></div>
{{# if shouldIncludeExtra }}
{{> questionExtraInfo }}
{{/if}}
</template>
The above template should be inside an each loop.
Then in your js something like:
Template.question.helpers({
'shouldIncludeExtra': function() {
// replace 'n' with the actual index. I think `this.index` is
// provided within #each blocks, or you can use the new `#each` helper.
var index = n;
return Session.get('shouldIncludeExtra' + index);
}
});
Then, in your click event, you set a session var based on the index to true:
Template.questions.events({
'click .question': function(e, tpl) {
var question = e.currentTarget;
// You can probably come up with something better here..
var index = $(question).parent().find('> .question').index(question);
Session.set('shouldIncludeExtra' + index, true);
}
});
Because of reactivity, you would see the inserts right away when you fire the click event.
I realize this doesn't really answer the headline of your question, but it should get you the desired outcome.
My app is crashing my browser after implementing this columnWidth helper method. All I'm trying to do is rotate between col-md-7 and col-md-5 (Bootstrap classes) so that no two consecutive posts are the same width.
columnWidth: function() {
if (Session.get('columnWidth') === 'col-md-7') {
Session.set('columnWidth', 'col-md-5');
} else {
Session.set('columnWidth', 'col-md-7');
}
return Session.get('columnWidth');
}
The post template:
{{#each this}}
<div class="{{columnWidth}}">
<img src="{{image}}" height="350" width="{{imageWidth}}" alt="">
<div class="content">
<h2>{{title}}</h2>
<p>{{content}}</p>
<span class="dateAuthored">{{date}}</span>
</div>
</div>
{{/each}}
this refers to:
data: function() {
return Articles.find();
}
Any ideas why this is happening? I'm not receiving any errors. The browser tab just becomes unresponsive. Thanks.
You are constantly setting the same reactive variable so for example with the first div when the helper is called it will set it to col-md-7, then when it is called for the 2nd row you are changing the same variable to col-md-5 which is problematic for 2 reasons:
a) the template will redraw the first column and so they will both be col-md-5
b) the same helpers will get called again. I believe your browser crashes because you have created an infinite loop. Try console logging something inside your columnWidth helper and see how many times it gets called.
To achieve what you want you need to get the index of the {{#each }} loop and then have the column class dependent on whether it's odd or even. Unfortunately getting the index in meteor handlebars is a little tricky.
Try:
{{#each articles}}
<div class="{{columnWidth index}}">
<img src="{{image}}" height="350" width="{{imageWidth}}" alt="">
<div class="content">
<h2>{{title}}</h2>
<p>{{content}}</p>
<span class="dateAuthored">{{date}}</span>
</div>
</div>
{{/each}}
Then the following helpers:
articles: function() {
//'this' in this context should work.
//if not just replace with Articles.find().map(...
var articles = this.map(function(article, index) {
var i = _.extend(article, {index: index});
return i;
});
return articles;
},
columnWidth: function(index) {
if (index % 2 === 0)
return "col-md-5";
else
return "col-md-7"
}
Im trying to make a search box to filter down results of my returned collection in the client.
however when i actually try searching I'm getting the above error in the console.
RangeError: Maximum call stack size exceeded.
here is a look at my code.
<body>
{{#isolate}}
<header class="row-fluid">
{{> modules}}
</header>
{{/isolate}}
<div id="main" class="span11">
{{#if currentUser}}
{{#isolate}}
{{> customers_list}}
{{/isolate}}
{{#isolate}}
{{> contacts_list}}
{{/isolate}}
{{/if}}
</div>
</body>
my search form in inside the modules template
<template name="modules">
{{templateLogger "modules"}}
<ul id="module_list" class="nav">
{{#each list}}
<li>
{{name}}
</li>
{{/each}}
<form><input type="text" id="search"></form>
</ul>
and my customers_list template that I'm trying to filter the results
<template name="customers_list">
<table class="table">
<tr>
<th>Name</th>
<th>Address</th>
<th>City</th>
<th>State</th>
<th>Zip</th>
<th>Phone</th>
</tr>
{{#each record}}
<tr>
<td>{{name}}</td>
<td>{{address}}</td>
<td>{{city}}</td>
<td>{{state}}</td>
<td>{{zip}}</td>
<td>{{phone}}</td>
</tr>
{{/each}}
</table>
</template>
and here is the event handler for the search form
Template.modules.events({
'keypress input#search': function (event) {
Session.set("currentFilter", $('input#search'));
}
});
and the form helper do display the results
Template.customers_list.record = function() {
qry = Session.get("currentFilter") || "";
if (qry != "") {
return Customers.find({$or: [ {'name': qry}, {'address': qry}, {'city': qry}, {'state': qry} ] });
} else {
return Customers.find({competitor: null}, {sort: {name: 1}});
};
}
I have no clue what the is causing this error from what i was able to read on other SO posts about the error it seems like its a infinite loop however those were not meteor specific questions and i don't know if that would make a difference? also if there is an infinite loop i cant find it.
any help would be grateful.
This error occurs when you pass large object as an argument to your method. For me for example the first Time I encountered this error, was when I passed a Meteor.Collection as an argument :s . I worked around that by passing the collection name as a String and then using eval() in the Methods to get the Collection on which proceed.
Conclusion: Always use strings, integers, small arrays or really small objects as arguments for methods called from your event handlers.
Changing this:
Template.modules.events({
'keypress input#search': function (event) {
Session.set("currentFilter", $('input#search'));
}
});
To This:
Template.modules.events({
'keyup input#search': function (event) {
Session.set("currentFilter", $('input#search').val());
}
});
I believe you just need the .val() on the jquery dom reference of the input field. Additionally I would recommend using keyup for the event for something like this.
For getting the results out like you want you likely want to use a regular expression. Here's what I'm using in my app.
Template.hudlies.found = function() {
var searchVal = Session.get("searchFilter");
if (searchVal != "") {
var searchResults = Hudlies.find({ name: { $regex: '^.*' + searchVal + '.*', $options: 'i' } });
};
return searchResults;
};
I have a array of elements, i am appending them all to my form element. all works fine. But i am not able to add a "hr" element to each of my 3rd label.. i tried this.
<script id="locale-template" type="text/x-handlebars-template">
{{#each this}}
{{#if #index % 3 === 0 }}
<hr/>
{{/if}}
<label><input type="checkbox" /> {{name}} </label>
{{/each}}
</script>
But not works.. can any one suggest me the correct way please..?
Thanks in advance
First register helper like showHr
Handlebars.registerHelper("showHr", function(index_count,block) {
if(parseInt(index_count)%3=== 0){
return block.fn(this);}
});
Now In Template
{{#showHr #index}}
<hr/>
{{/showHr}}
Or if you want you can write generic helper refer http://doginthehat.com.au/2012/02/comparison-block-helper-for-handlebars-templates/
Edit for comment question
Handlebars.registerHelper("moduloIf", function(index_count,mod,block) {
if(parseInt(index_count)%(mod)=== 0){
return block.fn(this);}
});
Considering index start from 0
// If index is 0 open div
// if index is 3 means open a div
{{#moduloIf #index 0}}
<div>
{{/moduloIf}}
{{#moduloIf #index 3}}
<div>
{{/moduloIf}}
{{name}}
// if index+1 is modulo 3 close div
{{#moduloIf #index+1 3}}
</div>
{{/moduloIf}}