[FIX] web: correctly update input field after onchange
In a form view, have an x2many list with two fields A and B (e.g. A is a many2one, and B is a char). There is an onchange on the x2many that is triggered when the user sets A, and that sets B. In this scenario, let's assume that the user first writes something in B, but directly deletes it. Then, he sets A. Before this commit, in this situation, B wasn't updated with the value returned by the onchange. Indeed, B's field widget was still flagged as 'isDirty', because the user interacted with it, but it didn't commit its value, as it hasn't actually changed. Basically, it remained flagged as 'isDirty' forever, and thus couldn't be updated with the value returned by the onchange. This commit ensures that the 'isDirty' status is correctly updated when the change is actually undone. Task 1958793 closes odoo/odoo#51085 Signed-off-by: Lucas Perais (lpe) <lpe@odoo.com> Co-authored-by: pka-odoo <pka@odoo.com> Co-authored-by: Mohammed Shekha <msh@odoo.com>
This commit is contained in:
co-authored by
pka-odoo
Mohammed Shekha
parent
e02a4b93e5
commit
efe10976a4
@@ -403,6 +403,18 @@ var AbstractField = Widget.extend({
|
||||
_getClassFromDecoration: function (decoration) {
|
||||
return `text-${decoration.split('-')[1]}`;
|
||||
},
|
||||
/**
|
||||
* Compares the given value with the last value that has been set.
|
||||
* Note that we compare unparsed values. Handles the special case where no
|
||||
* value has been set yet, and the given value is the empty string.
|
||||
*
|
||||
* @private
|
||||
* @param {any} value
|
||||
* @returns {boolean} true iff values are the same
|
||||
*/
|
||||
_isLastSetValue: function (value) {
|
||||
return this.lastSetValue === value || (this.value === false && value === '');
|
||||
},
|
||||
/**
|
||||
* This method check if a value is the same as the current value of the
|
||||
* field. For example, a fieldDate widget might want to use the moment
|
||||
@@ -499,9 +511,8 @@ var AbstractField = Widget.extend({
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_setValue: function (value, options) {
|
||||
// we try to avoid doing useless work, if the value given has not
|
||||
// changed. Note that we compare the unparsed values.
|
||||
if (this.lastSetValue === value || (this.value === false && value === '')) {
|
||||
// we try to avoid doing useless work, if the value given has not changed.
|
||||
if (this._isLastSetValue(value)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
this.lastSetValue = value;
|
||||
|
||||
@@ -453,6 +453,18 @@ odoo.define('web.AbstractFieldOwl', function (require) {
|
||||
_getClassFromDecoration(decoration) {
|
||||
return `text-${decoration.split('-')[1]}`;
|
||||
}
|
||||
/**
|
||||
* Compares the given value with the last value that has been set.
|
||||
* Note that we compare unparsed values. Handles the special case where no
|
||||
* value has been set yet, and the given value is the empty string.
|
||||
*
|
||||
* @private
|
||||
* @param {any} value
|
||||
* @returns {boolean} true iff values are the same
|
||||
*/
|
||||
_isLastSetValue(value) {
|
||||
return this._lastSetValue === value || (this.value === false && value === '');
|
||||
}
|
||||
/**
|
||||
* This method check if a value is the same as the current value of the
|
||||
* field. For example, a fieldDate component might want to use the moment
|
||||
@@ -498,9 +510,8 @@ odoo.define('web.AbstractFieldOwl', function (require) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_setValue(value, options) {
|
||||
// we try to avoid doing useless work, if the value given has not
|
||||
// changed. Note that we compare the unparsed values.
|
||||
if (this._lastSetValue === value || (this.value === false && value === '')) {
|
||||
// we try to avoid doing useless work, if the value given has not changed.
|
||||
if (this._isLastSetValue(value)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
this._lastSetValue = value;
|
||||
|
||||
@@ -326,7 +326,7 @@ var InputField = DebouncedField.extend({
|
||||
* @private
|
||||
*/
|
||||
_onInput: function () {
|
||||
this.isDirty = true;
|
||||
this.isDirty = !this._isLastSetValue(this.$input.val());
|
||||
this._doDebouncedAction();
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -1743,6 +1743,54 @@ QUnit.module('basic_fields', {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('input field: set and remove value, then wait for onchange', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
this.data.partner.onchanges = {
|
||||
product_id(obj) {
|
||||
obj.foo = obj.product_id ? "onchange value" : false;
|
||||
},
|
||||
};
|
||||
|
||||
let def;
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<form>
|
||||
<field name="p">
|
||||
<tree editable="bottom">
|
||||
<field name="product_id"/>
|
||||
<field name="foo"/>
|
||||
</tree>
|
||||
</field>
|
||||
</form>`,
|
||||
async mockRPC(route, args) {
|
||||
const result = this._super(...arguments);
|
||||
if (args.method === "onchange") {
|
||||
await Promise.resolve(def);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
fieldDebounce: 1000, // needed to accurately mock what really happens
|
||||
});
|
||||
|
||||
await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a'));
|
||||
assert.strictEqual(form.$('input[name="foo"]').val(), "");
|
||||
|
||||
await testUtils.fields.editInput(form.$('input[name="foo"]'), "test"); // set value for foo
|
||||
await testUtils.fields.editInput(form.$('input[name="foo"]'), ""); // remove value for foo
|
||||
|
||||
// trigger the onchange by setting a product
|
||||
await testUtils.fields.many2one.clickOpenDropdown('product_id');
|
||||
await testUtils.fields.many2one.clickHighlightedItem('product_id');
|
||||
assert.strictEqual(form.$('input[name="foo"]').val(), 'onchange value',
|
||||
'input should contain correct value after onchange');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.module('UrlWidget');
|
||||
|
||||
QUnit.test('url widget in form view', async function (assert) {
|
||||
|
||||
Reference in New Issue
Block a user