Using the Context API

The Context API exposes a service that can be injected in any Angular component. For more details, please refer to Context API documentation.

It allows storing the application state in a serializable object. It contains:

  • A list of Selected Scenarios

  • A list of Selected entities by type (optional)

  • A list of Filters (optional)

This context is sent as a parameter of the Data Service requests when you are opening a View or a Dashboard. Each time the user modifies the context by modifying Scenario/Entities selection, or changing filters, the context is updated, saved through the Scenario Service, and all widgets are notified about the Context update.

As stated above the Platform provides a notion of selected entities that are propagated to the web client elements that support this feature. There are currently three modes supported in the Platform :

  • selection: a widget supporting this mode will be able to be the source of selection, driving the selection for the web client.

  • filter: in this mode a widget is supposed to only take into account entities provided in context selection.

  • highlight: in this mode the widget can highlight the selected entities, for instance, with another color.

A widget can take part in this behavior by implementing GeneWidgetSelectionAware interface in a way similar to this for instance

export class SampleWidgetComponent implements GeneWidgetSelectionAware {

    // ...


    /**
     * Return among the context selections, the supported selection(s).
     * Returns an empty array if current selection is not supported (has no impact on data) on
     * the current widget state.
     *
     * @param context current context containing a selection
     */
    getSupportedSelection(context: GeneContext): GeneContextSelection {
        // simple utility method filtering entities by the given type
        return GeneSelectionUtils.getSubSelection(context.selection, this._widgetConfiguration.dataType);
    }

    /**
     * declare which selection mode this widget supports
     */
    getSupportedSelectionModes(): SelectionMode[] {
            return ['select', 'filter', 'highlight'];
    }
    /**
     * Get the current selection mode of the widget
     */
    getSelectionMode(): SelectionMode {
            return  this.selectionMode;
    }


    /**
     * Set the current selection mode of the widget
     * @param mode
     */
    setSelectionMode(mode: SelectionMode) {
        // store the selection mode and take it in account when loading data
        this.selectionMode = mode;
        // request reload of widget data
        this.requestLoadData();
    }
    // ...
}

It is important to understand that the selected entities provided by the context API are based on what we call Business Keys, it is a combination of all the properties that allow to describe a Business Entity instance in a unique way. This is implementation is chosen among other reasons, to allow to compare same entities across different scenarios, without relying on unique generated identifiers.

When you manipulate entities obtained by the Data API, those objects do not contain business key metadata by default.

For this reason if you want to compare data in a custom component against entities provided by Context API you have to enhance them using the MetaData API, see this example of GeneBaseDataWidget.loadData() implementation which takes into account the current selectionMode and populates all the entities with relevant business key information to be used for Context Selection support.

import { INTERNAL_BK_TOKEN_FIELD } from '@gene/data';


    loadData(context: GeneContext): Observable<MyCustomType[]> {


        const metaModel$ = this.metaDataService.getMetaModel();
        const dataObjects$ = this.dataApiService.runQuery<T[]>(context, this._widgetConfiguration.entityTypeName);


        return forkJoin([dataOjects$, metaModel$]).pipe(
                flatMap(([dataOjects, metaModel]) => {
                    const typeMetaData = metaModel.getTypeMetaData(this._widgetConfiguration.entityTypeName);
                        .pipe(
                            // Important! we need to "enrich" other events with business key information in order to support filtering and highlighting
                            tap((dataOjects: T[]) => dataOjects.forEach(e => metaModel.enrichEntityWithBusinessKeyInformations(e, type, (o) => this.colorService.getHexColorForObject(o)))),
                        );
                })
            );
    }



// ...
    refreshSelection() {
        if (this.selectionMode === 'highlight' || this.selectionMode === 'select') {
                this.contextService.context.pipe(first()).subscribe(c => {
                    const currentType = this._widgetConfiguration.dataType;
                    const selection = GeneSelectionUtils.getSubSelection(c.selection, currentType);

                    // grid options.api is the api to interact with ag-grid tables
                    this.gridOptions.api.forEachNode(node => {
                            // here is the important part: retrieve the business key token from the entities displayed in the widget
                            const nodeBKinfo = node.data[INTERNAL_BK_TOKEN_FIELD];
                            // compare the business key to all the entities in current context selection
                            const selected = selection.selectedEntities[currentType].indexOf(nodeBKinfo) >= 0;
                            // select the node accordingly
                            node.setSelected(selected);
                        });

                }
            );
        }
    }


    setSelectionMode(mode: SelectionMode) {
        // store the selection mode and take it in account when loading data
        this.selectionMode = mode;
        // request reload of widget data
        this.refreshSelection();
    }

// ...