Reputation: 1421
I'm curious to know if there is a way to create computed properties without having specific knowledge of the values of the properties to be computed. For example, if I have the following model:
TR.location = DS.Model.extend({
name: DS.attr('string'),
city: DS.attr('string'),
state: DS.attr('string'),
type: DS.attr('string')
});
with the following fixtures:
TR.location.FIXTURES = [
{ id: 1, name: 'The Four Seasons', city: 'New York', state: 'NY', type: 'Restaurant' },
{ id: 2, name: 'Le Cirque', city: 'New York', state: 'NY', type: 'Restaurant' },
{ id: 3, name: 'The Russian Tea Room', city: 'New York', state: 'NY', type: 'Restaurant' },
{ id: 4, name: 'The Waldorf Astoria', city: 'New York', state: 'NY', type: 'Hotel' },
{ id: 5, name: 'The Plaza', city: 'New York', state: 'NY', type: 'Hotel' }
];
I would like to display the totals based on the 'type' property. So, with the above information, I would like to display in my template the following:
The problem here is that there is no domain knowledge of what the values of the 'type' property could be, which is why I can't figure out how to create computed properties for the above. Any workarounds for this? Thanks in advance.
Upvotes: 0
Views: 461
Reputation: 106
Depending on the amount of aggregation being done, a good option might be to use Ember.Map, since its structure is well suited for these problems.
On the specific problem that you have, we could have something like this:
groupCount: function() {
var listResult = [];
// aggregating data
var mapResult = this.get('content').mapProperty('type').reduce(function(previousValue, item, index, enumerable) {
var currentItemCount = (previousValue.get(item) || 0);
previousValue.set(item, currentItemCount + 1);
return previousValue;
}, Ember.Map.create());
// prepare data for displaying in the template
mapResult.forEach(function(key, value) {
listResult.push({type: key, count: value});
});
return listResult;
}.property('[email protected]')
The first part of the computed property takes care on aggregating the information as desired, and the second part sets up the data for presentation in the template. If the array change, these values are also recalculated.
Here is a jsbin with this example: http://jsbin.com/OhenaPe/4/edit?html,js,output
Upvotes: 0
Reputation: 47367
filterProperty is your friend here. See http://emberjs.com/api/classes/Ember.Enumerable.html#method_filterProperty
boring inline
var models = TR.location.find();
var hotelCount = models.filterProperty('type','Hotel').get('length);
var restaurantCount = models.filterProperty('type', 'Restaurant').get('length');
As has been pointed out, you might not know what the types are, in this specific case, for performance I'd do an observes, iterate the list, and add em up. Essentially anytime the type on any item in the model is updated, your property, 'someCustomCounts' will be updated as well.
someCustomCounts: function(){
var agg= {};
this.get('model').forEach(function(item){
var type = item.get('type');
if(!agg[type]) agg[type] = 0;
agg[type]++;
});
return agg;
}.observes('[email protected]'),
on some arraycontroller, with the model set to the list of locations, computed properties
hotelCount: function(){
return this.get('model').filterProperty('type','Hotel').get('length);
}.property('model'),
restaurantCount: function(){
return this.get('model').filterProperty('type','Restaurant').get('length);
}.property('model'),
Upvotes: 1