From 768b0c47ae86e290aa36a28f9adfe2f1a26e412f Mon Sep 17 00:00:00 2001 From: Masha_Rudenko Date: Thu, 10 Sep 2026 10:39:08 +0300 Subject: [PATCH 1/5] [update] document group counter recalculation - DataCollection group(): new "Group counters and aggregates" section on $count, $totalCount and map fields being recomputed after filter(), resetFilter(), add(), remove(), update() and parse(); showEmptyGroups added to the group() config - Grid: two new properties of the group config documented in the config page and the "Grouping data" guide - counter and showEmptyGroups - Grid guide: new "Group counters and aggregates" section covering the service properties, the filtering scope and group removal; dropped the note claiming that changing values doesn't affect the aggregates - filter(), resetFilter(), add(), update() and parse() pages note the recalculation; a rule is matched against data rows only, so a callback never gets a $group or $groupSummary row - remove(): passing the id of a group header removes the whole group - helpers: dhx.methods signature and the new "Aggregating an empty set of items" section - sum and count give 0, avg, min and max give null - grid summary config: the same for the footer of a grid with no rows - related sample linked in the group config, guide and group() pages --- .../api/datacollection_add_method.md | 2 + .../api/datacollection_filter_method.md | 2 + .../api/datacollection_group_method.md | 53 +++++++- .../api/datacollection_parse_method.md | 2 + .../api/datacollection_remove_method.md | 2 + .../api/datacollection_resetfilter_method.md | 2 + .../api/datacollection_update_method.md | 2 + docs/grid/api/grid_group_config.md | 11 ++ docs/grid/api/grid_summary_config.md | 2 + docs/grid/usage.md | 114 +++++++++++++++++- docs/helpers/data_calculation_functions.md | 26 ++++ 11 files changed, 216 insertions(+), 2 deletions(-) diff --git a/docs/data_collection/api/datacollection_add_method.md b/docs/data_collection/api/datacollection_add_method.md index 3573ef27..5e374231 100644 --- a/docs/data_collection/api/datacollection_add_method.md +++ b/docs/data_collection/api/datacollection_add_method.md @@ -52,6 +52,8 @@ component.data.add([ @descr: +When data is [grouped](data_collection/api/datacollection_group_method.md), the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups are recalculated over the resulting data. + **Related sample**: [Data. Add](https://snippet.dhtmlx.com/ktd8ks0m) @changelog: The possibility to pass an array of items is added in v6.1. diff --git a/docs/data_collection/api/datacollection_filter_method.md b/docs/data_collection/api/datacollection_filter_method.md index 456a8a9b..bc15aa04 100644 --- a/docs/data_collection/api/datacollection_filter_method.md +++ b/docs/data_collection/api/datacollection_filter_method.md @@ -67,6 +67,8 @@ grid.data.filter({ Unless `config.add` is set, the method replaces the currently applied filters; calling it without a rule at all drops all non-permanent filters and restores the unfiltered order. Permanent filters are the exception: they always survive and are reapplied first. The new rule then narrows their result further, so an item remains in the result only if it matches both the permanent filter and the new rule. +When data is [grouped](data_collection/api/datacollection_group_method.md), the rule is matched against the data items only. Group headers and summary rows aren't checked against the rule, so a filtering function isn't called with a `$group` or a `$groupSummary` item. A group is kept or dropped by what is left inside it, and its [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) are recalculated. + **Related sample**: [Data. Filter](https://snippet.dhtmlx.com/csiwq3kj) diff --git a/docs/data_collection/api/datacollection_group_method.md b/docs/data_collection/api/datacollection_group_method.md index 151e94af..e2a9b64b 100644 --- a/docs/data_collection/api/datacollection_group_method.md +++ b/docs/data_collection/api/datacollection_group_method.md @@ -31,6 +31,7 @@ interface IGroupOrder { type TGroupOrder = string | TGroupOrderFunc | IGroupOrder; interface IGroupConfig { showMissed?: boolean | string; // true by default + showEmptyGroups?: boolean; // false by default field?: string; // "group" by default } @@ -48,7 +49,7 @@ group(order: TGroupOrder[], config?: IGroupConfig): void; config - (object) optional, the configuration of data grouping. The configuration object may include the following properties: + (object) optional, the configuration of data grouping. The configuration object may include the following properties: @@ -130,4 +131,54 @@ grid.data.group(["city"], { @descr: +## Group counters and aggregates + +Group headers follow the data they hold. They are recalculated after every change of the collection content, that is after the [`filter()`](data_collection/api/datacollection_filter_method.md), [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md), [`add()`](data_collection/api/datacollection_add_method.md), [`remove()`](data_collection/api/datacollection_remove_method.md), [`update()`](data_collection/api/datacollection_update_method.md) and [`parse()`](data_collection/api/datacollection_parse_method.md) methods. + +A header row provides the counters of the group in the following service properties: + +- `$count` - the number of data items that the group currently holds. For a nested grouping it is the size of the whole subtree of the group. Nested headers and summary rows aren't counted as data +- `$totalCount` - the number of data items that the group holds ignoring the active filters. It is equal to `$count` when no filtering is applied + +Every field listed in the `map` object of a grouping level is recomputed over the items that are left, both on the header row and on the group summary row set by the `summary` property. + +Recalculation needs no configuration, it happens on every data change while the collection is grouped: + +~~~jsx +const data = new dhx.DataCollection(); +data.parse(dataset); + +data.group([{ by: "status", map: { total: ["price", "sum"] }, summary: "bottom" }]); + +// the header row of the "wip" group, which holds two items with the total of 50 +const wip = data.map(item => item).find(item => item.$group); + +wip.$count; // 2 +wip.$totalCount; // 2 +wip.total; // 50 + +data.filter({ + by: "price", + match: 30, + compare: (value, match) => Number(value) >= Number(match) +}); + +wip.$count; // 1 +wip.$totalCount; // 2, the unfiltered number of items +wip.total; // 30, recomputed over the items that are left + +data.resetFilter(); +wip.$count; // 2 +~~~ + +The aggregates of the header row and of the summary row are recomputed together, so the summary row of a group always matches its header. + +Filtering is applied to the data items only: a group is kept or dropped by what is left inside it, and a group all the items of which are filtered out is removed from the collection together with its summary row and its nested groups. Such a group is skipped by [`map()`](data_collection/api/datacollection_map_method.md) and isn't included into [`getLength()`](data_collection/api/datacollection_getlength_method.md) unless the `showEmptyGroups: true` config is passed to the method, and [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md) brings it back either way. + +A group emptied by [`remove()`](data_collection/api/datacollection_remove_method.md) has no filter to be restored from, so it leaves the collection for good, its summary row included, and [`getItem()`](data_collection/api/datacollection_getitem_method.md) called with the id of its header returns *undefined*. + +The same applies to TreeCollection, and thus to Grid in the [TreeGrid mode](grid/treegrid_mode.md): a counter covers the whole subtree of a header row, and an emptied header row is dropped together with everything below it. + +**Related sample**: [Grid. Grouping counters and empty groups](https://snippet.dhtmlx.com/f4a5voun?mode=wide) + @changelog: added in v9.0 \ No newline at end of file diff --git a/docs/data_collection/api/datacollection_parse_method.md b/docs/data_collection/api/datacollection_parse_method.md index 011618ee..f2fdbfe9 100644 --- a/docs/data_collection/api/datacollection_parse_method.md +++ b/docs/data_collection/api/datacollection_parse_method.md @@ -46,4 +46,6 @@ Please note that if you specify the `id` fields in the data collection, their va The method resets the applied sorting and filtering: the sorting is dropped, and so are all the filters except those applied with `permanent: true`, which are reapplied to the new data. +When data is [grouped](data_collection/api/datacollection_group_method.md), the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups are recalculated over the new data. + **Related sample**: [Data. Parse](https://snippet.dhtmlx.com/0zrxtmvi) diff --git a/docs/data_collection/api/datacollection_remove_method.md b/docs/data_collection/api/datacollection_remove_method.md index 9c940ff6..f3c810ff 100644 --- a/docs/data_collection/api/datacollection_remove_method.md +++ b/docs/data_collection/api/datacollection_remove_method.md @@ -20,6 +20,8 @@ component.data.remove(["2", "4"]); @descr: +When data is [grouped](data_collection/api/datacollection_group_method.md), passing the id of a group header removes the whole group: the header itself, the items of the group, its summary row and its nested groups. The [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the remaining groups are recalculated. + **Related sample**: [Data. Remove](https://snippet.dhtmlx.com/ugdlqgp5) [comment]: # (@related:window/customization.md#controls-and-operations) diff --git a/docs/data_collection/api/datacollection_resetfilter_method.md b/docs/data_collection/api/datacollection_resetfilter_method.md index ba138bb6..fb53a14b 100644 --- a/docs/data_collection/api/datacollection_resetfilter_method.md +++ b/docs/data_collection/api/datacollection_resetfilter_method.md @@ -36,6 +36,8 @@ component.data.resetFilter({ id: "filter_id" }); @descr: +When data is [grouped](data_collection/api/datacollection_group_method.md), the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups are recalculated over the restored data, and the groups that were left with no items by the filter are brought back. + **Related sample**: - [Data. ResetFilter](https://snippet.dhtmlx.com/jg8wxfvc) - [Grid. ResetFilter](https://snippet.dhtmlx.com/15trblk2) \ No newline at end of file diff --git a/docs/data_collection/api/datacollection_update_method.md b/docs/data_collection/api/datacollection_update_method.md index dc5cab7f..1192f603 100644 --- a/docs/data_collection/api/datacollection_update_method.md +++ b/docs/data_collection/api/datacollection_update_method.md @@ -36,6 +36,8 @@ itemsForUpdate.forEach((item, index) => { }); ~~~ +When data is [grouped](data_collection/api/datacollection_group_method.md), the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups are recalculated over the resulting data. + **Related sample**: [Data. Update](https://snippet.dhtmlx.com/4g90gi6b) [comment]: # (@related:window/customization.md#controls-and-operations) diff --git a/docs/grid/api/grid_group_config.md b/docs/grid/api/grid_group_config.md index 404d2816..b24eaa8f 100644 --- a/docs/grid/api/grid_group_config.md +++ b/docs/grid/api/grid_group_config.md @@ -33,6 +33,8 @@ interface IGroup { panelHeight: number; // 40 by default hideableColumns?: boolean; // true by default showMissed?: boolean | string; // true by default + showEmptyGroups?: boolean; // false by default + counter?: boolean | ((row: IRow) => string); // true by default fields?: { [colId: string]: IGroupOrder }; order?: IGroupOrderItem[]; column?: string | ICol; @@ -60,6 +62,13 @@ You can find the detailed description of the `group` object properties with exam - if set to *true*, the rows that don't have values for grouping are rendered row by row after all the data - if a *string* value is set, e.g. "Missed", the rows that don't have values for grouping are rendered as a separate group the name of which will have the specified string value. This group will be rendered as the last one - if set to *false*, the rows that don't suit the grouping criteria won't be rendered +- `showEmptyGroups` - (optional) specifies whether a group that is left with no rows after filtering stays in the grid, *false* by default + - if set to *false*, such a group is removed from the view together with its summary row and its nested groups, and is restored by [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md) + - if set to *true*, such a group remains visible with the `$count: 0` value and emptied aggregates: the "sum" and "count" aggregations give *0*, while "avg", "min" and "max" give *null*, as described in the [Data calculation functions](helpers/data_calculation_functions.md#aggregating-an-empty-set-of-items) guide +- `counter` - (optional) defines the text rendered next to the group name in the column with grouped data, *true* by default + - if set to *true*, the current number of rows of the group is rendered in brackets, e.g. *(2)* + - if set to *false*, only the group name is rendered + - if set to a *function*, it takes the group header row as a parameter and returns the string to render. The returned value is inserted as HTML, so it may contain markup; an empty string renders no counter. The row gives access to the `$count`, `$totalCount` and `$by` service properties and to every aggregated field of the `map` object of the level - `fields` - (optional) predefines an extended configuration for data grouping by certain columns, by setting the rules of aggregation and rendering of the results. The attributes of the `fields` object correspond to the ids of columns for which the aggregation rules and the order of results are being configured. The configuration of a column is defined by the `IGroupOrder` object that has the following properties: - `map` - (optional) an object for data aggregation in a group, where the keys are field names, and the values can be: - a tuple `[string, TAggregate]` that specifies the field and the aggregation type ("sum", "count", "min", "max", "avg") from the [`dhx.methods`](helpers/data_calculation_functions.md) helper @@ -94,4 +103,6 @@ const grid = new dhx.Grid("grid_container", { **Related article**: [Grouping data](grid/usage.md#grouping-data) +**Related sample**: [Grid. Grouping counters and empty groups](https://snippet.dhtmlx.com/f4a5voun?mode=wide) + @changelog: added in v9.0 \ No newline at end of file diff --git a/docs/grid/api/grid_summary_config.md b/docs/grid/api/grid_summary_config.md index b02a8dc9..12b05af1 100644 --- a/docs/grid/api/grid_summary_config.md +++ b/docs/grid/api/grid_summary_config.md @@ -82,6 +82,8 @@ console.log(summary); // { totalPopulation: 1000000, totalArea: 50000, density: @descr: +When a grid has no rows, the built-in functors are called with an empty set of rows: the "sum" and "count" functors give *0*, while "avg", "min" and "max" give *null*, which is rendered as an empty value. Check the details in the [Data calculation functions](helpers/data_calculation_functions.md#aggregating-an-empty-set-of-items) guide. + **Related article:** [Custom statistics in the column header/footer and spans](grid/configuration.md#custom-statistics-in-the-column-headerfooter-and-spans) **Related API**: [getSummary()](grid/api/grid_getsummary_method.md) diff --git a/docs/grid/usage.md b/docs/grid/usage.md index 67984238..d7c8e10f 100644 --- a/docs/grid/usage.md +++ b/docs/grid/usage.md @@ -310,6 +310,8 @@ grid.data.filter({ Unless `config.add` is set, the method replaces the currently applied filters; calling it without a rule at all drops all non-permanent filters and restores the unfiltered order. Permanent filters are the exception: they always survive and are reapplied first. The new rule then narrows their result further, so an item remains in the result only if it matches both the permanent filter and the new rule. +When grid data is [grouped](#grouping-data), the rule is matched against the data rows only, while the group headers and the summary rows are kept or dropped by what is left inside them. The counters and the aggregated values of the remaining groups are [recalculated](#group-counters-and-aggregates). + **Related sample**: [Grid. Basic filter](https://snippet.dhtmlx.com/g0zpjqi1) ### Sorting data @@ -722,7 +724,6 @@ It is possible to [set a predefined Grid configuration](#configuring-data-groupi :::info important - Data grouping isn't intended for working with [`lazyDataProxy`](grid/data_loading.md#dynamic-loading) -- Modifying the values of grouped elements won't modify the aggregated values - You mustn't change the order of elements grouping by drag-n-drop ::: @@ -873,6 +874,58 @@ const grid = new dhx.Grid("grid_container", { **Related sample:** [Grid. Grouping missing data](https://snippet.dhtmlx.com/0geopa0v) +- `showEmptyGroups` - (optional) specifies whether a group that is left with no rows after filtering stays in the grid, *false* by default + - if set to *false*, such a group is removed from the view together with its summary row and its nested groups, and is restored when the filter is reset + - if set to *true*, such a group remains visible with the `$count: 0` value and emptied aggregates: the "sum" and "count" aggregations give *0*, while "avg", "min" and "max" give *null*, as described in the [Data calculation functions](helpers/data_calculation_functions.md#aggregating-an-empty-set-of-items) guide + +~~~jsx {8-10} +const grid = new dhx.Grid("grid_container", { + columns: [ + { id: "status", header: [{ text: "Status" }] }, + { id: "price", header: [{ text: "Price" }] } + ], + group: { + order: [{ by: "status", map: { total: ["price", "sum"] } }], + // the groups that lose all their rows after filtering + // stay in the grid with the zero count and the zero total + showEmptyGroups: true + }, + data: dataset +}); + +grid.data.filter({ + by: "price", + match: 40, + compare: (value, match) => Number(value) >= Number(match) +}); +~~~ + +**Related sample:** [Grid. Grouping counters and empty groups](https://snippet.dhtmlx.com/f4a5voun?mode=wide) + +- `counter` - (optional) defines the text rendered next to the group name in the column with grouped data, *true* by default + - if set to *true*, the current number of rows of the group is rendered in brackets, e.g. *(2)* + - if set to *false*, only the group name is rendered + - if set to a *function*, it takes the group header row as a parameter and returns the string to render. The returned value is inserted as HTML, so it may contain markup; an empty string renders no counter. The row gives access to the `$count`, `$totalCount` and `$by` service properties and to every aggregated field of the `map` object of the level + +The counter is a part of the default template of the column with grouped data, so it is ignored when the [`column`](#configuration-of-the-column-property-of-the-group-object) object carries a custom `template`. The same text is used as the tooltip of the cell. + +~~~jsx {8-9} +const grid = new dhx.Grid("grid_container", { + columns: [ + { id: "status", header: [{ text: "Status" }] }, + { id: "price", header: [{ text: "Price" }] } + ], + group: { + order: ["status"], + // e.g. "wip (1 of 2)" + counter: (row) => `(${row.$count} of ${row.$totalCount})` + }, + data: dataset +}); +~~~ + +**Related sample:** [Grid. Grouping counters and empty groups](https://snippet.dhtmlx.com/f4a5voun?mode=wide) + - `fields` - (optional) predefines an extended configuration for data grouping by certain columns, by setting the rules of aggregation and rendering of the results. The attributes of the `fields` object correspond to the ids of columns for which the aggregation rules and the order of results are being configured. The configuration of a column is defined by the `IGroupOrder` object that has the following properties: - `map` - (optional) an object for data aggregation in a group, where the keys are field names, and the values can be: - a tuple `[string, TAggregate]` that specifies the field and the aggregation type ("sum", "count", "min", "max", "avg") from the [`dhx.methods`](helpers/data_calculation_functions.md) helper @@ -1130,6 +1183,62 @@ column: { Note that the `column` object of the `group` configuration option has some properties of a Grid column. You can check the descriptions of the group column object properties enumerated above in the [Grid column properties](grid/api/api_gridcolumn_properties.md) guide. +### Group counters and aggregates + +Group headers follow the data they hold. Grid recalculates them after every change of the collection content, that is after the [](data_collection/api/datacollection_filter_method.md), [](data_collection/api/datacollection_resetfilter_method.md), [](data_collection/api/datacollection_add_method.md), [](data_collection/api/datacollection_remove_method.md), [](data_collection/api/datacollection_update_method.md) and [](data_collection/api/datacollection_parse_method.md) methods of DataCollection. + +A header row provides the counters of the group in the following service properties: + +- `$count` - the number of data rows that the group currently holds. For a nested grouping it is the size of the whole subtree of the group. Nested headers and summary rows aren't counted as data +- `$totalCount` - the number of data rows that the group holds ignoring the active filters. It is equal to `$count` when no filtering is applied + +A header row also provides the `$by` property with the name of the field that the level groups by. + +Every field listed in the `map` object of a grouping level is recomputed over the rows that are left, both on the header row and on the group summary row set by the `summary` property. + +In the snippet below the [`counter`](#configuring-data-grouping) function renders the current number of rows of a group against the initial one, while the `map` object puts the recalculated total of the group into the "price" cell of the header row and of the summary row: + +~~~jsx {8-12,14-15} +const grid = new dhx.Grid("grid_container", { + columns: [ + { id: "status", header: [{ text: "Status" }] }, + { id: "price", header: [{ text: "Price" }] } + ], + group: { + order: ["status"], + fields: { + status: { + map: { price: ["price", "sum"] }, + summary: "bottom" + } + }, + // e.g. "wip (1 of 2)" + counter: (row) => `(${row.$count} of ${row.$totalCount})` + }, + data: dataset +}); + +grid.data.filter({ + by: "price", + match: 30, + compare: (value, match) => Number(value) >= Number(match) +}); +~~~ + +After the filtering above a group renders the number of rows that passed the filter, while `$totalCount` keeps the unfiltered number of rows of the group. + +**Related sample:** [Grid. Grouping counters and empty groups](https://snippet.dhtmlx.com/f4a5voun?mode=wide) + +#### Filtering grouped data + +A filtering rule (or a filtering function) is matched against the data rows only. Group headers and summary rows aren't checked against the rule, so a custom filtering callback isn't called with a `$group` or a `$groupSummary` row. A group is kept or dropped by what is left inside it. + +A group all the rows of which are filtered out is removed from the grid together with its summary row and its nested groups, and comes back when the filter is reset. To keep such a group in the grid, set the [`showEmptyGroups`](grid/api/grid_group_config.md) property of the `group` configuration object to *true*. + +#### Removing a group + +Calling the [](data_collection/api/datacollection_remove_method.md) method with the id of a group header removes the whole group: the header itself, the rows of the group, its summary row and its nested groups. + ### Making group panel elements closable You can enable closing of all the elements of the group panel using the [`closable`](grid/api/grid_closable_config.md) configuration option of Grid. @@ -1204,6 +1313,9 @@ The method takes the following parameters: - if set to *true*, the rows that don't have values for grouping are rendered row by row after all the data - if a *string* value is set, e.g. "Missed", the rows that don't have values for grouping are rendered as a separate group the name of which will have the specified string value. This group will be rendered as the last one - if set to *false*, the rows that don't suit the grouping criteria won't be rendered + - `showEmptyGroups` - (optional) specifies whether a group that is left with no rows after filtering stays in the grid, *false* by default + - if set to *false*, such a group is removed from the view together with its summary row and its nested groups, and is restored when the filter is reset + - if set to *true*, such a group remains visible with the `$count: 0` value and emptied aggregates - `field` - (optional) the group field name, *"group"* by default There are several examples of grouping Grid data via the `group()` method of DataCollection: diff --git a/docs/helpers/data_calculation_functions.md b/docs/helpers/data_calculation_functions.md index 258fbf40..0f6b0c95 100644 --- a/docs/helpers/data_calculation_functions.md +++ b/docs/helpers/data_calculation_functions.md @@ -11,6 +11,18 @@ The following functors are available: - `min` - calculates the minimal value in the data - `sum` - calculates the sum of data values +Each functor takes a set of items and the name of the field to calculate. The `sum` and `count` functors always return a number, while `avg`, `min` and `max` return *null* when there is nothing to calculate: + +~~~ts +const methods: { + sum: (items: IDataItem[], field: string) => number; + count: (items: IDataItem[], field: string) => number; + avg: (items: IDataItem[], field: string) => number | null; + min: (items: IDataItem[], field: string) => number | null; + max: (items: IDataItem[], field: string) => number | null; +}; +~~~ + For example, this is how the `sum` functor is called: ~~~jsx @@ -18,6 +30,20 @@ const rows = [{ value: 10 }, { value: 20 }, { value: 30 }]; const sum = dhx.methods.sum(rows, "value"); // 60 ~~~ +### Aggregating an empty set of items + +Called with an empty set of items, or with a field that none of the items has, `sum` and `count` return *0*, while `avg`, `min` and `max` return *null*: + +~~~jsx +dhx.methods.sum([], "value"); // 0 +dhx.methods.count([], "value"); // 0 +dhx.methods.avg([], "value"); // null +dhx.methods.min([], "value"); // null +dhx.methods.max([], "value"); // null +~~~ + +A *null* value is rendered as an empty cell, so an `avg`, `min` or `max` cell with nothing to calculate stays empty. This is what the footer of a grid that has no rows shows, as well as the aggregates of a group that is kept in a grid by the [`showEmptyGroups`](grid/api/grid_group_config.md) property. + ### Defining a custom functor You can specify a custom function for calculating data. For example, you can use the `methods` helper function for adding custom calculations to [get a summary of counted values](grid/configuration.md#getting-the-summary-object). From 99e0a053539365bbe3e4bddc8f5e5300fb36e355 Mon Sep 17 00:00:00 2001 From: Masha_Rudenko Date: Thu, 10 Sep 2026 15:20:37 +0300 Subject: [PATCH 2/5] [add] note on grid summaries in a grouped grid The column and Grid summaries are calculated over the data rows only, the group header rows and the group summary rows aren't counted as data. - `configuration.md`: a note in the intro of "Custom statistics in the column header/footer and spans", before both summary subsections - `grid_summary_config.md`: the same in the description, ahead of the empty set paragraph - `usage.md`: a line in "Group counters and aggregates" linking back to the summary guide, matching the wording used for `$count` --- docs/grid/api/grid_summary_config.md | 2 ++ docs/grid/configuration.md | 4 ++++ docs/grid/usage.md | 2 ++ 3 files changed, 8 insertions(+) diff --git a/docs/grid/api/grid_summary_config.md b/docs/grid/api/grid_summary_config.md index 12b05af1..551953d7 100644 --- a/docs/grid/api/grid_summary_config.md +++ b/docs/grid/api/grid_summary_config.md @@ -82,6 +82,8 @@ console.log(summary); // { totalPopulation: 1000000, totalArea: 50000, density: @descr: +In a grid with [grouped data](grid/usage.md#grouping-data), the summaries are calculated over the data rows only: the group header rows and the group summary rows aren't counted as data. + When a grid has no rows, the built-in functors are called with an empty set of rows: the "sum" and "count" functors give *0*, while "avg", "min" and "max" give *null*, which is rendered as an empty value. Check the details in the [Data calculation functions](helpers/data_calculation_functions.md#aggregating-an-empty-set-of-items) guide. **Related article:** [Custom statistics in the column header/footer and spans](grid/configuration.md#custom-statistics-in-the-column-headerfooter-and-spans) diff --git a/docs/grid/configuration.md b/docs/grid/configuration.md index 59a04076..1adc2f67 100644 --- a/docs/grid/configuration.md +++ b/docs/grid/configuration.md @@ -1212,6 +1212,10 @@ It is also possible to [get the object with the calculated values](#getting-the- Use the [`dhx.methods`](helpers/data_calculation_functions.md) helper to define the default statistical functions and to create custom functions for data calculation while creating the summary list. ::: +:::note +In a grid with [grouped data](grid/usage.md#grouping-data), the summaries are calculated over the data rows only: the group header rows and the group summary rows aren't counted as data. +::: + ### Column summary To form a summary list that will be available at the column's level only, you should use the [`summary`](grid/api/api_gridcolumn_properties.md) configuration option of the column. The `summary` configuration option of a column can be initialized either as an *object* or as a *string*. As an object it contains calculated values set as *key:value* pairs, where the *keys* are the field names and *values* can be: diff --git a/docs/grid/usage.md b/docs/grid/usage.md index d7c8e10f..115f860b 100644 --- a/docs/grid/usage.md +++ b/docs/grid/usage.md @@ -1196,6 +1196,8 @@ A header row also provides the `$by` property with the name of the field that th Every field listed in the `map` object of a grouping level is recomputed over the rows that are left, both on the header row and on the group summary row set by the `summary` property. +The [summaries](grid/configuration.md#custom-statistics-in-the-column-headerfooter-and-spans) of a column and of the grid are calculated over the data rows only as well, so the group header rows and the group summary rows don't affect the totals. + In the snippet below the [`counter`](#configuring-data-grouping) function renders the current number of rows of a group against the initial one, while the `map` object puts the recalculated total of the group into the "price" cell of the header row and of the summary row: ~~~jsx {8-12,14-15} From 63013b1d583a5f1452f4e9580913ddeb3370b941 Mon Sep 17 00:00:00 2001 From: Masha_Rudenko Date: Thu, 10 Sep 2026 16:18:13 +0300 Subject: [PATCH 3/5] [fix] corrections in the group counters docs - `data_calculation_functions.md`: the empty set condition is stated as "an empty or missing set of items"; the unverified case of a field that none of the items has is dropped - `usage.md`: the `showEmptyGroups` example aggregates into the rendered "price" column instead of a `total` field that no column shows, so the zero total the comment mentions is actually visible - `usage.md`, `datacollection_group_method.md`: the sentence about a group losing all its rows is recast so that the subject isn't separated from its verb by a relative clause --- docs/data_collection/api/datacollection_group_method.md | 2 +- docs/grid/usage.md | 4 ++-- docs/helpers/data_calculation_functions.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/data_collection/api/datacollection_group_method.md b/docs/data_collection/api/datacollection_group_method.md index e2a9b64b..1e4c90cc 100644 --- a/docs/data_collection/api/datacollection_group_method.md +++ b/docs/data_collection/api/datacollection_group_method.md @@ -173,7 +173,7 @@ wip.$count; // 2 The aggregates of the header row and of the summary row are recomputed together, so the summary row of a group always matches its header. -Filtering is applied to the data items only: a group is kept or dropped by what is left inside it, and a group all the items of which are filtered out is removed from the collection together with its summary row and its nested groups. Such a group is skipped by [`map()`](data_collection/api/datacollection_map_method.md) and isn't included into [`getLength()`](data_collection/api/datacollection_getlength_method.md) unless the `showEmptyGroups: true` config is passed to the method, and [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md) brings it back either way. +Filtering is applied to the data items only: a group is kept or dropped by what is left inside it, and a group is removed from the collection together with its summary row and its nested groups when all its items are filtered out. Such a group is skipped by [`map()`](data_collection/api/datacollection_map_method.md) and isn't included into [`getLength()`](data_collection/api/datacollection_getlength_method.md) unless the `showEmptyGroups: true` config is passed to the method, and [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md) brings it back either way. A group emptied by [`remove()`](data_collection/api/datacollection_remove_method.md) has no filter to be restored from, so it leaves the collection for good, its summary row included, and [`getItem()`](data_collection/api/datacollection_getitem_method.md) called with the id of its header returns *undefined*. diff --git a/docs/grid/usage.md b/docs/grid/usage.md index 115f860b..705a0deb 100644 --- a/docs/grid/usage.md +++ b/docs/grid/usage.md @@ -885,7 +885,7 @@ const grid = new dhx.Grid("grid_container", { { id: "price", header: [{ text: "Price" }] } ], group: { - order: [{ by: "status", map: { total: ["price", "sum"] } }], + order: [{ by: "status", map: { price: ["price", "sum"] } }], // the groups that lose all their rows after filtering // stay in the grid with the zero count and the zero total showEmptyGroups: true @@ -1235,7 +1235,7 @@ After the filtering above a group renders the number of rows that passed the fil A filtering rule (or a filtering function) is matched against the data rows only. Group headers and summary rows aren't checked against the rule, so a custom filtering callback isn't called with a `$group` or a `$groupSummary` row. A group is kept or dropped by what is left inside it. -A group all the rows of which are filtered out is removed from the grid together with its summary row and its nested groups, and comes back when the filter is reset. To keep such a group in the grid, set the [`showEmptyGroups`](grid/api/grid_group_config.md) property of the `group` configuration object to *true*. +A group is removed from the grid together with its summary row and its nested groups when all its rows are filtered out, and comes back when the filter is reset. To keep such a group in the grid, set the [`showEmptyGroups`](grid/api/grid_group_config.md) property of the `group` configuration object to *true*. #### Removing a group diff --git a/docs/helpers/data_calculation_functions.md b/docs/helpers/data_calculation_functions.md index 0f6b0c95..f9459fd1 100644 --- a/docs/helpers/data_calculation_functions.md +++ b/docs/helpers/data_calculation_functions.md @@ -32,7 +32,7 @@ const sum = dhx.methods.sum(rows, "value"); // 60 ### Aggregating an empty set of items -Called with an empty set of items, or with a field that none of the items has, `sum` and `count` return *0*, while `avg`, `min` and `max` return *null*: +Called with an empty or missing set of items, `sum` and `count` return *0*, while `avg`, `min` and `max` return *null*: ~~~jsx dhx.methods.sum([], "value"); // 0 From 048ea0e213dba46e8d2fe0b35f564078ea3c86ca Mon Sep 17 00:00:00 2001 From: Masha_Rudenko Date: Tue, 15 Sep 2026 17:12:20 +0300 Subject: [PATCH 4/5] [update] refine the group counters docs - datacollection group(): the new section split into "Counters of a group", "Aggregated fields", "Filtering grouped data" and "Removing a group"; the scope note turned into an admonition; the service properties of a header row marked as such; api names linked as code - grid guide: the same sub-sections in "Group counters and aggregates"; $index added next to $count and $totalCount, and $by widened to cover the function form of `by` - grid group config: `by` added to IGroupOrder and to the `order` description, the usage snippet highlight fixed to point at `group` - getSummary(): a note that a grouped grid counts the data rows only - changelogs: v9.4 entries for the recalculation, the filtering scope, remove() on a group header, the summaries of a grouped grid, and the new `counter` and `showEmptyGroups` properties - the descriptions reworded from the passive to the active voice --- .../api/datacollection_add_method.md | 2 +- .../api/datacollection_filter_method.md | 5 +- .../api/datacollection_group_method.md | 38 +++++++++----- .../api/datacollection_parse_method.md | 2 +- .../api/datacollection_remove_method.md | 5 +- .../api/datacollection_resetfilter_method.md | 2 +- .../api/datacollection_update_method.md | 2 +- docs/grid/api/grid_getsummary_method.md | 3 ++ docs/grid/api/grid_group_config.md | 18 ++++--- docs/grid/api/grid_summary_config.md | 5 +- docs/grid/usage.md | 52 +++++++++++-------- docs/helpers/data_calculation_functions.md | 2 +- 12 files changed, 84 insertions(+), 52 deletions(-) diff --git a/docs/data_collection/api/datacollection_add_method.md b/docs/data_collection/api/datacollection_add_method.md index 5e374231..6750164a 100644 --- a/docs/data_collection/api/datacollection_add_method.md +++ b/docs/data_collection/api/datacollection_add_method.md @@ -52,7 +52,7 @@ component.data.add([ @descr: -When data is [grouped](data_collection/api/datacollection_group_method.md), the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups are recalculated over the resulting data. +When data is [grouped](data_collection/api/datacollection_group_method.md), DataCollection recalculates the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups over the resulting data. **Related sample**: [Data. Add](https://snippet.dhtmlx.com/ktd8ks0m) diff --git a/docs/data_collection/api/datacollection_filter_method.md b/docs/data_collection/api/datacollection_filter_method.md index bc15aa04..aeb83a5b 100644 --- a/docs/data_collection/api/datacollection_filter_method.md +++ b/docs/data_collection/api/datacollection_filter_method.md @@ -67,8 +67,11 @@ grid.data.filter({ Unless `config.add` is set, the method replaces the currently applied filters; calling it without a rule at all drops all non-permanent filters and restores the unfiltered order. Permanent filters are the exception: they always survive and are reapplied first. The new rule then narrows their result further, so an item remains in the result only if it matches both the permanent filter and the new rule. -When data is [grouped](data_collection/api/datacollection_group_method.md), the rule is matched against the data items only. Group headers and summary rows aren't checked against the rule, so a filtering function isn't called with a `$group` or a `$groupSummary` item. A group is kept or dropped by what is left inside it, and its [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) are recalculated. +When data is [grouped](data_collection/api/datacollection_group_method.md), DataCollection matches the rule against the data items only. It never checks group headers and summary rows against the rule, so a filtering function never receives a `$group` or a `$groupSummary` item. A group stays as long as any of its items match the rule, and DataCollection recalculates the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups that remain. **Related sample**: [Data. Filter](https://snippet.dhtmlx.com/csiwq3kj) +@changelog: +- As of v9.4, the rule applies to the data items only: a filtering function never receives a `$group` or a `$groupSummary` item + diff --git a/docs/data_collection/api/datacollection_group_method.md b/docs/data_collection/api/datacollection_group_method.md index 1e4c90cc..c5172bf3 100644 --- a/docs/data_collection/api/datacollection_group_method.md +++ b/docs/data_collection/api/datacollection_group_method.md @@ -49,7 +49,7 @@ group(order: TGroupOrder[], config?: IGroupConfig): void; config - (object) optional, the configuration of data grouping. The configuration object may include the following properties:
  • `showMissed?: boolean | string` - optional, specifies whether the elements that don't have the field for grouping should be displayed, *true* by default
    • if set to *true*, the rows that don't have values for grouping are rendered row by row after all the data
    • if a *string* value is set, e.g. "Missed", the rows that don't have values for grouping are rendered as a separate group the name of which will have the specified string value. This group will be rendered as the last one
    • if set to *false*, the rows that don't suit the grouping criteria won't be rendered
  • `showEmptyGroups?: boolean` - optional, specifies whether a group that is left with no items after filtering stays in the collection, *false* by default
    • if set to *false*, such a group is removed from the collection together with its summary row and its nested groups, so it is skipped by `map()` and isn't included into `getLength()`. A `resetFilter()` call brings it back
    • if set to *true*, such a group is kept with the `$count: 0` value and emptied aggregates: the "sum" and "count" aggregations give *0*, while "avg", "min" and "max" give *null*
  • `field?: string` - optional, the group field name, *"group"* by default
+ (object) optional, the configuration of data grouping. The configuration object may include the following properties:
  • `showMissed?: boolean | string` - optional, specifies whether the elements that don't have the field for grouping should be displayed, *true* by default
    • if set to *true*, the rows that don't have values for grouping are rendered row by row after all the data
    • if a *string* value is set, e.g. "Missed", the rows that don't have values for grouping are rendered as a separate group the name of which will have the specified string value. This group will be rendered as the last one
    • if set to *false*, the rows that don't suit the grouping criteria won't be rendered
  • `showEmptyGroups?: boolean` - optional, specifies whether a group that loses all its items to filtering stays in the collection, *false* by default
    • if set to *false*, such a group leaves the collection together with its summary row and its nested groups, so `map()` skips it and `getLength()` leaves it out. A `resetFilter()` call brings it back
    • if set to *true*, such a group stays with the `$count: 0` value and emptied aggregates: the "sum" and "count" aggregations give *0*, while "avg", "min" and "max" give *null*
  • `field?: string` - optional, the group field name, *"group"* by default
@@ -133,14 +133,11 @@ grid.data.group(["city"], { ## Group counters and aggregates -Group headers follow the data they hold. They are recalculated after every change of the collection content, that is after the [`filter()`](data_collection/api/datacollection_filter_method.md), [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md), [`add()`](data_collection/api/datacollection_add_method.md), [`remove()`](data_collection/api/datacollection_remove_method.md), [`update()`](data_collection/api/datacollection_update_method.md) and [`parse()`](data_collection/api/datacollection_parse_method.md) methods. +Group headers follow the data they hold. DataCollection recalculates them after every change of the collection content, that is after the [`filter()`](data_collection/api/datacollection_filter_method.md), [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md), [`add()`](data_collection/api/datacollection_add_method.md), [`remove()`](data_collection/api/datacollection_remove_method.md), [`update()`](data_collection/api/datacollection_update_method.md) and [`parse()`](data_collection/api/datacollection_parse_method.md) methods. -A header row provides the counters of the group in the following service properties: - -- `$count` - the number of data items that the group currently holds. For a nested grouping it is the size of the whole subtree of the group. Nested headers and summary rows aren't counted as data -- `$totalCount` - the number of data items that the group holds ignoring the active filters. It is equal to `$count` when no filtering is applied - -Every field listed in the `map` object of a grouping level is recomputed over the items that are left, both on the header row and on the group summary row set by the `summary` property. +:::note +The same applies to TreeCollection, and thus to Grid in the [TreeGrid mode](grid/treegrid_mode.md): a counter covers the whole subtree of a header row, and an emptied header row disappears together with everything below it. +::: Recalculation needs no configuration, it happens on every data change while the collection is grouped: @@ -166,19 +163,34 @@ data.filter({ wip.$count; // 1 wip.$totalCount; // 2, the unfiltered number of items wip.total; // 30, recomputed over the items that are left +data.getItem(`${wip.id}:summary`).total; // 30, the summary row follows data.resetFilter(); wip.$count; // 2 ~~~ -The aggregates of the header row and of the summary row are recomputed together, so the summary row of a group always matches its header. +### Counters of a group + +A group header row carries the following service properties: + +- `$count` - the number of data items that the group currently holds. For a nested grouping it is the size of the whole subtree of the group. Nested headers and summary rows don't count as data +- `$totalCount` - the number of data items that the group holds ignoring the active filters. It equals `$count` when no filter is active + +### Aggregated fields + +DataCollection recomputes every field listed in the `map` object of a grouping level over the items that are left, on the header row and on the group summary row that the `summary` property adds alike, so both rows show the same values. + +### Filtering grouped data -Filtering is applied to the data items only: a group is kept or dropped by what is left inside it, and a group is removed from the collection together with its summary row and its nested groups when all its items are filtered out. Such a group is skipped by [`map()`](data_collection/api/datacollection_map_method.md) and isn't included into [`getLength()`](data_collection/api/datacollection_getlength_method.md) unless the `showEmptyGroups: true` config is passed to the method, and [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md) brings it back either way. +DataCollection matches a filtering rule against the data items only: a group stays as long as any of its items match the rule, and a group that loses all of them leaves the collection together with its summary row and its nested groups. [`map()`](data_collection/api/datacollection_map_method.md) skips such a group and [`getLength()`](data_collection/api/datacollection_getlength_method.md) leaves it out, unless you pass the `showEmptyGroups: true` config to the method, and [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md) brings it back either way. -A group emptied by [`remove()`](data_collection/api/datacollection_remove_method.md) has no filter to be restored from, so it leaves the collection for good, its summary row included, and [`getItem()`](data_collection/api/datacollection_getitem_method.md) called with the id of its header returns *undefined*. +### Removing a group -The same applies to TreeCollection, and thus to Grid in the [TreeGrid mode](grid/treegrid_mode.md): a counter covers the whole subtree of a header row, and an emptied header row is dropped together with everything below it. +A group emptied by [`remove()`](data_collection/api/datacollection_remove_method.md) has no filter to bring it back, so it leaves the collection for good, its summary row included, and [`getItem()`](data_collection/api/datacollection_getitem_method.md) returns *undefined* for the id of its header. **Related sample**: [Grid. Grouping counters and empty groups](https://snippet.dhtmlx.com/f4a5voun?mode=wide) -@changelog: added in v9.0 \ No newline at end of file +@changelog: +- As of v9.4, DataCollection recalculates the counters and aggregated values of group headers after every change of the collection content +- The `showEmptyGroups` property of the `config` parameter is added in v9.4 +- Added in v9.0 \ No newline at end of file diff --git a/docs/data_collection/api/datacollection_parse_method.md b/docs/data_collection/api/datacollection_parse_method.md index f2fdbfe9..0048abd7 100644 --- a/docs/data_collection/api/datacollection_parse_method.md +++ b/docs/data_collection/api/datacollection_parse_method.md @@ -46,6 +46,6 @@ Please note that if you specify the `id` fields in the data collection, their va The method resets the applied sorting and filtering: the sorting is dropped, and so are all the filters except those applied with `permanent: true`, which are reapplied to the new data. -When data is [grouped](data_collection/api/datacollection_group_method.md), the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups are recalculated over the new data. +When data is [grouped](data_collection/api/datacollection_group_method.md), DataCollection recalculates the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups over the new data. **Related sample**: [Data. Parse](https://snippet.dhtmlx.com/0zrxtmvi) diff --git a/docs/data_collection/api/datacollection_remove_method.md b/docs/data_collection/api/datacollection_remove_method.md index f3c810ff..85aaff94 100644 --- a/docs/data_collection/api/datacollection_remove_method.md +++ b/docs/data_collection/api/datacollection_remove_method.md @@ -20,8 +20,11 @@ component.data.remove(["2", "4"]); @descr: -When data is [grouped](data_collection/api/datacollection_group_method.md), passing the id of a group header removes the whole group: the header itself, the items of the group, its summary row and its nested groups. The [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the remaining groups are recalculated. +When data is [grouped](data_collection/api/datacollection_group_method.md), passing the id of a group header removes the whole group: the header itself, the items of the group, its summary row and its nested groups. DataCollection recalculates the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the remaining groups. **Related sample**: [Data. Remove](https://snippet.dhtmlx.com/ugdlqgp5) +@changelog: +- As of v9.4, the method called with the id of a group header removes the items of that group as well + [comment]: # (@related:window/customization.md#controls-and-operations) diff --git a/docs/data_collection/api/datacollection_resetfilter_method.md b/docs/data_collection/api/datacollection_resetfilter_method.md index fb53a14b..f1953ff9 100644 --- a/docs/data_collection/api/datacollection_resetfilter_method.md +++ b/docs/data_collection/api/datacollection_resetfilter_method.md @@ -36,7 +36,7 @@ component.data.resetFilter({ id: "filter_id" }); @descr: -When data is [grouped](data_collection/api/datacollection_group_method.md), the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups are recalculated over the restored data, and the groups that were left with no items by the filter are brought back. +When data is [grouped](data_collection/api/datacollection_group_method.md), DataCollection recalculates the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups over the restored data and brings back the groups that the filter left with no items. **Related sample**: - [Data. ResetFilter](https://snippet.dhtmlx.com/jg8wxfvc) diff --git a/docs/data_collection/api/datacollection_update_method.md b/docs/data_collection/api/datacollection_update_method.md index 1192f603..4474e3f9 100644 --- a/docs/data_collection/api/datacollection_update_method.md +++ b/docs/data_collection/api/datacollection_update_method.md @@ -36,7 +36,7 @@ itemsForUpdate.forEach((item, index) => { }); ~~~ -When data is [grouped](data_collection/api/datacollection_group_method.md), the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups are recalculated over the resulting data. +When data is [grouped](data_collection/api/datacollection_group_method.md), DataCollection recalculates the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups over the resulting data. **Related sample**: [Data. Update](https://snippet.dhtmlx.com/4g90gi6b) diff --git a/docs/grid/api/grid_getsummary_method.md b/docs/grid/api/grid_getsummary_method.md index 52bc5d74..c0cb0322 100644 --- a/docs/grid/api/grid_getsummary_method.md +++ b/docs/grid/api/grid_getsummary_method.md @@ -56,10 +56,13 @@ console.log(columnSummary); //{ totalPopulation: 1000000, avgAge: 28 } - the val - When called without parameters, the method returns an object with the calculated values defined in the configuration of the component. - When the `id` parameter is passed to the method, it returns an object with the calculated values defined in the column's configuration together with the calculated values defined in the component's configuration. +In a grid with [grouped data](grid/usage.md#grouping-data), the method calculates the returned values over the data rows only: the group header rows and the group summary rows don't count as data. + **Related article:** [Getting the summary object](grid/configuration.md#getting-the-summary-object) **Related API**: [summary](grid/api/grid_summary_config.md) @changelog: +- As of v9.4, the method calculates the returned values of a grid with grouped data over the data rows only - Added in v9.0 diff --git a/docs/grid/api/grid_group_config.md b/docs/grid/api/grid_group_config.md index b24eaa8f..7561e9a4 100644 --- a/docs/grid/api/grid_group_config.md +++ b/docs/grid/api/grid_group_config.md @@ -18,10 +18,11 @@ Note that when you initialize Grid with the `group` configuration property, the #### Usage -~~~jsx {22} +~~~jsx {25} type TAggregate = "sum" | "count" | "min" | "max" | "avg" | string; interface IGroupOrder { + by: string | ((row: IRow) => string); map?: { [field: string]: [string, TAggregate] | ((row: IRow[]) => string | number) }; summary?: "top" | "bottom"; } @@ -62,13 +63,13 @@ You can find the detailed description of the `group` object properties with exam - if set to *true*, the rows that don't have values for grouping are rendered row by row after all the data - if a *string* value is set, e.g. "Missed", the rows that don't have values for grouping are rendered as a separate group the name of which will have the specified string value. This group will be rendered as the last one - if set to *false*, the rows that don't suit the grouping criteria won't be rendered -- `showEmptyGroups` - (optional) specifies whether a group that is left with no rows after filtering stays in the grid, *false* by default - - if set to *false*, such a group is removed from the view together with its summary row and its nested groups, and is restored by [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md) +- `showEmptyGroups` - (optional) specifies whether a group that loses all its rows to filtering stays in the grid, *false* by default + - if set to *false*, such a group leaves the view together with its summary row and its nested groups, and [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md) brings it back - if set to *true*, such a group remains visible with the `$count: 0` value and emptied aggregates: the "sum" and "count" aggregations give *0*, while "avg", "min" and "max" give *null*, as described in the [Data calculation functions](helpers/data_calculation_functions.md#aggregating-an-empty-set-of-items) guide - `counter` - (optional) defines the text rendered next to the group name in the column with grouped data, *true* by default - - if set to *true*, the current number of rows of the group is rendered in brackets, e.g. *(2)* - - if set to *false*, only the group name is rendered - - if set to a *function*, it takes the group header row as a parameter and returns the string to render. The returned value is inserted as HTML, so it may contain markup; an empty string renders no counter. The row gives access to the `$count`, `$totalCount` and `$by` service properties and to every aggregated field of the `map` object of the level + - if set to *true*, Grid renders the current number of rows of the group in brackets, e.g. *(2)* + - if set to *false*, Grid renders only the group name + - if set to a *function*, it takes the group header row as a parameter and returns the string to render. Grid inserts the returned value as HTML, so it may contain markup; an empty string renders no counter. The row gives access to the `$count`, `$totalCount` and `$by` service properties and to every aggregated field of the `map` object of the level - `fields` - (optional) predefines an extended configuration for data grouping by certain columns, by setting the rules of aggregation and rendering of the results. The attributes of the `fields` object correspond to the ids of columns for which the aggregation rules and the order of results are being configured. The configuration of a column is defined by the `IGroupOrder` object that has the following properties: - `map` - (optional) an object for data aggregation in a group, where the keys are field names, and the values can be: - a tuple `[string, TAggregate]` that specifies the field and the aggregation type ("sum", "count", "min", "max", "avg") from the [`dhx.methods`](helpers/data_calculation_functions.md) helper @@ -78,6 +79,7 @@ You can find the detailed description of the `group` object properties with exam - a string that represents a grouping field - a function `((row: IRow) => string)` for dynamic defining of a group - an `IGroupOrder` object that has the following properties: + - `by` - the field name or a function `((row: IRow) => string)` for user-defined grouping - `map` - (optional) an object for data aggregation in a group, where the keys are field names, and the values can be: - a tuple `[string, TAggregate]` that specifies the field and the aggregation type ("sum", "count", "min", "max", "avg") from the `dhx.methods` helper - a user-defined aggregation function `((row: IRow[]) => string | number)` @@ -105,4 +107,6 @@ const grid = new dhx.Grid("grid_container", { **Related sample**: [Grid. Grouping counters and empty groups](https://snippet.dhtmlx.com/f4a5voun?mode=wide) -@changelog: added in v9.0 \ No newline at end of file +@changelog: +- The `counter` and `showEmptyGroups` properties are added in v9.4 +- Added in v9.0 \ No newline at end of file diff --git a/docs/grid/api/grid_summary_config.md b/docs/grid/api/grid_summary_config.md index 551953d7..4c5ccb16 100644 --- a/docs/grid/api/grid_summary_config.md +++ b/docs/grid/api/grid_summary_config.md @@ -82,13 +82,14 @@ console.log(summary); // { totalPopulation: 1000000, totalArea: 50000, density: @descr: -In a grid with [grouped data](grid/usage.md#grouping-data), the summaries are calculated over the data rows only: the group header rows and the group summary rows aren't counted as data. +In a grid with [grouped data](grid/usage.md#grouping-data), Grid calculates the summaries over the data rows only: the group header rows and the group summary rows don't count as data. -When a grid has no rows, the built-in functors are called with an empty set of rows: the "sum" and "count" functors give *0*, while "avg", "min" and "max" give *null*, which is rendered as an empty value. Check the details in the [Data calculation functions](helpers/data_calculation_functions.md#aggregating-an-empty-set-of-items) guide. +When a grid has no rows, Grid calls the built-in functors with an empty set of rows: the "sum" and "count" functors give *0*, while "avg", "min" and "max" give *null*, which renders as an empty value. Check the details in the [Data calculation functions](helpers/data_calculation_functions.md#aggregating-an-empty-set-of-items) guide. **Related article:** [Custom statistics in the column header/footer and spans](grid/configuration.md#custom-statistics-in-the-column-headerfooter-and-spans) **Related API**: [getSummary()](grid/api/grid_getsummary_method.md) @changelog: +- As of v9.4, Grid calculates the summaries of a grid with grouped data over the data rows only - Added in v9.0 \ No newline at end of file diff --git a/docs/grid/usage.md b/docs/grid/usage.md index 705a0deb..6314d015 100644 --- a/docs/grid/usage.md +++ b/docs/grid/usage.md @@ -874,8 +874,8 @@ const grid = new dhx.Grid("grid_container", { **Related sample:** [Grid. Grouping missing data](https://snippet.dhtmlx.com/0geopa0v) -- `showEmptyGroups` - (optional) specifies whether a group that is left with no rows after filtering stays in the grid, *false* by default - - if set to *false*, such a group is removed from the view together with its summary row and its nested groups, and is restored when the filter is reset +- `showEmptyGroups` - (optional) specifies whether a group that loses all its rows to filtering stays in the grid, *false* by default + - if set to *false*, such a group leaves the view together with its summary row and its nested groups, and comes back when you reset the filter - if set to *true*, such a group remains visible with the `$count: 0` value and emptied aggregates: the "sum" and "count" aggregations give *0*, while "avg", "min" and "max" give *null*, as described in the [Data calculation functions](helpers/data_calculation_functions.md#aggregating-an-empty-set-of-items) guide ~~~jsx {8-10} @@ -903,11 +903,11 @@ grid.data.filter({ **Related sample:** [Grid. Grouping counters and empty groups](https://snippet.dhtmlx.com/f4a5voun?mode=wide) - `counter` - (optional) defines the text rendered next to the group name in the column with grouped data, *true* by default - - if set to *true*, the current number of rows of the group is rendered in brackets, e.g. *(2)* - - if set to *false*, only the group name is rendered - - if set to a *function*, it takes the group header row as a parameter and returns the string to render. The returned value is inserted as HTML, so it may contain markup; an empty string renders no counter. The row gives access to the `$count`, `$totalCount` and `$by` service properties and to every aggregated field of the `map` object of the level + - if set to *true*, Grid renders the current number of rows of the group in brackets, e.g. *(2)* + - if set to *false*, Grid renders only the group name + - if set to a *function*, it takes the group header row as a parameter and returns the string to render. Grid inserts the returned value as HTML, so it may contain markup; an empty string renders no counter. The row gives access to the `$count`, `$totalCount` and `$by` service properties and to every aggregated field of the `map` object of the level -The counter is a part of the default template of the column with grouped data, so it is ignored when the [`column`](#configuration-of-the-column-property-of-the-group-object) object carries a custom `template`. The same text is used as the tooltip of the cell. +The counter is a part of the default template of the column with grouped data, so Grid ignores it when the [`column`](#configuration-of-the-column-property-of-the-group-object) object carries a custom `template`. The same text serves as the tooltip of the cell. ~~~jsx {8-9} const grid = new dhx.Grid("grid_container", { @@ -973,6 +973,7 @@ b) the total rows under the grouped values set by the `summary` property - a string that represents a grouping field - a function `((row: IRow) => string)` for dynamic defining of a group - an `IGroupOrder` object that has the following properties: + - `by` - the field name or a function `((row: IRow) => string)` for user-defined grouping - `map` - (optional) an object for data aggregation in a group, where the keys are field names, and the values can be: - a tuple `[string, TAggregate]` that specifies the field and the aggregation type ("sum", "count", "min", "max", "avg") from the `dhx.methods` helper - a user-defined aggregation function `((row: IRow[]) => string | number)` @@ -1185,18 +1186,7 @@ Note that the `column` object of the `group` configuration option has some prope ### Group counters and aggregates -Group headers follow the data they hold. Grid recalculates them after every change of the collection content, that is after the [](data_collection/api/datacollection_filter_method.md), [](data_collection/api/datacollection_resetfilter_method.md), [](data_collection/api/datacollection_add_method.md), [](data_collection/api/datacollection_remove_method.md), [](data_collection/api/datacollection_update_method.md) and [](data_collection/api/datacollection_parse_method.md) methods of DataCollection. - -A header row provides the counters of the group in the following service properties: - -- `$count` - the number of data rows that the group currently holds. For a nested grouping it is the size of the whole subtree of the group. Nested headers and summary rows aren't counted as data -- `$totalCount` - the number of data rows that the group holds ignoring the active filters. It is equal to `$count` when no filtering is applied - -A header row also provides the `$by` property with the name of the field that the level groups by. - -Every field listed in the `map` object of a grouping level is recomputed over the rows that are left, both on the header row and on the group summary row set by the `summary` property. - -The [summaries](grid/configuration.md#custom-statistics-in-the-column-headerfooter-and-spans) of a column and of the grid are calculated over the data rows only as well, so the group header rows and the group summary rows don't affect the totals. +Group headers follow the data they hold. Grid recalculates them after every change of the collection content, that is after the [`filter()`](data_collection/api/datacollection_filter_method.md), [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md), [`add()`](data_collection/api/datacollection_add_method.md), [`remove()`](data_collection/api/datacollection_remove_method.md), [`update()`](data_collection/api/datacollection_update_method.md) and [`parse()`](data_collection/api/datacollection_parse_method.md) methods of DataCollection. In the snippet below the [`counter`](#configuring-data-grouping) function renders the current number of rows of a group against the initial one, while the `map` object puts the recalculated total of the group into the "price" cell of the header row and of the summary row: @@ -1231,15 +1221,31 @@ After the filtering above a group renders the number of rows that passed the fil **Related sample:** [Grid. Grouping counters and empty groups](https://snippet.dhtmlx.com/f4a5voun?mode=wide) +#### Counters of a group + +A group header row carries the following service properties: + +- `$count` - the number of data rows that the group currently holds. For a nested grouping it is the size of the whole subtree of the group. Nested headers and summary rows don't count as data +- `$totalCount` - the number of data rows that the group holds ignoring the active filters. It equals `$count` when no filter is active +- `$by` - the field that the level groups by: the field name, or the function passed as `by` when the level groups by a function + +Every row of a grid, a group header included, also carries the `$index` service property with the position of the row among the rendered ones. + +#### Aggregated fields + +Grid recomputes every field listed in the `map` object of a grouping level over the rows that are left, both on the header row and on the group summary row that the `summary` property adds. + +Grid calculates the [summaries](grid/configuration.md#custom-statistics-in-the-column-headerfooter-and-spans) of a column and of the grid over the data rows only as well, so the group header rows and the group summary rows don't affect the totals. + #### Filtering grouped data -A filtering rule (or a filtering function) is matched against the data rows only. Group headers and summary rows aren't checked against the rule, so a custom filtering callback isn't called with a `$group` or a `$groupSummary` row. A group is kept or dropped by what is left inside it. +Grid matches a filtering rule (or a filtering function) against the data rows only. It never checks group headers and summary rows against the rule, so a custom filtering callback never receives a `$group` or a `$groupSummary` row. A group stays as long as any of its rows match the rule. -A group is removed from the grid together with its summary row and its nested groups when all its rows are filtered out, and comes back when the filter is reset. To keep such a group in the grid, set the [`showEmptyGroups`](grid/api/grid_group_config.md) property of the `group` configuration object to *true*. +A group that loses all its rows to filtering leaves the grid together with its summary row and its nested groups, and comes back when you reset the filter. To keep such a group in the grid, set the [`showEmptyGroups`](grid/api/grid_group_config.md) property of the `group` configuration object to *true*. #### Removing a group -Calling the [](data_collection/api/datacollection_remove_method.md) method with the id of a group header removes the whole group: the header itself, the rows of the group, its summary row and its nested groups. +Calling the [`remove()`](data_collection/api/datacollection_remove_method.md) method with the id of a group header removes the whole group: the header itself, the rows of the group, its summary row and its nested groups. ### Making group panel elements closable @@ -1315,8 +1321,8 @@ The method takes the following parameters: - if set to *true*, the rows that don't have values for grouping are rendered row by row after all the data - if a *string* value is set, e.g. "Missed", the rows that don't have values for grouping are rendered as a separate group the name of which will have the specified string value. This group will be rendered as the last one - if set to *false*, the rows that don't suit the grouping criteria won't be rendered - - `showEmptyGroups` - (optional) specifies whether a group that is left with no rows after filtering stays in the grid, *false* by default - - if set to *false*, such a group is removed from the view together with its summary row and its nested groups, and is restored when the filter is reset + - `showEmptyGroups` - (optional) specifies whether a group that loses all its rows to filtering stays in the grid, *false* by default + - if set to *false*, such a group leaves the view together with its summary row and its nested groups, and comes back when you reset the filter - if set to *true*, such a group remains visible with the `$count: 0` value and emptied aggregates - `field` - (optional) the group field name, *"group"* by default diff --git a/docs/helpers/data_calculation_functions.md b/docs/helpers/data_calculation_functions.md index f9459fd1..effaa1da 100644 --- a/docs/helpers/data_calculation_functions.md +++ b/docs/helpers/data_calculation_functions.md @@ -42,7 +42,7 @@ dhx.methods.min([], "value"); // null dhx.methods.max([], "value"); // null ~~~ -A *null* value is rendered as an empty cell, so an `avg`, `min` or `max` cell with nothing to calculate stays empty. This is what the footer of a grid that has no rows shows, as well as the aggregates of a group that is kept in a grid by the [`showEmptyGroups`](grid/api/grid_group_config.md) property. +A *null* value renders as an empty cell, so an `avg`, `min` or `max` cell with nothing to calculate stays empty. This is what the footer of a grid that has no rows shows, as well as the aggregates of a group that is kept in a grid by the [`showEmptyGroups`](grid/api/grid_group_config.md) property. ### Defining a custom functor From 7aaa6fff88eaa99b022daaa67bfa7362b8622045 Mon Sep 17 00:00:00 2001 From: Masha_Rudenko Date: Wed, 16 Sep 2026 12:05:40 +0300 Subject: [PATCH 5/5] [update] revise the group counters docs - filter(): the description split into "Combining filters" and "Filtering grouped data"; grid summary config: into "Summaries in a grouped grid" and "Summaries of an empty grid"; the related links moved above the headings to keep them page-level - add(), update(), remove(): the commented @related links replaced with readable "Related article" ones - changelogs: the v9.4 behavior entries dropped where they only restated the description, kept on group() where the section is to be moved to the guides later - counter: a custom `template` hides it in the cell, while the tooltip keeps it until `tooltipTemplate` is redefined; the brackets dropped from the examples, as a counter function returns the whole string - the same wording for the group counters across the pages, and a fixed highlight of the snippet in the grid guide --- .../api/datacollection_add_method.md | 4 ++-- .../api/datacollection_filter_method.md | 11 ++++++----- .../api/datacollection_group_method.md | 2 +- .../api/datacollection_remove_method.md | 9 +++------ .../api/datacollection_update_method.md | 4 ++-- docs/grid/api/grid_getsummary_method.md | 1 - docs/grid/api/grid_summary_config.md | 13 ++++++++----- docs/grid/usage.md | 18 +++++++++--------- 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/docs/data_collection/api/datacollection_add_method.md b/docs/data_collection/api/datacollection_add_method.md index 6750164a..967af1fb 100644 --- a/docs/data_collection/api/datacollection_add_method.md +++ b/docs/data_collection/api/datacollection_add_method.md @@ -54,8 +54,8 @@ component.data.add([ When data is [grouped](data_collection/api/datacollection_group_method.md), DataCollection recalculates the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups over the resulting data. +**Related article**: [Controls and operations](window/customization.md#controls-and-operations) + **Related sample**: [Data. Add](https://snippet.dhtmlx.com/ktd8ks0m) @changelog: The possibility to pass an array of items is added in v6.1. - -[comment]: # (@related:window/customization.md#controls-and-operations) diff --git a/docs/data_collection/api/datacollection_filter_method.md b/docs/data_collection/api/datacollection_filter_method.md index aeb83a5b..1efaa24a 100644 --- a/docs/data_collection/api/datacollection_filter_method.md +++ b/docs/data_collection/api/datacollection_filter_method.md @@ -65,13 +65,14 @@ grid.data.filter({ @descr: -Unless `config.add` is set, the method replaces the currently applied filters; calling it without a rule at all drops all non-permanent filters and restores the unfiltered order. Permanent filters are the exception: they always survive and are reapplied first. The new rule then narrows their result further, so an item remains in the result only if it matches both the permanent filter and the new rule. +**Related sample**: [Data. Filter](https://snippet.dhtmlx.com/csiwq3kj) -When data is [grouped](data_collection/api/datacollection_group_method.md), DataCollection matches the rule against the data items only. It never checks group headers and summary rows against the rule, so a filtering function never receives a `$group` or a `$groupSummary` item. A group stays as long as any of its items match the rule, and DataCollection recalculates the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups that remain. +### Combining filters -**Related sample**: [Data. Filter](https://snippet.dhtmlx.com/csiwq3kj) +Unless `config.add` is set, the method replaces the currently applied filters; calling it without a rule at all drops all non-permanent filters and restores the unfiltered order. Permanent filters are the exception: they always survive and are reapplied first. The new rule then narrows their result further, so an item remains in the result only if it matches both the permanent filter and the new rule. + +### Filtering grouped data -@changelog: -- As of v9.4, the rule applies to the data items only: a filtering function never receives a `$group` or a `$groupSummary` item +When data is [grouped](data_collection/api/datacollection_group_method.md), DataCollection matches the rule against the data items only. It never checks group headers and summary rows against the rule, so a filtering function never receives a `$group` or a `$groupSummary` item. A group stays as long as any of its items match the rule, and DataCollection recalculates the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups that remain. diff --git a/docs/data_collection/api/datacollection_group_method.md b/docs/data_collection/api/datacollection_group_method.md index c5172bf3..808b913b 100644 --- a/docs/data_collection/api/datacollection_group_method.md +++ b/docs/data_collection/api/datacollection_group_method.md @@ -182,7 +182,7 @@ DataCollection recomputes every field listed in the `map` object of a grouping l ### Filtering grouped data -DataCollection matches a filtering rule against the data items only: a group stays as long as any of its items match the rule, and a group that loses all of them leaves the collection together with its summary row and its nested groups. [`map()`](data_collection/api/datacollection_map_method.md) skips such a group and [`getLength()`](data_collection/api/datacollection_getlength_method.md) leaves it out, unless you pass the `showEmptyGroups: true` config to the method, and [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md) brings it back either way. +DataCollection matches a filtering rule against the data items only, so a filtering function never receives a `$group` or a `$groupSummary` item. A group stays as long as any of its items match the rule, and a group that loses all of them leaves the collection together with its summary row and its nested groups. [`map()`](data_collection/api/datacollection_map_method.md) skips such a group and [`getLength()`](data_collection/api/datacollection_getlength_method.md) leaves it out, unless you pass the `showEmptyGroups: true` config to the method, and [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md) brings it back either way. ### Removing a group diff --git a/docs/data_collection/api/datacollection_remove_method.md b/docs/data_collection/api/datacollection_remove_method.md index 85aaff94..6914c411 100644 --- a/docs/data_collection/api/datacollection_remove_method.md +++ b/docs/data_collection/api/datacollection_remove_method.md @@ -20,11 +20,8 @@ component.data.remove(["2", "4"]); @descr: -When data is [grouped](data_collection/api/datacollection_group_method.md), passing the id of a group header removes the whole group: the header itself, the items of the group, its summary row and its nested groups. DataCollection recalculates the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the remaining groups. +When data is [grouped](data_collection/api/datacollection_group_method.md), passing the id of a group header removes the whole group: the header itself, the items of the group, its summary row and its nested groups. DataCollection recalculates the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups that remain. -**Related sample**: [Data. Remove](https://snippet.dhtmlx.com/ugdlqgp5) - -@changelog: -- As of v9.4, the method called with the id of a group header removes the items of that group as well +**Related article**: [Controls and operations](window/customization.md#controls-and-operations) -[comment]: # (@related:window/customization.md#controls-and-operations) +**Related sample**: [Data. Remove](https://snippet.dhtmlx.com/ugdlqgp5) diff --git a/docs/data_collection/api/datacollection_update_method.md b/docs/data_collection/api/datacollection_update_method.md index 4474e3f9..0cdbc9bb 100644 --- a/docs/data_collection/api/datacollection_update_method.md +++ b/docs/data_collection/api/datacollection_update_method.md @@ -38,6 +38,6 @@ itemsForUpdate.forEach((item, index) => { When data is [grouped](data_collection/api/datacollection_group_method.md), DataCollection recalculates the [counters and aggregated values](data_collection/api/datacollection_group_method.md#group-counters-and-aggregates) of the groups over the resulting data. -**Related sample**: [Data. Update](https://snippet.dhtmlx.com/4g90gi6b) +**Related article**: [Controls and operations](window/customization.md#controls-and-operations) -[comment]: # (@related:window/customization.md#controls-and-operations) +**Related sample**: [Data. Update](https://snippet.dhtmlx.com/4g90gi6b) diff --git a/docs/grid/api/grid_getsummary_method.md b/docs/grid/api/grid_getsummary_method.md index c0cb0322..4ee097bf 100644 --- a/docs/grid/api/grid_getsummary_method.md +++ b/docs/grid/api/grid_getsummary_method.md @@ -63,6 +63,5 @@ In a grid with [grouped data](grid/usage.md#grouping-data), the method calculate **Related API**: [summary](grid/api/grid_summary_config.md) @changelog: -- As of v9.4, the method calculates the returned values of a grid with grouped data over the data rows only - Added in v9.0 diff --git a/docs/grid/api/grid_summary_config.md b/docs/grid/api/grid_summary_config.md index 4c5ccb16..14fe6631 100644 --- a/docs/grid/api/grid_summary_config.md +++ b/docs/grid/api/grid_summary_config.md @@ -82,14 +82,17 @@ console.log(summary); // { totalPopulation: 1000000, totalArea: 50000, density: @descr: -In a grid with [grouped data](grid/usage.md#grouping-data), Grid calculates the summaries over the data rows only: the group header rows and the group summary rows don't count as data. - -When a grid has no rows, Grid calls the built-in functors with an empty set of rows: the "sum" and "count" functors give *0*, while "avg", "min" and "max" give *null*, which renders as an empty value. Check the details in the [Data calculation functions](helpers/data_calculation_functions.md#aggregating-an-empty-set-of-items) guide. - **Related article:** [Custom statistics in the column header/footer and spans](grid/configuration.md#custom-statistics-in-the-column-headerfooter-and-spans) **Related API**: [getSummary()](grid/api/grid_getsummary_method.md) +#### Summaries in a grouped grid + +In a grid with [grouped data](grid/usage.md#grouping-data), Grid calculates the summaries over the data rows only: the group header rows and the group summary rows don't count as data. + +#### Summaries of an empty grid + +When a grid has no rows, Grid calls the built-in functors with an empty set of rows: the "sum" and "count" functors give *0*, while "avg", "min" and "max" give *null*, which renders as an empty value. Check the details in the [Data calculation functions](helpers/data_calculation_functions.md#aggregating-an-empty-set-of-items) guide. + @changelog: -- As of v9.4, Grid calculates the summaries of a grid with grouped data over the data rows only - Added in v9.0 \ No newline at end of file diff --git a/docs/grid/usage.md b/docs/grid/usage.md index 6314d015..cf1591bc 100644 --- a/docs/grid/usage.md +++ b/docs/grid/usage.md @@ -875,7 +875,7 @@ const grid = new dhx.Grid("grid_container", { **Related sample:** [Grid. Grouping missing data](https://snippet.dhtmlx.com/0geopa0v) - `showEmptyGroups` - (optional) specifies whether a group that loses all its rows to filtering stays in the grid, *false* by default - - if set to *false*, such a group leaves the view together with its summary row and its nested groups, and comes back when you reset the filter + - if set to *false*, such a group leaves the view together with its summary row and its nested groups, and [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md) brings it back - if set to *true*, such a group remains visible with the `$count: 0` value and emptied aggregates: the "sum" and "count" aggregations give *0*, while "avg", "min" and "max" give *null*, as described in the [Data calculation functions](helpers/data_calculation_functions.md#aggregating-an-empty-set-of-items) guide ~~~jsx {8-10} @@ -907,7 +907,7 @@ grid.data.filter({ - if set to *false*, Grid renders only the group name - if set to a *function*, it takes the group header row as a parameter and returns the string to render. Grid inserts the returned value as HTML, so it may contain markup; an empty string renders no counter. The row gives access to the `$count`, `$totalCount` and `$by` service properties and to every aggregated field of the `map` object of the level -The counter is a part of the default template of the column with grouped data, so Grid ignores it when the [`column`](#configuration-of-the-column-property-of-the-group-object) object carries a custom `template`. The same text serves as the tooltip of the cell. +The counter is a part of the default template of the column with grouped data, so Grid ignores it when the [`column`](#configuration-of-the-column-property-of-the-group-object) object carries a custom `template`. The default tooltip of that column shows the counter as well, and a custom `tooltipTemplate` drops it there in the same way. ~~~jsx {8-9} const grid = new dhx.Grid("grid_container", { @@ -917,8 +917,8 @@ const grid = new dhx.Grid("grid_container", { ], group: { order: ["status"], - // e.g. "wip (1 of 2)" - counter: (row) => `(${row.$count} of ${row.$totalCount})` + // e.g. "wip 1 of 2" + counter: (row) => `${row.$count} of ${row.$totalCount}` }, data: dataset }); @@ -1190,7 +1190,7 @@ Group headers follow the data they hold. Grid recalculates them after every chan In the snippet below the [`counter`](#configuring-data-grouping) function renders the current number of rows of a group against the initial one, while the `map` object puts the recalculated total of the group into the "price" cell of the header row and of the summary row: -~~~jsx {8-12,14-15} +~~~jsx {8-15} const grid = new dhx.Grid("grid_container", { columns: [ { id: "status", header: [{ text: "Status" }] }, @@ -1204,8 +1204,8 @@ const grid = new dhx.Grid("grid_container", { summary: "bottom" } }, - // e.g. "wip (1 of 2)" - counter: (row) => `(${row.$count} of ${row.$totalCount})` + // e.g. "wip 1 of 2" + counter: (row) => `${row.$count} of ${row.$totalCount}` }, data: dataset }); @@ -1233,7 +1233,7 @@ Every row of a grid, a group header included, also carries the `$index` service #### Aggregated fields -Grid recomputes every field listed in the `map` object of a grouping level over the rows that are left, both on the header row and on the group summary row that the `summary` property adds. +Grid recomputes every field listed in the `map` object of a grouping level over the rows that are left, on the header row and on the group summary row that the `summary` property adds alike, so both rows show the same values. Grid calculates the [summaries](grid/configuration.md#custom-statistics-in-the-column-headerfooter-and-spans) of a column and of the grid over the data rows only as well, so the group header rows and the group summary rows don't affect the totals. @@ -1322,7 +1322,7 @@ The method takes the following parameters: - if a *string* value is set, e.g. "Missed", the rows that don't have values for grouping are rendered as a separate group the name of which will have the specified string value. This group will be rendered as the last one - if set to *false*, the rows that don't suit the grouping criteria won't be rendered - `showEmptyGroups` - (optional) specifies whether a group that loses all its rows to filtering stays in the grid, *false* by default - - if set to *false*, such a group leaves the view together with its summary row and its nested groups, and comes back when you reset the filter + - if set to *false*, such a group leaves the view together with its summary row and its nested groups, and [`resetFilter()`](data_collection/api/datacollection_resetfilter_method.md) brings it back - if set to *true*, such a group remains visible with the `$count: 0` value and emptied aggregates - `field` - (optional) the group field name, *"group"* by default