Группировка строк в таблицах

Данный материал рассчитан на ember-models-table версии 2.2.3 и выше. Группировка строк на данный момент поддерживается только для models-table («серверная» таблица с новым функционалом не проверялась)!

Чтоб models-table узнал, что вы хотите использовать группировку строк, ему надо передать параметр-флаг useDataGrouping со значением true. Это необходимое, но недостаточное условие. Так же надо указать имя свойства, по которому выполняется начальная группировка (currentGroupingPropertyName). После этого группировка строк заработает как надо и будет выглядеть как-то так:

Пример групировки строк

Строки таблицы сгрупированы по полю country (колонки с таким полем в таблице нет)

Клик по названию страны будет сворачивать и разворачивать строки таблицы, относящиеся к данной группе.

Это только верхушка всего доступного функционала по групировке строк. Кратко списком опишем все, что «умеет» Компонент по данной теме, а потом на реальном примере разберем детальнее. И так:

  • Групировка строк по значению определенного свойства. При этом, свойство может быть и вложенным.
  • Сортировка по значению это свойства.
  • Возможность задать список доступных для группировки свойств и переключение между ними. Но, в любом случае, группировать можно только по одному свойству.
  • Значение групируемого свойства можно выводить как в отдельной строке (пример выше), так и в отдельной колонке слева (в ней будет одна ячейка с rowspan равному количеству строк в группе).
  • Вместо значения групируемого свойства можно выводить свой Компонент, в котором доступно следующее:
    • Имя и значение свойства, по которому в данный момент выполняется групировка
    • closure action для сворачивания/разворачивания всей группы
    • closure action для сворачивания/разворачивания каждой строки группы
    • closure action для выделения/снятия выделения со всех строк группы
    • Массив с выделенными строками группы
    • Массив с развернутыми строками группы
    • Массив со всеми строками группы
    • Массив с видимыми строками группы. От предыдущего от отличается только в случае, если группа на странице находится последней и некоторые ее строки выводятся на следующей странице

Теперь используем весь доступный функционал на реальном примере. Как и раньше GitHub API нам в этом поможет. В таблице выведем все комментарии к вопросу ember.js/13071.

Нам понадобятся две Модели comment и user. Первая — это сам комментарий, а вторая — это данные про его автора.

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')
});

Данные для них будут получены из запроса api/comments. Соответственно, Сериализатор и Адаптер можно написать только для Модели 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;
  }
});

Данные для нашей таблицы теперь можно получить из Роута соответствующей страницы:

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});
  }
});

В самой таблице будем выводить четыре колонки:

1
2
3
4
5
6
[
  {propertyName: 'id'},
  {propertyName: 'user.login', title: 'User'},
  {propertyName: 'created_at'},
  {propertyName: 'updated_at'}
]

Свойство dataGroupProperties позволяет задать массив с именами свойств, по которым разрешена группировка. Так как он используется для обычного select, то можно задать не просто массив строк, а массив объектов с полями label и value:

1
[{value: 'user.login', label: 'User'}, {value: 'createdDay', label: 'Created Day'}]

Мы берем два свойства — createdDay и user.login. Как видим, группировка возможна даже по свойству из другой Модели.

В dataGroupProperties обязательно должно входить значение из currentGroupingPropertyName, заданное при инициализации Компонента!

Имя Компонента, используемого вместо стандартного в строке/колонке со значением поля группировки, задается через свойство groupingRowComponent. Рассмотрим подробно этот Компонент. Мы можем написать его полностью с нуля, а можем расширить стандартный models-table/row-group-toggle.js. Воспользуемся вторым вариантом:

1
2
3
4
5
// app/components/grouping-row.js
import DefaultComponent from './models-table/row-group-toggle';
 
export default DefaultComponent.extend({
});

Преимущество этого способа в том, что нам не придется дублировать описание трех обработчиков событий. Но, так или иначе, Шаблон нам придется написать свой. В демонстрационных целях в нем будет весь доступный функционал:

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 — это имя свойства, по которому сейчас выполняется группировка. groupedValue — значение этого свойства в данной группе. visibleGroupedItems — это видимые строки группы, а groupedItems — вообще все строки. Разница между ними только в случае, если группа выводится последней на текущей страницы таблицы и не влазит туда полностью.

Далее в Компоненте выводятся три кнопки. Первая переключает свернутое/развернутое состояние группы, вторая сворачивает или разворачивает все строки группы, а третья выделяет все строки группы или же снимает с них выделение. Обратите внимание, что для каждого обработчика события указано bubbles=false. В качестве текста двух последних кнопок (помимо иконок) выводится количество строк группы в соответствующем состоянии (развернутые и выбранные).

Развернутое демо как обычно есть на ember-twiddle — Rows Grouping. Примеры кода взяты оттуда, потому никаких фишек 2.16 нет (почему-то 2.12 там максимальная версия).

, , ,

Оставить комментарий

Top ↑ | Main page | Back