From c80f76da56eb6dec22f5b3be12931837fbdf9e98 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Mon, 10 Jul 2023 13:57:01 +0000 Subject: [PATCH] [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 --- .../views/web/activity/activity_controller.js | 11 ++----- .../src/views/calendar/calendar_controller.js | 4 +-- .../static/src/views/form/form_controller.js | 29 ++++--------------- .../src/views/graph/graph_controller.js | 4 +-- .../src/views/kanban/kanban_controller.js | 6 ++-- .../static/src/views/list/list_controller.js | 6 ++-- .../src/views/pivot/pivot_controller.js | 4 +-- .../static/tests/core/orm_service_tests.js | 4 +-- 8 files changed, 22 insertions(+), 46 deletions(-) diff --git a/addons/mail/static/src/views/web/activity/activity_controller.js b/addons/mail/static/src/views/web/activity/activity_controller.js index c9924195b07..e6022da9d26 100644 --- a/addons/mail/static/src/views/web/activity/activity_controller.js +++ b/addons/mail/static/src/views/web/activity/activity_controller.js @@ -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"); diff --git a/addons/web/static/src/views/calendar/calendar_controller.js b/addons/web/static/src/views/calendar/calendar_controller.js index 563f3b82c8b..ada6eeab49f 100644 --- a/addons/web/static/src/views/calendar/calendar_controller.js +++ b/addons/web/static/src/views/calendar/calendar_controller.js @@ -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, diff --git a/addons/web/static/src/views/form/form_controller.js b/addons/web/static/src/views/form/form_controller.js index 0e63d95da07..a6523efb0e9 100644 --- a/addons/web/static/src/views/form/form_controller.js +++ b/addons/web/static/src/views/form/form_controller.js @@ -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"); diff --git a/addons/web/static/src/views/graph/graph_controller.js b/addons/web/static/src/views/graph/graph_controller.js index e413dcdda93..a8f13535a42 100644 --- a/addons/web/static/src/views/graph/graph_controller.js +++ b/addons/web/static/src/views/graph/graph_controller.js @@ -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"), diff --git a/addons/web/static/src/views/kanban/kanban_controller.js b/addons/web/static/src/views/kanban/kanban_controller.js index 6ba4fe04672..40f3ffc307a 100644 --- a/addons/web/static/src/views/kanban/kanban_controller.js +++ b/addons/web/static/src/views/kanban/kanban_controller.js @@ -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( diff --git a/addons/web/static/src/views/list/list_controller.js b/addons/web/static/src/views/list/list_controller.js index 4bbd5b92a78..9291152f4fd 100644 --- a/addons/web/static/src/views/list/list_controller.js +++ b/addons/web/static/src/views/list/list_controller.js @@ -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), diff --git a/addons/web/static/src/views/pivot/pivot_controller.js b/addons/web/static/src/views/pivot/pivot_controller.js index 59535bf8ab4..86a63dddc0a 100644 --- a/addons/web/static/src/views/pivot/pivot_controller.js +++ b/addons/web/static/src/views/pivot/pivot_controller.js @@ -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"), diff --git a/addons/web/static/tests/core/orm_service_tests.js b/addons/web/static/tests/core/orm_service_tests.js index 9de164cfa6d..e958fb6b430 100644 --- a/addons/web/static/tests/core/orm_service_tests.js +++ b/addons/web/static/tests/core/orm_service_tests.js @@ -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();