[FIX] web: Input Field always display the correct value
Before this commit, a Field using the useInputField hook can ignore values
from an onchange in some cases.
Problem:
=======
In order to know if it should apply the value of an onchange on the field's input,
the useInputField hook uses a useEffect which will check if the field is
not dirty (being edited) and if it is not invalid. If it true, it will apply
the value returned by getValue which often corresponds to record.data[fieldName].
By executing getValue, it will therefore start to observe changes in record.data[fieldName]
until the next render. If getValue is not executed then changes applied
by onchange on record.data[fieldName] are ignored because we don't observe record.data[fieldName].
Solution:
=======
Always execute getValue in the useEffect to always observe changes on
record.data[fieldName] and apply them correctly on the input if needed.
(It s a hack but we don't have a better solution for now)
How to reproduce:
================
- Go to a Form view with a field B with onchange and a field A (char field)
- Edit field B (an onchange is in progress)
- Insert a value in the input of field A
- The onchange is resolved with a value for field A
- The value in the input for field A does not change (because the field is in edition)
- Click outside the input for field A (trigger change event)
- Edit field B (an onchange is executed directly)
Before this commit:
The value returned by the second onchange is not visually applied to
field A. The old value is still present in the input.
After this commit:
The value returned by the second onchange is correctly applied to field A.
Its input contains the value of the onchange.
closes odoo/odoo#148711
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
@@ -110,12 +110,16 @@ export function useInputField(params) {
|
||||
* If it is not such a case, we update the field with the new value.
|
||||
*/
|
||||
useEffect(() => {
|
||||
// We need to call getValue before the condition to always observe
|
||||
// the corresponding value in the record. Otherwise, in some cases,
|
||||
// if the value in the record change the useEffect isn't triggered.
|
||||
const value = params.getValue();
|
||||
if (
|
||||
inputRef.el &&
|
||||
!isDirty &&
|
||||
!component.props.record.isFieldInvalid(component.props.name)
|
||||
) {
|
||||
inputRef.el.value = params.getValue();
|
||||
inputRef.el.value = value;
|
||||
lastSetValue = inputRef.el.value;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -721,6 +721,77 @@ QUnit.module("Fields", (hooks) => {
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"input field: change value before pending onchange returns (2)",
|
||||
async function (assert) {
|
||||
serverData.models.partner.onchanges = {
|
||||
int_field(obj) {
|
||||
if (obj.int_field === 7) {
|
||||
obj.foo = "blabla";
|
||||
} else {
|
||||
obj.foo = "tralala";
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const def = makeDeferred();
|
||||
await makeView({
|
||||
type: "form",
|
||||
resModel: "partner",
|
||||
resId: 1,
|
||||
serverData,
|
||||
arch: `
|
||||
<form>
|
||||
<sheet>
|
||||
<field name="int_field" />
|
||||
<field name="foo" />
|
||||
</sheet>
|
||||
</form>`,
|
||||
async mockRPC(route, { method }) {
|
||||
if (method === "onchange") {
|
||||
await def;
|
||||
}
|
||||
},
|
||||
});
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_field_widget[name='foo'] input").value,
|
||||
"yop",
|
||||
"should contain the correct value"
|
||||
);
|
||||
|
||||
// trigger a deferred onchange
|
||||
await editInput(target, ".o_field_widget[name='int_field'] input", "7");
|
||||
|
||||
// insert a value in input foo
|
||||
target.querySelector(".o_field_widget[name=foo] input").value = "test";
|
||||
await triggerEvent(target, ".o_field_widget[name=foo] input", "input");
|
||||
|
||||
// complete the onchange
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_field_widget[name='foo'] input").value,
|
||||
"test",
|
||||
"The onchange value should not be applied because the input is in edition"
|
||||
);
|
||||
|
||||
// apply the value of the input foo
|
||||
await triggerEvent(target, ".o_field_widget[name=foo] input", "change");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_field_widget[name='foo'] input").value,
|
||||
"test"
|
||||
);
|
||||
|
||||
// trigger another onchange (not deferred)
|
||||
await editInput(target, ".o_field_widget[name='int_field'] input", "10");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_field_widget[name='foo'] input").value,
|
||||
"tralala",
|
||||
"the onchange value should be applied because the input is not in edition"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"input field: change value before pending onchange returns (with fieldDebounce)",
|
||||
async function (assert) {
|
||||
|
||||
Reference in New Issue
Block a user