From 64037d03d0076db5f02469f8bfe6306c987a094a Mon Sep 17 00:00:00 2001 From: FrancoisGe Date: Fri, 27 Oct 2023 15:09:32 +0200 Subject: [PATCH] [FIX] web: FormStatusIndicator flickering Purpose: -------- Before this commit, if you edit a char field with an onchange in an x2many, the FormStatusIndicator buttons will flicker. How to reproduce: ----------------- - Go to a form view with an x2many field in list mode - Type a char in the char field of a record - The FormStatusIndicator buttons are visible - Click outside the char field - An onchange is triggered Before this commit: The FormStatusIndicator buttons will disappear until the onchange returns. After this commit: The FormStatusIndicator buttons remain visible. closes odoo/odoo#140179 Signed-off-by: Pierre Rousseau (pro) --- .../src/views/fields/input_field_hook.js | 15 +++--- .../tests/views/fields/char_field_tests.js | 54 +++++++++++++++++++ 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/addons/web/static/src/views/fields/input_field_hook.js b/addons/web/static/src/views/fields/input_field_hook.js index b44054d60e7..8f39485ac17 100644 --- a/addons/web/static/src/views/fields/input_field_hook.js +++ b/addons/web/static/src/views/fields/input_field_hook.js @@ -53,7 +53,7 @@ export function useInputField(params) { * On blur, we consider the field no longer dirty, even if it were to be invalid. * However, if the field is invalid, the new value will not be committed to the model. */ - function onChange(ev) { + async function onChange(ev) { if (isDirty) { isDirty = false; let isInvalid = false; @@ -69,15 +69,12 @@ export function useInputField(params) { if (!isInvalid) { pendingUpdate = true; - Promise.resolve( - component.props.record.update({ [component.props.name]: val }) - ).then(() => { - pendingUpdate = false; - }); - lastSetValue = ev.target.value; - } + lastSetValue = inputRef.el.value; - component.props.record.model.bus.trigger("FIELD_IS_DIRTY", isDirty); + await component.props.record.update({ [component.props.name]: val }); + pendingUpdate = false; + component.props.record.model.bus.trigger("FIELD_IS_DIRTY", isDirty); + } } } function onKeydown(ev) { diff --git a/addons/web/static/tests/views/fields/char_field_tests.js b/addons/web/static/tests/views/fields/char_field_tests.js index 79eeee289b6..cd0346cccc6 100644 --- a/addons/web/static/tests/views/fields/char_field_tests.js +++ b/addons/web/static/tests/views/fields/char_field_tests.js @@ -1110,4 +1110,58 @@ QUnit.module("Fields", (hooks) => { assert.containsNone(target, "[name='display_name'] input"); } ); + + QUnit.test( + "edit a char field should display the status indicator buttons without flickering", + async function (assert) { + serverData.models.partner.records[0].p = [2]; + serverData.models.partner.onchanges = { + foo() {}, + }; + + const def = makeDeferred(); + await makeView({ + type: "form", + resModel: "partner", + serverData, + resId: 1, + arch: ` +
+ + + + + +
`, + async mockRPC(route, { method }) { + if (method === "onchange") { + assert.step("onchange"); + await def; + } + }, + }); + assert.containsOnce( + target, + ".o_form_status_indicator_buttons.invisible", + "form view is not dirty" + ); + + await click(target, ".o_data_cell"); + await editInput(target, "[name='foo'] input", "a"); + assert.verifySteps(["onchange"]); + assert.containsOnce( + target, + ".o_form_status_indicator_buttons:not(.invisible)", + "form view is dirty" + ); + + def.resolve(); + await nextTick(); + assert.containsOnce( + target, + ".o_form_status_indicator_buttons:not(.invisible)", + "form view is dirty" + ); + } + ); });