I'm trying to achieve a "group by" functionality in Ember.js and its proving more difficult than I originally thought.
[{date: '2014-01-15T16:22:16-08:00', message: 'Tidal wave occurred'},
{date: '2014-01-15T05:00:16-08:00', message: 'Tornado destroyed town'},
{date: '2014-01-13T14:22:16-08:00', message: 'Volcanic eruption, likely'},
{date: '2014-01-13T16:24:16-08:00', message: 'Ice shelf calving off completely'},
{date: '2014-01-11T11:27:26-08:00', message: 'Mother-in-law visiting'}]
I'm looking for final output similar to this:
Today
----------
4:22 pm - Tidal wave occurred
5:00 am - Tornado destroyed town
Monday
----------
2:22 pm - Volcanic eruption, likely
...etc., etc.
Right now this data is bound to an ArrayController. Each object has a computed property to get "year/month/day" for the grouping comparison.
var ActivityController = Em.ArrayController.extend({
groupedResults: function () {
var result = [];
//Iterate over each item in the list, but do what with it?
this.get('content').filter(function (item) {
});
return result;
}.property('content.[]')
});
Any tips on the best way to implement this? Ember ships with filterBy and mapBy, but no groupBy.
Here's a Groupable implementation I've adapted a bit:
Groupable = Ember.Mixin.create
group: null
ungroupedContent: null
groupedContent: (->
model = @
groupedContent = Ember.A([])
groupCallback = @get('group')
ungroupedContent = @get('ungroupedContent')
return groupedContent unless groupCallback
return groupedContent unless ungroupedContent
ungroupedContent.forEach (item) ->
group = groupCallback.call(model, item)
return unless groupKey = group.get('key')
foundGroup = groupedContent.findProperty('group.key', groupKey)
unless foundGroup
foundGroup = groupedContent.pushObject Ember.ArrayProxy.create
group: group,
content: Ember.A([])
foundGroup.get('content').pushObject(item)
groupedContent
).property('group', 'ungroupedContent.@each')
Controller usage:
ActivitiesController = Ember.ArrayController.extend Groupable,
ungroupedContentBinding: 'content' # tell Groupable where your default content is
# the callback that will be called for every
# item in your content array -
# just return the same 'key' to put it in the same group
group: (activity) ->
Ember.Object.create
key: moment.utc(activity.get('date')).format('YYYY-MM-DD') # using momentjs to pluck the day from the date
description: 'some string describing this group (if you want)'
Template usage:
{{#each groupedContent}}
{{group.key}} - {{group.description}}
{{#each content}}
Here's the really bad thing that happened: {{message}}
{{/each}}
{{/each}}
Essentially, all that the Groupable mixin does is loop through your normal content ArrayProxy and calls a function to determine what group it belongs to. If that group object doesn't already exist (i.e. no matching group.key
was found), it creates it before adding the current content item to the group's content.
So what you end up with in your template is a new array (ArrayProxy
) of objects (Ember.Object
), with each object having a group
property (which must have at least a key
property to identify it) and a content
property (which contains the content belonging to that group).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With