Reputation: 1712
I have a list of objects, fetched via Ajax from an API. It is something similar to this:
App.Card = Ember.Object.extend({
name: "",
color: ""
});
App.cardsController = Ember.ArrayController.create({
content: [],
createCard: function(data) {
this.pushObject(App.Card.create(data));
},
loadCards: function() {
// Fetch API and use createCard
}
});
When I use {{#each App.cardsController}}
, I list all cards inside my controller.
But I'd like to filter them by color. How can I filter a list (inside a Controller's content) and display them?
I tried this approach:
Added this code inside App.cardsController
:
filterCardsByColor: function() {
array = this.get('content').filter(function(item, index) {
return item.get('color') == 'red';
});
return array;
}.property('content.@each')
And added {{#each App.cardsController.filterCardsByColor}}
to my view.
But I am getting the following error at my Chrome console:
Uncaught TypeError: Object [object Object] has no method 'addArrayObserver' ember.min.js:18
What I am doing wrong? Or what should I do? Should I move that logic to a view? How? I even tried wrapping array
inside Ember.Array.create(array)
, but it didn't solved my issue.
Bonus: is it someway possible to send a parameter to filterCardsByColor, so I can ask for 'red'
cards, or for 'yellow'
cards, etc?
Upvotes: 1
Views: 1021
Reputation: 2886
Unfortunately, I don't think WildHoney's is the best answer.
Adding the following to their CSS:
@-webkit-keyframes onUpdate {
from { background: yellow; }
}
@keyframes onUpdate {
from { background: yellow; }
}
* {
-webkit-animation: onUpdate 1s;
animation: onUpdate 1s;
}
I can see that when the content is updated, the entire list gets redrawn. For very long lists, this can be a severe performance hit.
To avoid this, you should only generate the array providing filteredContent once - when the pointer to content changes - and update it in a targetted manner, based on changes to the subset.
See http://jsfiddle.net/wRzRn/2/
Upvotes: 1
Reputation: 4969
The best way to do it is to store an array that will hold the colours to filter out (or to filter in, if you wish), and have the computed property update every time its length
changes by specifying .property('colours.length');
:
filteredContent: function() {
// Triggered every time a colour is added/removed.
var colours = this.get('colours');
return this.get('content').filter(function(model) {
// Determine whether the current model's colour is in the array of those filtered.
return Boolean(jQuery.inArray(model.get('colour'), colours) == -1);
});
}.property('colours.length')
We then just need a way for our view to be able to pass in colours to add to that array. We can do this with a different function called applyFilters
that will accept one argument -- the colour we wish to exclude. You can pass in this colour in an {{action}}
like so: <a {{action "applyFilter" "red"}}>
.
applyFilter: function(colour) {
var colours = this.get('colours');
if (!colour) {
// Clear all of the colours if we're clearing the filters.
colours.clear();
return;
}
// Otherwise we can push the new colour into the array, which will trigger
// an update of the filteredContent computed property.
colours.pushObject(colour);
}
Fully working JSFiddle for you to mess around with: http://jsfiddle.net/9XmqL/
Upvotes: 2