[REF] web: specialize useModel for sample data

Before this commit, there was a hook, named useModel, which eases
the use of a model in views. The main (and almost only) role of the
hook was to deal with sample data (determine whether or not they
can be enabled, fallback on the sample orm when necessary, leave
sample mode at reload...). This hook is used by all views except
form and activity as they do not implement sample data.

This commit renames that hook into "useModelWithSampleData" and
introduces a new hook "useModel", which is a lot simpler that the
other one, as it's only goal is to do the plumbing between the
component using the hook and the model (i.e. spawning required
services and loading the data on willStart and willUpdateProps).
This new hook is used in form and activity views.

Note that useModel doesn't trigger deep renderings, as it is only
used on the RelationalModel which is based on fine-grained
reactivity.

Part-of: odoo/odoo#114024
This commit is contained in:
Aaron Bohy
2023-07-24 20:17:54 +02:00
parent e325603172
commit c80f76da56
8 changed files with 22 additions and 46 deletions
@@ -2,7 +2,7 @@
import { useMessaging } from "@mail/core/common/messaging_hook";
import { Component, onWillStart, onWillUpdateProps, useState } from "@odoo/owl";
import { Component, useState } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
import { sprintf } from "@web/core/utils/strings";
@@ -34,14 +34,7 @@ export class ActivityController extends Component {
fields,
},
};
const modelServices = Object.fromEntries(
this.props.Model.services.map((servName) => {
return [servName, useService(servName)];
})
);
this.model = useState(new this.props.Model(this.env, modelParams, modelServices));
onWillStart(() => this.model.load(this.props));
onWillUpdateProps((nextProps) => this.model.root.load(nextProps));
this.model = useState(useModel(this.props.Model, modelParams));
this.dialog = useService("dialog");
this.action = useService("action");
@@ -5,7 +5,7 @@ import { _t, _lt } from "@web/core/l10n/translation";
import { useOwnedDialogs, useService } from "@web/core/utils/hooks";
import { sprintf } from "@web/core/utils/strings";
import { Layout } from "@web/search/layout";
import { useModel } from "@web/model/model";
import { useModelWithSampleData } from "@web/model/model";
import { FormViewDialog } from "@web/views/view_dialogs/form_view_dialog";
import { useSetupView } from "@web/views/view_hook";
import { CalendarDatePicker } from "./date_picker/calendar_date_picker";
@@ -43,7 +43,7 @@ export class CalendarController extends Component {
this.orm = useService("orm");
this.displayDialog = useUniqueDialog();
this.model = useModel(this.props.Model, {
this.model = useModelWithSampleData(this.props.Model, {
...this.props.archInfo,
resModel: this.props.resModel,
domain: this.props.domain,
@@ -18,6 +18,7 @@ import { useViewButtons } from "@web/views/view_button/view_button_hook";
import { useSetupView } from "@web/views/view_hook";
import { ViewButton } from "@web/views/view_button/view_button";
import { Field } from "@web/views/fields/field";
import { useModel } from "@web/model/model";
import { addFieldDependencies, extractFieldsFromArchInfo } from "@web/model/relational_model/utils";
import { useViewCompiler } from "@web/views/view_compiler";
import { CogMenu } from "@web/search/cog_menu/cog_menu";
@@ -28,15 +29,7 @@ import { FormCompiler } from "./form_compiler";
import { FormErrorDialog } from "./form_error_dialog/form_error_dialog";
import { FormStatusIndicator } from "./form_status_indicator/form_status_indicator";
import {
Component,
onRendered,
onWillStart,
onWillUpdateProps,
useEffect,
useRef,
useState,
} from "@odoo/owl";
import { Component, onRendered, useEffect, useRef, useState } from "@odoo/owl";
const viewRegistry = registry.category("views");
@@ -129,19 +122,12 @@ export class FormController extends Component {
this.canCreate = create && !this.props.preventCreate;
this.canEdit = edit && !this.props.preventEdit;
const modelServices = Object.fromEntries(
this.props.Model.services.map((servName) => {
return [servName, useService(servName)];
})
);
this.model = useState(new this.props.Model(this.env, this.modelParams, modelServices));
this.display = { ...this.props.display };
if (this.env.inDialog) {
this.display.controlPanel = false;
}
onWillStart(async () => {
const beforeFirstLoad = async () => {
await loadSubViews(
this.archInfo.fieldNodes,
this.props.fields,
@@ -162,13 +148,8 @@ export class FormController extends Component {
}
this.model.config.activeFields = activeFields;
this.model.config.fields = fields;
return this.model.load();
});
onWillUpdateProps((nextProps) => {
if (nextProps.resId !== this.model.root.resId) {
return this.model.root.load(nextProps.resId);
}
});
};
this.model = useState(useModel(this.props.Model, this.modelParams, { beforeFirstLoad }));
this.cpButtonsRef = useRef("cpButtons");
@@ -1,7 +1,7 @@
/** @odoo-module **/
import { Layout } from "@web/search/layout";
import { useModel } from "@web/model/model";
import { useModelWithSampleData } from "@web/model/model";
import { standardViewProps } from "@web/views/standard_view_props";
import { useSetupView } from "@web/views/view_hook";
import { SearchBar } from "@web/search/search_bar/search_bar";
@@ -12,7 +12,7 @@ import { Component, useRef } from "@odoo/owl";
export class GraphController extends Component {
setup() {
this.model = useModel(this.props.Model, this.props.modelParams);
this.model = useModelWithSampleData(this.props.Model, this.props.modelParams);
useSetupView({
rootRef: useRef("root"),
@@ -10,7 +10,7 @@ import { SearchBar } from "@web/search/search_bar/search_bar";
import { useSearchBarToggler } from "@web/search/search_bar/search_bar_toggler";
import { session } from "@web/session";
import { useSetupView } from "@web/views/view_hook";
import { useModel } from "@web/model/model";
import { useModelWithSampleData } from "@web/model/model";
import { standardViewProps } from "@web/views/standard_view_props";
import { MultiRecordViewButton } from "@web/views/view_button/multi_record_view_button";
import { useViewButtons } from "@web/views/view_button/view_button_hook";
@@ -81,7 +81,9 @@ export class KanbanController extends Component {
}
}
this.model = useState(useModel(KanbanSampleModel, this.modelParams, this.modelOptions));
this.model = useState(
useModelWithSampleData(KanbanSampleModel, this.modelParams, this.modelOptions)
);
if (archInfo.progressAttributes) {
const { activeBars } = this.props.state || {};
this.progressBarState = useProgressBar(
@@ -9,7 +9,7 @@ import { omit } from "@web/core/utils/objects";
import { ActionMenus, STATIC_ACTIONS_GROUP_NUMBER } from "@web/search/action_menus/action_menus";
import { Layout } from "@web/search/layout";
import { usePager } from "@web/search/pager_hook";
import { useModel } from "@web/model/model";
import { useModelWithSampleData } from "@web/model/model";
import { DynamicRecordList } from "@web/model/relational_model/dynamic_record_list";
import { extractFieldsFromArchInfo } from "@web/model/relational_model/utils";
import { standardViewProps } from "@web/views/standard_view_props";
@@ -49,7 +49,7 @@ export class ListController extends Component {
this.activeActions = this.archInfo.activeActions;
this.editable =
this.activeActions.edit && this.props.editable ? this.archInfo.editable : false;
this.model = useState(useModel(this.props.Model, this.modelParams));
this.model = useState(useModelWithSampleData(this.props.Model, this.modelParams));
// In multi edition, we save or notify invalidity directly when a field is updated, which
// occurs on the change event for input fields. But we don't want to do it when clicking on
@@ -80,7 +80,7 @@ export class ListController extends Component {
: "x_active" in this.props.fields
? !this.props.fields.x_active.readonly
: false;
useSubEnv({ model: this.model }); // do this in useModel?
useSubEnv({ model: this.model }); // do this in useModelWithSampleData?
useViewButtons(this.model, this.rootRef, {
beforeExecuteAction: this.beforeExecuteActionButton.bind(this),
afterExecuteAction: this.afterExecuteActionButton.bind(this),
@@ -1,7 +1,7 @@
/** @odoo-module **/
import { Layout } from "@web/search/layout";
import { useModel } from "@web/model/model";
import { useModelWithSampleData } from "@web/model/model";
import { standardViewProps } from "@web/views/standard_view_props";
import { useSetupView } from "@web/views/view_hook";
import { SearchBar } from "@web/search/search_bar/search_bar";
@@ -12,7 +12,7 @@ import { Component, useRef } from "@odoo/owl";
export class PivotController extends Component {
setup() {
this.model = useModel(this.props.Model, this.props.modelParams);
this.model = useModelWithSampleData(this.props.Model, this.props.modelParams);
useSetupView({
rootRef: useRef("root"),
@@ -154,7 +154,7 @@ QUnit.test("nameGet method", async (assert) => {
await env.services.orm.nameGet("sale.order", [2, 5], { context });
assert.strictEqual(query.route, "/web/dataset/call_kw/sale.order/read");
assert.deepEqual(query.params, {
args: [[2, 5], ['display_name']],
args: [[2, 5], ["display_name"]],
kwargs: {
context: {
complete: true,
@@ -361,7 +361,7 @@ QUnit.test("webSearchRead method", async (assert) => {
});
});
QUnit.test("useModel is specialized for component", async (assert) => {
QUnit.test("orm is specialized for component", async (assert) => {
const [, /* query */ rpc] = makeFakeRPC();
serviceRegistry.add("rpc", rpc);
const env = await makeTestEnv();