[IMP] web: add support of owl custom widget
add support of <widget> tag for owl, In order to prepare the future, we want to convert everything in Owl, in future widgets generated by <widget> tag will also be converted to owl. with this commit we support widget to instantiate using ComponentWrapper. task-2337692 Co-authored-by: Aaron Bohy <aab@odoo.com>
This commit is contained in:
committed by
Yannick Tivisse
co-authored by
Aaron Bohy
parent
7fb9c81296
commit
877ef5552b
@@ -75,9 +75,6 @@ odoo.define('web.FieldWrapper', function (require) {
|
||||
// Getters
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
get $el() {
|
||||
return $(this.el);
|
||||
}
|
||||
get fieldDependencies() {
|
||||
return this.Component.fieldDependencies;
|
||||
}
|
||||
|
||||
@@ -518,6 +518,14 @@ odoo.define('web.OwlCompatibility', function () {
|
||||
this._mountArgs = arguments;
|
||||
return super.mount(...arguments);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Getters
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
get $el() {
|
||||
return $(this.el);
|
||||
}
|
||||
}
|
||||
ComponentWrapper.template = xml`<t t-component="Component" t-props="props" t-ref="component"/>`;
|
||||
|
||||
|
||||
@@ -13,10 +13,12 @@ var core = require('web.core');
|
||||
var dom = require('web.dom');
|
||||
const session = require('web.session');
|
||||
const utils = require('web.utils');
|
||||
var widgetRegistry = require('web.widget_registry');
|
||||
const widgetRegistry = require('web.widget_registry');
|
||||
const widgetRegistryOwl = require("web.widgetRegistry");
|
||||
|
||||
const { WidgetAdapterMixin } = require('web.OwlCompatibility');
|
||||
const FieldWrapper = require('web.FieldWrapper');
|
||||
const WidgetWrapper = require("web.WidgetWrapper");
|
||||
|
||||
var qweb = core.qweb;
|
||||
const _t = core._t;
|
||||
@@ -804,13 +806,29 @@ var BasicRenderer = AbstractRenderer.extend(WidgetAdapterMixin, {
|
||||
* @returns {jQueryElement}
|
||||
*/
|
||||
_renderWidget: function (record, node) {
|
||||
var Widget = widgetRegistry.get(node.attrs.name);
|
||||
var widget = new Widget(this, record, node);
|
||||
const name = node.attrs.name;
|
||||
const Widget = widgetRegistryOwl.get(name) || widgetRegistry.get(name);
|
||||
const legacy = !(Widget.prototype instanceof owl.Component);
|
||||
let widget;
|
||||
if (legacy) {
|
||||
widget = new Widget(this, record, node, { mode: this.mode });
|
||||
} else {
|
||||
widget = new WidgetWrapper(this, Widget, {
|
||||
record,
|
||||
node,
|
||||
options: { mode: this.mode },
|
||||
});
|
||||
}
|
||||
|
||||
this.widgets.push(widget);
|
||||
|
||||
// Prepare widget rendering and save the related promise
|
||||
var def = widget._widgetRenderAndInsert(function () {});
|
||||
let def;
|
||||
if (legacy) {
|
||||
def = widget._widgetRenderAndInsert(function () {});
|
||||
} else {
|
||||
def = widget.mount(document.createDocumentFragment());
|
||||
}
|
||||
this.defs.push(def);
|
||||
var $el = $('<div>');
|
||||
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
odoo.define('web.widget_registry', function (require) {
|
||||
"use strict";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// /!\ DEPRECATED
|
||||
//
|
||||
// /!\ This registry is deprecated. It is used to register legacy Widgets.
|
||||
// /!\ Use 'web.widgetRegistry' to register Owl Components.
|
||||
// /!\ Existing Widgets will be at some point converted into Owl Components.
|
||||
// /!\ All new code should be directly written using Owl Components.
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// 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
|
||||
@@ -23,5 +33,5 @@ odoo.define('web.widget_registry', function (require) {
|
||||
|
||||
var Registry = require('web.Registry');
|
||||
|
||||
return new Registry();
|
||||
return new Registry(null, (value) => !(value.prototype instanceof owl.Component));
|
||||
});
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
odoo.define("web.widgetRegistry", function (require) {
|
||||
"use strict";
|
||||
|
||||
// This registry is supposed to contain all custom components that will be
|
||||
// available in the basic views, with the tag <widget/>. Those components
|
||||
// allow to cleanly add custom behavior in basic views.
|
||||
//
|
||||
// The way custom components work is that they register themselves to this
|
||||
// registry:
|
||||
//
|
||||
// widgetRegistry.add('some_name', MyComponent);
|
||||
//
|
||||
// Then, they are available with the <widget/> tag (in the arch):
|
||||
//
|
||||
// <widget name="some_name"/>
|
||||
//
|
||||
// Those components will be then properly instantiated, rendered and
|
||||
// destroyed at the appropriate time, and will receive the current state in
|
||||
// props.
|
||||
|
||||
const Registry = require("web.Registry");
|
||||
|
||||
return new Registry(null, (value) => value.prototype instanceof owl.Component);
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
odoo.define('web.WidgetWrapper', function (require) {
|
||||
"use strict";
|
||||
|
||||
const { ComponentWrapper } = require('web.OwlCompatibility');
|
||||
|
||||
class WidgetWrapper extends ComponentWrapper {
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Public
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
updateState(state) {
|
||||
if (this.componentRef.comp.updateState) {
|
||||
this.componentRef.comp.updateState(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
return WidgetWrapper;
|
||||
});
|
||||
@@ -13,7 +13,10 @@ var field_utils = require('web.field_utils');
|
||||
const FieldWrapper = require('web.FieldWrapper');
|
||||
var utils = require('web.utils');
|
||||
var Widget = require('web.Widget');
|
||||
const { WidgetAdapterMixin } = require('web.OwlCompatibility');
|
||||
var widgetRegistry = require('web.widget_registry');
|
||||
const widgetRegistryOwl = require('web.widgetRegistry');
|
||||
const WidgetWrapper = require("web.WidgetWrapper");
|
||||
|
||||
var _t = core._t;
|
||||
var QWeb = core.qweb;
|
||||
@@ -21,7 +24,7 @@ var QWeb = core.qweb;
|
||||
var KANBAN_RECORD_COLORS = require('web.basic_fields').FieldColorPicker.prototype.RECORD_COLORS;
|
||||
var NB_KANBAN_RECORD_COLORS = KANBAN_RECORD_COLORS.length;
|
||||
|
||||
var KanbanRecord = Widget.extend({
|
||||
var KanbanRecord = Widget.extend(WidgetAdapterMixin, {
|
||||
events: {
|
||||
'click .oe_kanban_action': '_onKanbanActionClicked',
|
||||
'click .o_kanban_manage_toggle_button': '_onManageTogglerClicked',
|
||||
@@ -54,12 +57,21 @@ var KanbanRecord = Widget.extend({
|
||||
start: function () {
|
||||
return Promise.all([this._super.apply(this, arguments), this._render()]);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
destroy: function () {
|
||||
this._super(...arguments);
|
||||
WidgetAdapterMixin.destroy.call(this);
|
||||
},
|
||||
/**
|
||||
* Called each time the record is attached to the DOM.
|
||||
*/
|
||||
on_attach_callback: function () {
|
||||
this.isInDOM = true;
|
||||
_.invoke(this.subWidgets, 'on_attach_callback');
|
||||
// call on_attach_callback on child components (tag <widget>)
|
||||
WidgetAdapterMixin.on_attach_callback.call(this);
|
||||
},
|
||||
/**
|
||||
* Called each time the record is detached from the DOM.
|
||||
@@ -67,6 +79,8 @@ var KanbanRecord = Widget.extend({
|
||||
on_detach_callback: function () {
|
||||
this.isInDOM = false;
|
||||
_.invoke(this.subWidgets, 'on_detach_callback');
|
||||
// call on_detach_callback on child components (tag <widget>)
|
||||
WidgetAdapterMixin.on_detach_callback.call(this);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -330,10 +344,24 @@ var KanbanRecord = Widget.extend({
|
||||
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 () { });
|
||||
const name = $field.attr('name');
|
||||
const Widget = widgetRegistryOwl.get(name) || widgetRegistry.get(name);
|
||||
const legacy = !(Widget.prototype instanceof owl.Component);
|
||||
let widget;
|
||||
if (legacy) {
|
||||
widget = new Widget(self, self.state);
|
||||
} else {
|
||||
widget = new WidgetWrapper(this, Widget, { record: self.state });
|
||||
}
|
||||
// Prepare widget rendering and save the related promise
|
||||
let def;
|
||||
if (legacy) {
|
||||
def = widget._widgetRenderAndInsert(function () { });
|
||||
} else {
|
||||
def = widget.mount(document.createDocumentFragment());
|
||||
}
|
||||
|
||||
self.defs.push(def);
|
||||
def.then(function () {
|
||||
$field.replaceWith(widget.$el);
|
||||
|
||||
@@ -16,6 +16,7 @@ var pyUtils = require('web.py_utils');
|
||||
var RamStorage = require('web.RamStorage');
|
||||
var testUtils = require('web.test_utils');
|
||||
var widgetRegistry = require('web.widget_registry');
|
||||
const widgetRegistryOwl = require('web.widgetRegistry');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var _t = core._t;
|
||||
@@ -7854,6 +7855,8 @@ QUnit.module('Views', {
|
||||
});
|
||||
|
||||
QUnit.test('basic support for widgets', async function (assert) {
|
||||
// This test could be removed as soon as we drop the support of legacy widgets (see test
|
||||
// below, which is a duplicate of this one, but with an Owl Component instead).
|
||||
assert.expect(1);
|
||||
|
||||
var MyWidget = Widget.extend({
|
||||
@@ -7884,6 +7887,35 @@ QUnit.module('Views', {
|
||||
delete widgetRegistry.map.test;
|
||||
});
|
||||
|
||||
QUnit.test('basic support for widgets (being Owl Components)', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
class MyComponent extends owl.Component {
|
||||
get value() {
|
||||
return JSON.stringify(this.props.record.data);
|
||||
}
|
||||
}
|
||||
MyComponent.template = owl.tags.xml`<div t-esc="value"/>`;
|
||||
widgetRegistryOwl.add('test', MyComponent);
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<form>
|
||||
<field name="foo"/>
|
||||
<field name="bar"/>
|
||||
<widget name="test"/>
|
||||
</form>`,
|
||||
});
|
||||
|
||||
assert.strictEqual(form.$('.o_widget').text(), '{"foo":"My little Foo Value","bar":false}');
|
||||
|
||||
form.destroy();
|
||||
delete widgetRegistryOwl.map.test;
|
||||
});
|
||||
|
||||
QUnit.test('attach document widget calls action with attachment ids', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ var mixins = require('web.mixins');
|
||||
var testUtils = require('web.test_utils');
|
||||
var Widget = require('web.Widget');
|
||||
var widgetRegistry = require('web.widget_registry');
|
||||
const widgetRegistryOwl = require('web.widgetRegistry');
|
||||
|
||||
var makeTestPromise = testUtils.makeTestPromise;
|
||||
var nextTick = testUtils.nextTick;
|
||||
@@ -5709,6 +5710,8 @@ QUnit.module('Views', {
|
||||
});
|
||||
|
||||
QUnit.test('basic support for widgets', async function (assert) {
|
||||
// This test could be removed as soon as we drop the support of legacy widgets (see test
|
||||
// below, which is a duplicate of this one, but with an Owl Component instead).
|
||||
assert.expect(1);
|
||||
|
||||
var MyWidget = Widget.extend({
|
||||
@@ -5741,6 +5744,41 @@ QUnit.module('Views', {
|
||||
delete widgetRegistry.map.test;
|
||||
});
|
||||
|
||||
QUnit.test('basic support for widgets (being Owl Components)', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
class MyComponent extends owl.Component {
|
||||
get value() {
|
||||
return JSON.stringify(this.props.record.data);
|
||||
}
|
||||
}
|
||||
MyComponent.template = owl.tags.xml`<div t-esc="value"/>`;
|
||||
widgetRegistryOwl.add('test', MyComponent);
|
||||
|
||||
const kanban = await createView({
|
||||
View: KanbanView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<kanban class="o_kanban_test">
|
||||
<field name="foo"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div>
|
||||
<t t-esc="record.foo.value"/>
|
||||
<widget name="test"/>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>`,
|
||||
});
|
||||
|
||||
assert.strictEqual(kanban.$('.o_widget:eq(2)').text(), '{"foo":"gnap","id":3}');
|
||||
|
||||
kanban.destroy();
|
||||
delete widgetRegistryOwl.map.test;
|
||||
});
|
||||
|
||||
QUnit.test('subwidgets with on_attach_callback when changing record color', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ var RamStorage = require('web.RamStorage');
|
||||
var testUtils = require('web.test_utils');
|
||||
const { patch, unpatch } = require('web.utils');
|
||||
var widgetRegistry = require('web.widget_registry');
|
||||
const widgetRegistryOwl = require('web.widgetRegistry');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
|
||||
@@ -8647,6 +8648,8 @@ QUnit.module('Views', {
|
||||
});
|
||||
|
||||
QUnit.test('basic support for widgets', async function (assert) {
|
||||
// This test could be removed as soon as we drop the support of legacy widgets (see test
|
||||
// below, which is a duplicate of this one, but with an Owl Component instead).
|
||||
assert.expect(1);
|
||||
|
||||
var MyWidget = Widget.extend({
|
||||
@@ -8673,6 +8676,30 @@ QUnit.module('Views', {
|
||||
delete widgetRegistry.map.test;
|
||||
});
|
||||
|
||||
QUnit.test('basic support for widgets (being Owl Components)', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
class MyComponent extends owl.Component {
|
||||
get value() {
|
||||
return JSON.stringify(this.props.record.data);
|
||||
}
|
||||
}
|
||||
MyComponent.template = owl.tags.xml`<div t-esc="value"/>`;
|
||||
widgetRegistryOwl.add('test', MyComponent);
|
||||
|
||||
const list = await 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}');
|
||||
|
||||
list.destroy();
|
||||
delete widgetRegistryOwl.map.test;
|
||||
});
|
||||
|
||||
QUnit.test('use the limit attribute in arch', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
|
||||
@@ -305,6 +305,8 @@
|
||||
<script type="text/javascript" src="/web/static/src/js/fields/field_registry.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/fields/field_registry_owl.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/views/basic/widget_registry_owl.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/views/basic/widget_wrapper.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>
|
||||
|
||||
Reference in New Issue
Block a user