I am using handlebars for a project and I have a data file with 10 items in it. I am using the 'each' command to show them all on one page (which works fine) but for another page i would like to show only the first 4 items. How can I do this? Thanks in advance!
You can do that with the #index variable within a {{#each}} loop and a custom helper that will test the value of #index. Look at the snippet code below to suit your needs.
$(document).ready(function () {
var context = {
"items" : [{"name":"item1"},{"name":"item2"},{"name":"item3"},{"name":"item4"},{"name":"item5"},{"name":"item6"},{"name":"item7"},{"name":"item8"},{"name":"item9"},{"name":"item10"},{"name":"item11"},{"name":"item12"},{"name":"item13"}]
};
Handlebars.registerHelper('test', function(lvalue, operator, rvalue, options) {
var doDisplay = false;
var items = (""+rvalue).split("|");
var arrayLength = items.length;
for (var i = 0; (i < arrayLength); i++) {
if (operator == "eq") {
if (lvalue == items[i]) {
doDisplay = true;
}
} else if (operator == "ne") {
if (lvalue != items[i]) {
doDisplay = true;
}
} else if (operator == "gt") {
if (parseFloat(lvalue) > parseFloat(items[i])) {
doDisplay = true;
}
} else if (operator == "lt") {
if (parseFloat(lvalue) < parseFloat(items[i])) {
doDisplay = true;
}
}else if (operator == "le") {
if (parseFloat(lvalue) <= parseFloat(items[i])) {
doDisplay = true;
}
}else if (operator == "ge") {
if (parseFloat(lvalue) >= parseFloat(items[i])) {
doDisplay = true;
}
}
}
if (doDisplay) {
return options.fn(this);
} else {
return "";
}
});
var source = $("#sourceTemplate").html();
var template = Handlebars.compile(source);
var html = template(context);
$("#resultPlaceholder").html(html);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script id="sourceTemplate" type="text/x-handlebars-template">
Complete list<br/>
<ul>
{{#each items}}
<li>
{{#index}} {{name}}
</li>
{{/each}}
</ul>
Partial list<br/>
<ul>
{{#each items}}
{{#test #index 'lt' 4}}
<li>
{{#index}} {{name}}
</li>
{{/test}}
{{/each}}
</ul>
</script>
<div id="resultPlaceholder">
</div>
Related
I am using Parse inside a Meteor application. Here is my code:
HTML:
<body>
<div class="container">
<header>
<h1 style="color: red;">Meteor & Parse DB Demo</h1>
</header>
{{>header}}
</div>
</body>
<template name="header">
{{#each alphabets}}
<div class="alphabets">{{this}}</div>
{{/each}}
JS:
Parse.initialize("appid", "jsid");
var list_item=Parse.Object.extend("className");
var query=new Parse.Query(list_item);
var list=[];
if (Meteor.isClient) {
query.find({
success:function(o){
for (var i = 0; i < o.length; i++) {
var object = o[i];
list.push(object.get('name'));
console.log(list);
}
return list;
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
Template.header.alphabets = function() {
return list;
};
}
</template>
The data is coming from the database, but the problem is that the template is loaded before the data arrives:
template.headed.alphabet is called before query.find is is called
Make your list a client side collection, it will then be reactive and update
list = new Mongo.Collection(null);
then do list.insert(object.get('name'))
Parse.initialize("appid", "jsid");
var list_item=Parse.Object.extend("className");
var query=new Parse.Query(list_item);
if (Meteor.isClient) {
list = new Mongo.Collection(null);
query.find({
success:function(o){
for (var i = 0; i < o.length; i++) {
var object = o[i];
list.insert(object.get('name'));
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
Template.header.helpers({
alphabets : function() {
return list.find({});
}
});
}
</template>
Here is the code which worked for me i am posting this so other can refer it.
HTML:
<body>
<div class="container">
<header>
<h1 style="color: red;">Meteor & Parse DB Demo</h1>
</header>
{{>header}}
</div>
</body>
<template name="header">
{{#each alphabets}}
<div class="alphabets">{{this.name}}</div>
{{/each}}
JS:
Parse.initialize("appid", "jsid");
var list_item=Parse.Object.extend("className");
var query=new Parse.Query(list_item);
var list=[];
if (Meteor.isClient) {
query.find({
success:function(o){
for (var i = 0; i < o.length; i++) {
var object = o[i];
list.push({name:object.get('name')});
console.log(list);
}
return list;
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
Template.header.alphabets = function() {
return list;
};
}
</template>
I need to know about to increment helper variable count in meteor.
For example :
<head>
<title>hello</title>
</head>
<body>
<h1>Welcome to Meteor!</h1>
{{> hello}}
</body>
<template name="hello">
<button>Click Me</button>
{{#each arr}}
{{counter}} <!-- How to get the foo index here? --> {{name}}
{{/each}}
</template>
Js Code :
if (Meteor.isClient) {
// counter starts at 0
Template.hello.helpers({
counter: function () {
return Session.get('counter');
}
});
Template.hello.helpers({
arr: function () {
console.log(Session.get('arrres'));
return Session.get('arrres');
}
});
Template.hello.events({
'click button': function () {
Session.set('counter', 0);
Meteor.call('arrfun',10, function (error, res) {
if (!error)
{ arrres = res;
Session.set('arrres', arrres);
console.log(res);
}
else{}
} );
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
Meteor.methods({
arrfun:function arrfun(properties, callback)
{
var obj = [];
for(var i = 0 ; i < 10; i++)
{
var obj1 = new Object();
obj1.name = 'abc'+i;
obj.push(obj1);
}
return obj;
}
});
});
}
The above 'arr' contains list of names that present in object.Now can iterate 'arr' it will comes names.
now can we print names as like 1 abc
2 xyz until 'arr' completed.
So how can we print numbers from 1 to 'arr' length before names.
So please suggest me what to do for this.
It's a little hard to interpret your question but I think you want this:
Template.hello.events({
'click button': function(event) {
Session.set('counter', Math.round(Math.random() * 10));
}
});
Template.hello.helpers({
'arr': function() {
var counter = Session.get('counter');
var arr = [];
for(var i = 0; i < counter; i++) {
arr.push(i + 1);
}
return arr;
}
});
And in your hello template:
<ul>
{{#each arr}}
<li>{{this}}</li>
{{/each}}
</ul>
This will generate a random number between 0-10 every time you click the button, and then the template will count up to this random number in sequence.
I am querying data via breeze.js which works fine the first time. The second time the view isn't updated.
html
<a id="linkQDate">Order by Newest</a>
<a id="linkQScore">Order by Score</a>
<div id="questionWrapper">
<ul data-bind="foreach: results">
<li>
<strong><span data-bind="text: Score"></span></strong>
<span data-bind="text: Titel"></span>
</li>
</ul>
</DIV>
js
$(document).ready(function () {
var manager = new breeze.EntityManager('/breeze/dbentities');
var isApplied = false;
var dmodel;
$("#linkQDate").click(function () {
var query = breeze.EntityQuery.from("Questions").orderBy("Date");
manager.executeQuery(query).then(querySucceeded);
function querySucceeded(data) {
dmodel = data;
if (!isApplied) {
ko.applyBindings(dmodel, $("#questionWrapper")[0]);
isApplied = true;
}
}
});
$("#linkQScore").click(function () {
var query = breeze.EntityQuery.from("Questions").orderBy("Score");
manager.executeQuery(query).then(querySucceeded);
function querySucceeded(data) {
dmodel = data;
if (!isApplied) {
ko.applyBindings(dmodel, $("#questionWrapper")[0]);
isApplied = true;
}
}
});
});
If you just using plain JS objects it will not work. Because it does not know when underlying data changes. Use mapping plugin for this to work:
$(document).ready(function () {
var manager = new breeze.EntityManager('/breeze/dbentities');
var isApplied = false;
var dmodel;
function querySucceeded(data) {
if (!isApplied) {
dmodel = ko.mapping.fromJS(data);
ko.applyBindings(dmodel, $("#questionWrapper")[0]);
isApplied = true;
} else {
ko.mapping.fromJS(data, dmodel);
}
}
$("#linkQDate").click(function () {
var query = breeze.EntityQuery.from("Questions").orderBy("Date");
manager.executeQuery(query).then(querySucceeded);
});
$("#linkQScore").click(function () {
var query = breeze.EntityQuery.from("Questions").orderBy("Score");
});
});
Working code from Tomas without mappings. It is much faster:
$(document).ready(function () {
var manager = new breeze.EntityManager('/breeze/dbentities');
var isApplied = false;
var dmodel = { results: ko.observableArray() };
function queryFailed(data) {
console.log(data);
}
function querySucceeded(data) {
if (!isApplied) {
for (var i = 0; i < data.results.length; i++) {
dmodel.results.push(data.results[i]);
}
ko.applyBindings(dmodel, $("#questionWrapper")[0]);
isApplied = true;
} else {
dmodel.results.removeAll();
for (var i = 0; i < data.results.length; i++) {
dmodel.results.push(data.results[i]);
}
}
}
$("#linkQDate").click(function () {
var query = breeze.EntityQuery.from("Questions").orderBy("Date");
manager.executeQuery(query).then(querySucceeded).fail(queryFailed);;
});
$("#linkQScore").click(function () {
var query = breeze.EntityQuery.from("Questions").orderBy("Score");
manager.executeQuery(query).then(querySucceeded).fail(queryFailed);;
});
});
I am trying to solve a problem of rendering one template in context of another template with knockout. The outer template doesn't know and shouldn't care about the inner template and its view model. All it cares about is it's own template, a place to embed the inner template passing the name of it and its view model.
So ideally I wish I know how to implement the following binding:
<script type="text/html" id="outerTemplate">
<div class="outer-template" data-bind="here: {}"></div>
</script>
<!-- ko nested: { to: 'outerTemplate', data: { name: 'I am an inner view model' } } -->
<div class="inner-template" data-bind="text: name"></div>
<!-- /ko -->
If anyone knew the knockout well enough to easily outline such kind of binding I would greatly appreciate it.
UPDATE: The feature request was proposed: https://github.com/knockout/knockout/issues/1251
The template binding allows you to dynamically select the template name to use, so you can do something like:
<script id="outer" type="text/html">
<h2>Outer</h2>
<div data-bind="template: { name: tmplName, data: data }"></div>
</script>
<script id="inner" type="text/html">
<h3>Inner</h3>
<input data-bind="value: name" />
</script>
<div data-bind="template: 'outer'"></div>
In this case the view model would look like:
var vm = {
tmplName: 'inner',
data: {
name: ko.observable("Bob")
}
};
ko.applyBindings(vm);
The view model could be structured however you want. The key is just that you are passing the template name and data into the template binding.
Sample: http://jsfiddle.net/rniemeyer/LHhc8/
There is a working example I made myself: http://jsfiddle.net/m34wp/4/
var templateComputedDomDataKey = '__ko__templateComputedDomDataKey__';
function disposeOldComputedAndStoreNewOne(element, newComputed) {
var oldComputed = ko.utils.domData.get(element, templateComputedDomDataKey);
if(oldComputed && (typeof (oldComputed.dispose) == 'function')) {
oldComputed.dispose();
}
ko.utils.domData.set(element, templateComputedDomDataKey, (newComputed && newComputed.isActive()) ? newComputed : undefined);
}
function makeArray(arrayLikeObject) {
var result = [];
for(var i = 0, j = arrayLikeObject.length; i < j; i++) {
result.push(arrayLikeObject[i]);
}
;
return result;
}
function moveCleanedNodesToContainerElement(nodes) {
var nodesArray = makeArray(nodes);
var container = document.createElement('div');
for(var i = 0, j = nodesArray.length; i < j; i++) {
container.appendChild(ko.cleanNode(nodesArray[i]));
}
return container;
}
ko.bindingHandlers['nested'] = {
'init': function (element, valueAccessor) {
var elementType = 1;
var commentType = 8;
var bindingValue = ko.utils.unwrapObservable(valueAccessor());
if(element.nodeType == elementType || element.nodeType == commentType) {
// It's an anonymous template - store the element contents, then clear the element
var templateNodes = element.nodeType == 1 ? element.childNodes : ko.virtualElements.childNodes(element);
var container = moveCleanedNodesToContainerElement(templateNodes);
new ko.templateSources.anonymousTemplate(element)['nodes'](container);
}
return {
'controlsDescendantBindings': true
};
},
'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var options = ko.utils.unwrapObservable(valueAccessor());
var outerTemplateName = options['to'];
var dataValue = ko.utils.unwrapObservable(options['data']) || viewModel;
var innerContext = bindingContext['createChildContext'](dataValue);
innerContext.innerTemplateElement = element;
var templateComputed = ko.renderTemplate(outerTemplateName, innerContext, options, element);
disposeOldComputedAndStoreNewOne(element, templateComputed);
}
};
ko.bindingHandlers['here'] = {
'init': function (element, valueAccessor) {
return {
'controlsDescendantBindings': true
};
},
'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var templateElement = bindingContext.innerTemplateElement;
if(viewModel != null) {
var innerContext = bindingContext['createChildContext'](viewModel);
var templateComputed = ko.renderTemplate(templateElement, innerContext, {
}, element);
disposeOldComputedAndStoreNewOne(element, templateComputed);
} else {
}
}
};
ko.virtualElements.allowedBindings['nested'] = true;
The playlist loads every time in FF but only the first time in IE (6-8), after that only randomly.
If I alert the error that's thrown I get "TypeError: playerReady is undefined".
My code looks good and obviously works since FF displays the playlist perfectly. I've got no idea how to solve this. Anyone?
<script type='text/javascript'>
var so = new SWFObject('/UI/Flash/player.swf', 'ply', '<%=FlashWidth %>', '<%=FlashHeight %>', '9', '#ffffff'),
playlistURL = '<%=PlaylistURL %>',
imageURL = '<%=GetBackgroundImageUrl() %>';
so.addParam('allowfullscreen', 'true');
so.addParam('allowscriptaccess', 'always');
if (playlistURL !== '') {
so.addVariable('playlistfile', playlistURL);
so.addVariable('playlist', 'none');
so.addVariable('enablejs', 'true');
}
else {
so.addVariable('file', '<%=FlashURL %>');
}
if (imageURL.length > 0) {
so.addVariable('image', imageURL);
}
so.write('preview<%=PlayerID %>');
</script>
The playerReady function is called by the player once it's finished it's setup process. Do you happen to define that function and then set it to undefined? That might cause an error.
Also, what version of the player are you using?
so.addVariable('enablejs', 'true');
I don't belive that's been a flashvar since the 3.X player, which is no longer supported.
Best,
Zach
Developer, LongTail Video
Not sure how I solved it, but here is the final code that actually works:
HTML PAGE:
<script type='text/javascript'>
var so = new SWFObject('/UI/Flash/player.swf', 'ply', '700', '345', '9', '#ffffff'),
playlistUrl = 'XML-PLaylist',
imageURL = '/ImageVault/Images/conversionFormat_2/id_1577/scope_0/ImageVaultHandler.aspx';
so.addParam('allowfullscreen', 'true');
so.addParam('allowscriptaccess', 'always');
so.addParam('wmode', 'opaque');
if (playlistUrl !== '') {
so.addVariable('playlistfile', playlistUrl);
so.addVariable('playlist', 'none');
so.addVariable('controlbar', 'bottom');
so.addVariable('backcolor', '0xDDE5FF');
so.addVariable('frontcolor', '0x142864');
so.addVariable('screencolor', '0xffffff');
so.addVariable('enablejs', 'true');
so.addVariable('overstretch', 'true');
}
else {
so.addVariable('file', '');
}
if (imageURL.length > 0) {
so.addVariable('image', imageURL);
}
so.write('preview');
</script>
And here's the JavaScript:
try {
var playlistReady = playerReady;
} cat
ch (err) {
//alert('1' + err);
}
playerReady = function(obj) {
setTimeout(function() { checkPlaylistLoaded(obj) }, 1);
try {
playlistReady(obj);
} catch (err) {
//alert(err);
}
}
function itemHandler(obj) {
var item = obj['index'];
var playlist = $("#" + obj['id']).next();
var currentItem = 0;
playlist.children().each(function() {
if (currentItem == item) {
$(this).addClass("playing");
} else {
$(this).removeClass("playing");
}
currentItem++;
});
}
function checkPlaylistLoaded(obj) {
//debugger;
var player = document.getElementById(obj['id']),
jsPlaylist = player.getPlaylist();
if (jsPlaylist.length > 0) {
var playlist = createPlaylist(obj);
populatePlaylist(player, jsPlaylist, playlist);
player.addControllerListener("PLAYLIST", "playlistHandler");
player.addControllerListener("ITEM", "itemHandler");
player.addControllerListener("STOP", "showPlaylist");
player.addModelListener("STATE", "stateListener");
} else {
setTimeout(function() { checkPlaylistLoaded(obj) }, 150);
}
}
function stateListener(obj) {
if (obj.newstate === 'PLAYING') {
hidePlaylist();
}
if (obj.newstate === 'PAUSED') {
showPlaylist();
}
}
function createPlaylist(obj) {
var playerDiv = $("#" + obj['id']);
playerDiv.after("<div class='jw_playlist_playlist'></div>");
return playerDiv.next();
}
function hidePlaylist() {
$('.jw_playlist_playlist').animate({ left: "-320px" }, 1000);
}
function showPlaylist() {
$('.jw_playlist_playlist').animate({ left: "-10px" }, 1000);
}
function playlistHandler(obj) {
var player = document.getElementById(obj['id']),
jsPlaylist = player.getPlaylist(),
playerDiv = $("#" + obj['id']),
playlist = playerDiv.next();
populatePlaylist(player, jsPlaylist, playlist);
}
function populatePlaylist(player, jsPlaylist, playlist) {
playlist.empty();
for (var i = 0; i < jsPlaylist.length; i++) {
var jsItem = jsPlaylist[i];
var alternate = "even";
if (i % 2) {
alternate = "odd";
}
playlist.append("<div id='" + getItemId(jsItem) + "' class='jw_playlist_item " + alternate + "'>" + dump(jsItem) + "</div>");
}
var playlistItem = 0;
playlist.children().each(function() {
var currentItem = playlistItem;
$(this).click(function() {
player.sendEvent("ITEM", currentItem);
});
playlistItem++;
});
}
function getItemId(arr) {
var output = '${link}',
variables = getVars(output),
j;
for (j = 0; j < variables.length; j++) {
var variable = variables[j],
varName = variable.replace('${', '').replace('}', ''),
value = arr[varName];
if (!value) {
value = '';
}
output = output.replace(variable, value);
}
return output;
}
function dump(arr) {
var output = "<div class='jw_playlist_title'>${title}</div><div class='jw_playlist_description'>${description}</div>",
variables = getVars(output),
j;
for (j = 0; j < variables.length; j++) {
var variable = variables[j],
varName = variable.replace('${', '').replace('}', ''),
value = arr[varName];
if (!value) {
value = '';
}
output = output.replace(variable, value);
}
return output;
}
function dumpText(arr) {
var dumped_text = "";
if (typeof (arr) == 'object') {
for (var item in arr) {
var value = arr[item];
if (typeof (value) == 'object') {
dumped_text += "<div class='" + item + "'>";
dumped_text += dump(value);
dumped_text += "</div>";
} else {
dumped_text += "<div class='" + item + "'>" + value + "</div>";
}
}
} else {
dumped_text += arr + " (" + typeof (arr) + ")";
}
return dumped_text;
}
function getVars(str) {
return str.match(/\$\{(.*?)\}/g);
}