mirror of https://github.com/OCA/web.git
[16.0][MIG]web_widget_dropdown_dynamic: Migrate to version 16.0
parent
cbdb1b6edc
commit
0e1e1c09e1
|
@ -2,10 +2,13 @@
|
|||
Dynamic Dropdown Widget
|
||||
=======================
|
||||
|
||||
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
..
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:e2b8eb236b4dc206f3a4ab9b5932f86c2e46c33bdac31c385725384f3fe970e3
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
|
@ -14,16 +17,16 @@ Dynamic Dropdown Widget
|
|||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
|
||||
:target: https://github.com/OCA/web/tree/15.0/web_widget_dropdown_dynamic
|
||||
:target: https://github.com/OCA/web/tree/16.0/web_widget_dropdown_dynamic
|
||||
:alt: OCA/web
|
||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||
:target: https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_widget_dropdown_dynamic
|
||||
:target: https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_widget_dropdown_dynamic
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
|
||||
:target: https://runbot.odoo-community.org/runbot/162/15.0
|
||||
:alt: Try me on Runbot
|
||||
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
|
||||
:target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=16.0
|
||||
:alt: Try me on Runboat
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
||||
Dynamic dropdown widget that supports resolving options from backend of:
|
||||
|
||||
|
@ -64,7 +67,7 @@ Usage
|
|||
<field
|
||||
name="char_field"
|
||||
widget="dynamic_dropdown"
|
||||
values="method_name"
|
||||
options="{'values':'method_name'}"
|
||||
context="{'depending_on': other_field}"
|
||||
/>
|
||||
|
||||
|
@ -73,8 +76,8 @@ Bug Tracker
|
|||
|
||||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_widget_dropdown_dynamic%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_widget_dropdown_dynamic%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
|
@ -98,6 +101,14 @@ Contributors
|
|||
* Ronald Portier <ronald@therp.nl>
|
||||
|
||||
* Thanakrit Pintana <thanakrit.p39@gmail.com>
|
||||
* `Trobz <https://trobz.com>`_:
|
||||
|
||||
* Son Ho <sonho@trobz.com>
|
||||
|
||||
Other credits
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The migration of this module from 15.0 to 16.0 was financially supported by Camptocamp
|
||||
|
||||
Maintainers
|
||||
~~~~~~~~~~~
|
||||
|
@ -112,6 +123,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
|
|||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.
|
||||
|
||||
This module is part of the `OCA/web <https://github.com/OCA/web/tree/15.0/web_widget_dropdown_dynamic>`_ project on GitHub.
|
||||
This module is part of the `OCA/web <https://github.com/OCA/web/tree/16.0/web_widget_dropdown_dynamic>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
|
|
|
@ -13,11 +13,7 @@
|
|||
"installable": True,
|
||||
"assets": {
|
||||
"web.assets_backend": [
|
||||
"web_widget_dropdown_dynamic/static/src/js/basic_model.js",
|
||||
"web_widget_dropdown_dynamic/static/src/js/field_dynamic_dropdown.js",
|
||||
],
|
||||
"web.qunit_suite_tests": [
|
||||
"web_widget_dropdown_dynamic/static/tests/web_widget_dropdown_dynamic_tests.js"
|
||||
"web_widget_dropdown_dynamic/**/*",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
|
|
@ -7,3 +7,6 @@
|
|||
* Ronald Portier <ronald@therp.nl>
|
||||
|
||||
* Thanakrit Pintana <thanakrit.p39@gmail.com>
|
||||
* `Trobz <https://trobz.com>`_:
|
||||
|
||||
* Son Ho <sonho@trobz.com>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
The migration of this module from 15.0 to 16.0 was financially supported by Camptocamp
|
|
@ -19,6 +19,6 @@
|
|||
<field
|
||||
name="char_field"
|
||||
widget="dynamic_dropdown"
|
||||
values="method_name"
|
||||
options="{'values':'method_name'}"
|
||||
context="{'depending_on': other_field}"
|
||||
/>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
|
||||
<meta name="generator" content="Docutils: http://docutils.sourceforge.net/" />
|
||||
<title>Dynamic Dropdown Widget</title>
|
||||
<style type="text/css">
|
||||
|
||||
|
@ -366,8 +366,10 @@ ul.auto-toc {
|
|||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:e2b8eb236b4dc206f3a4ab9b5932f86c2e46c33bdac31c385725384f3fe970e3
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/web/tree/15.0/web_widget_dropdown_dynamic"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_widget_dropdown_dynamic"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/162/15.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
|
||||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/web/tree/16.0/web_widget_dropdown_dynamic"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_widget_dropdown_dynamic"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
||||
<p>Dynamic dropdown widget that supports resolving options from backend of:</p>
|
||||
<blockquote>
|
||||
<ul class="simple">
|
||||
|
@ -387,7 +389,8 @@ instead.</p>
|
|||
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul>
|
||||
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li>
|
||||
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li>
|
||||
<li><a class="reference internal" href="#maintainers" id="id6">Maintainers</a></li>
|
||||
<li><a class="reference internal" href="#other-credits" id="id6">Other credits</a></li>
|
||||
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -413,7 +416,7 @@ instead.</p>
|
|||
<span class="nt"><field</span>
|
||||
<span class="na">name=</span><span class="s">"char_field"</span>
|
||||
<span class="na">widget=</span><span class="s">"dynamic_dropdown"</span>
|
||||
<span class="na">values=</span><span class="s">"method_name"</span>
|
||||
<span class="na">options=</span><span class="s">"{'values':'method_name'}"</span>
|
||||
<span class="na">context=</span><span class="s">"{'depending_on': other_field}"</span>
|
||||
<span class="nt">/></span>
|
||||
</pre>
|
||||
|
@ -422,8 +425,8 @@ instead.</p>
|
|||
<h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1>
|
||||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/web/issues">GitHub Issues</a>.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||
<a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_widget_dropdown_dynamic%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
<a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_widget_dropdown_dynamic%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||
</div>
|
||||
<div class="section" id="credits">
|
||||
|
@ -436,26 +439,40 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
|
|||
</div>
|
||||
<div class="section" id="contributors">
|
||||
<h2><a class="toc-backref" href="#id5">Contributors</a></h2>
|
||||
<ul>
|
||||
<li><p class="first"><a class="reference external" href="https://corporatehub.eu/">CorporateHub</a></p>
|
||||
<ul class="simple">
|
||||
<li><a class="reference external" href="https://corporatehub.eu/">CorporateHub</a><ul>
|
||||
<li>Alexey Pelykh <<a class="reference external" href="mailto:alexey.pelykh@corphub.eu">alexey.pelykh@corphub.eu</a>></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference external" href="https://therp.nl/">Therp BV</a><ul>
|
||||
<li><p class="first"><a class="reference external" href="https://therp.nl/">Therp BV</a></p>
|
||||
<ul class="simple">
|
||||
<li>Ronald Portier <<a class="reference external" href="mailto:ronald@therp.nl">ronald@therp.nl</a>></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Thanakrit Pintana <<a class="reference external" href="mailto:thanakrit.p39@gmail.com">thanakrit.p39@gmail.com</a>></li>
|
||||
<li><p class="first">Thanakrit Pintana <<a class="reference external" href="mailto:thanakrit.p39@gmail.com">thanakrit.p39@gmail.com</a>></p>
|
||||
</li>
|
||||
<li><p class="first"><a class="reference external" href="https://trobz.com">Trobz</a>:</p>
|
||||
<blockquote>
|
||||
<ul class="simple">
|
||||
<li>Son Ho <<a class="reference external" href="mailto:sonho@trobz.com">sonho@trobz.com</a>></li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="other-credits">
|
||||
<h2><a class="toc-backref" href="#id6">Other credits</a></h2>
|
||||
<p>The migration of this module from 15.0 to 16.0 was financially supported by Camptocamp</p>
|
||||
</div>
|
||||
<div class="section" id="maintainers">
|
||||
<h2><a class="toc-backref" href="#id6">Maintainers</a></h2>
|
||||
<h2><a class="toc-backref" href="#id7">Maintainers</a></h2>
|
||||
<p>This module is maintained by the OCA.</p>
|
||||
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
|
||||
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.</p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/15.0/web_widget_dropdown_dynamic">OCA/web</a> project on GitHub.</p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/16.0/web_widget_dropdown_dynamic">OCA/web</a> project on GitHub.</p>
|
||||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/** @odoo-module **/
|
||||
import BasicModel from "web.BasicModel";
|
||||
|
||||
BasicModel.include({
|
||||
/**
|
||||
* Fetches all the values associated to the given fieldName.
|
||||
*
|
||||
* @param {Object} record - an element from the localData
|
||||
* @param {Object} fieldName - the name of the field
|
||||
* @param {Object} fieldInfo
|
||||
* @returns {Promise<any>}
|
||||
* The promise is resolved with the fetched special values.
|
||||
* If this data is the same as the previously fetched one
|
||||
* (for the given parameters), no RPC is done and the promise
|
||||
* is resolved with the undefined value.
|
||||
*/
|
||||
_fetchDynamicDropdownValues: function (record, fieldName, fieldInfo) {
|
||||
var model = fieldInfo.options.model || record.model;
|
||||
var method = fieldInfo.values || fieldInfo.options.values;
|
||||
if (!method) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
var context = record.getContext({fieldName: fieldName});
|
||||
|
||||
// Avoid rpc if not necessary
|
||||
var hasChanged = this._saveSpecialDataCache(record, fieldName, {
|
||||
context: context,
|
||||
});
|
||||
if (!hasChanged) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this._rpc({
|
||||
model: model,
|
||||
method: method,
|
||||
context: context,
|
||||
}).then(function (result) {
|
||||
var new_result = result.map((val_updated) => {
|
||||
return val_updated.map((e) => {
|
||||
if (typeof e !== "string") {
|
||||
return String(e);
|
||||
}
|
||||
return e;
|
||||
});
|
||||
});
|
||||
return new_result;
|
||||
});
|
||||
},
|
||||
});
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019-2020 Brainbean Apps (https://brainbeanapps.com)
|
||||
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
*/
|
||||
odoo.define("web_widget_dropdown_dynamic.basic_model", function (require) {
|
||||
"use strict";
|
||||
|
||||
var BasicModel = require("web.BasicModel");
|
||||
|
||||
BasicModel.include({
|
||||
/**
|
||||
* Fetches all the values associated to the given fieldName.
|
||||
*
|
||||
* @param {Object} record - an element from the localData
|
||||
* @param {Object} fieldName - the name of the field
|
||||
* @param {Object} fieldInfo
|
||||
* @returns {Promise<any>}
|
||||
* The promise is resolved with the fetched special values.
|
||||
* If this data is the same as the previously fetched one
|
||||
* (for the given parameters), no RPC is done and the promise
|
||||
* is resolved with the undefined value.
|
||||
*/
|
||||
_fetchDynamicDropdownValues: function (record, fieldName, fieldInfo) {
|
||||
var model = fieldInfo.options.model || record.model;
|
||||
var method = fieldInfo.values || fieldInfo.options.values;
|
||||
if (!method) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
var context = record.getContext({fieldName: fieldName});
|
||||
|
||||
// Avoid rpc if not necessary
|
||||
var hasChanged = this._saveSpecialDataCache(record, fieldName, {
|
||||
context: context,
|
||||
});
|
||||
if (!hasChanged) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this._rpc({
|
||||
model: model,
|
||||
method: method,
|
||||
context: context,
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,88 @@
|
|||
/** @odoo-module **/
|
||||
import core from "web.core";
|
||||
import {registry} from "@web/core/registry";
|
||||
import {standardFieldProps} from "@web/views/fields/standard_field_props";
|
||||
import {Component} from "@odoo/owl";
|
||||
|
||||
var _lt = core._lt;
|
||||
|
||||
export class FieldDynamicDropdown extends Component {
|
||||
get options() {
|
||||
var field_type = this.props.record.fields[this.props.name].type || "";
|
||||
if (["char", "integer", "selection"].includes(field_type)) {
|
||||
this._setValues();
|
||||
return this.props.record.fields[this.props.name].selection.filter(
|
||||
(option) => option[0] !== false && option[1] !== ""
|
||||
);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
get value() {
|
||||
const rawValue = this.props.value;
|
||||
this.props.setDirty(false);
|
||||
return this.props.type === "many2one" && rawValue ? rawValue[0] : rawValue;
|
||||
}
|
||||
|
||||
parseInteger(value) {
|
||||
return Number(value);
|
||||
}
|
||||
/**
|
||||
* @param {Event} ev
|
||||
*/
|
||||
onChange(ev) {
|
||||
let lastSetValue = null;
|
||||
let isInvalid = false;
|
||||
var isDirty = ev.target.value !== lastSetValue;
|
||||
const field = this.props.record.fields[this.props.name];
|
||||
let value = JSON.parse(ev.target.value);
|
||||
if (isDirty) {
|
||||
if (value && field.type === "integer") {
|
||||
value = Number(value);
|
||||
if (!value) {
|
||||
if (this.props.record) {
|
||||
this.props.record.setInvalidField(this.props.name);
|
||||
}
|
||||
isInvalid = true;
|
||||
}
|
||||
}
|
||||
if (!isInvalid) {
|
||||
Promise.resolve(this.props.update(value));
|
||||
lastSetValue = ev.target.value;
|
||||
}
|
||||
}
|
||||
if (this.props.setDirty) {
|
||||
this.props.setDirty(isDirty);
|
||||
}
|
||||
}
|
||||
stringify(value) {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
_setValues() {
|
||||
if (this.props.record.preloadedData[this.props.name]) {
|
||||
var sel_value = this.props.record.preloadedData[this.props.name];
|
||||
// Convert string element to integer if field is integer
|
||||
if (this.props.record.fields[this.props.name].type === "integer") {
|
||||
sel_value = sel_value.map((val_updated) => {
|
||||
return val_updated.map((e) => {
|
||||
if (typeof e === "string" && !isNaN(Number(e))) {
|
||||
return Number(e);
|
||||
}
|
||||
return e;
|
||||
});
|
||||
});
|
||||
}
|
||||
this.props.record.fields[this.props.name].selection = sel_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FieldDynamicDropdown.description = _lt("Dynamic Dropdown");
|
||||
FieldDynamicDropdown.template = "web.SelectionField";
|
||||
FieldDynamicDropdown.legacySpecialData = "_fetchDynamicDropdownValues";
|
||||
FieldDynamicDropdown.props = {
|
||||
...standardFieldProps,
|
||||
};
|
||||
FieldDynamicDropdown.supportedTypes = ["char", "integer", "selection"];
|
||||
registry.category("fields").add("dynamic_dropdown", FieldDynamicDropdown);
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 Brainbean Apps (https://brainbeanapps.com)
|
||||
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
*/
|
||||
odoo.define("web_widget_dropdown_dynamic.field_dynamic_dropdown", function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require("web.core");
|
||||
var AbstractField = require("web.AbstractField");
|
||||
var field_registry = require("web.field_registry");
|
||||
|
||||
var _lt = core._lt;
|
||||
|
||||
var FieldDynamicDropdown = AbstractField.extend({
|
||||
description: _lt("Dynamic Dropdown"),
|
||||
template: "FieldSelection",
|
||||
specialData: "_fetchDynamicDropdownValues",
|
||||
supportedFieldTypes: ["selection", "char", "integer"],
|
||||
events: _.extend({}, AbstractField.prototype.events, {
|
||||
change: "_onChange",
|
||||
}),
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this._setValues();
|
||||
},
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Public
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
getFocusableElement: function () {
|
||||
return this.$el.is("select") ? this.$el : $();
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
isSet: function () {
|
||||
return this.value !== false;
|
||||
},
|
||||
/**
|
||||
* Listen to modifiers updates to hide/show the falsy value in the dropdown
|
||||
* according to the required modifier.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
updateModifiersValue: function () {
|
||||
this._super.apply(this, arguments);
|
||||
if (!this.attrs.modifiersValue.invisible && this.mode !== "readonly") {
|
||||
this._setValues();
|
||||
this._renderEdit();
|
||||
}
|
||||
},
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Private
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_formatValue: function (value) {
|
||||
var options = _.extend(
|
||||
{},
|
||||
this.nodeOptions,
|
||||
{data: this.recordData},
|
||||
this.formatOptions
|
||||
);
|
||||
var formattedValue = _.find(this.values, function (option) {
|
||||
return option[0] === value;
|
||||
});
|
||||
if (!formattedValue) {
|
||||
return value;
|
||||
}
|
||||
formattedValue = formattedValue[1];
|
||||
if (options && options.escape) {
|
||||
formattedValue = _.escape(formattedValue);
|
||||
}
|
||||
return formattedValue;
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_renderEdit: function () {
|
||||
this.$el.empty();
|
||||
for (var i = 0; i < this.values.length; i++) {
|
||||
this.$el.append(
|
||||
$("<option/>", {
|
||||
value: JSON.stringify(this.values[i][0]),
|
||||
text: this.values[i][1],
|
||||
})
|
||||
);
|
||||
}
|
||||
this.$el.val(JSON.stringify(this.value));
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_renderReadonly: function () {
|
||||
this.$el.empty().text(this._formatValue(this.value));
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_reset: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this._setValues();
|
||||
},
|
||||
/**
|
||||
* Sets the possible field values.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_setValues: function () {
|
||||
this.values = _.reject(this.record.specialData[this.name], function (v) {
|
||||
return v[0] === false && v[1] === "";
|
||||
});
|
||||
if (!this.attrs.modifiersValue || !this.attrs.modifiersValue.required) {
|
||||
this.values = [[false, this.attrs.placeholder || ""]].concat(
|
||||
this.values
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Handlers
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onChange: function () {
|
||||
var value = JSON.parse(this.$el.val());
|
||||
this._setValue(value.toString());
|
||||
},
|
||||
});
|
||||
field_registry.add("dynamic_dropdown", FieldDynamicDropdown);
|
||||
|
||||
return FieldDynamicDropdown;
|
||||
});
|
|
@ -0,0 +1,182 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import {editInput, getFixture} from "@web/../tests/helpers/utils";
|
||||
import {makeView, setupViewRegistries} from "@web/../tests/views/helpers";
|
||||
const {QUnit} = window;
|
||||
|
||||
let serverData = {};
|
||||
let target = getFixture();
|
||||
|
||||
QUnit.module("web_widget_dropdown_dynamic", (hooks) => {
|
||||
hooks.beforeEach(() => {
|
||||
target = getFixture();
|
||||
serverData = {
|
||||
models: {
|
||||
"sale.order": {
|
||||
fields: {
|
||||
content_string: {string: "Content", type: "char"},
|
||||
bool_field: {string: "Boolean", type: "boolean"},
|
||||
content_integer: {string: "Integer", type: "integer"},
|
||||
change_field: {string: "Change", type: "char"},
|
||||
content_selection: {
|
||||
string: "Selection",
|
||||
type: "selection",
|
||||
selection: [["default", "Default"]],
|
||||
},
|
||||
},
|
||||
records: [
|
||||
{id: 1, bool_field: false, change_field: ""},
|
||||
{id: 2, bool_field: true, change_field: ""},
|
||||
],
|
||||
methods: {
|
||||
method_name() {
|
||||
return Promise.resolve([["value a", "Value A"]]);
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
setupViewRegistries();
|
||||
});
|
||||
QUnit.test("values are fetched with changing context", async function (assert) {
|
||||
assert.expect(13);
|
||||
|
||||
await makeView({
|
||||
type: "form",
|
||||
resModel: "sale.order",
|
||||
serverData,
|
||||
arch: `
|
||||
<form>
|
||||
<field name="change_field"/>
|
||||
<field name="content_string" widget="dynamic_dropdown" options="{'values':'method_name'}" context="{'depending_on': change_field}" />
|
||||
</form>`,
|
||||
resId: 1,
|
||||
mockRPC: function (route, args) {
|
||||
assert.step(args.method);
|
||||
if (args.method === "method_name") {
|
||||
if (args.kwargs.context.depending_on === "step-1") {
|
||||
return Promise.resolve([["value", "Title"]]);
|
||||
} else if (args.kwargs.context.depending_on === "step-2") {
|
||||
return Promise.resolve([
|
||||
["value", "Title"],
|
||||
["value_2", "Title 2"],
|
||||
]);
|
||||
}
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
await editInput(target, ".o_field_widget[name='change_field'] input", "step-1");
|
||||
assert.containsN(target, "option", 2);
|
||||
assert.containsOnce(target, "option[value='\"value\"']");
|
||||
await editInput(target, ".o_field_widget[name='change_field'] input", "step-2");
|
||||
|
||||
assert.containsN(target, "option", 3);
|
||||
assert.containsOnce(target, "option[value='\"value\"']");
|
||||
assert.containsOnce(target, "option[value='\"value_2\"']");
|
||||
await editInput(
|
||||
target,
|
||||
".o_field_widget[name='change_field'] input",
|
||||
"step-other"
|
||||
);
|
||||
|
||||
assert.containsN(target, "option", 1);
|
||||
assert.verifySteps([
|
||||
"get_views",
|
||||
"read",
|
||||
"method_name",
|
||||
"method_name",
|
||||
"method_name",
|
||||
"method_name",
|
||||
]);
|
||||
});
|
||||
QUnit.test("values are fetched w/o context (char)", async (assert) => {
|
||||
assert.expect(6);
|
||||
console.log("Start assert", serverData);
|
||||
console.log("Start makeView");
|
||||
await makeView({
|
||||
type: "form",
|
||||
resModel: "sale.order",
|
||||
serverData,
|
||||
arch: `
|
||||
<form>
|
||||
<field name="bool_field"/>
|
||||
<field name="content_string" widget="dynamic_dropdown" options="{'values':'method_name'}" context="{'depending_on': bool_field}" />
|
||||
</form>`,
|
||||
resId: 2,
|
||||
mockRPC(route, args) {
|
||||
assert.step(args.method);
|
||||
if (args.method === "method_name") {
|
||||
if (args.kwargs.context.depending_on) {
|
||||
return Promise.resolve([["value b", "Value B"]]);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
const field_target = target.querySelector("div[name='content_string']");
|
||||
assert.verifySteps(["get_views", "read", "method_name"]);
|
||||
assert.containsN(field_target, "option", 2);
|
||||
assert.containsOnce(
|
||||
field_target,
|
||||
"option[value='\"value b\"']",
|
||||
"got `value b` "
|
||||
);
|
||||
|
||||
console.log("Ending makeView", target);
|
||||
});
|
||||
|
||||
QUnit.test("values are fetched w/o context (integer)", async (assert) => {
|
||||
assert.expect(6);
|
||||
await makeView({
|
||||
type: "form",
|
||||
resModel: "sale.order",
|
||||
serverData,
|
||||
arch: `
|
||||
<form>
|
||||
<field name="bool_field"/>
|
||||
<field name="content_integer" widget="dynamic_dropdown" options="{'values':'method_name'}" context="{'depending_on': bool_field}" />
|
||||
</form>`,
|
||||
resId: 2,
|
||||
mockRPC(route, args) {
|
||||
assert.step(args.method);
|
||||
if (args.method === "method_name") {
|
||||
if (args.kwargs.context.depending_on) {
|
||||
return Promise.resolve([["10", "Value B"]]);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
const field_target = target.querySelector("div[name='content_integer']");
|
||||
assert.verifySteps(["get_views", "read", "method_name"]);
|
||||
assert.containsN(field_target, "option", 2);
|
||||
assert.containsOnce(field_target, 'option[value="10"]');
|
||||
});
|
||||
|
||||
QUnit.test("values are fetched w/o context (selection)", async (assert) => {
|
||||
assert.expect(6);
|
||||
await makeView({
|
||||
type: "form",
|
||||
resModel: "sale.order",
|
||||
serverData,
|
||||
arch: `
|
||||
<form>
|
||||
<field name="bool_field"/>
|
||||
<field name="content_selection" widget="dynamic_dropdown" options="{'values':'method_name'}" context="{'depending_on': bool_field}" />
|
||||
</form>`,
|
||||
resId: 2,
|
||||
mockRPC(route, args) {
|
||||
assert.step(args.method);
|
||||
if (args.method === "method_name") {
|
||||
if (args.kwargs.context.depending_on) {
|
||||
return Promise.resolve([["choice b", "Choice B"]]);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
const field_target = target.querySelector("div[name='content_selection']");
|
||||
assert.verifySteps(["get_views", "read", "method_name"]);
|
||||
assert.containsN(field_target, "option", 2);
|
||||
assert.containsOnce(field_target, "option[value='\"choice b\"']");
|
||||
});
|
||||
});
|
|
@ -1,187 +0,0 @@
|
|||
odoo.define(
|
||||
"web_widget_dropdown_dynamic.web_widget_dropdown_dynamic_tests",
|
||||
function (require) {
|
||||
"use strict";
|
||||
|
||||
/* global QUnit*/
|
||||
|
||||
var FormView = require("web.FormView");
|
||||
var testUtils = require("web.test_utils");
|
||||
|
||||
QUnit.module("web_widget_dropdown_dynamic", {}, function () {
|
||||
QUnit.test(
|
||||
"values are fetched w/o context (char)",
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var form = await testUtils.createView({
|
||||
View: FormView,
|
||||
model: "demo_entry",
|
||||
data: {
|
||||
demo_entry: {
|
||||
fields: {
|
||||
test_field: {string: "Test Field", type: "char"},
|
||||
},
|
||||
records: [{id: 1, test_field: ""}],
|
||||
},
|
||||
},
|
||||
arch:
|
||||
"<form>" +
|
||||
'<field name="test_field" widget="dynamic_dropdown" values="_get_test_field_values"/>' +
|
||||
"</form>",
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === "_get_test_field_values") {
|
||||
return Promise.resolve([["value", "Title"]]);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsN(form, "option", 2);
|
||||
assert.containsOnce(form, "option[value='\"value\"']");
|
||||
|
||||
form.destroy();
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"values are fetched w/o context (integer)",
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var form = await testUtils.createView({
|
||||
View: FormView,
|
||||
model: "demo_entry",
|
||||
data: {
|
||||
demo_entry: {
|
||||
fields: {
|
||||
test_field: {string: "Test Field", type: "integer"},
|
||||
},
|
||||
records: [{id: 1, test_field: 0}],
|
||||
},
|
||||
},
|
||||
arch:
|
||||
"<form>" +
|
||||
'<field name="test_field" widget="dynamic_dropdown" values="_get_test_field_values"/>' +
|
||||
"</form>",
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === "_get_test_field_values") {
|
||||
return Promise.resolve([[0, "Title"]]);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsN(form, "option", 2);
|
||||
assert.containsOnce(form, "option[value='0']");
|
||||
|
||||
form.destroy();
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"values are fetched w/o context (selection)",
|
||||
async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
var form = await testUtils.createView({
|
||||
View: FormView,
|
||||
model: "demo_entry",
|
||||
data: {
|
||||
demo_entry: {
|
||||
fields: {
|
||||
test_field: {
|
||||
string: "Test Field",
|
||||
type: "selection",
|
||||
},
|
||||
},
|
||||
records: [{id: 1, test_field: ""}],
|
||||
},
|
||||
},
|
||||
arch:
|
||||
"<form>" +
|
||||
'<field name="test_field" widget="dynamic_dropdown" values="_get_test_field_values"/>' +
|
||||
"</form>",
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === "_get_test_field_values") {
|
||||
return Promise.resolve([["value", "Title"]]);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsN(form, "option", 2);
|
||||
assert.containsOnce(form, "option[value='\"value\"']");
|
||||
|
||||
form.destroy();
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"values are fetched with changing context",
|
||||
async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
var form = await testUtils.createView({
|
||||
View: FormView,
|
||||
model: "demo_entry",
|
||||
data: {
|
||||
demo_entry: {
|
||||
fields: {
|
||||
other_field: {string: "Other Field", type: "char"},
|
||||
test_field: {string: "Test Field", type: "char"},
|
||||
},
|
||||
records: [{id: 1, other_field: "", test_field: ""}],
|
||||
},
|
||||
},
|
||||
arch:
|
||||
"<form>" +
|
||||
'<field name="other_field" />' +
|
||||
'<field name="test_field" widget="dynamic_dropdown" values="_get_test_field_values" context="{\'step\': other_field}"/>' +
|
||||
"</form>",
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === "_get_test_field_values") {
|
||||
if (args.kwargs.context.step === "step-1") {
|
||||
return Promise.resolve([["value", "Title"]]);
|
||||
} else if (args.kwargs.context.step === "step-2") {
|
||||
return Promise.resolve([
|
||||
["value", "Title"],
|
||||
["value_2", "Title 2"],
|
||||
]);
|
||||
}
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
await testUtils.fields.editAndTrigger(
|
||||
form.$('.o_field_widget[name="other_field"]'),
|
||||
"step-1",
|
||||
["input"]
|
||||
);
|
||||
assert.containsN(form, "option", 2);
|
||||
assert.containsOnce(form, "option[value='\"value\"']");
|
||||
|
||||
await testUtils.fields.editAndTrigger(
|
||||
form.$('.o_field_widget[name="other_field"]'),
|
||||
"step-2",
|
||||
["input"]
|
||||
);
|
||||
assert.containsN(form, "option", 3);
|
||||
assert.containsOnce(form, "option[value='\"value\"']");
|
||||
assert.containsOnce(form, "option[value='\"value_2\"']");
|
||||
|
||||
await testUtils.fields.editAndTrigger(
|
||||
form.$('.o_field_widget[name="other_field"]'),
|
||||
"step-other",
|
||||
["input"]
|
||||
);
|
||||
assert.containsN(form, "option", 1);
|
||||
|
||||
form.destroy();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
Loading…
Reference in New Issue