Iterate over array of arrays in SCA Template - handlebars.js

How can I nest for loops in SCA (Handlebars) template language?
For example; I have an array of arrays and I want to loop over the inner array elements:
// Loop 1
{{#each footerNavigationLinks}}
<ul class="footer-content-nav-list">
// Nested loop
{{#each objectToAtrributes item}}
<li>aa
<a {{objectToAtrributes item}}>
{{text}}
</a>
</li>
{{/each}}
</ul>
{{/each}}
And footerNavigationLinks is an array of arrays:
footerNavigationLinks: [
[
{text: 'Link a', href:'#'}
, {text: 'Link b', href:'#'}
, {text: 'Link c', href:'#'}
],
[
{text: 'Link a', href:'#'}
, {text: 'Link b', href:'#'}
, {text: 'Link c', href:'#'}
]
]

Not familiar with the helpers that SCA is providing (I presume that objectToAtrributes is one of those), but I would suggest that your nested #each should just be
{{#each this}}
since at that point all you have is an array. Within the nested #each, applying the 'objectToAttributes' helper may make sense since you are now working with each object with the array. However, if this doesn't work, I would try removing the helper since you should already have access to the 'text' property without it.

Related

Handlebars get first element that passes a condition

Using handlebars, I want to get the first item in an array that matches a conditional statement.
Here is an example object:
var objects = {[
"A": {
"isRed": false,
"name": "Yellow Circle"
},
"B": {
"isRed": true,
"name": "Red Square"
},
"C": {
"isRed": true,
"name": "Red Triangle"
}]};
I want to do something sort of like this
{{#each objects}}
{{#if this.isRed}}
{{#if #first}} {{!-- Want the first item that matches the above conditional --}}
{{this.name}}
{{/if}
{{/if}}
{{/each}}
Expected output:
Red Square
I believe that {{if #first}} won't work because it is looking at the second element in the array so it fails the condition.

Use a function inside {{#each element}} in Meteor?

In a Meteor project, I have a collection of documents which can have one of two formats. To simplify, a given document may either have a type property or a label property. I would like to display this is a multiple select element, using whichever property they have to identify them.
Here's a working example:
HTML
<body>
{{> test}}
</body>
<template name="test">
<select name="elements" size="2">
{{#each elements}}
<option value="{{id}}">{{id}}: {{type}}{{label}}</option>
{{/each}}
</select>
</template>
JavaScript
Template.test.helpers({
elements: function () {
return [
{ id: 1, type: "type" }
, { id: 2, label: "label" }
]
}
})
This happily displays:
1: type
2: label
However, this is a simplification. The actual data that I want to display is more complex than this. I would rather not put all the concatenation logic into the HTML template. I would much rather call a JavaScript function, passing it the current document, and get the string to display. Something like:
<option value="{{id}}">{{functionToCall_on_this}}</option>
How could I do this with Meteor?
Answering my own question, I have found that I can change my template to include this line...
<option value="{{id}}">{{id}}: {{string}}</option>
And my helpers entry to use the map method:
Template.test.helpers({
elements: function () {
data = [
{ id: 1, type: "type" }
, { id: 2, label: "label" }
]
return data.map(function (item, index, array) {
var string = item.type || item.label
return { id: item.id, string: string }
})
}
})

Using a string as part of an #each loop in handlebars.js

Relatively new to handlebars.js, but not templating. I am using two loops to output markup in specific order. Both collections.intro and collections.elements are created by https://github.com/segmentio/metalsmith-collections.
Javascript
var order = ['intro', 'elements'];
collections.intro = [
{ title: 'One' },
{ title: 'Two' }
];
collections.elements = [
{ title: 'One' },
{ title: 'Two' }
];
Handlebars
{{#each order}}
{{this}} /* intro, elements */
{{#each ../collections.[this]}}
{{this.title}}
{{/each}}
{{/each}}
Is there anyway to use this from the order loop to access the correct collections. Both collections[intro] and collections[elements] work when hard coded.
Managed to resolve this with a custom helper:
Javascript
Handlebars.registerHelper( 'collection', function ( slug, options ) {
return options.data.root.collections[slug];
});
Handlebars
{{#each order}}
{{#each (collection this)}}
{{this.title}}
{{/each}}
{{/each}}

Meteor: getting the HTML element which is using a global helper

Is it possible to know the HTML element which is invoking a global helper?
I have this blaze template:
<template name="tools">
<div>
<a id="pencil" class="{{toolIsActive}}">Pencil</a>
<a id="shape" class="{{toolIsActive}}">Shape</a>
<a id="poly" class="{{toolIsActive}}">Polygon</a>
<a id="arrow" class="{{toolIsActive}}">Arrow</a>
</div>
</template>
and so it'd be useful a helper like this:
UI.registerHelper('toolIsActive', function() {
return (this.id === currentTool) ? 'active' : '';
});
where I want this to be the invoking HTML element instead of template's data context.
Is there a way to access the element? I know I could use this.$('#pencil') but it's useless since the id it's exactly what I want to know.
You can work around this problem by passing the tool name as an argument for the helper:
<a id="pencil" class="{{toolIsActive 'pencil'}}">Pencil</a>
UI.registerHelper('toolIsActive', function(tool) {
return (tool === currentTool) ? 'active' : '';
});
Since this sort of helper is useful in many different parts of the application, you can make an universal one instead:
<a id="pencil" class="{{classIfEqual 'pencil' currentTool 'active'}}">Pencil</a>
UI.registerHelper('classIfEqual', function(a, b, className) {
return (a === b) ? className : '';
});
Another approach, which could make it easier to add more tools in the future:
<template name="tools">
<div>
{{#each tools}}
<a id="{{id}}" class="{{toolIsActive}}">{{humanName}}</a>
{{/each}}
</div>
</template>
Template.tools.helpers({
tools: [
{id: "pencil", humanName: "Pencil"},
{id: "shape", humanName: "Shape"},
{id: "poly", humanName: "Polygon"},
{id: "arrow", humanName: "Arrow"}
],
toolIsActive: function() {
return (this.id === currentTool) ? "active" : ""
}
});
You could potentially use that tools structure in multiple places, and then if you want to add more tools, you only have to add it in one place.

AngularJS - Change multiple CSS classes on 1 <div> from multiple options

I'm afraid I'm new to Angular and therefore my knowledge is limited. I'm hoping there is an easy solution to this and I'd very grateful for any help.
Problem: I have multiple dropdown lists all populated with data from a $scope.list and when one of the lists is clicked, it updates the variables $scope.current. I'm hoping to use the $scope.current array data in a container class elsewhere.
Code:
// ANGULAR
Set the current item
$scope.current = {
'activeClass1': 'initial-class1',
'activeClass2': 'initial-class2'
};
Populate the lists
$scope.itemsList1 = [
{'name': 'foo1'},
{'name': 'foo2'},
{'name': 'foo3'}
];
$scope.itemsList2 = [
{'name': 'bar1'},
{'name': 'bar2'},
{'name': 'bar3'}
];
HTML LIST 1
<ul class="dropdown-menu" role="menu">
<li ng-repeat="item in itemList1">
<a href="#" ng-click="current.activeClass1=item.name;>{{item.name}}</a>
</li>
</ul>
HTML LIST 2
<ul class="dropdown-menu" role="menu">
<li ng-repeat="item in itemList2">
<a href="#" ng-click="current.activeClass2=item.name;>{{item.name}}</a>
</li>
</ul>
// HTML TO UPDATE WHEN ABOVE LISTS ARE CLICKED
<div class="container" ng-class="{{current.activeClass1}} {{current.activeClass2}}">
Note: two classes need to be added to the parent and kept updated
</div>
// OUTPUT
Only initial 'container' class present.
// UPDATE
Thanks to the answers given, the jsFiddle showed that it should be working, but my problem was that ng-view was causing problems with my $scope - as soon as separated those dynamic classes and the ng-view, everything worked. Thanks again!
You have two extra closing curly braces in your code. this works: http://jsfiddle.net/s7AK7/
$scope.current = {
'activeClass1': 'initialClass',
'activeClass2': 'initialClass'
};
$scope.itemsList1 = [
{'name': 'item1'},
{'name': 'item2'},
{'name': 'item3'}
];
$scope.itemsList2 = [
{'name': 'item1'},
{'name': 'item2'},
{'name': 'item3'}
];
Try doing:
ng-class="[current.activeClass1, current.activeClass2]"

Resources