Access computed property in ArrayController - ember.js

With this controller:
CMS.PeopleController = Em.ArrayController.extend
orderedNames: ( ->
// Some ordering here...
).property "content.names"
...nothing appears here:
{{#each controllers.people.orderedNames}}
<p>Hello {{name}}</p>
{{/each}}
I guess that's because some binding to content, but I have no idea how to get it working... Can anybody help me?

Use #each to refer to properties on individual items in an array [as a dependent key]: content.#each.name Also, ensure your property is returning a value.

Related

Ember.js/handlebars how to bind variable passed to a component to a model

Let's say I have a model called "Article" with only one property called "title"...
I could write hanblebars to edit the article property like this:
<span>Title</span><span>{{input value=title}}</span>
And Ember/handlebars magically binds the value of that input box to the "title" property value.
No problem there. But I am working on a project in which I will need to generate the handlebars code dynamically, based on a model definition model.
For example, I will not know there is a property called "title", but would have instead to loop into a "modelFields" list in the model definition model.
So, the handlebars will be something like this:
Looking at the code below:
{{#each modelField in modelFields}}
<span>modelField.displayName</span><span>{{input value=modelField.propertyName}}</span>
{{/each}}
The result HTML for the "title" property will be:
<span>Title</span><span><input value="title"></span>
Now here is my question, is there a way to have the value coming dynamically from propertyName (title, in this example) to be handled by ember as a binding property title, instead of a string title?
To clarify, is there a way for the result of this:
{{#each modelField in modelFields}}
<span>modelField.displayName</span><span>{{input value=modelField.propertyName}}</span>
{{/each}}
to be treated as this (title is a binding property):
<span>Title</span><span>{{input value=title}}</span>
instead of this (title is a string):
<span>Title</span><span><input value="title"></span>
?
I tried with views,components, with no luck.
I've found answer in another post that help me find the solution. The post is:
Ember.js: TextField with dynamic binding
Although, for my purposes, I had to tweak his/her solution a little bit:
Ember.Handlebars.helper('dataTextField', function (key, options) {
options.hash.valueBinding = 'controller.' + key;
return Ember.Handlebars.helpers.input.apply(this, [options]);
});
and in the template I call:
{{#each modelField in modelFields}}
<span>{{modelField.displayName}}</span>
<span>
{{dataTextField modelField.name}}
</span>
{{/each}}

setting #each property, proper use - splitting search results with EmberJs

Im trying to split search results into bulks of 5 so each bulk could be presented desperately.
my data:
Wacky.Video = Ember.Object.extend({
DATA: null
presenter : null
});
The presenter holds the page number on which the data should appear - e.g model[0-5] hold val. 1, model[6-10] hold val. 2 and so on...
my controller:
Wacky.SearchController = Ember.Controller.extend({
...
resPageNum: 1,
...
This property will de/increment whenever a page number will be changed (by button click).
my HTML code:
{{#each res in model}}
{{#if pagePresentor}}
<div class="results">
//DO STUFF WITH RES. OBJECT
</div>
{{/if}}
{{/each}}
finally "pagePresentor" is a property that needs to determine which bulk to present in the current iteration.
So far Iv got this:
pagePresentor: function(value){
return value.presenter == this.get('resPageNum');
}.property('resPageNum','#each.presentor')
But I guess Im using it wrong, because nothing is getting printed at all.
Could anyone please explain how to set this working or at least the base principal for what am I doing wrong here?
Thanks in advance.
pagePresentor: function(value){
return value.presenter == this.get('resPageNum');
}.property('resPageNum','#each.presentor')
Here #each.presentor is a mistake. You should specify observed property which is in the array in the following format: arrayName.#each.propertyName according to http://emberjs.com/guides/object-model/computed-properties-and-aggregate-data/. So, here the name of array which contains the objects with presentor property is missing.

Bind to a value that will be fetched from an API

I have an endpoint that returns a list of artists (json data).
And an endpoint that returns a specific property given an id.
What I would like to do is to iterate through all the artists and display one or more properties in the template to the user but the property should only be fetched from the API if it is bound in the template.
In my ArtistsRoute, I set the model to be all those artists fetched by calling getJSON...
I want, somehow to be able to fetch a property for an artist and display it (through binding).
The Properties map could be stored in the ArtistController maybe.
I could not find a good example for this. Any help is appreciated!
Template example:
Name is on the artist object itself, but the Properties object has been created manually. So in the ArtistController it could be initialized to empty:
Properties = {}
And then it sets Propertes['ShortName'] = to the fetched value.
<ul>
{{#each}}
<li>
{{Name}}
{{Properties.ShortName}}
<img {{bind-attr src=Properties.MainImage}} />
</li>
{{/each}}
</ul>
Should I use a function instead as a property or a Handlebars helper? Like:
{{Property this 'ShortName'}}
where 'this' is the ArtistController and 'ShortName' is the property to fetched. The property id can be calculated through the ArtistController and propertyName.
function(tag, propertyName) {
Ember.$.getJSON('/Properties/' + tag.Id + '_' + propertyName).then(function(response) {
var propertyValueToBind = response.Value; // This is the value I want to display in the template.
});
}
Then the Property function has to know when to rerender the template (once the property has been fetched from the API).
Firstly you'll want to generally avoid properties that are uppercase. They are usually considered global properties, and not in scope.
Ember lazy loads computed properties by default, so adding that functionality to a computed property would be an excellent way to go.
someProperty: function(){
var promise = Ember.$.getJSON('/Properties/buildupurl');
return Ember.ObjectProxy.extend(Ember.PromiseProxyMixin).create({
promise: promise
});
}.property()
Then in your template someProperty would be accessed like this
{{someProperty.value}}
And would be asynchronously populated.
Example: http://emberjs.jsbin.com/OxIDiVU/769/edit

Programmatically setting computed property of an itemController

I have a template with the following code:
{{#each types itemController='type'}}
<div class='col checkbox'>
<label>
{{input type='checkbox' checked=isSelected disabled=notAllowed}}
<span {{bind-attr class='isSelected'}}>{{name}}</span>
</label>
</div>
{{/each}}
types is set in setupController:
this.store.find('type').then(function(types){
controller.set('types', types);
});`
//Having 2 other models here that I am setting and having an itemController for, exactly in the same fashion as types.
for the ArrayController which has the itemController.
NOTE: To clarify, I am using and setting 3 different models, which work pretty much in the same way as type, that makes this a bit more complicated.
Then the itemController itself:
App.TagController = Ember.ObjectController.extend({
isSelected: function(key, value){
//bunch of code that does some stuff and returns true or false depending on value
}.property()
});
App.TypeController = App.TagController.extend();
Now the problem: I have a resetbutton that should deselect all checkboxes and remove the span classes.
I would have thought about using an action (in the ArrayController) that sets all the isSelected properties to false, but I don't seem to be able to find a way to access and manually set that itemController computed property.
One thing I tried in the ArrayController is the following:
actions: {
resetFilters: function(){
this.get('types').forEach(function(type) {
console.log(type.get('isSelected'));
//type.set('isSelected', false);
});
}
}
But unfortunately this returns undefined. And using jQuery manually to remove the class and uncheck the checkbox seems to work the first instance, but the problem is, the computed property doesn't get updated and that messes things up.
Any idea how I can achieve what I want?
If anything is unclear let me know and I will do my best to clarify.
Thank you.
You are setting controller.types, this will not work with itemController. You should always be setting an array controller's content property.
The following should work:
controller.set('content', this.store.find('type'));
Then to set the isSelected:
controller.setEach('isSelected', false);
This assumes that controller is an instance of an ArrayController that has an itemController set in it's definition, e.g.
App.TypesController = Em.ArrayController.extend({itemController: 'type'});
store.find returns a PromiseArray, so it should be resolved first. You can set the types as follows in setupController:
this.store.find('type').then(function(types){
controller.set('types', types);
});
Or you can resolve types in the reset:
this.get('types').then(function(types) {
types.forEach(function(type) {
console.log(type.get('isSelected'));
});
});
I would recommend the first one though.

Reversing a content array

I'm storing a list of search terms in my ArrayController. I'd like the search terms to be displayed newest to oldest. By default Ember outputs them in order.
You can see my current implementation here: http://andymatthews.net/code/emberTweets/
And here's the pertinent code.
{{#each App.recentUsersArray.reverse}}
<li>
<a href="#" title="view again" {{action "searchAgain" target="App.recentUsersArray"}}>{{this}}</a>
</li>
{{/each}}
App.recentUsersArray = Em.ArrayController.create({
content: [],
reverse: function(){
return this.content.reverse();
}.property(),
});
You can see that I'm trying to reverse it using a property() method but it's not working. Am I doing something wrong?
You should always use get and set to access properties. Also if a computed property depends on other ones, you have to add these in the property declaration. The use of cacheable can be omitted in the next release of ember, see discussion. You can see a working example here.
reverse: function(){
return this.get('content').toArray().reverse();
}.property('content.#each').cacheable()
You could also use the unshiftObject method on an array and hereby circumvent creating a computed property, see http://jsfiddle.net/ez7bV/.