diff options
| -rw-r--r-- | addons/web/static/src/js/views/basic/basic_renderer.js | 39 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/basic/widget_registry.js | 27 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/form/form_renderer.js | 23 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/kanban/kanban_record.js | 17 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/list/list_renderer.js | 4 | ||||
| -rw-r--r-- | addons/web/static/tests/views/form_tests.js | 33 | ||||
| -rw-r--r-- | addons/web/static/tests/views/kanban_tests.js | 36 | ||||
| -rw-r--r-- | addons/web/static/tests/views/list_tests.js | 30 | ||||
| -rw-r--r-- | addons/web/views/webclient_templates.xml | 1 |
9 files changed, 196 insertions, 14 deletions
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; @@ -332,6 +333,19 @@ var BasicRenderer = AbstractRenderer.extend({ 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) * @@ -541,6 +555,31 @@ var BasicRenderer = AbstractRenderer.extend({ 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 <widget/>. 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 <widget/> tag (in the arch): + // + // <widget name="some_name"/> + // + // 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 '<widget' in the test folder) + + var Registry = require('web.Registry'); + + return new Registry(); +}); diff --git a/addons/web/static/src/js/views/form/form_renderer.js b/addons/web/static/src/js/views/form/form_renderer.js index 3cb22af5dfd..7d962e223ce 100644 --- a/addons/web/static/src/js/views/form/form_renderer.js +++ b/addons/web/static/src/js/views/form/form_renderer.js @@ -227,19 +227,6 @@ var FormRenderer = BasicRenderer.extend({ }, /** * @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); - } - }, - /** - * @private * @param {Object} node * @returns {jQueryElement} */ @@ -759,6 +746,16 @@ var FormRenderer = BasicRenderer.extend({ return $sheet; }, /** + * Instantiate custom widgets + * + * @private + * @param {Object} node + * @returns {jQueryElement} + */ + _renderTagWidget: function (node) { + return this._renderWidget(this.state, node); + }, + /** * Main entry point for the rendering. From here, we call _renderNode on * the root of the arch, then, when every deferred (from the field widgets) * are done, it will resolves itself. diff --git a/addons/web/static/src/js/views/kanban/kanban_record.js b/addons/web/static/src/js/views/kanban/kanban_record.js index 1dabc93abdb..52597e6ac71 100644 --- a/addons/web/static/src/js/views/kanban/kanban_record.js +++ b/addons/web/static/src/js/views/kanban/kanban_record.js @@ -11,6 +11,7 @@ var Domain = require('web.Domain'); var field_utils = require('web.field_utils'); var utils = require('web.utils'); var Widget = require('web.Widget'); +var widgetRegistry = require('web.widget_registry'); var _t = core._t; var QWeb = core.qweb; @@ -262,6 +263,21 @@ var KanbanRecord = Widget.extend({ this._setFieldDisplay(widget.$el, field_name); return widget; }, + _processWidgets: function () { + var self = this; + this.$("widget").each(function () { + var $field = $(this); + var Widget = widgetRegistry.get($field.attr('name')); + var widget = new Widget(self, self.state); + + var def = widget.__widgetRenderAndInsert(function () {}); + if (def.state() === 'pending') { + self.defs.push(def); + } + widget.$el.addClass('o_widget'); + $field.replaceWith(widget.$el); + }); + }, /** * Renders the record */ @@ -274,6 +290,7 @@ var KanbanRecord = Widget.extend({ this.$el.on('click', this._onGlobalClick.bind(this)); } this._processFields(); + this._processWidgets(); this._setupColor(); this._setupColorPicker(); this._attachTooltip(); diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js index 69d56acdbcc..75633629e5e 100644 --- a/addons/web/static/src/js/views/list/list_renderer.js +++ b/addons/web/static/src/js/views/list/list_renderer.js @@ -222,7 +222,7 @@ var ListRenderer = BasicRenderer.extend({ var tdClassName = 'o_data_cell'; if (node.tag === 'button') { tdClassName += ' o_list_button'; - } else { + } else if (node.tag === 'field') { var typeClass = FIELD_CLASSES[this.state.fields[node.attrs.name].type]; if (typeClass) { tdClassName += (' ' + typeClass); @@ -245,6 +245,8 @@ var ListRenderer = BasicRenderer.extend({ if (node.tag === 'button') { return $td.append(this._renderButton(record, node)); + } else if (node.tag === 'widget') { + return $td.append(this._renderWidget(record, node)); } if (node.attrs.widget || (options && options.renderWidgets)) { var widget = this._renderFieldWidget(node, record, _.pick(options, 'mode')); diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index 91495cb4670..4ac4213f378 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -8,6 +8,8 @@ var FormView = require('web.FormView'); var pyeval = require('web.pyeval'); var RainbowMan = require('web.rainbow_man'); var testUtils = require('web.test_utils'); +var widgetRegistry = require('web.widget_registry'); +var Widget = require('web.Widget'); var _t = core._t; var createView = testUtils.createView; @@ -5867,5 +5869,36 @@ QUnit.module('Views', { form.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 form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form string="Partners">' + + '<field name="foo"/>' + + '<field name="bar"/>' + + '<widget name="test"/>' + + '</form>', + }); + + 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: '<kanban class="o_kanban_test"><templates><t t-name="kanban-box">' + + '<div>' + + '<t t-esc="record.foo.value"/>' + + '<field name="foo" blip="1"/>' + + '<widget name="test"/>' + + '</div>' + + '</t></templates></kanban>', + }); + + 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: '<tree><field name="foo"/><field name="int_field"/><widget name="test"/></tree>', + }); + + 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 @@ <script type="text/javascript" src="/web/static/src/js/fields/abstract_field.js"></script> <script type="text/javascript" src="/web/static/src/js/fields/basic_fields.js"></script> <script type="text/javascript" src="/web/static/src/js/fields/field_registry.js"></script> + <script type="text/javascript" src="/web/static/src/js/views/basic/widget_registry.js"></script> <script type="text/javascript" src="/web/static/src/js/fields/field_utils.js"></script> <script type="text/javascript" src="/web/static/src/js/fields/relational_fields.js"></script> <script type="text/javascript" src="/web/static/src/js/fields/special_fields.js"></script> |
