Lets say I've an array with 6 items and I want print them 3 per list
Ex
//arr = [1,2,3,4,5,6];
//html
<div class="first">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
<div class="second">
<ul>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
</div>
How can I accomplish that with ember/handlebars?
Thanks
One option would be to write a computed property on your controller that splits the larger array into an array of arrays.
Then you could iterate through the arrays of the computed property and use a component to display each of the smaller arrays.
I'll leave this as an excercise to you unless you have other questions.
Similar to what #Oren is saying, you would need to decorate your model (the array) inside the controller and then display the decorated model in the handlebars (as there is no way to perform logic inside the handlebars itself).
So, something like:
App.IndexController = Ember.ArrayController.extend({
decoratedModel: function(){
var model = this.get('model');
return [
Ember.Object.create({
className: "first",
arr: model.slice(0, 3)
}),
Ember.Object.create({
className: "second",
arr: model.slice(3)
})
];
}.property('model')
});
Then, you can display that in your template as follows:
<script type="text/x-handlebars" data-template-name="index">
{{#each item in decoratedModel}}
<div {{ bind-attr class=item.className}}>
<ul>
{{#each thing in item.arr }}
<li>{{thing}}</li>
{{/each}}
</ul>
</div>
{{/each}}
</script>
I ended doing this:
in template.hbs
{{#each set in arraySets}}
<div class="col-sm-6">
<ul class="list-unstyled">
{{#each item in set }}
<li>{{item.name}}</li>
{{/each}}
</ul>
</div>
{{/each}}
in the related controller
import Ember from "ember";
import Collection from "../../utils/collection";
export default Ember.ObjectController.extend({
// ....
arraySets: function() {
var infos = this.get('model.infos');
return Collection.create({ content: infos }).divide();
}.property()
});
and who does the hard work is the utils/collection.js
import Ember from "ember";
var Collection = Ember.ArrayProxy.extend({
divide: function (size = 2) {
var array = this.get('content');
var length = array.length;
var limit = Math.ceil(length / size);
var sets = Ember.A([]);
for (var i = 0; i < Math.min(size, length); i++) {
sets.pushObject(array.slice(i * limit, (i + 1) * limit));
}
return Collection.create({content: sets});
}
});
export default Collection;
I hope that this could help someone else!
Related
The new syntax
{{#each pages as |page| itemController="listedPage"}}
causes parse error. Is item Controller usable with the new "each" syntax?
If yes, how to specify it? If not, what is the preferred way to use computed properties with the items of "each"?
Controllers are being phased out - so I would recommend using components..
Component for Each Item
You could implement a 'listed-page' component which would look something like this:
Your template:
<ul>
{{#each pages as |page|}}
{{listed-page page=page}}
{{/each}}
</ul>
The component:
## components/listed-page.js
export default Ember.Component.extend({
tagName: 'li',
page: null,
pageNumberFancy: function() {
return '^'+this.get('page.number')+'^';
}.property('page.number')
})
## templates/components/listed-page.hbs
Number: {{page.number}} ({{pageNumberFancy}})
The output:
<div class='template-level'>
<ul class='template-level'>
<li class='listed-page-level'>Number: 1 (^1^)</li>
<li class='listed-page-level'>Number: 2 (^2^)</li>
</ul>
</div>
Component for the List
Or a listed-pages component - if 'pages' is an array computed property - or both:
Your template:
{{listed-pages pages=pages}}
The component:
## components/listed-pages.js
export default Ember.Component.extend({
tagName: 'ul',
pages: null,
pagesPlusOne: function() {
var pagesList = this.get('pages');
pagesList.addObject(specialPage);
return pagesList;
}.property('pages.[]')
})
## templates/components/listed-pages.hbs
{{#each pagesPlusOne as |page|}}
{{listed-page page=page}}
{{/each}}
The output:
<div class='template-level'>
<ul class='listed-pages-level'>
<li class='listed-page-level'>Number: 1 (^1^)</li>
<li class='listed-page-level'>Number: 2 (^2^)</li>
<li class='listed-page-level'>Number: +1 (^+1^)</li>
</ul>
</div>
I have an index route where I need to populate a menu in one outlet based on a list of models. However I cannot get the models to be represented in the each statement.
Here is the Category Model:
App.Category = DS.Model.extend({
title: DS.attr('string'),
parent: DS.belongsTo('App.Category')
})
And here is the indexRoute where I render it
App.IndexRoute = Em.Route.extend({
renderTemplate: function(){
this.render('index')
this.render('categoryMenu',
{
outlet: 'sidebar',
into: 'index',
model: function() {
return App.Category.find()
},
controller: App.CategoriesController
})
this.render('badgesList',{
outlet: 'badgesList',
into: 'index'
})
}
})
Index Template:
<script type="text/x-handlebars" data-template-name="index">
<div class="span3">
{{outlet sidebar}}
</div>
<div class="span8">
{{outlet badgesList}}
</div>
</script>
Nested Category Template
<script type="text/x-handlebars" data-template-name="categoryMenu">
<ul>
{{#each model}}
{{title}}
{{/each}}
</ul>
</script>
I have tried to change the each statement to several different things like controller or item in model but nothing is displayed.
Thank you for any help!
Make yourself CategoriesController first like this:
App.CategoriesController = Ember.ArrayController.extend({
content: function () {
return App.Category.find()
}.property()
});
rename your template just to categories
<script type="text/x-handlebars" data-template-name="categories">
<ul>
{{#each model}}
{{title}}
{{/each}}
</ul>
</script>
in your index template replace {{outlet}} with {{render 'categoies'}}
<script type="text/x-handlebars" data-template-name="index">
<div class="span3">
{{render 'categoies'}}
</div>
<div class="span8">
{{outlet badgesList}}
</div>
</script>
as last thing remove the call rendering categoryMenu.
I'm creating an Ember application to display twitter feeds but I am having trouble with displaying individual tweets through embedded resources.
The code is as follows:
Templates
<script type="text/x-handlebars" data-template-name="tweets">
<div id="stream">
{{#each tweet in controller}}
<div class="tweet">
<p class="tweet_text">{{tweet.text}}</p>
<p> {{#linkTo "tweet" tweet}} {{tweet.id}} {{/linkTo}}</p>
</div>
{{/each}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="tweet">
<div id="detail">
{{text}}
</div>
</script>
Router
window.App = Ember.Application.create({
LOG_TRANSITIONS: true
});
App.Router.map(function(){
this.resource('tweets',function(){
this.resource('tweet',{path: ':tweet_id'})
});
});
// (1) App.Router.map(function(){
// this.resource('tweets')
// this.resource('tweet',{path: ':tweet_id'})
// });
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('tweets');
}
});
App.TweetsRoute = Ember.Route.extend({
model: function(){
var me = [];
$.getJSON("http://search.twitter.com/search.json?q=emberjs&rpp=200&count=200&callback=?",
{},
function (data) {
$.each(data.results,function(k,tweet){
var tweet = App.Tweet.create({
created_at: tweet.created_at,
from_user: tweet.from_user,
profile_image_url: tweet.profile_image_url,
text: tweet.text,
id: tweet.id
});
me.pushObject( tweet );
});
});
return me;
}
});
Objects & Controllers
App.TweetsController = Ember.ArrayController.extend({});
App.Tweet = Ember.Object.extend({
created_at: "",
from_user: "",
profile_image_url: "",
text: "",
id: 0
})
As you can see, I have a commented our router (1) which works in finding the correct tweet, and rendering it in the tweet template. However, I would like this route to be nested so that I can implement it as a Master-Detail application.
Using the LOG_TRANSITIONS, I can see that the correct routes are initialised, but I cannot get the nested resource path to render.
Any ideas would be hugely appreciated, thanks in advance.
I got this working. For anyone stuck on something similar, this is how I did it:
Templates - Changed the {{#linkTo}} "tweet"... to {{#linkTo}} "tweets.tweet"... AND added an {{outlet}}
<script type="text/x-handlebars" data-template-name="tweets">
<div id="stream">
{{#each tweet in controller}}
<div class="tweet">
<p class="tweet_text">{{tweet.text}}</p>
<p> {{#linkTo "tweets.tweet" tweet}} {{tweet.id}} {{/linkTo}}</p>
</div>
{{/each}}
</div>
{{ outlet }}
</script>
Router - Changed 'this.resource' to 'this.route'
App.Router.map(function(){
this.resource('tweets',function(){
this.route('tweet',{path: ':tweet_id'})
});
});
Caveat
I think this is a workaround and that the nested resource was the correct approach in this context. I understand that a nested route should be "a verb" or action route. I would still be grateful if anyone knows the correct approach to the question but hope the above helps others where relevant.
I have a grid of objects to display. Each row of the grid displays 4 objects. Depending on where an object is in the grid I want certain classes to be on the elements of the objects. For example the last object in the grid should have the class "last_in_grid". This calculation depends on the index of the object in an array.
My template looks like:
{{#each row in objects}}
{{#each object in row}}
{{view MyApp.MyView objectBinding="object"}}
{{/each}}
{{/each}}
MyApp.MyView needs to know the index in of iteration of the each helper.
Ideally I want something like:
{{#each row in objects}}
{{#each object in row}}
{{view MyApp.MyView objectBinding="object" indexBinding="index_of_each_loop"}}
{{/each}}
{{/each}}
Django's template language can do this:
{% for item in items %}
{% if forloop.counter0 == 0 %}
blah blah blah
{% endif %}
{% endfor %}
https://docs.djangoproject.com/en/1.4/ref/templates/builtins/#for
Is this possible using Ember and Handlebars?
Do I have to write a custom version of each to do this?
Instead of {{each}} helper, You can use {{collection}} helper to get current index of iteration like this Fiddle:
http://jsfiddle.net/secretlm/67GQb/73/
HTML:
{{#collection contentBinding="App.peopleController"}}
{{#view App.PersonView personBinding="content" indexParentBinding="contentIndex" }}
<td>{{indexParent}}</td>
<td>{{person.fullName}}</td>
{{/view}}
{{/collection}}
Javascript:
App = Ember.Application.create();
App.Person = Ember.Object.extend({
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName')
});
App.peopleController = Ember.ArrayController.create({
content: [App.Person.create({ firstName: "Yehuda", lastName: "Katz" }),
App.Person.create({ firstName: "Tom", lastName: "Dale" })]
});
App.PersonView = Ember.View.extend({
tagName: 'tr',
indexParent: null
});
If you use a CollectionView the items/views of that collection can query it and see if they are the last item.
Fiddle: http://jsfiddle.net/qKXJt/138/
ArrayControllers are just proxies to their content, which is an Ember Array. This means you get all the goodies that come with Enumerable, Array, MutableArray, and so on.
I need to trigger the {{showAlias}} view method from within the {{#each content.activitytypes}} I have tried {{_parentView.showAlias}}. I believe I need to call it on the parent's parent, is that correct and how? I can call the {{showAlias}} outside of the {{#each...
Handlebars Template
<script type="text/x-handlebars">
<h2>Activities</h2>
<ul>
{{#each Km.activityController}}
{{#view Km.ActivityView contentBinding="this"}}
<li>
{{content.id}}:{{content.name}}
<ul>
{{#each content.activitytypes}}
<li>{{showAlias}}
{{name}} {{aliasname}}
</li>
{{/each}}
</ul>
</li>
{{/view}}
{{/each}}
</ul>
</script>
View
Km.ActivityView = Em.View.extend({
showAlias: function () {
var arr = this.getPath('content.activitytypes'),
show = false;
console.log(arr)
arr.forEach(function(item) {
var aliasArr = item.showaliasaccountid;
if (typeof aliasArr !== 'undefined') {
if (jQuery.inArray(2,aliasArr)) {
console.log(aliasArr);
show = true;
}
}
});
}.property('content.#each.activitytype.#each'),
});
{{parentView.showAlias}} will work, but in these situations with nested views and sub eachs I always find the code to be more maintainable with CollectionViews. Otherwise you end up stuffing too much inside of a single template/view.
http://docs.emberjs.com/#doc=Ember.CollectionView&src=false