Im rewriting an angularjs directive ,moving all the markup from angular to reactiveJS, due to the poor dom performance of angularjs.
i have a simple a question about reactiveJS templating.
Template
{{#each dataTable:i}}
<li
{{#options.keysDisplayName:j}}
<span>{{dataTable[i][this]}}</span>
{{/each}}
</li>
{{/each}}
JS
var ractive = new Ractive({
el: 'details-table',
template: '#details-table-tmplt',
data: {
options: {
keysDisplayName : [ 'name', 'age']
},
dataTable : [
{name : 'dog', age:15, gender: 'f'},
{name : 'cat', age:15, gender: 'f'}
]
}
});
Im printing a table/list ,
I want to iterate on the each row in the dataTable , but only on the keys that present in options.keysDisplayName . ( so in this example , gender property wont be shown)
I guess im missing somthing basic in framework.
Thanks , much appreciated
Fix your template to:
{{#each dataTable:i}}
<li>
{{#each options.keysDisplayName:j}}
<span>{{dataTable[i][this]}}</span>
{{/each}}
</li>
{{/each}}
After fix the output will be:
dog15
cat15
Related
So, I have an object like this:
object = {
"group_1": [
{
"name": "Foo"
}
],
"group_2": [
{
"name": "Bar"
}
]
}
And in my hbs view I'm doing like this:
{{#each group_1}}
<p>{{name}}</p>
{{/each}}
{{#each group_2}}
<p>{{name}}</p>
{{/each}}
Is there any way of concatenating both and not repeating code? The solution would be something like this:
{{#each group_1 + group_2}}
<p>{{name}}</p>
{{/each}}
Any idea how to do this?
Handlebars itself does not support this.
But you can still do:
object.groups = object.group_1.concat(object.group_2);
{{#each groups}}
<p>{{name}}</p>
{{/each}}
Which seems to be a straightforward solution.
Alternatively you can put both your groups into an object and iterate over it like this:
let object = {
groups: {
'group_1': [
{
'name': 'Foo'
}
],
'group_2': [
{
'name': 'Bar'
}
]
}
};
{{#each groups}}
{{!-- you can refference group name as #key here --}}
{{#each this}}
{{name}}
{{/each}}
{{/each}}
I have an extremely hierarchical JSON structure as a scope variable in my AngularJS controller. I want to loop around different sections of that variable. I thought about using ng-init to specify where in the structure I am. Here is some code:
my_app.js:
(function() {
var app = angular.module("my_app");
app.controller("MyController", [ "$scope", function($scope) {
$scope.things = {
name: "a",
children: [
{
name: "a.a",
children: [
{ name: "a.a.a" },
{ name: "a.a.b" }
]
},
{
name: "a.b",
children: [
{ name: "a.b.a" },
{ name: "a.b.b" }
]
}
]
}
}]);
});
my_template.html:
<div ng-app="my_app" ng-controller="MyController">
<ul>
<li ng-init="current_thing=things.children[0]" ng-repeat="thing in current_thing.children>
{{ thing.name }}
</li>
</ul>
</div>
I would expect this to display a list:
a.a.a
a.a.b
But it displays nothing.
Of course, if I specify the loop explicitly (ng-repeat="thing in things.children[0].children") it works just fine. But that little snippet of template code will have to be run at various points in my application at various levels of "things."
(Just to make life complicated, I can get the current thing level using standard JavaScript or else via Django cleverness.)
Any ideas?
ng-init runs at a lower priority (450) than ng-repeat (1000). As a result, when placed on the same element, ng-repeat is compiled first meaning that the scope property created by ng-init won't be defined until after ng-repeat is executed.
As a result, if you want to use it in this manner, you'd need to place it on the parent element instead.
<div ng-app="my_app" ng-controller="MyController">
<ul ng-init="current_thing=things.children[0]">
<li ng-repeat="thing in current_thing.children>
{{ thing.name }}
</li>
</ul>
</div>
I'm trying to iterate in a array nested in an other array.
My collection data :
"roles" : [
{
"id" : 126987,
"name" : "Ergonomic Wooden Fish",
"containers" : [
{
"id" : "2654213845" ,
"name" : "FirstCont",
"rights" : [
{
"id" : "54684213",
"name: "FirstRight"
}
]
}
]
}
]
JS/Helpers
Template.myTemplate.helpers({
'roles': function() {
return Roles.find({});
}
});
HTML
<template name='myTemplate'>
{{#each roles}}
{{id}}
{{name}}
{{#each containers}}
{{name}}
{{/each}}
{{/each}}
</template>
I can display name and id from roles, but not name from containers.
I try to visualize the object back but I can't.
What am I doing wrong?
create another helper containers.
containers: function(){
return this.containers;
}
and then keep your template as it is.
<template name='myTemplate'>
{{#each roles}}
{{id}}
{{name}}
{{#each containers}}
{{name}}
{{/each}}
{{/each}}
I'm working on a meteor project where I want to place an objects from the same collection in different divs depending on their properties. The easiest way to explain is probably to show a test case:
html
<template name="board">
{{#each rows}}
<div id="row-{{this}}" class="row">
{{#each columns}}
<div id="{{..}}-{{this}}" class="column column-{{this}} row-{{..}}- column">
{{>pins }}
</div>
{{/each}}
</div>
{{/each}}
</template>
<template name="pins">
{{#each pins}}
<div class = "pin" >{{this}}</div>
{{/each}}
</template>
js
Template.board.helpers({
rows: [
'top',
'middle',
'bottom'
],
columns: [
'left',
'center',
'right'
]
});
Template.pins.helpers({
pins:[
{name: 'test1', loaction: 'bottomcenter'},
{name: 'test2', loaction: 'topleft'},
{name: 'test3', loaction: 'bottommcenter'},
{name: 'test4', loaction: 'middleright'}
]
});
I'd like to place the pins in the correct div based on their location. Now I can of course manually write out every div in the html and a helper for each one (and will if there's no better solution), but I'm trying to figure out what the most efficient solution is.
I tried passing the location back to the helper function with the following code:
{{#each pins location="{{..}}{{this}}}}
and this
{{#each pins location="{{..}}{{this}}"}}
and running a function, But the tags after location= come through as {{..}}{{this}} instead of the values.
I also tried restructuring the data like this:
pins:{
bottomcenter: [{name: 'test1'}, {name: 'test3'}]
topleft:[{name: 'test2'}]
}
etc, and passing the parameter as a data context:
{{>pins {{..}}{{this}}}}
but that didn't seem to work either. Any help is appreciated!
your template should like:
<template name="pins">
{{#each pins}}
<div class = "pin" >{{this.name}} {{this.location}}</div>
{{/each}}
</template>
and the helper like this:
Template.pins.helpers({
pins: function() {
return [
{name: 'test1', loaction: 'bottomcenter'},
{name: 'test2', loaction: 'topleft'},
{name: 'test3', loaction: 'bottommcenter'},
{name: 'test4', loaction: 'middleright'}
]
}
});
I think I got it! The trick seems to be that you can't embed {{}} brackets in other {{}} brackets. So you use:
<template name="pins">
{{#each pins .. this}}
<div class="pin">
{{this.name}}
</div>
{{/each}}
</template>
.. and this get passed back to the helper function, and then you can search based on the results:
Template.pins.helpers({
pins: function(row,col){
var search = row+"-"+col;
var results = [];
var pinarr = [
{insert data here}
];
for(var i = 0; i < pinarr.length ; i++){
if(pinarr[i].location === search) {
results.push(pinarr[i]);
}
}
return results;
}
});
}
What I'm trying to do is very basic but I'm having very little luck...
Simply enough, I don't want to display a chunk of HTML until a certain Ember Data model property is fully loaded.
As you can see from the jsfiddle, the parent model: App.Person gets loaded into the DOM and it also loads the 3 placeholders for its hasMany property belts.
It then executes the request to populate App.Belt and fills in the placeholders.
While this is usually ok, it makes a big mess of things when trying to build an SVG, for example. Since the surrounding <svg> tags will get appended to the DOM immediately and then some time down the track (once the asynchronous request returns data), the inner svg components will be added between the tags. This usually creates browser rendering errors.
TL;DR
In the example, how do I defer the <h3>...</h3> section of the template from being added to the DOM until the model data and its relationships (belts) are fully loaded? This way everything gets visually and physically added to the DOM at once.
The JS:
// Create Ember App
App = Ember.Application.create();
// Create Ember Data Store
App.store = DS.Store.create({
revision: 11,
//Exagerate latency to demonstrate problem with relationships being loaded sequentially.
adapter: DS.FixtureAdapter.create({latency: 5000})
});
// Create parent model with hasMany relationship
App.Person = DS.Model.extend({
name: DS.attr( 'string' ),
belts: DS.hasMany( 'App.Belt' )
});
// Create child model with belongsTo relationship
App.Belt = DS.Model.extend({
type: DS.attr( 'string' ),
parent: DS.belongsTo( 'App.Person' )
});
// Add Parent fixtures
App.Person.FIXTURES = [{
"id" : 1,
"name" : "Trevor",
"belts" : [1, 2, 3]
}];
// Add Child fixtures
App.Belt.FIXTURES = [{
"id" : 1,
"type" : "leather"
}, {
"id" : 2,
"type" : "rock"
}, {
"id" : 3,
"type" : "party-time"
}];
// Set route behaviour
App.IndexRoute = Ember.Route.extend({
model: function() {
return App.Person.find();
},
renderTemplate: function() {
this.render('people');
}
});
The HTML/HBS:
<script type="text/x-handlebars">
<h1>Application</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" id="people">
<h3>Don't load this header until every belt defined in App.Person.belts is loaded</h3>
<ul>
{{#each controller}}
{{debugger}}
<li>Id: {{id}}</li>
<li>Name: {{name}}</li>
<li>Belt types:
<ul>
{{#each belts}}
<li>{{type}}</li>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
</script>
The fiddle: http://jsfiddle.net/zfkNp/4/
Check for the controller.content.length and belts.isLoaded, See the jsfiddle for a solution.
<script type="text/x-handlebars" id="people">
{{#if controller.ready}}
<h3>Don't load this header until every belt defined in App.Person.belts is loaded</h3>
{{/if}}
<ul>
{{#each controller}}
{{debugger}}
{{#if belts.isLoaded}}
<li>Id: {{id}}</li>
<li>Name: {{name}}</li>
<li>Belt types:
<ul>
{{#each belts}}
<li>{{type}}</li>
{{/each}}
</ul>
</li>
{{/if}}
{{/each}}
</ul>
</script>
App.IndexController = Ember.ArrayController.extend({
content: null,
ready:function() {
return this.get('content.length')>0
}.property('content.length')
});