[REF] web: Use graph renderer to render pie_chart widget

Before this rev., the pie_chart widget was rendered from scratch
with nv.d3 and we had duplicated code.
In comparison mode, two widgets were instancied and it was a bit
difficult to synchronize the size of these svg graphs.

nv.d3 graphs are attached when the dom is ready because it needs
to calculate the available space. UI bugs can appear if the dom
changes... .update() must be executed on the graph to avoid this
as we already do in graph renderer.

So now, the rendering of the pie_chart widget is done by the
graph renderer.
As comparaison mode is supported by the rendering, only one
widget needs to be instancied.

Task ID: 1835644
This commit is contained in:
Adrien Dieudonne
2018-09-20 13:30:50 +02:00
parent b5a12b4aac
commit 067754f0f6
3 changed files with 60 additions and 153 deletions
+1
View File
@@ -25,6 +25,7 @@ This module provides the core of the Odoo Web Client.
"static/src/xml/base.xml",
"static/src/xml/kanban.xml",
"static/src/xml/menu.xml",
"static/src/xml/pie_chart.xml",
"static/src/xml/rainbow_man.xml",
"static/src/xml/report.xml",
"static/src/xml/web_calendar.xml",
+49 -153
View File
@@ -4,187 +4,83 @@ odoo.define('web.PieChart', function (require) {
/**
* This widget render a Pie Chart. It is used in the dashboard view.
*/
var ajax = require('web.ajax');
var config = require('web.config');
var core = require('web.core');
var Domain = require('web.Domain');
var viewRegistry = require('web.view_registry');
var Widget = require('web.Widget');
var widgetRegistry = require('web.widget_registry');
var GROUPABLE_TYPES = ['many2one', 'char', 'boolean', 'selection', 'date', 'datetime'];
var qweb = core.qweb;
var PieChart = Widget.extend({
className: 'o_pie_chart',
cssLibs: [
'/web/static/lib/nvd3/nv.d3.css'
],
jsLibs: [
'/web/static/lib/nvd3/d3.v3.js',
'/web/static/lib/nvd3/nv.d3.js',
'/web/static/src/js/libs/nvd3.js'
],
xmlDependencies: ['/web/static/src/xml/pie_chart.xml'],
/**
* override
*
* @override
* @param {Widget} parent
* @param {Object} record
* @param {Object} node
* @param {Object} node node from arch
*/
init: function (parent, record, node) {
this._super(parent);
this._super.apply(this, arguments);
this.record = record;
this.model = record.model;
var modifiers = node.attrs.modifiers;
var domain = record.domain.concat(
Domain.prototype.stringToArray(modifiers.domain || '[]'));
var arch = qweb.render('web.PieChart', {
modifiers: modifiers,
});
if (node.attrs.modifiers) {
this.measure = node.attrs.modifiers.measure || '';
this.measureField = this.record.fields[this.measure];
this.groupBy = node.attrs.modifiers.groupby.split(':')[0] || '';
this.interval = node.attrs.modifiers.groupby.split(':')[1];
this.subViewParams = {
context: record.context,
domain: domain,
groupBy: [],
modelName: record.model,
withControlPanel: false,
isEmbedded: true,
mode: 'pie',
title: modifiers.title || modifiers.measure || '',
};
if (!_.contains(Object.keys(this.record.fields), this.groupBy)) {
return;
}
this.groupByField = this.record.fields[this.groupBy];
this.groupByType = this.groupByField.type;
this.title = node.attrs.modifiers.title || this.measure || '';
this.trueLabel = node.attrs.modifiers.trueLabel || 'True';
this.falseLabel = node.attrs.modifiers.falseLabel || 'False';
}
this.viewInfo = {
arch: arch,
fields: record.fields,
viewFields: record.fieldsInfo.dashboard,
};
},
/**
* override
* Instantiates the pie chart view and starts the graph controller.
*
* @override
*/
willStart: function () {
var self = this;
this.data = [];
var def1 = this._super.apply(this, arguments);
var query = {
model: this.model,
method: 'read_group',
domain: [],
groupBy: [this.groupBy + (this.interval ? ':' + this.interval : '')],
fields: [this.measure],
lazy: false,
};
// Handle case where measure attribute is missing.
query.fields = this.measure ? [this.measure] : [];
return $.when(this._rpc(query), ajax.loadLibs(this)).then(
function (result) {
if (result) {
if (_.contains(GROUPABLE_TYPES, self.groupByType)) {
for (var i = 0; i < result.length; i++) {
var value = result[i][self.measure];
var label = undefined;
var interval = self.interval ? ':' + self.interval : '';
var groupby = self.groupBy + interval;
switch (self.groupByType) {
case 'boolean':
label = ['False', 'True'][i];
break;
case 'many2one':
label = result[i][groupby][1];
break;
default:
label = result[i][groupby];
}
self.data.push({
'label': label,
'value': value,
});
}
}
}
});
},
/**
* This method ensure that Pie Chart is attached to DOM before being rendered.
*
* If we don't do that, Pie Chart can be rendered without being aware of the
* size of its container, it is then rendered with a size of 0 0 and remains
* invisible (even with a good lens).
*/
on_attach_callback: function() {
this._render();
},
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @private
*/
_render: function () {
// Note: The rendering of this widget is aynchronous as NVD3 does a
// setTimeout(0) before executing the callback given to addGraph.
var self = this;
if(!this.data || !_.isArray(this.data)) {
return;
}
var $label = this._renderLabel(this.title);
this.$el.empty();
$label.appendTo(this.$el);
$('<svg width=auto height=auto>').appendTo(this.$el);
var legend_right = config.device.size_class > config.device.SIZES.XS;
var legendPosition = legend_right ? 'right' : 'top';
var color = d3.scale.category10().range();
nv.addGraph(function () {
self.chart = nv.models.pieChart()
.x(function(d) { return d.label })
.y(function(d) { return d.value })
.legendPosition(legendPosition)
.labelType('percent')
.showLabels(true)
.showLegend(true)
.color(color);
self.chart.legend.rightAlign(false);
self.chart.legend.align(true);
self.chart.legend.expanded(true);
d3.select(self.$('svg')[0])
.datum(self.data)
.transition().duration(0)
.call(self.chart);
self.chart.update();
var SubView = viewRegistry.get('graph');
var subView = new SubView(this.viewInfo, this.subViewParams);
var def2 = subView.getController(this).then(function (controller) {
self.controller = controller;
return self.controller.appendTo(document.createDocumentFragment());
});
return $.when(def1, def2);
},
/**
* @private
* @param {any} value
* @param {any} field
* @returns {string}
* @override
*/
_getNumberedValue: function (value, field) {
var id = value[0];
var name = value[1];
this.numbering[field] = this.numbering[field] || {};
this.numbering[field][name] = this.numbering[field][name] || {};
var numbers = this.numbering[field][name];
numbers[id] = numbers[id] || _.size(numbers) + 1;
return name + (numbers[id] > 1 ? " (" + numbers[id] + ")" : "");
start: function () {
this.$el.append(this.controller.$el);
return this._super.apply(this, arguments);
},
/**
* Renders a pie chart's label.
/**
* Call `on_attach_callback` for each subview
*
* @private
* @param {String} title
* @returns {jQueryElement}
* @override
*/
_renderLabel: function (title) {
var $result = $('<label>', {text: title});
return $result;
on_attach_callback: function () {
this.controller.on_attach_callback();
},
});
+10
View File
@@ -0,0 +1,10 @@
<template>
<t t-name="web.PieChart">
<graph type="pie">
<field t-att-name="modifiers.groupby.split(':')[0]" t-att-interval="modifiers.groupby.split(':')[1]"/>
<field t-if="modifiers.measure" t-att-name="modifiers.measure" type="measure"/>
</graph>
</t>
</template>