How to access controller property in template? - ember.js

Here,I am trying to access property of controller but it is throwing an exception
Uncaught TypeError: Object [object Object] has no method 'addArrayObserver'
Template Code :
{{#each itemController="index"}}
<div class="row" {{bindAttr class="item.isWithBorder:border"}}>
{{#each item in model}}
{{#each item in item.home_products}}
{{#each item in item.contents}}
<li>{{item.product_name}}</li>
{{/each}}
{{/each}}
{{/each}}
</div>
{{/each}}
I want to show border property only for first iteration i.e. first product.
Hence, one property is added in controller which i am accessing in template.
Controller Code :
Astcart.IndexController = Ember.ObjectController.extend({
init: function() {
console.log("Item controller initialized");
this._super();
},
isWithBorder : function(){
return this.get("model.id") == 1;
}.property("model.id")
});
I have updated my code Here.

Here working jsfiddle: http://jsfiddle.net/fQNRk/2/
You where using the name index as your itemController index is already a reserved name so I just changed it to item now it works.
Astcart.ItemController = Ember.ObjectController.extend({
...
{{#each itemController="item"}}
...
Hope it helps.

Related

ArrayController's property for each item in the model in ember

I've an Array controller which has the property "isChecked" (boolean property). In my controller I want to get the collection of elements which are "checked" (I mean selected). I'm not sure how to access the controller's property in the model.
My controller is as follows:
App.ExampleController = Ember.ArrayController.extend({
isChecked: false,
totalElements: function()
{
return this.model.get('length');
}.property('#each'),
selectedElements: function()
{
var content = this.get('content');
console.log(content.filterBy('isChecked'));
return content.filterBy('isChecked');
}.property('isChecked'),
});
I linked the "isChecked" property to a checkbox inside each helper as follows..
<ul>
{{#each model}}
<li>
{{input type="checkbox" checked=isChecked}}
{{name}}
</li>
{{/each}}
</ul>
I will display all the items in the model with a checkbox associated with it. The user can select few items from it. So I want those items.
Now I want to get the list of elements which are "checked". Either as a computed property or under any action.
Thank you.
I think you need to move the isChecked property onto an ObjectController, then reference that controller in the array controller with the itemController property.
Array Controller:
App.IndexController = Ember.ArrayController.extend({
itemController: 'color',
totalElements: function() {
return this.get('length');
}.property('[]'),
selectedElements: Ember.computed.filterBy('#this', 'isChecked', true)
});
(The #this means that the computed property will reference the array of item controllers.)
Item Controller:
App.ColorController = Ember.ObjectController.extend({
isChecked: false
});
http://emberjs.jsbin.com/tazojejuwi/1/edit
Hope that helps.

Ember select view controller based on content

I currently have a view setup that renders the template depending on the model:
<ul>
{{#each controller.sortedAll}}
{{view App.ScoreView}}
{{/each}}
</ul>
.
App.ScoreView = Ember.View.extend({
templateName: function(){
var item = this.get('context')
if (item.sort < 8){
return 'low'
} else {
return 'high'
}
}.property(),
})
I'm struggling with assigning a specific controller for each separate view. The jsbin is: http://jsbin.com/tahag/5/edit
Is it possible to specify the controller in App.ScoreView using controller:? Or would I be better off trying to set an item controller:
{{view App.ScoreView itemController="VAL"}}
And pass the VAL from the parent controller as a property?
The controller is inherited from the current scope (defining itemController on the view won't do anything). You should do it on the each.
{{#each controller.sortedAll itemController='val'}}
{{view App.ScoreView}}
{{/each}}
Then within the view you can do this.get('controller')...
Also you can do an if statement and do {{render 'high' this}} if you want to have different types of controllers on each item.
{{#each controller.sortedAll}}
{{#if isBlue}}
{{render 'blue' this}}
{{/if}}
{{#if isGreen}}
{{render 'green' this}}
{{/if}}
{{/each}}
Personal recommendations:
To avoid making your template super convoluted I would just use a single controller.
{{#each controller.sortedAll itemController='score'}}
{{input value=sort}}
{{view App.ScoreView}}
{{/each}}
Add what the computed property is dependent on in order for it to automagically update:
App.ScoreView = Ember.View.extend({
templateName: function(){
var sort = this.get('controller.sort');
if (sort < 8){
return 'low';
} else {
return 'high';
}
}.property('controller.sort'),
});
Take advantage of the computed helpers
App.ScoreController = Ember.ObjectController.extend({
isVeryHigh: Em.computed.gt('sort', 20),
isVeryLow: Em.computed.lt('sort', 4)
});
Example: http://jsbin.com/sidebozi/1/edit

Using computed property in Ember to get model data and display extra text

In my Ember template, I want to be able to loop over each item coming from the model (an array) and if the value is 'blue', display some text next to the value.
My template looks like this:
<script type="text/x-handlebars" data-template-name="index">
<h2>Loop over colors</h2>
<ul>
{{#each color in model}}
<li>{{color}} {{#if isBlue}} - Its Blue!{{/if}} </li>
{{/each}}
</ul>
</script>
And my app.js file looks like this:
App = Ember.Application.create({});
App.Router.map( function() {
this.resource( 'about');
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
}
});
App.IndexController = Ember.ArrayController.extend({
isBlue: function() {
return this.get('content') == 'blue';
}.property()
});
I'm using this.get('content') because I thought that was supposed to be a reference to the actual model data.
I've tried numerous variations of the code but I'm now blocked. Hope someone can help.
You are defining the isBlue property on the IndexController, which is an ArrayController, and not on each item in the content. You can instruct the {{each}} helper to use an itemController for each item in the loop. By doing that you are able to define additional computed properties, that are not present in the original objects, and make them available within the each loop:
<script type="text/x-handlebars" data-template-name="index">
<h2>Loop over colors</h2>
<ul>
{{#each color in model itemController="color"}}
<li>{{color}} {{#if isBlue}} - Its Blue!{{/if}}</li>
{{/each}}
</ul>
</script>
App.ColorController = Ember.ObjectController.extend({
isBlue: function() {
return this.get('content') === 'blue';
}.property('content')
});
You can also check out JSBIN.
ArrayController means that the content property is an array, not just an object. Also, you don't want to access content directly. Controllers proxy their models, so use the controller as if it was an array. So your isBlue function is wrong in a few ways. It's probably possible to do what you want using the isBlue property, but I would use something like this:
colorItems: Em.computed.map('#this', function(color) {
return {
color: color,
isBlue: color === 'blue'
};
})
Then, in your template:
{{#each colorItems}}
<li>
{{color}}
{{#if isBlue}}
- It's Blue!
{{/if}}
</li>
{{/each}}

Ember template doesn't update after model update

http://jsbin.com/qoyudape/1/edit
Despite using .pushObject() template doesn't update. I've noticed it DOES update, if instead this I use model or content in template;
What is this in view is referring to if not model ? Is it possible to get it working using this and not model or content ?
var App = Ember.Application.create();
App.ApplicationRoute = Ember.Route.extend({
model: function(){
return Ember.A();
}
});
App.ApplicationController = Ember.ArrayController.extend({
actions: {
update: function(){
this.get("model").pushObject( Ember.Object.create({a:"b"}) );
console.log( this.get("model") );
}
}
});
template:
<script type="text/x-handlebars">
<button {{action "update"}}>update</button>
<br><br>
{{#if this}}
array not empty
{{else}}
array empty
{{/if}}
</script>
this is referring to the controller. btw, an easy way to find that out is to do {{log this}} in your template see also: http://emberjs.com/guides/understanding-ember/debugging/.
I'm not actually sure what it's checking to be truthy/falsy, but you can always just use length. I'll update once I find it.
{{#if this.length}}
array not empty
{{else}}
array empty
{{/if}}
http://jsbin.com/qoyudape/3/edit

Positional index in Ember.js collections iteration

Is there a way to get positional index during iteration in ember.js?
{{#each itemsArray}}
{{name}}
{{/each}}
I'm looking for a way to have something like:
{{#each itemsArray}}
{{name}} - {{index}}th place.
{{/each}}
Update:
As per the comment by #ebryn the below code works without using a nested view for each item:
<script type="text/x-handlebars">
{{#collection contentBinding="App.peopleController"}}
Index {{contentIndex}}: {{content.name}} <br />
{{/collection}}
</script>​
http://jsfiddle.net/WSwna/14/
Although to have something like adjustedIndex, a nested view would be required.
It's old question but still gets a lot of hits. In the current version of Ember.JS one can use _view.contentIndex to display current index inside the loop.
{{#each}}
Index: {{_view.contentIndex}}
{{/each}}
If you need an adjusted index (for instance starting from 1) then there is a possibility of creating reusable helper increment
Ember.Handlebars.registerBoundHelper('increment', function(integer) {
return integer + 1;
});
then you would use it in the following way
{{#each}}
Index: {{increment _view.contentIndex}}
{{/each}}
Update
Starting with ember 1.11 you can do as well
{{#each people as |person index|}}
Index: {{increment index}}
{{/each}}
In RC6 CollectionView provides contentIndex propery for each rendered view of collection. Each helper uses CollectionView in its implementation so uou can access index in this way:
{{#each itemsArray}}
{{_view.contentIndex}}
{{/each}}
Actually yes you can get the position of the current index using the {{each}} helper. You have to create a view for every item in a list and then use {{_parentView.contentIndex}}.
<script type="text/x-handlebars">
{{#each App.peopleController}}
{{#view App.PersonView contentBinding="this"}}
Index {{_parentView.contentIndex}}: {{content.name}} {{adjustedIndex}} <br />
{{/view}}
{{/each}}
</script>
App = Ember.Application.create();
App.peopleController = Ember.ArrayController.create({
content: [ { name: 'Roy' }, { name: 'Mike' }, { name: 'Lucy' } ]
});
App.PersonView = Ember.View.extend(Ember.Metamorph, {
content: null,
// Just to show you can get the current index here too...
adjustedIndex: function() {
return this.getPath('_parentView.contentIndex') + 1;
}.property()
});
See this jsFiddle for a working example.
As of Ember 9.8 and pre-1.0 you can wrap the "contentIndex" with a view in order to get at the virtual parent (the {{#each}}). If you don't add the view, your context ends up being either the main template, an item in your list or whatever you manually set with your {{#with}}. It is not impossible to get at the {{#each}} from the JS side but it is a lot more of a pain flipping through those child views.
{{#each App.peopleController}}
{{#view}}
Index {{view._parentView.contentIndex}}: {{name}} <br />
{{/view}}
{{/each}}
...OR...
{{#each people in App.peopleController}}
{{#view}}
Index {{view._parentView.contentIndex}}: {{people.name}} <br />
{{/view}}
{{/each}}
Just in-case you would like a fiddler.
DEMO
Note: You can create a view and do a this.get("_parentView.contentIndex") to get at the index if you want to modify the number at all.
This isn't currently a feature of Handlebars or Ember.Handlebars. We have contentIndex available inside #collection/Ember.CollectionView. I think it's useful to support in #each too. Please file an issue at the GitHub repository: https://github.com/emberjs/ember.js/issues
As of Ember 1.11.0, index is an optional parameter in each blocks:
{{#each items as |item index|}}
{{item.name}} is at index {{index}}
{{/each}}
I have modified a bit ud3323 solution using collection.
Check here: http://jsfiddle.net/johngouf/WSwna/13/
<script type="text/x-handlebars">
{{#collection contentBinding="App.peopleController"}}
{{#view App.PersonView contentBinding="this"}}
Index {{_parentView.contentIndex}}: {{content.name}} {{adjustedIndex}} <br />
{{/view}}
{{/collection}}
</script>
App = Ember.Application.create();
App.peopleController = Ember.ArrayController.create({
content: [ { name: 'Roy' }, { name: 'Mike' }, { name: 'Lucy' } ]
});
App.PersonView = Ember.View.extend({
content: null,
// Just to show you can get the current index here too...
adjustedIndex: function() {
return this.getPath('_parentView.contentIndex') + 1;
}.property()
});
​​
I think you could probably do something like this too
//add index property to all each queries
Handlebars.registerHelper('each', function(context, block) {
var ret = "";
for(var i=0, j=context.length; i<j; i++) {
context[i].index = i;
ret = ret + block(context[i]);
}
return ret;
});