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 @@
+