This tutorial fits ember-models-table version 2.2.3 or higher. Rows grouping is supported only for models-table
and it wasn’t tested with a models-table-server-paginated
!
First of all to start use rows grouping you must set component’s parameter useDataGrouping
to true
. Next you have to set property name used to group initially (currentGroupingPropertyName
). Next you’ll see that rows grouping is working and looks like this:
Table rows are grouped by property country
()table doesn’t have a column with such property
Clicking on country name will collapse or expand rows group.
Briefly, the list will describe everything that the Component is able to do in scope of rows grouping, and then on a real example we will analyze in more detail:
- Rows grouping by some property values. Property many be nested.
- Sorting by groping property values.
- Ability to set a list of properties available for grouping and switch between them. However, grouping may be done only by single property.
- Property used for grouping may shown in the separated row on the top of the group or in the first left column (it’ll contain a single cell with
rowspan
equal to the rows number in the group). - Instead of the value of the group property, you can render a custom Component. It has next properties and handlers:
- Name and value of the current group property
- closure action to toggle group
- closure action to expand/collapse all rows in the group
- closure action to select/deselect all rows in the group
- List will all selected rows in the group
- List will all expanded rows in the group
- List with all rows in the group
- List will all visible rows in the group. The previous one differs only if the group on the page is the last one and some of its rows are displayed on the next page
Let’s use all of this in the real example. As usual GitHub API will be used. We’ll show all comments for issue ember.js/13071 in the table.
Two Models are needed – comment
and user
. Firs one is a comment itself and second one is a data about comment’s author.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // app/models/comment.js import Model from "ember-data/model"; import attr from "ember-data/attr"; import { belongsTo, hasMany } from "ember-data/relationships"; import Ember from 'ember'; export default Model.extend({ url: attr('string'), html_url: attr('string'), issue_url: attr('string'), user: belongsTo('user'), created_at: attr('string'), updated_at: attr('string'), author_association: attr('string'), body: attr('string'), createdDay: Ember.computed('created_at', function () { const d = new Date(this.get('created_at')); let month = d.getMonth(); month = (month < 10 ? '0': '') + month; let day = d.getDate(); day = (day < 10 ? '0' : '') + day; return `${d.getFullYear()}-${month}-${day}`; }) }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 | // app/models/user.js import Model from "ember-data/model"; import attr from "ember-data/attr"; import { belongsTo, hasMany } from "ember-data/relationships"; export default Model.extend({ login: attr('string'), avatar_url: attr('string'), url: attr('string'), html_url: attr('string'), type: attr('string'), site_admin: attr('boolean') }); |
Response for request to the api/comments will give us whole needed data. So, we may use only Adapter and Serializer for Model comment
:
1 2 3 4 5 6 7 8 9 10 11 | // app/adapters/comment.js import DS from 'ember-data'; export default DS.RESTAdapter.extend({ host: 'https://api.github.com', namespace: 'repos/emberjs/ember.js/issues/13071/comments', query(store, b, query) { const url = `${this.get('host')}/${this.get('namespace')}`; return this.ajax(url, 'GET', {data: query}); } }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | // app/serializers/comment.js import Ember from 'ember'; import DS from 'ember-data'; export default DS.JSONAPISerializer.extend({ _mapIncluded(data, type) { const clb = item => ({id: item.id, type, attributes: item}); return Ember.isArray(data) ? data.map(clb) : [clb(data)]; }, normalizeQueryResponse(a, b, payload) { const included = []; const newPayload = { data: payload.map(item => { const itemData = { id: item.id, type: 'comment', attributes: item, relationships: { user: { data: { id: item.user.id, type: 'user' } } } }; included.pushObjects(this._mapIncluded(item.user, 'user')); return itemData; }) }; newPayload.included = included; return newPayload; } }); |
Data for table may be get in the Route for page where table is:
1 2 3 4 5 6 7 8 | // app/routes/index.js import Ember from 'ember'; export default Ember.Route.extend({ model() { return this.get('store').query('comment', {per_page: 100}); } }); |
Our table will have four columns:
1 2 3 4 5 6 | [ {propertyName: 'id'}, {propertyName: 'user.login', title: 'User'}, {propertyName: 'created_at'}, {propertyName: 'updated_at'} ] |
dataGroupProperties
allows to set a list of properties used to group rows. It can be a list of string or objects with two fields label
and value
.
1 | [{value: 'user.login', label: 'User'}, {value: 'createdDay', label: 'Created Day'}] |
We use two properties createdDay
and user.login
. As you can see, grouping can be done even for property from the another Model.
dataGroupProperties
must have a value from currentGroupingPropertyName
set on Component init!
groupingRowComponent
contains a Component name used in place of the default one. Let’s consider this Component in detail. We can create it completely from scratch or extend a default models-table/row-group-toggle.js
. Let’s use a second option:
1 2 3 4 5 | // app/components/grouping-row.js import DefaultComponent from './models-table/row-group-toggle'; export default DefaultComponent.extend({ }); |
The advantage of this method is that we do not have to duplicate the declaration of the three event handlers. However, we have to create our custom template. In the demo purposes it will have all available functional:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | {{! app/templates/components/grouping-row.hbs}} <p>{{currentGroupingPropertyName}}: {{groupedValue}} ({{visibleGroupedItems.length}} / {{groupedItems.length}})</p> <div class="btn-group"> <button class="btn" {{action "toggleGroupedRows" bubbles=false}}> <i class="glyphicon glyphicon-resize-full"></i>/<i class="glyphicon glyphicon-resize-small"></i> </button> <button class="btn" {{action "toggleGroupedRowsExpands" bubbles=false}}> <i class={{themeInstance.expand-row}}></i>/<i class={{themeInstance.collapse-row}}></i> ({{expandedGroupedItems.length}}) </button> <button class="btn" {{action "toggleGroupedRowsSelection" bubbles=false}}> <i class={{themeInstance.select-row}}></i>/<i class={{themeInstance.deselect-row}}></i> ({{selectedGroupedItems.length}}) </button> </div> |
currentGroupingPropertyName
– is a property name used now for rows grouping. groupedValue
– is its value for current rows group. visibleGroupedItems
– is a list with visible rows in the group. groupedItems
– is a list with rows in the group. The difference between them is only if the group is displayed last on the current page of the table and does not get there completely.
Next we have three buttons. First one toggle rows group. Second one is used to expand/collapse all rows in the group. Third one os used to select/deselect all rows in the group. Keep in mind that all handler are declared with bubbles=false
. As the text of the last two buttons (besides the icons), the number of rows of the group in the corresponding state (expanded and selected) is displayed.
Demo as usual availalbe on the ember-twiddle – Rows Grouping. Code examples are taken from there, so the are no 2.16 features (for some reasons only 2.12 can be used in the twiddle).
addon, ember, emberModelsTable, table
Tips and tricks for working with ember-models-table
- Upgrade ember-models-table to version 2
- Theming ember-models-table 2.x
- Nested models-table usage
- Additional requests in the models-table-server-paginated for data loading
- Sync table state with query parameters
- Rows grouping in the table
- Inline edit and aggregated data
- Navigation with a keyboard in the ember-models-table
Add comment