Gapminder/vizabi — pull request #46 by ambodi (2014-11-19 14:37:19+00:00)
Title: Update base/components for resize and update in print mode

We are developing tools that helps us create nice print visualizations (see http://www.gapminder.org/downloads/world-pdf/ for examples for bubble chart). In the print mode, certain functionalities are needed. One example is the fact that *all* labels whether on bubbles, bars and etc needs to be both visible on the chart and in a nice position, meaning that the user should be able to drag them around to place them somewhere that human eye can see better.

If we address the issue at tool level, having a state flag called (```print_version === true || false```), With the current codebase that we have, things start to get messy. For example, here is how the code will look like:

```js
// code from components/bubble-chart.js

this.bubbles
.append("circle")
.attr("class", "vzb-bc-bubble")
.style("fill", function(d) {
return _this.cScale(d.region);
});
// only show (append) the labels if we are in the print mode
if (this.model.show.print_version) {
this.bubbles.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) {
return d.name;
})
.attr("transform", function(d) {
return "translate(" + _this.xScale(d[_this.indicator[1]]) + "," + _this.yScale(d[_this.indicator[0]]) + ")";
});
}
```

First of all, functions start to have IFs in both render and update functions. This way that we once tried in earlier version as well can make the code messy. Also, every component will have different way to handle this.

However, if we manage to attack the issue at ```component``` level (which is the focus of this pull request), things will look way better.

1) Add print component with ```print_version``` flag (defaults to *false*):
```js
//instantiating components
this.components = [{
component: '_gapminder/header',
placeholder: '.vzb-tool-title'
}, {
component: '_examples/bubble-chart',
placeholder: '.vzb-tool-viz', //div to render
model: ["state.show", "data", "state.time"],
print_version: false
}, ... ]
```

2) This will set the print_version in base/component class:

```js
this.print_version = config.print_version || false;
```

3) Based on this flag, base/component will fire ```update_print``` and ```resize_print```:

```js

_.each(_this.components, function(component) {
if (component.print_version) {
component.resize_print();
}
else {
component.resize();
}
});
```

4) In each component, we are able to have now separated functions for both ```print`` and ```interactive``` mode:
```js
// From component/bubble-chart.js

resize: function() {
...
this.bubbles
.append("circle")
.attr("class", "vzb-bc-bubble")
.style("fill", function(d) {
return _this.cScale(d.region);
});

//update selection
this.bubbles.select("circle")
.transition().duration(speed).ease("linear")
.attr("cy", function(d) {
return _this.yScale(d[_this.indicator[0]]);
})
.attr("cx", function(d) {
return _this.xScale(d[_this.indicator[1]]);
})
.attr("r", function(d) {
return _this.rScale(d[_this.indicator[2]] || 1);
});

},

// resize for print
resize_print: function() {
...

this.bubbles = this.bubbles.enter()
.append("g");

this.bubbles
.append("circle")
.attr("class", "vzb-bc-bubble")
.style("fill", function(d) {
return _this.cScale(d.region);
});

this.bubbles.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) {
return d.name;
})
.attr("transform", function(d) {
return "translate(" + _this.xScale(d[_this.indicator[1]]) + "," + _this.yScale(d[_this.indicator[0]]) + ")";
});
```

This way, we also have plans for how to develop print version across different components. With this approach, we can also _refactor_ the common code among these two approaches (```print``` vs ```interactive```).