[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) <aab@odoo.com>
This commit is contained in:
FrancoisGe
2023-09-22 11:55:55 +00:00
parent 8e1bbf526e
commit d682871a8b
12 changed files with 115 additions and 87 deletions
@@ -4,15 +4,16 @@
<t t-name="account.AccountPaymentField">
<div>
<t t-if="outstanding">
<t t-set="info" t-value="this.getInfo()"/>
<t t-if="info.outstanding">
<div>
<strong class="float-start" id="outstanding" t-out="title"/>
<strong class="float-start" id="info.outstanding" t-out="info.title"/>
</div>
</t>
<table style="width:100%;">
<t t-foreach="lines" t-as="line" t-key="line_index">
<t t-foreach="info.lines" t-as="line" t-key="line_index">
<tr>
<t t-if="outstanding">
<t t-if="info.outstanding">
<td>
<a title="assign to invoice"
role="button"
@@ -21,7 +22,7 @@
style="margin-right: 0px; padding-left: 5px; padding-right: 5px;"
href="#"
data-bs-toggle="tooltip"
t-on-click.prevent="() => this.assignOutstandingCredit(line.id)">Add</a>
t-on-click.prevent="() => this.assignOutstandingCredit(info.moveId, line.id)">Add</a>
</td>
<td style="max-width: 11em;">
<a t-att-title="line.date"
@@ -34,7 +35,7 @@
t-out="line.journal_name"/>
</td>
</t>
<t t-if="!outstanding">
<t t-if="!info.outstanding">
<td>
<a role="button" tabindex="0" class="js_payment_info fa fa-info-circle" t-att-index="line_index" style="margin-right:5px;" aria-label="Info" title="Journal Entry Info" data-bs-toggle="tooltip" t-on-click.stop="(ev) => this.onInfoClick(ev, line_index)"></a>
</td>
@@ -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();
}
@@ -2,15 +2,16 @@
<templates>
<t t-name="account.ResequenceRenderer" >
<table t-if="data.changeLines.length" class="table table-sm">
<t t-set="value" t-value="this.getValue()"/>
<table t-if="value.changeLines.length" class="table table-sm">
<thead><tr>
<th>Date</th>
<th>Before</th>
<th>After</th>
</tr></thead>
<tbody>
<t t-foreach="data.changeLines" t-as="changeLine" t-key="changeLine.id">
<ChangeLine changeLine="changeLine" ordering="data.ordering"/>
<t t-foreach="value.changeLines" t-as="changeLine" t-key="changeLine.id">
<ChangeLine changeLine="changeLine" ordering="value.ordering"/>
</t>
</tbody>
</table>
@@ -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";
@@ -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();
}
}
}
@@ -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: [] } };
}
}
@@ -1,18 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<templates>
<t t-name="account.GroupedListTemplate">
<table t-if="data.groups_vals.length" class="table table-sm o_list_table table table-sm table-hover table-striped o_list_table_grouped">
<t t-set="value" t-value="this.getValue()"/>
<table t-if="value.groups_vals.length" class="table table-sm o_list_table table table-sm table-hover table-striped o_list_table_grouped">
<thead><tr>
<t t-foreach="data.options.columns" t-as="col" t-key="col_index">
<t t-foreach="value.options.columns" t-as="col" t-key="col_index">
<th t-out="col['label']" t-attf-class="{{col['class']}}"/>
</t>
</tr></thead>
<t t-foreach="data.groups_vals" t-as="group_vals" t-key="group_vals_index">
<ListGroup group_vals="group_vals" options="data.options"/>
<t t-foreach="value.groups_vals" t-as="group_vals" t-key="group_vals_index">
<ListGroup group_vals="group_vals" options="value.options"/>
</t>
</table>
<t t-if="data.options.discarded_number">
<span><t t-out="data.options.discarded_number"/> are not shown in the preview</span>
<t t-if="value.options.discarded_number">
<span><t t-out="value.options.discarded_number"/> are not shown in the preview</span>
</t>
</t>
@@ -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');
}
}
+9 -8
View File
@@ -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 = {
@@ -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]);
}
}
@@ -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];
});
}
@@ -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: `
<form>
<field name="display_name"/>
<field name="foo" widget="image_url" options="{'size': [90, 90]}"/>
</form>`,
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"
);
});
});