Grouping Data Items

Grouping Types

Grouping data in Webix components can be obligatory and optional, static and dynamic.

Obligatory grouping

Grouping is a must-have feature for components that present data hierarchically, yet can be populated with non-hierarchical (inline) datasets.

Such components are subject to initial static grouping. Each group features a group header (parent branch) that are opened to show the group members.

When they are fed with hierarchical data, grouping is not necessary. Read more about data types.

Optional grouping

Data can be grouped, if you need to present data united by some criterion.

With these components, you can see only the result of grouping.

Static/Initial grouping

Grouping criteria are set during component init by the $group key of the scheme object. This is necessary for the components with obligatory grouping.

webix.ui({
    view:"tree",
    scheme:{
        $group:"year"
    }
});

Related sample:  Tree: Plain JSON Data with Client-side Grouping

Dynamic grouping

Grouping is defined by a function applied to the component.

$$("mytree").group("year");

To cancel grouping, use the ungroup method.

$$("mychart").ungroup();

Setting Grouping Pattern

Syntax for the group() method and the $group object property is the same - you need to specify here the grouping criterion as:

  • data item;
  • function;
  • object.

Data Item

A string that points to the property from the initial dataset:

//static mode
var tree = webix.ui({
    view:"tree",
    scheme:{
        $group:"year"
    }
});
 
//dynamic mode
tree.group("title");

Function

The function is applied to a data object and returns the grouping criterion(-a);

scheme:{
    $group:function(obj){
        return obj.year/* + ..*/;
    }
}

Related sample:  List: Grouping

Object

An object with properties defining the grouping criterion and the grouping pattern:

  • by (must-have) - sets the grouping criterion either as string or as function.
  • map - an object that redefines values to display in the current template. Here math functors are applied to the specified properties of group members.
  • missing (boolean, string) - defines how the objects the group header of which returns ""/null/false/undefined will be grouped. The possible values:
    • true (default) the missing values are appended after the grouped values,
    • false - the missing values are ignored,
    • string - creates a separate group for missing values or, if the string is the name of some existing group, adds them to the group.
  • footer (for treetable) - defines a template for an extra table line that displays a common property of the group members.
  • row (for datatable and treetable) - defines a template for the whole row regardless of the initial columns.
//static mode
webix.ui({
    view:"chart",
    scheme:{
        $group:{
            by:"year",
            map:{/* ... */}
        }
    }
});
 
//dynamic mode
grida.group({
    by:"year",
    map:{/* ... */}
});

If you use only the by property in the group object, it is the same as if you defined a criterion with a data item or a function. Hence, the following definitions are equal:

$group:"year"
 
$group: function(obj){ return obj.year;}
 
$group:{ by:"year" }
 
$group:{
    by: function(obj){ return obj.year }
}

The function can be of any complexity:

by: function(obj){ return Math.floor(obj.year/10); }

Related sample:  Static Grouping

Grouping Functors

Grouping functors are used within map and footer that redefine templates.

$group:{
    by:"year",
    map:{
        property(template):['property', functor]
    }   //or property:[function (obj){...}]
}

Grouping functors:

  • any to show all values for this property; the same effect will be set if you omit the functor at all.
  • sum to calculate and show the total sum of the values;
  • max to pick the maximum value of the property;
  • min to pick the minimum value of the property;
  • count to calculate the number of items in each group;
  • string to show the property name as a string rather than its value. For {year:1994}, the word "year" will be displayed in the component;
  • custom functor for other math operations.

Learn more about grouping functors.

Grouping of Missing Values

If you data set contains data items with missing values ("", null, false or undefined) and you are grouping by that value, there are several options for you to follow:

  • (default) show records with missing values after the grouped records
$$("grid").group({
    by:"year",
    row:"title",
    missing:true    //do not need to state, this is default
});
  • hide records with missing values
$$("grid").group({
    by:"year",
    row:"title",
    missing:false
});
  • create a separate group for the records with missing values
$$("grid").group({
    by:"year",
    row:"title",
    missing:"Other"
});

If the name of the group is the same as the name of the groups that already exist, the records with missing values will be appended to that group.

Related sample:  Treetable: Grouping for Missed Items

Templates for Groups and Items

By default, component items display the object value which is:

  • the value property of the data source;
  • the value from the dataset defined by template;
  • the value of an array element (if data comes in JSArray);

For items that display group headers (parent branches), the value is the one defined by the by criterion:

webix.ui({
    view:"tree",
    scheme:{
        $group:{
            by:"year" // group header value
        }
    },
    data: [  //default value
        { id:1, value:"The Shawshank Redemption", year:1994,
            votes:678790, rating:9.2, rank:1},
        {/* ... */}
    ]
});

Related sample:  Tree: Plain JSON Data with Client-side Grouping

If a component has a template that sets other property as the value for its items, you need to define a map to redefine this value during grouping:

webix.ui({
    view:"tree",
    template:'{common.icon()} {common.folder()}#title#',
    scheme:{
        $group:{
            by:"year",
            map:{
                title:["year", "any"]
            }
        }
    }
});

Initially, the template allows showing titles only. The map tells that group title is year and makes "year" the value to show.

Each component has its own peculiarities concerning templates in the grouped mode. Study them below.

In addition, you can change the displayed data by defining a function for the "onGroupCreated" event.

DataTable and TreeTable Specificity

For DataTable and TreeTable data templates are set by column IDs, or column templates, or both.

Yet, in the grouped mode, the grids won't show data for those columns the templates of which are not set either by a template function or by a map.

A map can redefine more than one property.

map:{
    votes:["votes", "sum"], // "votes" values will be summed up
    title:["year"] // title will take year values
}

Related sample:  Data Grouping: Aggregate Values

Function templates allow setting different presentation ways for group items and group headers.

  • obj.$group - stores the item state (whether it is a group item or a group header).
  • obj.$level - stores the hierarchy level with the upper one being 1.

The two snippets produce the same result:

template: function(){
    if(obj.$group) return /* ... */; //for group headers
    return /* ... */ //for group items
}
 
 
template: function(){
    if (obj.$level==1) return /* ... */; //for group headers
    return /* ... */ //for group items
}

A treetable with a defined map:

columns:[
    {
        id:"title", template:function(obj, common){
            if (obj.$group)
                return common.treetable(obj, common) + obj.title;
            return obj.title;
        }
    }
],
scheme: {
    $group:{
        by: "year",
        map:{
            title:["year"] //year is 'obj.title' for group header
        }
    }
}

Related sample:  Data Grouping: Aggregate Values

A treetable without a map:

columns:[
    {
        id:"title", template:function(obj, common){
            if (obj.$level == 1)
                return common.treetable(obj, common) + obj.value;
            return obj.title;
        }
    }
],
scheme:{
    $group:"year" //year is obj.value for group header
}

Related sample:  Static Grouping

Row Templates and Additional Rows

Row template

The row property within the grouping object sets the group header template for the whole row regardless of grid columns.

grida.group({
    by:"year",
    map:{
        "year":["year",  "any"],
        "votes":["votes", "sum"]
    },
    row:function(obj, common){
        return "Year "+obj.year+", total votes: "+webix.i18n.numberFormat(obj.votes);
    }
});

Related sample:  Data Aggregation in Custom Format

Group Footer

The group footer displays a common value for all the members of this group, e.g. total sum, max or min value, etc.

Footers are defined in the same way as map and include the chosen property treated with a functor and a template for the footer row.

grida.group({
    by:/* ... */,
    map:{/* ... */},
    footer:{
        votes:["votes", "sum"],
        row:function(obj, common){
            return "<span style='float:right;'>Total: "
            +webix.i18n.numberFormat(obj.votes)+"</span>"
        }
    }
});

Related sample:  Dynamic Grouping: Extra Lines

Chart Specificity

For a grouped chart grouped items and group headers correspond to each of its axes:

  • X-axis values display the grouping criterion (introduced by by);
  • Y-axis values display the chosen values of group items treated with a functor.

For instance, if you need to display sales of each company, you should group all sales values by company while calculating the sum for each company.

webix.ui({
    view:"chart",
    value:"#sales#", // the default template for Y-axis
    scheme:{
        $group({
            by:"company", // the template for X-axis
            map:{
                sales:["sales","sum"]
            } //Y-axis template now shows the sum of all sales for each year
        });
    }
});

Related sample:  Grouping

The first element in the map is a data item property, while the second one is a functor applied to all the values of this property.

GroupList Specificity

GroupList features its own properties:

  • templateItem - to set a template for items in a group;
  • templateGroup/templateBack - to set a template for the group header.

See the GroupList article for details.

Grouping by Several Values

You can group data by several values.

Treetable

To group Treetable by several columns, call the group() method for one more time and pass the ID of the branch for grouping as the second parameter:

// first grouping
grida.group({
    by:function(obj){ return obj.unitName + "-" + obj.tagName },
    map:{
        value:["unitName"],
        tagName:["tagName"],
        samples:["samples","sum"]
    }
});
 
//second grouping
grida.group({
    by:"tagName",
    map:{
        value:["tagName"],
        unitName:["unitName"],
        samples:["samples","sum"]
    }
}, 0);

Related sample:  Treetable: Multiple Grouping

Datatable

To group Datatable by several columns, pass true as the second parameter of the group() method:

grida.group({
    by:function(obj){ return obj.unitName + "-" + obj.tagName },
    map:{
        value:["unitName"],
        tagName:["tagName"],
        samples:["samples","sum"]
    }
});
 
grida.group({
    by:"tagName",
    map:{
        value:["tagName"],
        unitName:["unitName"],
        samples:["samples","sum"]
    }
}, true);

Grouping Events

  • onGroupCreated - fires when grouping is applied to the component and takes the following parameters:
    • id - the ID of the group header (parent branch);
    • value - the value of the grouping criterion;
    • data - the component data object;
  • onGroupFooter - fires when grouping is applied to the component and a footer for each group is provided.
webix.ui({
    view:"treetable",
    on:{
        "data->onGroupCreated":function(id, value, data){
            this.item(id).value = "Year "+value;
        }
    }
});
 
grida.group({
    by:"year" //obj.value = year
});

Related sample:  Handling Events while Data Grouping

Back to top