Registering Side Effects

In this section, we will learn about registering custom side effects. By registering our own side effects we can have any kind of visual change we need when the associated behaviour changes.

Let us understand more about registering side effects using the cars.json data.

How to register a Sideeffect?

We can register a side effect by extending the Spawnable Side Effect or the Surrogate Side Effect and defining our own side effect.

To recall,

  • A Spawnable Side Effect is an effect which creates/removes elements from the visualisation(the context)
  • A Surrogate Side Effect is an effect which modifies elements present in the visualization but doesn't add/remove DOM elements

We will see how we can register both of them separately

Registering a Spawnable Side Effect

Registering a spawnable sideeffect allows to add/remove elements from the canvas.

Getting the base class

We can register a Spawnable Side Effect by extending theSpawnableSideEffect class in Muze. This base class contains the properties required for us to register a Spawnable Side Effect :

SpawnableSideEffect = Muze.SideEffects.standards.SpawnableSideEffect

We also need the ActionModel which handles the interaction model for Muze:

const ActionModel = muze.ActionModel

Using the base class to create a new side effect

We will understand how to create a new spawnable side effect by registering a new side effect that displays the set of selected values whenever anything is selected in the chart.

ActionModel
    .registerSideEffects(
            class TextSideEffect extends SpawnableSideEffect {
                 static formalName() {
                        return 'selection-text'
                 }
            }
    )

You start by creating a new class by extending the SpawnableSideEffect class. Here, we have created the TextSideEffect class.
Thereafter, we have to give a formal name for the side effect so that we can uniquely identify it.

This formal name is very important as it identifies the side effect.

logo

Note

Every side effect created is a unique side effect and Muze only generates instances of that effect to put use every where in the canvas(es).

Using the side effect properties

Once we have the class, we need to add our own side effect implementation to this class. To do this, Muze provides a set of methods and utilities which are useful when applying the effect. All of these methods are avaialble in the apply method available from the base class. This is the method that is triggered whenever the behaviour attached to the side effect changes. Some of them are given below:

ActionModel
.registerSideEffects(class TextSideEffect extends SpawnableSideEffect {
    static formalName() {
        return 'selection-text';
    }

    apply(selectionSet) {
    /* Getting the datamodel */
       const dataModel = selectionSet.mergedEnter.model;

    /* Getting the Drawing Context */
       const drawingContext = this.drawingContext();

    /* Getting the side effect drawing area */ 
       const sideEffectGroup = drawingContext.sideEffectGroup;

    /* Getting the html container of the drawing area */ 
       const htmlContainer = drawingContext.htmlContainer;

   }
})

Getting the DataModel

The base class, SpawnableSideEffect provides the selectionSet which can be used to create a side effect. This set contains all the different kinds of selection and rejection sets, i.e, New Entry, Old Entry, New Exit and Old Exit sets.

Additionally, it also contains the Merged Enter and the Merged Exit sets, which are basically the selection and rejection sets respectively.

In order to get the side effect, you need to apply it using the DataModel available in either of the sets available to you.

For our example, we'll use the DataModel for the mergedEnter entry set so that we always get all the values in the selection sets.

Getting the Drawing Context

Next, we'll get the drawing context, i.e., the context of the canvas where the sideeffect will be applied:

const drawingContext = this.drawingContext();

The drawing context contains the information of where in the canvas the side effect will be added to and where the html container containing the canvas lies. To use the side effect group, where the side effect can be added to, simply use it from the drawingContext:

const sideEffectGroup = drawingContext.sideEffectGroup;

Applying the side effect

Everytime the behaviour attached to this sideeffect changes( for our example, when we select values), the apply method applies this side effect. As such, on the behaviour change, we'll have access to the selection set and rejection sets and we can add/remove any elements from the visualization based on the information in these sets.

Here's an example where we've added text to be appended every time the side effect is applied:

    apply(selectionSet) {
       const dataModel = selectionSet.mergedEnter.model;
       const drawingContext = this.drawingContext();

       const textGroups = this.createElement(drawingContext.sideEffectGroup, 'text', [1]);

       /* createElement is a utility method for side effects */
       textGroups.html(`Selected:${dataModel.getData().data.map(e => e.join(', '))}`);

       textGroups.attr('y', '30');
       return this;
   }

Here, we have appended a text element to the sideEffectGroup. The this.createElement is a utility function that enables you to create elements quickly.

Note: This utility function makes use of d3 selections. So before you use it, please understand the General Update Pattern.

We have now registered a side effect and we need to map it in order to use it.

main
run-button
run-button
reset-button
        canvas
          .width(600)
          .height(400)
          .rows(['Horsepower'])
          .columns(['Origin'])
          .data(dataModelInstance)
          .mount(node);
       

Registering a Surrogate Side Effect

Registering a surrogate sideeffect allows to modify the elements present in the canvas.

Getting the base class

We can register a Surrogate Side Effect by extending the SurrogateSideEffect class in Muze. This base class contains the properties required for us to register a Surrogate Side Effect :

SurrogateSideEffect = Muze.SideEffects.standards.SurrogateSideEffect

We also need the ActionModel which handles the interaction model for Muze:

const ActionModel = muze.ActionModel

Using the base class to create a new side effect

We will understand how to create a new surrogate side effect by registering a new side effect that displays the set of selected values whenever anything is selected in the chart.

ActionModel
    .registerSideEffects(
            class StrokeSideEffect extends SurrogateSideEffect {
                 static formalName() {
                        return 'stroke-effect'
                 }
            }
    )

You start by creating a new class by extending the SurrogateSideEffect class. Here, we have created the StrokeSideEffect class.
Thereafter, we have to give a formal name for the side effect so that we can uniquely identify it.

This formal name is very important as it identifies the side effect.

logo

Every side effect created is a unique side effect

Every side effect created is a unique side effect and Muze only generates instances of that effect to put use every where in the canvas(es).

Using the side effect properties

Once we have the class, we need to add our own side effect implementation to this class. To do this, Muze provides a set of methods and utilities which are useful when applying the effect. All of these methods are avaialble in the apply method available from the base class. This is the method that is triggered whenever the behaviour attached to the side effect changes. Some of them are given below:

ActionModel
.registerSideEffects(class StrokeSideEffect extends SurrogateSideEffect {
    static formalName() {
        return 'stroke-effect';
    }

    apply(selectionSet) {
       // Getting the selection and rejection sets
       const { completeSet, mergedExit, mergedEnter } = selectionSet;

      // Getting the context, i.e., each of the unit instances present in the canvas
       const context = this.firebolt.context;

      // Getting the set of  layers associated to a particular unit instance
       const layers = context.layers();
   }
})

Getting the Selection and Rejection sets

The base class, SurrogateSideEffect provides the selectionSet which can be used to create a side effect. This set contains all the different kinds of selection and rejection sets, i.e, New Entry, Old Entry, New Exit and Old Exit sets.

Additionally, it also contains the Merged Enter and the Merged Exit sets, which are basically the selection and rejection sets respectively. Together they make up the Complete Set, which is also available as shown in the code below:

  const { completeSet, mergedExit, mergedEnter } = selectionSet;

Each set has a unique set of row ids based on the DataModel attached to it and can be referred to as following:

const uniqueIds = mergedEnter.uids;

Getting the context

The context for a Surrogate Side Effect is the instance based on which the sideeffect will be applied. As such, it provides each of the unit instances present in the canvas:

   const context = this.firebolt.context;

Each unit is made up of a set of layers. You can use the properties of these layers to modify the visual properties of the canvas. Each layer provides a set of methods and properties based on which you can modify said properties. We will now see how to use the layers to do so.

Applying the side effect

Everytime the behaviour attached to this sideeffect is attached( for our example, when we select values), the apply method applies this side effect on each of the unit instances in the canvas.

As such, when this behaviour changes, we'll have access to the selection and rejection sets and we can modify the required properties using the layers associated with the unit.

    apply(selectionSet) {
       const { completeSet, mergedExit, mergedEnter } = selectionSet;
       const context = this.firebolt.context;
       const layers = context.layers();
       layers.forEach((layer) => {
          const allElements = layer.getPlotElementsFromSet(completeSet.uids);
          allElements.style('stroke', '').style('stroke-width', 2);
          const enterElements = layer.getPlotElementsFromSet(mergedEnter.uids);
          enterElements.style('stroke', 'red');
          const exitElements = layer.getPlotElementsFromSet(mergedExit.uids);
          exitElements.style('stroke', 'green');
      });
   }

Getting plot elements from set

The getPlotElementsFromSet gets the elements attached to the respective part of the selection(or rejection sets). For instance,

layer.getPlotElementsFromSet(mergedEnter.uids): provides the elements attached to all the points that are present in the selection set,

layer.getPlotElementsFromSet(mergedExit.uids): provides the elements attached to all the points that are present in the rejection set

layer.getPlotElementsFromSet(completeSet.uids): provides all the plot elements in the layer.

Applying desired styles and/or attributes

Since the elements in the plot are SVG elements, you can apply either styles and/or SVG attributes to the plot elements. For instance, in the example here, we have added a stroke to all the bars based on whether they are present in the selection set or the rejection set:

enterElements.style('stroke', 'red');
exitElements.style('stroke', 'green');

Now, everytime you select bar(s) on the chart, the strokes on the respective bars will appear as red or green based on whether they are selected or not.

main
run-button
run-button
reset-button
        canvas
          .width(600)
          .height(400)
          .rows(['Horsepower'])
          .columns(['Origin'])
          .data(dataModelInstance)
          .mount(node);
       

Wrapping Up

In this section we gave an overview of what it takes to add side effects to the canvas. To put it in brief:

  • We can choose to add our own custom side effects to the canvas
  • These side effects can either be Spawnable(adding/removing elements) or Surrogate(modifying elements)
  • To add any side effect, a formal name has to be provided which uniquely defines the side effect
  • To add a side effect, the selection set is available in the apply method which contains all the selection and rejection subsets like New Entry Set, Old Entry Set, New Exit Set, Old Exit Set, Complete Set, Merged Enter Set and Merged Exit Set
  • To add a Spawnable Side effect, the drawing context is available with respect to the canvas where you can add/remove elements in either the sideEffectGroup or the htmlContainer
  • To add a Surrogate Side effect, the context is available which is essentially each of the units attached to it and the layers that bind them
  • The getPlotElementsFromSet helps in getting elements attached to a particular selection set and elements can be modified using this set