GroupingList Controller
The GroupingList controller renders a data grid with all records grouped by a specified field, featuring collapsible group headers with expand/collapse toggles. Unlike the standard List controller, GroupingList loads all records at once (no paging) and organizes them into visual groups.
It inherits from the List controller, reusing its filter panel, toolbar, column headers, and standard actions (Add, Edit, View, Delete). The only difference is in how records are displayed: instead of a flat paginated table, records appear under collapsible group headings.
Basic usage
Set Controller: GroupingList on the view and configure the Grouping node under MainTable/Controller:
Type: Data
Controller: GroupingList
MainTable:
Model: ACTIVITY
Controller:
Grouping:
FieldName: TYPE
StartCollapsed: True
ShowCount: True
ItemName: activity
PluralItemName: activitiesThis produces a grid where activities are grouped by the TYPE field (resolved through its reference to display the type name), with each group initially collapsed. The group header shows the type name and the count of records, for example: Analysis (5 activities).
Grouping configuration
All grouping properties go under MainTable/Controller/Grouping:
| Property | Type | Default | Description |
|---|---|---|---|
FieldName | String | (required) | Name of the field to group by. Can be any field including reference fields (which display the referenced value) or expression-based fields. |
StartCollapsed | Boolean | False | If True, all groups appear initially collapsed (only the group header is visible). Users can click to expand. |
ShowCount | Boolean | False | If True, shows the record count in each group header. |
ShowCount/ItemName | String | (empty) | Singular noun used when count is 1 (e.g. activity). |
ShowCount/PluralItemName | String | (empty) | Plural noun used when count > 1 (e.g. activities). |
ShowName | Boolean | False | If True, prefixes the group value with the field's display label (e.g. Type: Analysis instead of just Analysis). |
SortFieldNames | String | (grouping field) | Space-separated list of field names for the ORDER BY clause. Defaults to the grouping field. Use this to add a secondary sort within each group. |
Aggregates | Node | (none) | Per-group totals appended to each header — see Aggregates in the group header. |
Properties inherited from Kitto1 metadata that are not supported
ShowCount/Template and EnableMenu are accepted by the YAML parser but ignored. See Migrating a grouped view from Kitto1.
Aggregates in the group header
Each child of Aggregates names a field of the view table and describes what to compute over the records of the group:
Grouping:
FieldName: Descrizione
Aggregates:
ImportoDare:
Operation: Sum
Label: Totale Dare
ImportoAvere:
Operation: Sum
Label: Totale Avere0601 - Cassa - Totale Dare: € 6.156,80 - Totale Avere: € 4.110,80| Property | Default | Description |
|---|---|---|
Operation | Sum | Sum, Avg, Min, Max or Count. Count counts the non-null values of the field, which is not the same as the record count of ShowCount. |
Label | field's DisplayLabel | Text shown before the value. Localized like every other label. |
Aggregates are listed in the order they appear in the YAML, each separated by -, and they coexist with ShowName and ShowCount.
The value is formatted like the column it comes from: the user's format settings, and the currency symbol when the field is a Currency. Count is always a plain integer.
Only numeric fields can be aggregated. A field that does not exist in the view table, or that is not numeric, is skipped and the reason is written to the log — the header renders without it rather than failing. If a total you expect is missing, look there first.
A group with nothing to report shows nothing. When the column is null throughout the group, the aggregate is left out instead of being rendered as zero, which would read as a real total. Count is the exception: zero is a legitimate answer to "how many values are there".
Cost
Aggregates and ShowCount share a single pass over the records, and that pass is skipped entirely when neither is declared. Adding aggregates to a view that already shows the count costs nothing.
Group header format
The group header text is built as follows:
- ShowName: False (default):
Analysis - ShowName: True:
Type: Analysis - ShowCount: True with count=1 and ItemName:
Analysis (1 activity) - ShowCount: True with count>1 and PluralItemName:
Analysis (5 activities) - ShowCount: True without ItemName/PluralItemName:
Analysis (5)
Sorting
By default, records are sorted by the grouping field only. Use SortFieldNames to define a multi-field sort:
Grouping:
FieldName: TYPE
SortFieldNames: TYPE ACTIVITY_DATEThis sorts first by TYPE (to keep groups together), then by ACTIVITY_DATE within each group. Each field name in the list must correspond to a field defined in the view table.
Expand/Collapse behavior
- Clicking a group header toggles the visibility of the data rows in that group.
- The toggle icon changes between ▶ (collapsed) and ▼ (expanded).
- The toggle is pure client-side JavaScript (no server round-trip).
Column layout
GroupingList selects and orders its columns exactly like the GridPanel: a layout named <ViewName>_Grid.yaml under Metadata\Views\Layouts is picked up by convention, or you can name one explicitly in Controller/Grid/Layout to share it between similar views. Per-field properties such as DisplayLabel, DisplayWidth and Align apply to grouped grids in the same way.
Without a layout, the columns are all the visible fields of the view table, in model order.
The grouping field gets no column of its own. Its value heads every group, so a column would repeat it identically on every row. This matches Kitto1, whose ExtJS grouping view set HideGroupedColumn, and it applies whether the columns come from a layout or from the view table.
You do not need to remove the field from the grid layout: leaving it there is harmless — it keeps the layout usable by an ungrouped view that shares it — and the grouped grid simply skips it. HelloKitto's Parties_Grid declares Party_Period for exactly that reason.
Filters
GroupingList supports the same filter system as the List controller. If a Filters node is defined under Controller, the filter panel is rendered above the grid and all records are reloaded (with the filter applied) when the user changes filter criteria.
Toolbar and actions
The toolbar is identical to the List controller: Add, Edit, View, Delete, Refresh, and custom ToolViews are all supported. Double-clicking a data row opens the form in Edit or View mode (depending on permissions).
Complete example
From the TasKitto sample application (ActivitiesByType.yaml):
Type: Data
Controller: GroupingList
MainTable:
PluralDisplayLabel: Activities by Type
Model: ACTIVITY
EditController:
CloneButton:
Width: 1220
Height: 460
Controller:
Form:
Layout: ActivityInput_Form
Grouping:
FieldName: TYPE
EnableMenu: False
StartCollapsed: True
ShowName: False
ShowCount: True
ItemName: activity
PluralItemName: activities
SortFieldNames: TYPE ACTIVITY_DATEDifferences from List controller
| Feature | List | GroupingList |
|---|---|---|
| Paging | Yes (configurable page size) | No (all records loaded) |
| Record display | Flat rows | Grouped with collapsible headers |
| Sorting | User-clickable column sort with arrow indicators | Fixed sort by SortFieldNames |
| Filters | Supported | Supported |
| Toolbar | Supported | Supported |
| Grid layout | Supported | Supported |
| Grouping field as a column | Shown | Hidden (its value heads the group) |
| Double-click | Opens form | Opens form |
Cell rendering is otherwise identical, HTMLMemo fields included: their markup is emitted as is in both controllers.
Because GroupingList loads every record, it is best paired with IsLarge: True on the model: that makes AutoOpen default to False, so the grid opens empty and the user populates it through a filter instead of pulling the whole table.
Migrating a grouped view from Kitto1
The Grouping node alone is not enough
In Kitto1 grouping was a property of the standard grid: any view could declare a Grouping node under Controller: List and the grid would group. In Kittox grouping is a separate controller, and a Grouping node under Controller: List is ignored without any error or warning — the view simply renders flat. When porting a grouped view, changing the controller type is the first and mandatory step.
# Kitto1
Controller: List
...
Grouping:
FieldName: Hair
# Kittox
Controller: GroupingList
...
Grouping:
FieldName: HairTranslating ShowCount/Template
Kitto1 composed the header from an ExtJS template, GroupTextTpl. That syntax is not supported: it was evaluated client-side against an ExtJS store, which no longer exists. Express the same intent with the properties above:
Kitto1 Template | Kittox equivalent | Result |
|---|---|---|
{text} | ShowName: False, ShowCount: False | Analysis |
{text} ({[values.rs.length]}) | ShowCount: True | Analysis (5) |
Label ({text}) | ShowName: True plus the field's DisplayLabel | Label: Analysis |
Note the punctuation in the third case: the label is joined with a colon, not parentheses. If the field's general DisplayLabel is not the wording you want in the header, set it on the field and override the form caption with DisplayLabel_Form.
Templates that compute aggregates — anything calling a JavaScript helper over values.rs, such as a per-group total — are expressed declaratively by the Aggregates node. A Kitto1 template like
{text} - Totale €: {[sumValues(values.rs, "ImportoTot")]}becomes
ShowCount: False
Aggregates:
ImportoTot:
Operation: Sum
Label: TotaleShowCount: True did not mean "show the number"
In Kitto1 it was the switch that enabled a custom header, and the count appeared only because it was part of the default template. A view that declared a Template of its own without a count showed no count. Port such a view with ShowCount: False, or it gains a (N) it never had.
EnableMenu
The ExtJS grouping menu (group by any column, turn grouping off) has no counterpart. The property is ignored; leaving EnableMenu: False in a ported view is harmless.
Limitations
- Top-level views only. Grouping is activated by the controller type, so it cannot be applied to a detail grid inside a form: a
Groupingnode underDetailTables/Table/Controllerhas no effect. - No paging. Grouping and paging are mutually exclusive: a group would otherwise be split across pages.
