[FIX] web: calculate specialData when record is updated
The goal of this commit is to cause a recalculation of specialData when
this depends on a record value.
Currently, specialData are only calculated during onWillStart and
onWillUpdateProps. But the specialData of certain fields do not depend
only on the props, they can also depend on the values present in the record.
We therefore want to recalculate them when the value of the record changes.
How to reproduce:
- Go to a form view with a field "x" and
a field with specialData dependent on field "x".
- Edit the field "x"
Before this commit:
The specialData have not changed
After this commit:
The specialData have been recalculated.
closes odoo/odoo#138372
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
@@ -11,7 +11,7 @@ export class Many2ManyAttendee extends Many2ManyTagsAvatarField {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.specialData = useSpecialData((orm, props) => {
|
||||
const { context, name, record } = this.props;
|
||||
const { context, name, record } = props;
|
||||
return orm.call(
|
||||
"res.partner",
|
||||
"get_attendee_detail",
|
||||
|
||||
@@ -614,7 +614,7 @@ export function useRecordObserver(callback) {
|
||||
async (record) => {
|
||||
if (firstCall) {
|
||||
firstCall = false;
|
||||
await callback(record);
|
||||
await callback(record, props);
|
||||
def.resolve();
|
||||
} else {
|
||||
return batched(
|
||||
@@ -624,7 +624,7 @@ export function useRecordObserver(callback) {
|
||||
// We must do it manually.
|
||||
return;
|
||||
}
|
||||
await callback(record);
|
||||
await callback(record, props);
|
||||
def.resolve();
|
||||
},
|
||||
() => new Promise((resolve) => window.requestAnimationFrame(resolve))
|
||||
|
||||
@@ -18,7 +18,7 @@ import { createElement, parseXML } from "@web/core/utils/xml";
|
||||
import { FormArchParser } from "@web/views/form/form_arch_parser";
|
||||
import { loadSubViews } from "@web/views/form/form_controller";
|
||||
import { FormRenderer } from "@web/views/form/form_renderer";
|
||||
import { extractFieldsFromArchInfo } from "@web/model/relational_model/utils";
|
||||
import { extractFieldsFromArchInfo, useRecordObserver } from "@web/model/relational_model/utils";
|
||||
import { computeViewClassName, isNull } from "@web/views/utils";
|
||||
import { ViewButton } from "@web/views/view_button/view_button";
|
||||
import { useViewButtons } from "@web/views/view_button/view_button_hook";
|
||||
@@ -39,11 +39,11 @@ import { SelectCreateDialog } from "@web/views/view_dialogs/select_create_dialog
|
||||
|
||||
import {
|
||||
Component,
|
||||
onWillStart,
|
||||
onWillUpdateProps,
|
||||
useComponent,
|
||||
useEffect,
|
||||
useEnv,
|
||||
useState,
|
||||
useSubEnv,
|
||||
} from "@odoo/owl";
|
||||
|
||||
@@ -156,12 +156,15 @@ export function useSpecialData(loadFn) {
|
||||
}
|
||||
ormWithCache.call = (...args) => specialDataCaches[key].read(...args);
|
||||
|
||||
const result = {};
|
||||
onWillStart(async () => {
|
||||
result.data = await loadFn(ormWithCache, component.props);
|
||||
const result = useState({});
|
||||
useRecordObserver(async (record, props) => {
|
||||
result.data = await loadFn(ormWithCache, { ...props, record });
|
||||
});
|
||||
onWillUpdateProps(async (props) => {
|
||||
result.data = await loadFn(ormWithCache, props);
|
||||
// useRecordObserver callback is not called when the record doesn't change
|
||||
if (props.record.id === component.props.record.id) {
|
||||
result.data = await loadFn(ormWithCache, props);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -407,6 +407,7 @@ QUnit.module("Fields", (hooks) => {
|
||||
".o_statusbar_status button:not(.dropdown-toggle)"
|
||||
);
|
||||
await click(buttons[buttons.length - 1]);
|
||||
await nextTick();
|
||||
assert.containsN(target, ".o_statusbar_status button:not(.dropdown-toggle)", 2);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -54,6 +54,7 @@ import { CharField } from "@web/views/fields/char/char_field";
|
||||
import { DateTimeField } from "@web/views/fields/datetime/datetime_field";
|
||||
import { Field } from "@web/views/fields/field";
|
||||
import { IntegerField } from "@web/views/fields/integer/integer_field";
|
||||
import { useSpecialData } from "@web/views/fields/relational_utils";
|
||||
import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field";
|
||||
import { FormController } from "@web/views/form/form_controller";
|
||||
import { companyService } from "@web/webclient/company_service";
|
||||
@@ -14262,4 +14263,39 @@ QUnit.module("Views", (hooks) => {
|
||||
});
|
||||
assert.strictEqual(target.querySelector(".o_breadcrumb").innerText, "Unnamed");
|
||||
});
|
||||
|
||||
QUnit.test("field with special data", async function (assert) {
|
||||
class MyWidget extends Component {
|
||||
static template = xml`<div>MyWidget</div>`;
|
||||
setup() {
|
||||
this.specialData = useSpecialData((orm, props) => {
|
||||
const { record } = props;
|
||||
return orm.call("my.model", "get_special_data", [record.data.int_field]);
|
||||
});
|
||||
}
|
||||
}
|
||||
widgetRegistry.add("my_widget", {
|
||||
component: MyWidget,
|
||||
});
|
||||
await makeView({
|
||||
type: "form",
|
||||
resModel: "partner",
|
||||
serverData,
|
||||
arch: `
|
||||
<form>
|
||||
<field name="int_field" />
|
||||
<widget name="my_widget" />
|
||||
</form>`,
|
||||
resId: 2,
|
||||
async mockRPC(route, args) {
|
||||
if (args.method === "get_special_data") {
|
||||
assert.step(`get_special_data ${args.args[0]}`);
|
||||
return {};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
await editInput(target, "[name='int_field'] input", "42");
|
||||
assert.verifySteps(["get_special_data 9", "get_special_data 42"]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user