[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:
@@ -5,21 +5,17 @@ import { useModel } from "@web/views/model";
|
|||||||
import { RelationalModel } from "@web/views/relational_model";
|
import { RelationalModel } from "@web/views/relational_model";
|
||||||
import { Component, xml, onWillStart, onWillUpdateProps } from "@odoo/owl";
|
import { Component, xml, onWillStart, onWillUpdateProps } from "@odoo/owl";
|
||||||
|
|
||||||
|
const defaultActiveField = { attrs: {}, options: {}, domain: "[]", string: "" };
|
||||||
|
|
||||||
class _Record extends Component {
|
class _Record extends Component {
|
||||||
setup() {
|
setup() {
|
||||||
const activeFields =
|
|
||||||
this.props.info.activeFields ||
|
|
||||||
Object.fromEntries(
|
|
||||||
this.props.info.fieldNames.map((f) => [f, { attrs: {}, options: {}, domain: "[]" }])
|
|
||||||
);
|
|
||||||
|
|
||||||
this.model = useModel(RelationalModel, {
|
this.model = useModel(RelationalModel, {
|
||||||
resId: this.props.info.resId,
|
resId: this.props.info.resId,
|
||||||
resModel: this.props.info.resModel,
|
resModel: this.props.info.resModel,
|
||||||
fields: this.props.fields,
|
fields: this.props.fields,
|
||||||
viewMode: "form",
|
viewMode: "form",
|
||||||
rootType: "record",
|
rootType: "record",
|
||||||
activeFields,
|
activeFields: this.getActiveFields(),
|
||||||
mode: this.props.info.mode === "edit" ? "edit" : undefined,
|
mode: this.props.info.mode === "edit" ? "edit" : undefined,
|
||||||
initialValues: this.props.info.initialValues,
|
initialValues: this.props.info.initialValues,
|
||||||
});
|
});
|
||||||
@@ -29,9 +25,36 @@ class _Record extends Component {
|
|||||||
mode: nextProps.info.mode,
|
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.template = xml`<t t-slot="default" record="model.root"/>`;
|
||||||
|
_Record.props = ["slots", "info", "fields"];
|
||||||
|
|
||||||
export class Record extends Component {
|
export class Record extends Component {
|
||||||
setup() {
|
setup() {
|
||||||
@@ -61,4 +84,5 @@ Record.props = [
|
|||||||
"resId?",
|
"resId?",
|
||||||
"mode?",
|
"mode?",
|
||||||
"initialValues?",
|
"initialValues?",
|
||||||
|
"onRecordChanged?",
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { click, getFixture, mount } from "../helpers/utils";
|
|||||||
import { setupViewRegistries } from "../views/helpers";
|
import { setupViewRegistries } from "../views/helpers";
|
||||||
|
|
||||||
import { Component, xml, useState } from "@odoo/owl";
|
import { Component, xml, useState } from "@odoo/owl";
|
||||||
|
import { editInput } from "@web/../tests/helpers/utils";
|
||||||
|
|
||||||
let serverData;
|
let serverData;
|
||||||
let target;
|
let target;
|
||||||
@@ -183,4 +184,50 @@ QUnit.module("Record Component", (hooks) => {
|
|||||||
assert.verifySteps([]);
|
assert.verifySteps([]);
|
||||||
assert.strictEqual(target.querySelector(".o_field_widget input").value, "abc");
|
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");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user