[IMP] web: add a onRecordChanged prop to the Record component

Before this commit, there was no way to instanciate a Record component and be
able to handle changes occuring in that record.

After this commit, a simple props is added, making this use case possible.

closes odoo/odoo#109846

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Lucas Perais
2023-01-13 14:00:04 +01:00
parent 413a311125
commit 475f8aa2c6
2 changed files with 78 additions and 7 deletions
+31 -7
View File
@@ -5,21 +5,17 @@ import { useModel } from "@web/views/model";
import { RelationalModel } from "@web/views/relational_model";
import { Component, xml, onWillStart, onWillUpdateProps } from "@odoo/owl";
const defaultActiveField = { attrs: {}, options: {}, domain: "[]", string: "" };
class _Record extends Component {
setup() {
const activeFields =
this.props.info.activeFields ||
Object.fromEntries(
this.props.info.fieldNames.map((f) => [f, { attrs: {}, options: {}, domain: "[]" }])
);
this.model = useModel(RelationalModel, {
resId: this.props.info.resId,
resModel: this.props.info.resModel,
fields: this.props.fields,
viewMode: "form",
rootType: "record",
activeFields,
activeFields: this.getActiveFields(),
mode: this.props.info.mode === "edit" ? "edit" : undefined,
initialValues: this.props.info.initialValues,
});
@@ -29,9 +25,36 @@ class _Record extends Component {
mode: nextProps.info.mode,
});
});
if (this.props.info.onRecordChanged) {
const load = this.model.load;
this.model.load = async (...args) => {
const res = await load.call(this.model, ...args);
const root = this.model.root;
root.onChanges = async () => {
const changes = root.getChanges();
this.props.info.onRecordChanged(root, changes);
};
return res;
};
}
}
getActiveFields() {
if (this.props.info.activeFields) {
const activeFields = {};
for (const [fName, fInfo] of Object.entries(this.props.info.activeFields)) {
activeFields[fName] = { ...defaultActiveField, ...fInfo };
}
return activeFields;
}
return Object.fromEntries(
this.props.info.fieldNames.map((f) => [f, { ...defaultActiveField }])
);
}
}
_Record.template = xml`<t t-slot="default" record="model.root"/>`;
_Record.props = ["slots", "info", "fields"];
export class Record extends Component {
setup() {
@@ -61,4 +84,5 @@ Record.props = [
"resId?",
"mode?",
"initialValues?",
"onRecordChanged?",
];
@@ -7,6 +7,7 @@ import { click, getFixture, mount } from "../helpers/utils";
import { setupViewRegistries } from "../views/helpers";
import { Component, xml, useState } from "@odoo/owl";
import { editInput } from "@web/../tests/helpers/utils";
let serverData;
let target;
@@ -183,4 +184,50 @@ QUnit.module("Record Component", (hooks) => {
assert.verifySteps([]);
assert.strictEqual(target.querySelector(".o_field_widget input").value, "abc");
});
QUnit.test("provides a way to handle changes in the record", async function (assert) {
class Parent extends Component {
setup() {
this.fields = {
foo: {
name: "foo",
type: "char",
},
bar: {
name: "bar",
type: "boolean",
},
};
this.values = {
foo: "abc",
bar: true,
};
}
onRecordChanged(record, changes) {
assert.step("record changed");
assert.strictEqual(record.model.constructor.name, "RelationalModel");
assert.deepEqual(changes, { foo: "753" });
}
}
Parent.components = { Record, Field };
Parent.template = xml`
<Record resModel="'partner'" fieldNames="['foo']" fields="fields" initialValues="values" t-slot-scope="data" onRecordChanged.bind="onRecordChanged">
<Field name="'foo'" record="data.record"/>
</Record>
`;
await mount(Parent, target, {
env: await makeTestEnv({
serverData,
mockRPC(route) {
assert.step(route);
},
}),
});
assert.strictEqual(target.querySelector("[name='foo'] input").value, "abc");
await editInput(target, "[name='foo'] input", "753");
assert.verifySteps(["record changed"]);
assert.strictEqual(target.querySelector("[name='foo'] input").value, "753");
});
});