From 93cf24f73ae79cdfd0346145d0b828ebd1ea07f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Wed, 13 Sep 2017 11:48:02 +0200 Subject: [PATCH] [FIX] web: reimplement custom widgets in basic views With the new views, we lost a (almost unused and undocumented) feature: the ability to instantiate custom widgets in a form view, not linked to a particular field. For example: We reintroduce this feature in this commit, with a nice twist: it also works for the list view and the kanban view. --- .../src/js/views/basic/basic_renderer.js | 39 +++++++++++++++++++ .../src/js/views/basic/widget_registry.js | 27 +++++++++++++ .../static/src/js/views/form/form_renderer.js | 23 +++++------ .../src/js/views/kanban/kanban_record.js | 17 ++++++++ .../static/src/js/views/list/list_renderer.js | 4 +- addons/web/static/tests/views/form_tests.js | 33 ++++++++++++++++ addons/web/static/tests/views/kanban_tests.js | 36 +++++++++++++++++ addons/web/static/tests/views/list_tests.js | 30 ++++++++++++++ addons/web/views/webclient_templates.xml | 1 + 9 files changed, 196 insertions(+), 14 deletions(-) create mode 100644 addons/web/static/src/js/views/basic/widget_registry.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 0e26e2fa19f..ee6e685c3d4 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -11,6 +11,7 @@ var AbstractRenderer = require('web.AbstractRenderer'); var config = require('web.config'); var core = require('web.core'); var dom = require('web.dom'); +var widgetRegistry = require('web.widget_registry'); var qweb = core.qweb; @@ -331,6 +332,19 @@ var BasicRenderer = AbstractRenderer.extend({ _getModifiersData: function (node) { return _.findWhere(this.allModifiersData, {node: node}); }, + /** + * @private + * @param {jQueryElement} $el + * @param {Object} node + */ + _handleAttributes: function ($el, node) { + if (node.attrs.class) { + $el.addClass(node.attrs.class); + } + if (node.attrs.style) { + $el.attr('style', node.attrs.style); + } + }, /** * Used by list and kanban renderers to determine whether or not to display * the no content helper (if there is no data in the state to display) @@ -540,6 +554,31 @@ var BasicRenderer = AbstractRenderer.extend({ _renderView: function () { return $.when(); }, + /** + * Instantiate custom widgets + * + * @private + * @param {Object} record + * @param {Object} node + * @returns {jQueryElement} + */ + _renderWidget: function (record, node) { + var Widget = widgetRegistry.get(node.attrs.name); + var widget = new Widget(this, record); + + // Prepare widget rendering and save the related deferred + var def = widget.__widgetRenderAndInsert(function () {}); + if (def.state() === 'pending') { + this.defs.push(def); + } + + // handle other attributes/modifiers + this._handleAttributes(widget.$el, node); + this._registerModifiers(node, record, widget); + widget.$el.addClass('o_widget'); + return widget.$el; + }, + /** * Rerenders a given widget and make sure the associated data which * referenced the old one is updated. diff --git a/addons/web/static/src/js/views/basic/widget_registry.js b/addons/web/static/src/js/views/basic/widget_registry.js new file mode 100644 index 00000000000..470127bd12e --- /dev/null +++ b/addons/web/static/src/js/views/basic/widget_registry.js @@ -0,0 +1,27 @@ +odoo.define('web.widget_registry', function (require) { + "use strict"; + + // This registry is supposed to contain all custom widgets that will be + // available in the basic views, with the tag . There are + // currently no such widget in the web client, but the functionality is + // certainly useful to be able to cleanly add custom behaviour in basic + // views (and most notably, the form view) + // + // The way custom widgets work is that they register themselves to this + // registry: + // + // widgetRegistry.add('some_name', MyWidget); + // + // Then, they are available with the tag (in the arch): + // + // + // + // Widgets will be then properly instantiated, rendered and destroyed at the + // appropriate time, with the current state in second argument. + // + // For more examples, look at the tests (grep '' + + '' + + '' + + '' + + '', + }); + + assert.strictEqual(form.$el.text(), '{"foo":"My little Foo Value","bar":false}', + "widget should have been instantiated"); + + form.destroy(); + delete widgetRegistry.map.test; + }); + }); }); diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js index cf03187a051..724b080d5f4 100644 --- a/addons/web/static/tests/views/kanban_tests.js +++ b/addons/web/static/tests/views/kanban_tests.js @@ -3,6 +3,8 @@ odoo.define('web.kanban_tests', function (require) { var KanbanView = require('web.KanbanView'); var testUtils = require('web.test_utils'); +var widgetRegistry = require('web.widget_registry'); +var Widget = require('web.Widget'); var createView = testUtils.createView; @@ -1898,6 +1900,40 @@ QUnit.module('Views', { "the add button should still be visible"); kanban.destroy(); }); + + QUnit.test('basic support for widgets', function (assert) { + assert.expect(1); + + var MyWidget = Widget.extend({ + init: function (parent, dataPoint) { + this.data = dataPoint.data; + }, + start: function () { + this.$el.text(JSON.stringify(this.data)); + }, + }); + widgetRegistry.add('test', MyWidget); + + var kanban = createView({ + View: KanbanView, + model: 'partner', + data: this.data, + arch: '' + + '
' + + '' + + '' + + '' + + '
' + + '
', + }); + + assert.strictEqual(kanban.$('.o_widget:eq(2)').text(), '{"foo":"gnap","id":3}', + "widget should have been instantiated"); + + kanban.destroy(); + delete widgetRegistry.map.test; + }); + }); }); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index ddb662a19e6..9585f154b80 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -5,6 +5,8 @@ var config = require('web.config'); var FormView = require('web.FormView'); var ListView = require('web.ListView'); var testUtils = require('web.test_utils'); +var widgetRegistry = require('web.widget_registry'); +var Widget = require('web.Widget'); var createView = testUtils.createView; @@ -2936,6 +2938,34 @@ QUnit.module('Views', { "The first element of the row name should be a span"); list.destroy(); }); + + + QUnit.test('basic support for widgets', function (assert) { + assert.expect(1); + + var MyWidget = Widget.extend({ + init: function (parent, dataPoint) { + this.data = dataPoint.data; + }, + start: function () { + this.$el.text(JSON.stringify(this.data)); + }, + }); + widgetRegistry.add('test', MyWidget); + + var list = createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '', + }); + + assert.strictEqual(list.$('.o_widget').first().text(), '{"foo":"yop","int_field":10,"id":1}', + "widget should have been instantiated"); + + list.destroy(); + delete widgetRegistry.map.test; + }); }); }); diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 16142eb2ae7..4aa0eb812da 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -181,6 +181,7 @@ +