Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ember.js Grouping Results

Tags:

ember.js

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.

like image 210
Jarrod Nettles Avatar asked Jan 15 '14 17:01

Jarrod Nettles


1 Answers

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).

like image 164
sheldonbaker Avatar answered Sep 23 '22 03:09

sheldonbaker