Skip to content

An Ember.js helper for grouping objects by async properties.

License

Notifications You must be signed in to change notification settings

scottwernervt/ember-cli-group-by

Repository files navigation

ember-cli-group-by

Latest NPM release License TravisCI Build Status Ember Observer Score Code Climate Dependencies Dev Dependencies

A group by computed property and helper that supports nested properties, e.g. model with a belongsTo relationship.

import Controller from '@ember/controller';
import { groupByPath } from 'ember-cli-group-by/macros';

export default Controller.extend({
  arrayGrouped: groupByPath('array', 'nested.property'),
});
{{#each-in (group-by-path array "nested.property") as |category items|}}
  <h3>{{category}}</h3>
  <ul>
    {{#each items as |item|}}
    	<li>{{item.name}}</li>
    {{/each}}
  </ul>
{{/each-in}}

Installation

Requires Ember 2.10 or higher, see Issue #2.

ember install ember-cli-group-by

Usage

General

Computed property

import Controller from '@ember/controller';
import { A } from '@ember/array';
import { groupByPath } from 'ember-cli-group-by/macros';

export default Controller.extend({
  cartGrouped: groupByPath('cart', 'category'),
  
  init() {
    this._super(...arguments);
    this.set('cart', A([
      { name: 'Cinnamon ', category: 'Spice' },
      { name: 'Banana', category: 'Fruit' },
      { name: 'Apple', category: 'Fruit' },
      { name: 'Lettuce', category: 'Vegetable' },
      { name: 'Broccoli', category: 'Vegetable' },
    ]);
  },
});
{{#each-in cartGrouped as |category products|}}
  <h3>{{category}}</h3>
  <ul>
    {{#each products as |product|}}
    	<li>{{product.name}}</li>
    {{/each}}
  </ul>
{{/each-in}}

Handlebars helper

import Controller from '@ember/controller';
import { A } from '@ember/array';

export default Controller.extend({
  init() {
    this._super(...arguments);
    this.set('cart', A([
      { name: 'Cinnamon ', category: 'Spice' },
      { name: 'Banana', category: 'Fruit' },
      { name: 'Apple', category: 'Fruit' },
      { name: 'Lettuce', category: 'Vegetable' },
      { name: 'Broccoli', category: 'Vegetable' },
    ]);
  },
});
{{#each-in (group-by-path cart "category") as |category products|}}
  <h3>{{category}}</h3>
  <ul>
    {{#each products as |product|}}
    	<li>{{product.name}}</li>
    {{/each}}
  </ul>
{{/each-in}}

Default Category

The group name for an item can be overridden by implementing a computed property function or by passing a closure action to the helper.

Computed property

import Controller from '@ember/controller';
import { A } from '@ember/array';
import { isNone } from '@ember/utils';
import { groupByPath } from 'ember-cli-group-by/macros';

export default Controller.extend({
  cartGrouped: groupByPath('cart', 'category', function (value) {
    return isNone(value) ? 'Other' :  value;
  }),
  
  init() {
    this._super(...arguments);
    this.set('cart', A([
      { name: 'Cinnamon ', category: 'Spice' },
      { name: 'Banana', category: 'Fruit' },
      { name: 'Apple', category: 'Fruit' },
      { name: 'Lettuce', category: 'Vegetable' },
      { name: 'Broccoli', category: 'Vegetable' },
      { name: 'Salt', category: null },
      { name: 'Sugar' },
    ]);
  },
});

Handlebars helper

import Controller from '@ember/controller';
import { A } from '@ember/array';
import { isNone } from '@ember/utils';

export default Controller.extend({
  init() {
    this._super(...arguments);
    this.set('cart', A([
      { name: 'Cinnamon ', category: 'Spice' },
      { name: 'Banana', category: 'Fruit' },
      { name: 'Apple', category: 'Fruit' },
      { name: 'Lettuce', category: 'Vegetable' },
      { name: 'Broccoli', category: 'Vegetable' },
      { name: 'Salt', category: null },
      { name: 'Sugar' },
    ]);
  },
  
  actions: {
    defaultCategory(value) {
      return isNone(value) ? 'Other' :  value;
    },
  },
});
{{#each-in (group-by-path cart "category" (action "defaultCategory")) as |category products|}}
  <h3>{{category}}</h3>
  <ul>
    {{#each products as |product|}}
    	<li>{{product.name}}</li>
    {{/each}}
  </ul>
{{/each-in}}

Async Relationship Property

The group by property path can be a nested belongsTo relationship that is loaded asynchronously. Check out the example at ember-twiddle.

// models/user.js
export default Model.extend({
  fullname: attr('string'),
  
  cart: hasMany('product', { inverse: 'shopper' }),
});

// models/product.js
export default Model.extend({
  name: attr('string'),
  
  category: belongsTo('category'),
  shopper: belongsTo('user', { inverse: 'cart' }),
});

// models/category.js
export default Model.extend({
  name: attr('string'),
});
import Controller from '@ember/controller';
import { isNone } from '@ember/utils';
import { alias } from '@ember/object/computed';
import { groupByPath } from 'ember-cli-group-by/macros';

export default Controller.extend({
  user: alias('model'),
  cart: alias('user.cart'),
  cartGrouped: groupByPath('cart', 'category.name', function (value) {
    return isNone(value) ? 'Other' :  value;
  }),
});