From d682871a8bd393c1d497e57675904d10355331d1 Mon Sep 17 00:00:00 2001 From: FrancoisGe Date: Tue, 19 Sep 2023 13:18:46 +0200 Subject: [PATCH] [FIX] *: Wrong usage of onWillUpdateProps in Field Since the new model (PR: odoo/odoo#114024), updating a record no longer triggers a deep render and therefore no longer triggers the onWillUpdateProps for Field components. The goal of this commit is to adapt the usage of onWillUpdateProps in Field composents in order to fix the bugs introduced by the RelationalModel closes odoo/odoo#135842 Signed-off-by: Aaron Bohy (aab) --- .../account_payment_field/account_payment.xml | 13 +++--- .../account_payment_field.js | 29 +++++++------ .../account_resequence/account_resequence.xml | 7 +-- .../account_resequence_field.js | 12 ++---- .../autosave_many2many_tags.js | 12 +++--- .../grouped_view_widget.js | 14 +++--- .../grouped_view_widget.xml | 13 +++--- .../static/src/js/mass_mailing_html_field.js | 21 ++++----- addons/mrp/static/src/widgets/timer.js | 17 ++++---- .../stock/static/src/widgets/json_widget.js | 10 ++--- .../views/fields/image_url/image_url_field.js | 11 +++-- .../views/fields/image_url_field_tests.js | 43 ++++++++++++++++++- 12 files changed, 115 insertions(+), 87 deletions(-) diff --git a/addons/account/static/src/components/account_payment_field/account_payment.xml b/addons/account/static/src/components/account_payment_field/account_payment.xml index 344a24c9038..2fc8b32cee9 100644 --- a/addons/account/static/src/components/account_payment_field/account_payment.xml +++ b/addons/account/static/src/components/account_payment_field/account_payment.xml @@ -4,15 +4,16 @@
- + +
- +
- + - + - + diff --git a/addons/account/static/src/components/account_payment_field/account_payment_field.js b/addons/account/static/src/components/account_payment_field/account_payment_field.js index a38a11615aa..543031fce5a 100644 --- a/addons/account/static/src/components/account_payment_field/account_payment_field.js +++ b/addons/account/static/src/components/account_payment_field/account_payment_field.js @@ -10,7 +10,7 @@ import { parseDate, formatDate } from "@web/core/l10n/dates"; import { formatMonetary } from "@web/views/fields/formatters"; import { standardFieldProps } from "@web/views/fields/standard_field_props"; -const { Component, onWillUpdateProps } = owl; +const { Component } = owl; class AccountPaymentPopOver extends Component {} AccountPaymentPopOver.props = { @@ -26,30 +26,31 @@ export class AccountPaymentField extends Component { this.popover = usePopover(AccountPaymentPopOver, { position }); this.orm = useService("orm"); this.action = useService("action"); - - this.formatData(this.props); - onWillUpdateProps((nextProps) => this.formatData(nextProps)); } - formatData(props) { - const info = props.record.data[props.name] || { + getInfo() { + const info = this.props.record.data[this.props.name] || { content: [], outstanding: false, title: "", move_id: this.props.record.resId, }; - for (let [key, value] of Object.entries(info.content)) { + for (const [key, value] of Object.entries(info.content)) { value.index = key; - value.amount_formatted = formatMonetary(value.amount, { currencyId: value.currency_id }); + value.amount_formatted = formatMonetary(value.amount, { + currencyId: value.currency_id, + }); if (value.date) { // value.date is a string, parse to date and format to the users date format value.date = formatDate(parseDate(value.date)); } } - this.lines = info.content; - this.outstanding = info.outstanding; - this.title = info.title; - this.move_id = info.move_id; + return { + lines: info.content, + outstanding: info.outstanding, + title: info.title, + moveId: info.move_id, + }; } onInfoClick(ev, idx) { @@ -61,8 +62,8 @@ export class AccountPaymentField extends Component { }); } - async assignOutstandingCredit(id) { - await this.orm.call(this.props.record.resModel, 'js_assign_outstanding_line', [this.move_id, id], {}); + async assignOutstandingCredit(moveId, id) { + await this.orm.call(this.props.record.resModel, 'js_assign_outstanding_line', [moveId, id], {}); await this.props.record.model.root.load(); } diff --git a/addons/account/static/src/components/account_resequence/account_resequence.xml b/addons/account/static/src/components/account_resequence/account_resequence.xml index a18065b84b5..2578c6552af 100644 --- a/addons/account/static/src/components/account_resequence/account_resequence.xml +++ b/addons/account/static/src/components/account_resequence/account_resequence.xml @@ -2,15 +2,16 @@ -
Add + t-on-click.prevent="() => this.assignOutstandingCredit(info.moveId, line.id)">Add
+ +
- - + +
Date Before After
diff --git a/addons/account/static/src/components/account_resequence/account_resequence_field.js b/addons/account/static/src/components/account_resequence/account_resequence_field.js index 4f81c146357..85c62be3b43 100644 --- a/addons/account/static/src/components/account_resequence/account_resequence_field.js +++ b/addons/account/static/src/components/account_resequence/account_resequence_field.js @@ -2,20 +2,16 @@ import { registry } from "@web/core/registry"; -const { Component, onWillUpdateProps } = owl; +const { Component } = owl; class ChangeLine extends Component {} ChangeLine.template = "account.ResequenceChangeLine"; ChangeLine.props = ["changeLine", "ordering"]; class ShowResequenceRenderer extends Component { - setup() { - this.formatData(this.props); - onWillUpdateProps((nextProps) => this.formatData(nextProps)); - } - - formatData(props) { - this.data = props.record.data[props.name] ? JSON.parse(props.record.data[props.name]) : { changeLines: [], ordering: "date" }; + getValue() { + const value = this.props.record.data[this.props.name]; + return value ? JSON.parse(value) : { changeLines: [], ordering: "date" }; } } ShowResequenceRenderer.template = "account.ResequenceRenderer"; diff --git a/addons/account/static/src/components/autosave_many2many_tags/autosave_many2many_tags.js b/addons/account/static/src/components/autosave_many2many_tags/autosave_many2many_tags.js index 8c1860d5c39..37e546f2652 100644 --- a/addons/account/static/src/components/autosave_many2many_tags/autosave_many2many_tags.js +++ b/addons/account/static/src/components/autosave_many2many_tags/autosave_many2many_tags.js @@ -1,16 +1,13 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; +import { useRecordObserver } from "@web/model/relational_model/utils"; import { Many2ManyTagsField, many2ManyTagsField } from "@web/views/fields/many2many_tags/many2many_tags_field"; -const { onWillUpdateProps } = owl; - export class AutosaveMany2ManyTagsField extends Many2ManyTagsField { setup() { super.setup(); - onWillUpdateProps((nextProps) => this.willUpdateProps(nextProps)); - this.lastBalance = this.props.record.data.balance; this.lastAccount = this.props.record.data.account_id; this.lastPartner = this.props.record.data.partner_id; @@ -20,6 +17,7 @@ export class AutosaveMany2ManyTagsField extends Many2ManyTagsField { super_update(recordlist); this._saveOnUpdate(); }; + useRecordObserver(this.onRecordChange.bind(this)); } async deleteTag(id) { @@ -27,8 +25,8 @@ export class AutosaveMany2ManyTagsField extends Many2ManyTagsField { await this._saveOnUpdate(); } - willUpdateProps(nextProps) { - const line = this.props.record.data; + onRecordChange(record) { + const line = record.data; if (line.tax_ids.records.length > 0) { if (line.balance !== this.lastBalance || line.account_id[0] !== this.lastAccount[0] @@ -36,7 +34,7 @@ export class AutosaveMany2ManyTagsField extends Many2ManyTagsField { this.lastBalance = line.balance; this.lastAccount = line.account_id; this.lastPartner = line.partner_id; - this._saveOnUpdate(); + return record.model.root.save(); } } } diff --git a/addons/account/static/src/components/grouped_view_widget/grouped_view_widget.js b/addons/account/static/src/components/grouped_view_widget/grouped_view_widget.js index 77021996ae2..28fcc7d0d8c 100644 --- a/addons/account/static/src/components/grouped_view_widget/grouped_view_widget.js +++ b/addons/account/static/src/components/grouped_view_widget/grouped_view_widget.js @@ -2,7 +2,7 @@ import { registry } from "@web/core/registry"; -const { Component, onWillUpdateProps } = owl; +const { Component } = owl; class ListItem extends Component {} ListItem.template = "account.GroupedItemTemplate"; @@ -14,14 +14,10 @@ ListGroup.components = { ListItem }; ListGroup.props = ["group_vals", "options"]; class ShowGroupedList extends Component { - setup() { - this.formatData(this.props); - onWillUpdateProps((nextProps) => this.formatData(nextProps)); - } - - formatData(props) { - this.data = props.record.data[props.name] - ? JSON.parse(props.record.data[props.name]) + getValue() { + const value = this.props.record.data[this.props.name]; + return value + ? JSON.parse(value) : { groups_vals: [], options: { discarded_number: "", columns: [] } }; } } diff --git a/addons/account/static/src/components/grouped_view_widget/grouped_view_widget.xml b/addons/account/static/src/components/grouped_view_widget/grouped_view_widget.xml index 12ce877604e..b4faa939593 100644 --- a/addons/account/static/src/components/grouped_view_widget/grouped_view_widget.xml +++ b/addons/account/static/src/components/grouped_view_widget/grouped_view_widget.xml @@ -1,18 +1,19 @@ - + +
- + - - + +
- - are not shown in the preview + + are not shown in the preview diff --git a/addons/mass_mailing/static/src/js/mass_mailing_html_field.js b/addons/mass_mailing/static/src/js/mass_mailing_html_field.js index beafe5a910d..24e8f24d42e 100644 --- a/addons/mass_mailing/static/src/js/mass_mailing_html_field.js +++ b/addons/mass_mailing/static/src/js/mass_mailing_html_field.js @@ -2,6 +2,7 @@ import { registry } from "@web/core/registry"; import { _t } from "@web/core/l10n/translation"; +import { useRecordObserver } from "@web/model/relational_model/utils"; import { standardFieldProps } from "@web/views/fields/standard_field_props"; import { initializeDesignTabCss } from "@mass_mailing/js/mass_mailing_design_constants" import { toInline, getCSSRules } from "@web_editor/js/backend/convert_inline"; @@ -15,11 +16,7 @@ import { getRangePosition } from '@web_editor/js/editor/odoo-editor/src/utils/ut import { MassMailingWysiwyg } from '@mass_mailing/js/mass_mailing_wysiwyg'; import { utils as uiUtils } from "@web/core/ui/ui_service"; -const { - useSubEnv, - onWillUpdateProps, - status, -} = owl; +const { useSubEnv, status } = owl; export class MassMailingHtmlField extends HtmlField { static props = { @@ -44,9 +41,9 @@ export class MassMailingHtmlField extends HtmlField { this.rpc = useService('rpc'); this.dialog = useService('dialog'); - onWillUpdateProps(() => { - if (this.props.record.data.mailing_model_id && this.wysiwyg) { - this._hideIrrelevantTemplates(); + useRecordObserver((record) => { + if (record.data.mailing_model_id && this.wysiwyg) { + this._hideIrrelevantTemplates(record); } }); } @@ -453,7 +450,7 @@ export class MassMailingHtmlField extends HtmlField { this.wysiwyg.$iframeBody.find('.iframe-utils-zone').removeClass('d-none'); if (this.env.mailingFilterTemplates && this.wysiwyg) { - this._hideIrrelevantTemplates(); + this._hideIrrelevantTemplates(this.props.record); } } _getCodeViewEl() { @@ -474,10 +471,10 @@ export class MassMailingHtmlField extends HtmlField { * * @private */ - _hideIrrelevantTemplates() { + _hideIrrelevantTemplates(record) { const iframeContent = this.wysiwyg.$iframe.contents(); - const mailing_model_id = this.props.record.data.mailing_model_id[0]; + const mailing_model_id = record.data.mailing_model_id[0]; iframeContent .find(`.o_mail_template_preview[model-id!="${mailing_model_id}"]`) .addClass('d-none') @@ -496,7 +493,7 @@ export class MassMailingHtmlField extends HtmlField { iframeContent.find('.o_mailing_template_preview_wrapper').removeClass('d-none'); } else { iframeContent.find('.o_mailing_template_message').removeClass('d-none'); - iframeContent.find('.o_mailing_template_message span').text(this.props.record.data.mailing_model_id[1]); + iframeContent.find('.o_mailing_template_message span').text(record.data.mailing_model_id[1]); iframeContent.find('.o_mailing_template_preview_wrapper').addClass('d-none'); } } diff --git a/addons/mrp/static/src/widgets/timer.js b/addons/mrp/static/src/widgets/timer.js index d878ed4a49d..51b9e202c90 100644 --- a/addons/mrp/static/src/widgets/timer.js +++ b/addons/mrp/static/src/widgets/timer.js @@ -95,10 +95,6 @@ class MrpTimerField extends Component { parse: (v) => parseFloatTime(v), }); - // duration is expected to be given in minutes - this.duration = this.props.record.data[this.props.name]; - this.ongoing = this.props.record.data.is_user_working; - onWillStart(async () => { if ( !this.props.record.model.useSampleModel && @@ -111,10 +107,6 @@ class MrpTimerField extends Component { ); } }); - onWillUpdateProps((nextProps) => { - this.ongoing = nextProps.record.data.is_user_working; - this.duration = nextProps.record.data[nextProps.name]; - }); onWillDestroy(() => clearTimeout(this.timer)); } @@ -125,6 +117,15 @@ class MrpTimerField extends Component { } return formatMinutes(this.duration); } + + get ongoing() { + return this.props.record.data.is_user_working; + } + + get duration() { + // duration is expected to be given in minutes + return this.props.record.data[this.props.name]; + } } export const mrpTimerField = { diff --git a/addons/stock/static/src/widgets/json_widget.js b/addons/stock/static/src/widgets/json_widget.js index 909eabaa2e1..cd5381def44 100644 --- a/addons/stock/static/src/widgets/json_widget.js +++ b/addons/stock/static/src/widgets/json_widget.js @@ -4,15 +4,11 @@ import { registry } from "@web/core/registry"; import { _t } from "@web/core/l10n/translation"; import { useService } from "@web/core/utils/hooks"; -const { Component, onWillStart, onWillUpdateProps } = owl; +const { Component, onWillStart } = owl; export class JsonPopOver extends Component { - - setup(){ - this.jsonValue = JSON.parse(this.props.record.data[this.props.name]); - onWillUpdateProps(nextProps => { - this.jsonValue = JSON.parse(nextProps.record.data[nextProps.name]); - }); + get jsonValue() { + return JSON.parse(this.props.record.data[this.props.name]); } } diff --git a/addons/web/static/src/views/fields/image_url/image_url_field.js b/addons/web/static/src/views/fields/image_url/image_url_field.js index e6b7f4a0be1..b4950c8cd2e 100644 --- a/addons/web/static/src/views/fields/image_url/image_url_field.js +++ b/addons/web/static/src/views/fields/image_url/image_url_field.js @@ -1,11 +1,12 @@ /** @odoo-module **/ +import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { _t } from "@web/core/l10n/translation"; import { standardFieldProps } from "../standard_field_props"; -import { Component, onWillUpdateProps, useState } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl"; +import { useRecordObserver } from "@web/model/relational_model/utils"; export class ImageUrlField extends Component { static template = "web.ImageUrlField"; @@ -23,10 +24,8 @@ export class ImageUrlField extends Component { src: this.props.record.data[this.props.name], }); - onWillUpdateProps((nextProps) => { - if (this.props.record.data[this.props.name] !== nextProps.record.data[nextProps.name]) { - this.state.value = nextProps.record.data[nextProps.name]; - } + useRecordObserver((record) => { + this.state.src = record.data[this.props.name]; }); } diff --git a/addons/web/static/tests/views/fields/image_url_field_tests.js b/addons/web/static/tests/views/fields/image_url_field_tests.js index 274562f0b5b..37b291a3518 100644 --- a/addons/web/static/tests/views/fields/image_url_field_tests.js +++ b/addons/web/static/tests/views/fields/image_url_field_tests.js @@ -1,7 +1,13 @@ /** @odoo-module **/ import { KanbanController } from "@web/views/kanban/kanban_controller"; -import { click, getFixture, patchWithCleanup } from "@web/../tests/helpers/utils"; +import { + click, + editInput, + getFixture, + nextTick, + patchWithCleanup, +} from "@web/../tests/helpers/utils"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; let serverData; @@ -238,4 +244,39 @@ QUnit.module("Fields", (hooks) => { "the widget should not contain an image" ); }); + + QUnit.test("onchange update image fields", async function (assert) { + const srcTest = "/my/test/src"; + serverData.models.partner.onchanges = { + display_name(record) { + record.foo = srcTest; + }, + }; + + await makeView({ + serverData, + type: "form", + resModel: "partner", + arch: ` +
+ + + `, + resId: 1, + }); + + assert.strictEqual( + target.querySelector('div[name="foo"] > img').dataset.src, + FR_FLAG_URL, + "the image should have the correct src" + ); + + await editInput(target, '[name="display_name"] input', "test"); + await nextTick(); + assert.strictEqual( + target.querySelector('div[name="foo"] > img').dataset.src, + srcTest, + "the image should have the onchange src" + ); + }); });