diff --git a/addons/project/views/project_views.xml b/addons/project/views/project_views.xml
index d26291524c9..c5119628dce 100644
--- a/addons/project/views/project_views.xml
+++ b/addons/project/views/project_views.xml
@@ -750,13 +750,6 @@
of
-
diff --git a/addons/web/static/src/js/views/basic/basic_view.js b/addons/web/static/src/js/views/basic/basic_view.js
index 5e3938fb3ce..49c1d2bb94b 100644
--- a/addons/web/static/src/js/views/basic/basic_view.js
+++ b/addons/web/static/src/js/views/basic/basic_view.js
@@ -18,6 +18,8 @@ var fieldRegistry = require('web.field_registry');
var fieldRegistryOwl = require('web.field_registry_owl');
var pyUtils = require('web.py_utils');
var utils = require('web.utils');
+const widgetRegistry = require('web.widget_registry');
+const widgetRegistryOwl = require('web.widgetRegistry');
var BasicView = AbstractView.extend({
config: _.extend({}, AbstractView.prototype.config, {
@@ -372,6 +374,28 @@ var BasicView = AbstractView.extend({
* @returns {boolean} false iff subnodes must not be visited.
*/
_processNode: function (node, fv) {
+ const viewType = fv.type;
+ const fieldsInfo = fv.fieldsInfo[viewType];
+ const fields = fv.viewFields;
+
+ const _addFieldDependencies = (deps) => {
+ for (const dependencyName in deps) {
+ const dependencyDict = { name: dependencyName, type: deps[dependencyName].type };
+ if (!(dependencyName in fieldsInfo)) {
+ fieldsInfo[dependencyName] = _.extend({}, dependencyDict, {
+ options: deps[dependencyName].options || {},
+ });
+ }
+ if (!(dependencyName in fields)) {
+ fields[dependencyName] = dependencyDict;
+ }
+
+ if (fv.fields && !(dependencyName in fv.fields)) {
+ fv.fields[dependencyName] = dependencyDict;
+ }
+ }
+ };
+
if (typeof node === 'string') {
return false;
}
@@ -382,29 +406,27 @@ var BasicView = AbstractView.extend({
node.attrs.options = node.attrs.options ? JSON.parse(node.attrs.options) : {};
}
if (node.tag === 'field') {
- var viewType = fv.type;
- var fieldsInfo = fv.fieldsInfo[viewType];
- var fields = fv.viewFields;
fieldsInfo[node.attrs.name] = this._processField(viewType,
fields[node.attrs.name], node.attrs ? _.clone(node.attrs) : {});
if (fieldsInfo[node.attrs.name].fieldDependencies) {
var deps = fieldsInfo[node.attrs.name].fieldDependencies;
- for (var dependency_name in deps) {
- var dependency_dict = {name: dependency_name, type: deps[dependency_name].type};
- if (!(dependency_name in fieldsInfo)) {
- fieldsInfo[dependency_name] = _.extend({}, dependency_dict, {
- options: deps[dependency_name].options || {},
- });
- }
- if (!(dependency_name in fields)) {
- fields[dependency_name] = dependency_dict;
- }
-
- if (fv.fields && !(dependency_name in fv.fields)) {
- fv.fields[dependency_name] = dependency_dict;
- }
- }
+ _addFieldDependencies(deps);
+ }
+ return false;
+ }
+ // custom widget may have fieldDependencies so add it to fields of fields_view
+ if (node.tag === 'widget') {
+ const Widget = widgetRegistryOwl.get(node.attrs.name) || widgetRegistry.get(node.attrs.name);
+ const legacy = !(Widget.prototype instanceof owl.Component);
+ let deps;
+ if (legacy && Widget.prototype.fieldDependencies) {
+ deps = Widget.prototype.fieldDependencies;
+ } else if (Widget.fieldDependencies) {
+ deps = Widget.fieldDependencies;
+ }
+ if (deps) {
+ _addFieldDependencies(deps);
}
return false;
}
diff --git a/addons/web/static/tests/views/basic_view_tests.js b/addons/web/static/tests/views/basic_view_tests.js
new file mode 100644
index 00000000000..f8adf2334bd
--- /dev/null
+++ b/addons/web/static/tests/views/basic_view_tests.js
@@ -0,0 +1,78 @@
+odoo.define('web.basic_view_tests', function (require) {
+ "use strict";
+
+ const BasicView = require('web.BasicView');
+ const BasicRenderer = require("web.BasicRenderer");
+ const testUtils = require('web.test_utils');
+ const widgetRegistryOwl = require('web.widgetRegistry');
+ const { xml } = owl.tags;
+
+ const createView = testUtils.createView;
+
+ QUnit.module('Views', {
+ beforeEach: function () {
+ this.data = {
+ fake_model: {
+ fields: {},
+ record: [],
+ },
+ foo: {
+ fields: {
+ foo: { string: "Foo", type: "char" },
+ bar: { string: "Bar", type: "boolean" },
+ },
+ records: [
+ { id: 1, bar: true, foo: "yop" },
+ { id: 2, bar: true, foo: "blip" },
+ ]
+ },
+ };
+ },
+ }, function () {
+
+ QUnit.module('BasicView');
+
+ QUnit.test('fields given in fieldDependencies of custom widget are loaded', async function (assert) {
+ assert.expect(1);
+
+ const basicView = BasicView.extend({
+ viewType: "test",
+ config: Object.assign({}, BasicView.prototype.config, {
+ Renderer: BasicRenderer,
+ })
+ });
+
+ class MyWidget extends owl.Component {}
+ MyWidget.fieldDependencies = {
+ foo: { type: 'char' },
+ bar: { type: 'boolean' },
+ };
+ MyWidget.template = xml/* xml */`
+
Hello World!
+ `;
+ widgetRegistryOwl.add('testWidget', MyWidget);
+
+ const view = await createView({
+ View: basicView,
+ data: this.data,
+ model: "foo",
+ arch:
+ `
+
+ `,
+ mockRPC: function (route, args) {
+ if (route === "/web/dataset/search_read") {
+ assert.deepEqual(args.fields, ["foo", "bar"],
+ "search_read should be called with dependent fields");
+ return Promise.resolve();
+ }
+ return this._super.apply(this, arguments);
+ }
+ });
+
+ view.destroy();
+ delete widgetRegistryOwl.map.testWidget;
+ });
+
+ });
+});
diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml
index ed3433ebefa..1e2e2fc9f91 100644
--- a/addons/web/views/webclient_templates.xml
+++ b/addons/web/views/webclient_templates.xml
@@ -720,6 +720,7 @@
+