Tutorial: Working with Events

Working with component emitted events in Viuzly is very straightforward.

There are three types of events that you can capture using a Vizuly component

  1. User Interaction Events: Each Vizuly component can emit one or more user interaction events depending on the specific component. Events like mouseover, mouseout, click are examples of these types of events. By capturing and responding to these events developers are able to have other parts of their application respond to Vizuly interaction events.

  2. Property Change Events: Each time the value of any Vizuly component public property changes an event is emited that the developer can capture and respond to. For instance, you might want to employ some special logic when one of the properties of the component has been changed by some part of the application. For instance, if the data property is updated by some part of the application, another part of the application might want to make updates based on that data change.

  3. Component Lifcycle Events: All Vizuly components share a common set of internal lifecycle events that are documented under the Vizuly factory component. These events are emited after each of the components primary internal methods have been called and give the developer an opportunity externally modify public component features during the render pipeline process. Caputuring and responding to these events are usually used in more advanced techniques and are discussed in more detail within our Custom Components tutorial.

User Interaction Events

Capturing and responding to user interaction events is very straightforward using the simple viz.on() syntax. Each component will pass its own unique (and documented) arguments into a listener function, but generally the arguments are as follows:

Parameter Description
e DOMElement that triggered the interaction event.
d The datum associated with the event.
i The index of the datum in its parent series.
j The group index (optional) that the datum belongs to.

For instance, lets say we wanted to capture the mouseover event on one of the bars of the Bar Chart component. We would write code that looks like this:

   viz.on('mouseover', function (e, d, i, j) { 
   	console.log (d.country + ' won ' + d.value + ' ' +  d.category + ' medals of group ' + j) 
   })

which would create a console output like the below depending on where you placed your mouse.

USA won 1072 Gold medals of group 0
GB won 284 Bronze medals of group 2
GER won 252 Gold medals of group 3

This is a very simple example and you can clearly create event handlers that do something more sophisticated than logging to the console.

Property Change Events

Like user interaction events, property change events follow a similar syntax but instead of using the on watcher we use the onChange watcher which passes both the old and new property values to the listener function. Here is an example that shows how we might capture the width property change.

 viz.width(500)
    .onChange('width',function (newValue, oldValue) { console.log('old width = ' + oldValue + ' new width = ' + newValue);})
    .width(800)

The code above would produce the following output in the console at runtime:

old width = 500 new width = 800

Using property change events can be helpful in more complex web applications frameworks like React or Angular where certain component properties use some type of binding mechanism update property values automatically and the developer can not anticipate all of the logic flow that would potentially update a property value. In these cases, using the onChange watcher to listen for these property changes gives the developer a robust means for responding to component property changes that could come from multiple sources.

Component Lifecycle Events

Each Vizuly component follows a universal and discrete set of component lifecycle events from initial creation, rendering, and decoupling/destruction of the component. Below is a list of these lifecycle events that can be intercepted by the developer.

Vizuly Component Lifecycle

  1. Initialize: var viz = vizuly2.viz.BarChart(parentDomElement)
  2. Update: viz.update() when update is called the following methods are triggered internally.
    1. component.validate()
    2. component.measure()
    3. component.update()
    4. component.applyStyles()
  3. Destroy: viz.destroy()

The associated events which can also be captured using the viz.on syntax are initialized, validated, measured, updated and styled. Note, there is no event emited for viz.destroy() as the component becomes decoupled and garbage collected.

Sometimes it is helpful to intercept these events and override some of the properties and values that the component is acting upon. For instance, a common use case can be altering the automatically calculated scales used in rendering a chart. For instance, the Bar Chart component automatically sets the minimum value used for the x axis scale domain as '0' and the maximum value as the highest value of all datums across all series. But perhaps, instead of wanting to use these automated values for the x axis, you may want to set it your own. Since the measure() method is responsible for calculating scales we will intercept this event and modify the Bar Charts x-scale on the fly.

 viz.on('measured',function () { viz.xScale().domain([50,5000]); })

In this code, we listen on the measured event and then used the Bar Chart's publicly accessible xScale property to reset the scales domain. You will notice that when we reference the public property viz.xScale() we use the () to access its current value. The result of this operation can be seen below where we have extended the scales range to be between 50 and 5000.

Vizuly Bar Chart Measure

Intercepting component lifecycle events and updating internal component properties is a more advanced technique, and in some cases could be considered an 'anti-pattern' as it requires you to have advanced knowledge of how the component uses internal properties and this violates many Object Oriented design patterns such as Encapsulation and Single Responsiblity Principle. The advantage of this technique though, is that it allows you to handle un-anticipated use cases without having to modify the source code of a given component with conditional code to handle your unique use case. By using this design pattern it allows you to keep the implementation specific details of your modification external to the core component and closer to the code that requires these modifications. So instead of creating three different versions of a Bar Chart that modify the measure() code to handle your unique scale requirements, you can leverage one version of the Bar Chart, and have three different implementations that intercept the measured event to apply your unique scale logic.