Skip to content

Filter Control

The Filter Control (FC) is an special visualization that adds a higher level of interaction to an app or dashboard. If we have a set of visuals and we add a FC visualization, it will automatically register and listen for any click or range operation (Time Range Filter, Time Range Picker, Range Filter) that occurs to immediately build and trigger the corresponding filter that will affect all visuals in the dashboard.

The FC is not attached to any provider or source.

1
2
3
4
5
// Define the Filter Control
cf.create().graph('Filter Control').element('chart').execute()

// Or use the shortcut for it
cf.create().fc().element('chart').execute()

The FC won’t show anything until one or many of the previously mentioned events is triggered.

Displaying Filters

As soon as we fire an event by clicking or applying a range of values the FC will start showing the respective filters. For example if we click in a Bars visualization that is grouped by “venuecity”, we’ll have the following filter reflected in the FC:

FC1

The filter displayed by itself also have a high level of interaction. The green circle allows the filter to be disabled/enabled:

FC2

The name of the field is used to remove the filter permanently:

FC3

The red square represents the operation. By default the operation is “IN” when clicking on visuals. Filters with this operation can be switched to “NOT IN” and backwards by clicking on the red square:

FC4

Range visualizations will trigger filters with operation of type “BETWEEN”:

FC5 A time range filter FC6 A metric range filter

Multiple Filters

The more visualizations interact with, the more filters we’ll have. Multigroups visualizations will trigger two or more filters at a time (Heat Map, Trend, Pivot….). They all will be joined by the operation “AND”.

FC7

Repeated filters won’t re-query the visual.

Excluding visuals

Sometimes we may want a visual not to be affected by filters, or that if we click on it, it doesn’t trigger any filter. The way to accomplish this is by setting the following options to the chart NOT to the FC.

1
2
3
4
5
// Don't be affected by any filter
myChart.set('noFilters', true)

// Don't trigger filters from it
myChart.set('noTrigger', true)

Exposed API

Sometime we may want a custom way to handle filters. Or even have a different UI to interact with filters. In any of these cases instead of handling events, multi-source and visuals validations, applying and removing filters and some other taks, we can also let the filter control to take of all that and we just need to create our custom UI.

To avoid the FC from rendering its own UI , we use the noUI option when is created:

1
cf.create().fc().set('noUI', true).element('fc').execute();
The FC API exposes several methods that allows the interaction with it:

1
2
3
// First obtain the FC component and access the API
let fc = cf.getFC();
let api = fc.get('api');

Api Methods

getFilters()

Returns the list of Filters objects that are currently applied. No parameters required.

setFilters()

Sets new filters to the FC. Takes as a parameter a list of Filters objects. These filters won't be applied and won't affect any visual until the applyFilters method is called.

1
2
3
4
5
6
let filter = cf.Filter('myfield')
                .label('My Field')
                .operation('IN')
                .value(['Value-1','Value-2']);

api.setFilters([filter])

Note

Filter objects to be used with the FC, must have the property label(), and it must match with the label of the field that we want to filter. This ensures the cross-source filtering capability.

applyFilters()

It triggers the filters that are stored by the FC. No parameters required.

removeFilter()

Removes one filter. May take two parameters: The filter object to be removed (required) and the option if the filter is permanently removed or just disabled.

1
2
3
4
5
6
7
let toRemove = api.getFilters().find(f => f._path === 'myfield');

// Remove filter and delete it from FC
api.removeFilter(toRemove) 

// Remove but don't delete so it can be re-applied
api.removeFilter(toRemove, { disable: true}) 

deleteAllFilters()

Completly removes all filters from the visualizations and from the FC. No parameters required.

updateContent()

This should be only used if we use the built-in FC UI. For example if we have created an app that when it loads y should also load any set of filters stored somewhere. In that case we need to set all the filters to the FC, apply them and then update the UI.

1
2
3
api.setFilters(storedFilters)
api.applyFilters();
api.updateContent();

setCallback()

It takes one parameter: a function. This function will be called everytime a filter affects a visual and this one (the visual) finishes the loading. The callback will be called from each affected visual.

The callback will receive as a parameter the same object received when we query data. Click here for more information.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let myCallback = function(data){
    if (data.visualization === 'Heat Map') {
        // Do something after heatmap loads
    }
}

let myCallback2 = function(data){
    // Hide a loading indicator or do something else
}

api.setCallback(myCallback);
api.setCallback(myCallback2);

Many callbacks can be executed after the visualization loads the data after the filter is applied.

Drill Hierarchy

Attribute Fields

The drill hierarchy feature allows to define a path (a list) of fields that a visual will follow according to the filter being applied on it, as long as the filter matches a field defined in that path.

So if we have a Bars that is grouped by “venuecity”, we can tell the FC that when the Bars is clicked, right after being affected by the “venuecity” filter, it should switch the group by to the next field in the path (let’s say “venuename”). This is what is called drill-in. Then, whenever the filter “venuecity” is removed, it should switch or drill-out back to “venuecity”.

Drill paths can have 2 or more field names. The main rule here is that the first field name in the path must be the current “group by” of the visual, so the FC knows what it should use as the base field for that group.

If we follow the example of the Bars explained before, we can configure a drill hierarchy like this:

1
2
3
4
5
6
7
8
9
// Define the path for the group by
let drill = {
    'barsElementId': { 
        'group1': ['venuecity','venuename', 'eventname'],
        // If the Bars is stacked or clustered, we could also configure the 2nd group:
        //'group2': ['catdesc', 'catname']
    }
}
cf.create().fc().set('drill',drill).element('chart').execute()

Let’s go into what we have:

The drill hierarchy configuration is a JSON. Each visual that we want to assign a configuration is represented by the HTML element that hosts that visual.

Single group visuals must only have the key group1 configured, as they only have one group by. Multigroups visuals on the other hand may have 1 or many groups (depending on the visual) configured by using group1, group2…. groupN, depending on how many groups we want to configure. It is NOT required to set a path to all groups, since we may only want to specify a path for the 2nd group for example.

Special drill hierarchy configuration: Pivot Table

The Pivot Table doesn't use groups, but rows and columns. This is how the drill hierarchy is defined for it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
let drill = {
    'pivotElementId': { 

        'rows': {
            // Conf for the first row
            '1': ['venuecity','venuename', 'eventname'],

            // Conf for the third row
            '3': ['state','city']
        },

        'columns': {
            // Conf for the first column
            '1': ['catgroup','catname'],
        }
    }
}

The configuration above shows how to specify a drill path for the specific rows or columns. The number represents the row or column as it was specified in the Pivot Table aql.

Note

The first row or colum is identified with 1 and not with 0 for clarity purpouses.

Time Fields

Another type of drill hierarchy can be used: Time units or granularities hierarchy.

This type of hierarchy is used with fields of type time. As we explain here, ChartFactor supports a set of granularities for time fields that we can also use as a drill path.

For time fields, we support drilling into different granularities. So let’s say we have a Trend visualization that is grouped by “saletime”, and the data is in months. So if we apply a filter from the saletime we’d like to see the filtered data in the Trend in days, and if we click a day, then the data should be represented in minutes. How cool is that?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Define the path for the group by
let drill = {
    'barsElementId': { 
        'group1': ['venuecity','venuename', 'eventname'],
        // If the Bars is stacked or clustered, we could also configure the 2nd group:
        //'group2': ['catdesc', 'catname']
    },
    'trendElementId': {
        'group1': ['MONTH','DAY', 'MINUTE'],
   }
}
cf.create().fc().set('drill',drill).element('chart').execute()

Of course, for drill paths with time, we can specify any supported time unit as long as they are in order. This means that after MONTH, we can not ask the Trend to drill into YEAR.

The other thing is that this type of visuals will only drill-in because when the respective time filter is removed they will go back to the base granularity no matter on which position in the drill path it is.

Mixed Fields

Multigroups visualizations can group by an attribute and a time field at the same time. In these cases the FC will ignore any configuration specified for the time field and will only use the drill path for the attribute field if any.