Intro Reactive

Making changes with reactive json objects

Our reactive programming system allows you to easily make changes to your chart after it has been created. Let's consider following simple bar chart:


  var series = {
    id: "series id",        
    data: [1, 5, 6, 2],
    shape: "column"
  }
  var chart = ReactiveChart.chart({
    element: "%chart_element_id%",
    type: "x",
    xAxis: {            
      type: "discrete",
      categories: ["A", "B", "C", "D"]
    },
    series: [series]
  });
Let's say we want to change the data and id of the series after creating the chart. To do this, we first make the series configuration object reactive using the reactive function:

var series = ReactiveChart.reactive({
    id: "series id",
    data: [1, 5, 6, 2]
});
If we change the data and id properties of our series configuration after the chart was created, the chart will be automatically updated to reflect the new configuration:.

  series.data = [2, 4, 6, 8];
  series.id = "new series id";

  var series = ReactiveChart.reactive({
    id: "series id",
    data: [1, 5, 6, 2],
    shape: "column"
  })
  var chart = ReactiveChart.chart({
    element: "_chart5",
    type: "x",
    xAxis: {            
      type: "discrete",
      categories: ["A", "B", "C", "D"]
    },
    series: [series]
  });

  series.data = [2, 4, 6, 8];
  series.id = "new series id"

Reactive collections

In the previous example, we've changed the data of the series by changing the data property of it's configuration. If we just want to add or remove data points from an existing series, it's more performant to use a RingBuffer as our data source:

  var data = ReactiveChart.reactive.ringbuffer.xsorted([{x: 0, y: 1}, {x: 1, y: 5}, {x: 2, y: 6}, {x: 3, y: 2}]);
  var series = ReactiveChart.reactive({
    id: "series id",
    data: data,
    shape: "column"
  })
  var chart = ReactiveChart.chart({
    element: "_chart6",
    type: "x",
    xAxis: {            
      type: "discrete",
      categories: ["A", "B", "C", "D"]
    },
    series: [series]
  });
  data.push({x: 4, y: 12});

A lot of configurations, like layout, use a ReactiveArray as collection:

  var layout = ReactiveChart.reactive.array([{
    position: "bottom",
    component: "axis-labels"
  },{
    position: "left",
    component: "axis-labels"
  }]);
  ReactiveChart.chart({
    element: "_chart8",
    type: "x",
    xAxis: {            
      type: "discrete",
      categories: ["A", "B", "C", "D"]
    },
    series: [{
      id: "series id",
      data: [3, 6, 3, 2],
      shape: "column"
    }],
    layout: layout
  });
  layout.push({
    position: "top",
    component: {
      type: "label",
      text: "Added after chart creation"
    }
  });

Sharing reactive objects

Reactive objects can be shared between charts or custom shapes. For example, we can create a new window object and share it between 2 different charts:
  var wind = ReactiveChart.reactive({start: 0, end: 4});
  var chart1 = ReactiveChart.chart({
    element: "_chart10_1",
    type: "x",
    xAxis: {            
      type: "discrete",
      categories: ["A", "B", "C", "D"],
      window: wind
    },
    series: [{
      id: "series id",
      data: [3, 6, 3, 2],
      shape: "column"
    }]
  });
  var chart2 = ReactiveChart.chart({
    element: "_chart10_2",
    type: "x",
    xAxis: {            
      type: "discrete",
      categories: ["E", "F", "G", "H"],
      window: wind
    },
    series: [{
      id: "series id",
      data: [6, 2, 4, 8],
      shape: "column"
    }]
  });
If you change the window object, both charts will show the new interval you set. By zooming into either chart, the other chart will be zoomed in too as they both share the same window. You can also check that both charts are using the same window objects like follows:

chart1.xAxis.primary.window === chart2.xAxis.primary.window; //Returns true

Changing reactive objects in batches

Changing a property of a reactive object causes the chart to recalculate or redraw objects that depend on this particular property. For example, adding 5 new data points into a series will cause its domain to be recalculated 5 times. To avoid this inefficiency, we can add the 5 points inside a reactive transaction as follows:

ReactiveChart.reactive.inTransaction(function(){
    data.push({x: 1, y: 1});
    data.push({x: 2, y: 2});
    data.push({x: 3, y: 3});
    data.push({x: 4, y: 4});
    data.push({x: 5, y: 5});
});

This will cause the domain to be recalculated only once.

Another use case of a reactive transaction is consistency. For example, changing the window of the x-axis from position (0, 10) to position (5, 2) will cause the intermediate position (5, 10) to be observed by dependent functions if you first change the start position. Changing the window position inside a reactive transaction will avoid the intermediate results to be observed by other dependent functions.

Observing reactive objects

You can observe reactive objects by using a reactive procedure. Let's print the window coordinates at the top of the chart:
  var windowShape = ReactiveChart.reactive({
    tag: "html",
    x: 0,
    y: 0,
    html: ReactiveChart.reactive({
      tag: "span",
      style: {
        whiteSpace: "nowrap"
      },
      child: ""
    })
  });
  var chart = ReactiveChart.chart({
    element: "_chart14",
    type: "x",
    xAxis: {            
      type: "discrete",
      categories: ["A", "B", "C", "D"]
    },
    series: [{
      id: "series id",        
      data: [1, 5, 6, 2],
      shape: "column"
    }],
    layout: [{
      position: "bottom",
      component: "axis-labels"
    },{
      position: "left",
      component: "axis-labels"
    },{
      position: "top",
      component: windowShape
    }]
  });
  var wind = chart.xAxes.primary.window;
  ReactiveChart.reactive.procedure(function(){
    var pos = wind.start.toFixed(2)+" - "+wind.end.toFixed(2);
    windowShape.html.child = pos;
  });

Zooming the chart will change the window, which will cause our procedure function to be rerun.