From 905e01921f3c3ef43ace6fc537d1d8c0d280002c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Sun, 5 Mar 2017 10:05:56 +0100 Subject: [REF] web, *: redesign all JS views This commit introduce a full redesign of all JS views. We started basically from scratch. The goal was to unify all the various views under a common framework, to make them testable, to make then usable in different conditions (in studio, or in the frontend), and to make our lives easier. Some important points are: - we introduced new coding guidelines (camelCase, 80 chars width, ...) - we have a brand new testing framework (still QUnit based) - kanban view moved to the web addon - calendar view (formerly web_calender) moved to web as well - the tree view was removed - all new code should be documented We hope that this code is the start of a new era for the Odoo web client, we want to have a high quality codebase, well documented, well tested, well designed. Work done by the framework team: mostly aab, ged, chm, dmo, qsm --- .../static/src/js/views/basic/basic_renderer.js | 115 +++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 addons/web/static/src/js/views/basic/basic_renderer.js (limited to 'addons/web/static/src/js/views/basic/basic_renderer.js') diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js new file mode 100644 index 00000000000..22ec34f714b --- /dev/null +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -0,0 +1,115 @@ +odoo.define('web.BasicRenderer', function (require) { +"use strict"; + +/** + * The BasicRenderer is an abstract class designed to share code between all + * views that uses a BasicModel. The main goal is to keep track of all field + * widgets, and properly destroy them whenever a rerender is done. + * + * When a concrete view extends a BasicRenderer, it should add each field + * widgets in the this.widget list. + */ + +var AbstractRenderer = require('web.AbstractRenderer'); +var config = require('web.config'); +var core = require('web.core'); + +var qweb = core.qweb; + +return AbstractRenderer.extend({ + /** + * @override + */ + init: function (paret, state, params) { + this._super.apply(this, arguments); + this.widgets = []; + this.activeActions = params.activeActions; + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Add a tooltip on a $node, depending on a field description + * + * @param {FieldWidget} widget + * @param {$node} $node + */ + _addFieldTooltip: function (widget, $node) { + // optional argument $node, the jQuery element on which the tooltip + // should be attached if not given, the tooltip is attached on the + // widget's $el + $node = $node.length ? $node : widget.$el; + $node.tooltip({ + delay: { show: 1000, hide: 0 }, + title: function () { + return qweb.render('WidgetLabel.tooltip', { + debug: config.debug, + widget: widget, + }); + } + }); + }, + /** + * Helper method, used to get an object with the 'raw' field values. It is + * important when we want to evaluate if a record matches a given domain. + * + * A basic model record is a tree structure, but we sometimes need 'pure' + * data, so for example, [1,4] for the value of a one2many, not + * [{Object}, {Object}]. + * + * @param {Object} record + * @returns {Object} + */ + _getFieldValues: function (record) { + return _.mapObject(record.data, function (value, name) { + var field = record.fields[name]; + if (field.type === 'many2one') { + return value.data ? value.data.id || false : false; + } + if (field.type === 'one2many' || field.type === 'many2many') { + if (value) { + return _.pluck(value.data, 'res_id'); + } else { + return []; + } + } + if (field.type === 'boolean') { + // we want an explicit false value, not null if it is unset + return value || false; + } + return value; + }); + }, + /** + * Render the view + * + * @override + * @returns {Deferred} + */ + _render: function () { + var oldWidgets = this.widgets; + this.widgets = []; + return this._renderView().then(_.invoke.bind(_, oldWidgets, 'destroy')); + }, + /** + * Actual rendering. Supposed to be overridden by concrete renderers. + * The basic responsabilities of _renderView are: + * - use the xml arch of the view to render a jQuery representation + * - instantiate a widget from the widgets_registry for each field in the + * arch + * + * Note that the 'state' field should contains all necessary information + * for the rendering. The field widgets should be as synchronous as + * possible. + * + * @abstract + * @returns {Deferred} + */ + _renderView: function () { + return $.when(); + }, +}); + +}); -- cgit v1.2.3 From 454b21538917558cfb71c0b98bf49a1d8efa7eee Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Wed, 8 Mar 2017 15:49:52 +0100 Subject: [FIX] web: properly transfer field properties Before this commit, the field properties (attrs, fieldNames, fields) were not correctly given to sub views in many case expected: * use widget many2many on one2many field * create record when click on save in formDialog * do not send create command to the server * use the fields defined in the field node (instead of tree view fields) * use the fieldNames instead of the record to get field values --- addons/web/static/src/js/views/basic/basic_renderer.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_renderer.js') diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js index 22ec34f714b..e581b222417 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -63,24 +63,28 @@ return AbstractRenderer.extend({ * @returns {Object} */ _getFieldValues: function (record) { - return _.mapObject(record.data, function (value, name) { + var result = {}; + _.each(record.fieldNames, function (name) { var field = record.fields[name]; + var value = record.data[name]; + result[name] = value; if (field.type === 'many2one') { - return value.data ? value.data.id || false : false; + result[name] = value.data ? value.data.id || false : false; } if (field.type === 'one2many' || field.type === 'many2many') { if (value) { - return _.pluck(value.data, 'res_id'); + result[name] = _.pluck(value.data, 'res_id'); } else { - return []; + result[name] = []; } } if (field.type === 'boolean') { // we want an explicit false value, not null if it is unset - return value || false; + result[name] = value || false; } - return value; }); + result.id = record.data.id; + return result; }, /** * Render the view -- cgit v1.2.3 From f0cfe7c174307009575661d8ebe4c2e5d84c1768 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 9 Mar 2017 12:44:40 +0100 Subject: [FIX] web: load missing fields when opening form view dialogs Problem was that sub form field has usually more fields than the list view in one2many, so we have to load missing fields --- addons/web/static/src/js/views/basic/basic_renderer.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_renderer.js') diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js index e581b222417..7b57f16dd84 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -68,17 +68,20 @@ return AbstractRenderer.extend({ var field = record.fields[name]; var value = record.data[name]; result[name] = value; - if (field.type === 'many2one') { + if (!value) { + return; + } + else if (field.type === 'many2one') { result[name] = value.data ? value.data.id || false : false; } - if (field.type === 'one2many' || field.type === 'many2many') { + else if (field.type === 'one2many' || field.type === 'many2many') { if (value) { result[name] = _.pluck(value.data, 'res_id'); } else { result[name] = []; } } - if (field.type === 'boolean') { + else if (field.type === 'boolean') { // we want an explicit false value, not null if it is unset result[name] = value || false; } -- cgit v1.2.3 From 49f7a9f657b011080666475f836982665615dc7c Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Thu, 9 Mar 2017 15:12:58 +0100 Subject: [REF] web, mail: use field registry in views, not renderers Before this commit, the AbstractField specialization to use to instantiate a field was determined in the renderers. Now this is regrouped in BasicView. Note: renderers are still left free to not use the determined Class (like Kanban and not editable List which instantiate widgets only if the "widget" attribute is present). --- addons/web/static/src/js/views/basic/basic_renderer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_renderer.js') diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js index 7b57f16dd84..d8ddf02d169 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -104,8 +104,7 @@ return AbstractRenderer.extend({ * Actual rendering. Supposed to be overridden by concrete renderers. * The basic responsabilities of _renderView are: * - use the xml arch of the view to render a jQuery representation - * - instantiate a widget from the widgets_registry for each field in the - * arch + * - instantiate a widget from the registry for each field in the arch * * Note that the 'state' field should contains all necessary information * for the rendering. The field widgets should be as synchronous as -- cgit v1.2.3 From 3d959382fd92aca6c48ea2413c646edcc1f178f4 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 31 Mar 2017 14:10:46 +0200 Subject: [FIX] web,mail: one2many records: multiple views A record of the basic model may need to be displayed in different views (e.g. a o2m record is displayed in the, e.g., Kanban view inside the main form view, and if clicked, it is also displayed in the form view opened in a dialog). However, the fields present in those views may differ. The previous implementation of fieldsInfo in the record didn't handle that, as it was a dict whose keys were field names. With this commit, the fieldsInfo is dict whose keys are view types, and the values are dicts whose keys are field names (the fields of the corresponding views, with they attrs in the view). This allows to create multiple o2m records in a row in a form view. --- addons/web/static/src/js/views/basic/basic_renderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/web/static/src/js/views/basic/basic_renderer.js') diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js index d8ddf02d169..2aa4479648b 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -64,7 +64,7 @@ return AbstractRenderer.extend({ */ _getFieldValues: function (record) { var result = {}; - _.each(record.fieldNames, function (name) { + _.each(record.getFieldNames(), function (name) { var field = record.fields[name]; var value = record.data[name]; result[name] = value; -- cgit v1.2.3 From 3ff7ff3e94e4fe34ec09a45fda04f0c8de33cd32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 4 Apr 2017 15:08:00 +0200 Subject: [FIX] web: properly evaluate unset numeric values in domain Before this commit, when a numeric value was not set (for example, a new record with no default_get value), then it was not properly evaluated as 0 in a domain. We also slightly refactored the code so we always use the same method to compute the evaluation context. --- .../static/src/js/views/basic/basic_renderer.js | 38 ---------------------- 1 file changed, 38 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_renderer.js') diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js index 2aa4479648b..1db0718545e 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -51,44 +51,6 @@ return AbstractRenderer.extend({ } }); }, - /** - * Helper method, used to get an object with the 'raw' field values. It is - * important when we want to evaluate if a record matches a given domain. - * - * A basic model record is a tree structure, but we sometimes need 'pure' - * data, so for example, [1,4] for the value of a one2many, not - * [{Object}, {Object}]. - * - * @param {Object} record - * @returns {Object} - */ - _getFieldValues: function (record) { - var result = {}; - _.each(record.getFieldNames(), function (name) { - var field = record.fields[name]; - var value = record.data[name]; - result[name] = value; - if (!value) { - return; - } - else if (field.type === 'many2one') { - result[name] = value.data ? value.data.id || false : false; - } - else if (field.type === 'one2many' || field.type === 'many2many') { - if (value) { - result[name] = _.pluck(value.data, 'res_id'); - } else { - result[name] = []; - } - } - else if (field.type === 'boolean') { - // we want an explicit false value, not null if it is unset - result[name] = value || false; - } - }); - result.id = record.data.id; - return result; - }, /** * Render the view * -- cgit v1.2.3