From 88b148b66b5c4032fe8de3c330451dcd5adf1f1c Mon Sep 17 00:00:00 2001 From: FrancoisGe Date: Fri, 10 Mar 2023 16:00:08 +0000 Subject: [PATCH] [REF] web: model is no longer an event bus In order to make the model reactive, it must not be an EventBus. An EventBus cannot be reactive. So we will add a key bus to the model that will always trigger all events that were triggered on the model before. Part of task 3179751 closes odoo/odoo#114975 Related: odoo/enterprise#38032 Signed-off-by: Aaron Bohy (aab) --- .../views/list/inventory_report_list_controller.js | 2 +- .../web/static/src/views/basic_relational_model.js | 4 ++-- addons/web/static/src/views/fields/ace/ace_field.js | 4 ++-- .../web/static/src/views/fields/date/date_field.js | 4 ++-- .../src/views/fields/datetime/datetime_field.js | 4 ++-- .../static/src/views/fields/domain/domain_field.js | 2 +- .../web/static/src/views/fields/input_field_hook.js | 12 +++++------- .../web/static/src/views/fields/relational_utils.js | 2 +- .../static/src/views/fields/x2many/x2many_field.js | 2 +- .../form_status_indicator/form_status_indicator.js | 2 +- addons/web/static/src/views/kanban/kanban_model.js | 12 ++++++------ addons/web/static/src/views/list/list_renderer.js | 10 +++------- addons/web/static/src/views/model.js | 8 ++++---- addons/web/static/src/views/record.js | 2 +- addons/web/static/src/views/relational_model.js | 10 +++++----- .../web_editor/static/src/js/backend/html_field.js | 8 ++++---- 16 files changed, 41 insertions(+), 47 deletions(-) diff --git a/addons/stock/static/src/views/list/inventory_report_list_controller.js b/addons/stock/static/src/views/list/inventory_report_list_controller.js index abd97615884..6d2dee0a9d8 100644 --- a/addons/stock/static/src/views/list/inventory_report_list_controller.js +++ b/addons/stock/static/src/views/list/inventory_report_list_controller.js @@ -8,7 +8,7 @@ export class InventoryReportListController extends ListController { setup() { super.setup(); if (this.props.context.inventory_mode || this.props.context.inventory_report_mode) { - useBus(this.model, "record-updated", this.recordUpdated); + useBus(this.model.bus, "record-updated", this.recordUpdated); } } diff --git a/addons/web/static/src/views/basic_relational_model.js b/addons/web/static/src/views/basic_relational_model.js index cea3472433a..e8b896499c6 100644 --- a/addons/web/static/src/views/basic_relational_model.js +++ b/addons/web/static/src/views/basic_relational_model.js @@ -180,7 +180,7 @@ export class Record extends DataPoint { async askChanges() { const proms = []; - this.model.trigger("NEED_LOCAL_CHANGES", { proms }); + this.model.bus.trigger("NEED_LOCAL_CHANGES", { proms }); return Promise.all([...proms, this._updatePromise]); } @@ -674,7 +674,7 @@ export class Record extends DataPoint { async urgentSave() { this.model.__bm__.bypassMutex = true; this._urgentSave = true; - this.model.trigger("WILL_SAVE_URGENTLY"); + this.model.bus.trigger("WILL_SAVE_URGENTLY"); await Promise.resolve(); this.__syncData(); let isValid = true; diff --git a/addons/web/static/src/views/fields/ace/ace_field.js b/addons/web/static/src/views/fields/ace/ace_field.js index a8e2f6d4e6f..9fa0cc51c0d 100644 --- a/addons/web/static/src/views/fields/ace/ace_field.js +++ b/addons/web/static/src/views/fields/ace/ace_field.js @@ -48,8 +48,8 @@ export class AceField extends Component { ); const { model } = this.props.record; - useBus(model, "WILL_SAVE_URGENTLY", () => this.commitChanges()); - useBus(model, "NEED_LOCAL_CHANGES", ({ detail }) => + useBus(model.bus, "WILL_SAVE_URGENTLY", () => this.commitChanges()); + useBus(model.bus, "NEED_LOCAL_CHANGES", ({ detail }) => detail.proms.push(this.commitChanges()) ); } diff --git a/addons/web/static/src/views/fields/date/date_field.js b/addons/web/static/src/views/fields/date/date_field.js index 6d2a9ec1674..e34109da8db 100644 --- a/addons/web/static/src/views/fields/date/date_field.js +++ b/addons/web/static/src/views/fields/date/date_field.js @@ -55,13 +55,13 @@ export class DateField extends Component { } } onDatePickerInput(ev) { - this.props.record.model.trigger( + this.props.record.model.bus.trigger( "FIELD_IS_DIRTY", ev.target.value !== this.lastSetValue ); } onUpdateInput(date) { - this.props.record.model.trigger("FIELD_IS_DIRTY", false); + this.props.record.model.bus.trigger("FIELD_IS_DIRTY", false); this.lastSetValue = date; } } diff --git a/addons/web/static/src/views/fields/datetime/datetime_field.js b/addons/web/static/src/views/fields/datetime/datetime_field.js index 9b8a3c3e0b0..1deac9a0a42 100644 --- a/addons/web/static/src/views/fields/datetime/datetime_field.js +++ b/addons/web/static/src/views/fields/datetime/datetime_field.js @@ -40,13 +40,13 @@ export class DateTimeField extends Component { } } onDatePickerInput(ev) { - this.props.record.model.trigger( + this.props.record.model.bus.trigger( "FIELD_IS_DIRTY", ev.target.value !== this.lastSetValue ); } onUpdateInput(date) { - this.props.record.model.trigger("FIELD_IS_DIRTY", false); + this.props.record.model.bus.trigger("FIELD_IS_DIRTY", false); this.lastSetValue = date; } } diff --git a/addons/web/static/src/views/fields/domain/domain_field.js b/addons/web/static/src/views/fields/domain/domain_field.js index ad956c95410..aa2073449e0 100644 --- a/addons/web/static/src/views/fields/domain/domain_field.js +++ b/addons/web/static/src/views/fields/domain/domain_field.js @@ -48,7 +48,7 @@ export class DomainField extends Component { } }); - useBus(this.props.record.model, "NEED_LOCAL_CHANGES", async (ev) => { + useBus(this.props.record.model.bus, "NEED_LOCAL_CHANGES", async (ev) => { if (this.isDebugEdited) { const prom = this.loadCount(this.props); ev.detail.proms.push(prom); diff --git a/addons/web/static/src/views/fields/input_field_hook.js b/addons/web/static/src/views/fields/input_field_hook.js index 75e90f71a03..7a081233392 100644 --- a/addons/web/static/src/views/fields/input_field_hook.js +++ b/addons/web/static/src/views/fields/input_field_hook.js @@ -45,7 +45,7 @@ export function useInputField(params) { */ function onInput(ev) { isDirty = ev.target.value !== lastSetValue; - component.props.record.model.trigger("FIELD_IS_DIRTY", isDirty); + component.props.record.model.bus.trigger("FIELD_IS_DIRTY", isDirty); } /** @@ -76,7 +76,7 @@ export function useInputField(params) { lastSetValue = ev.target.value; } - component.props.record.model.trigger("FIELD_IS_DIRTY", isDirty); + component.props.record.model.bus.trigger("FIELD_IS_DIRTY", isDirty); } } function onKeydown(ev) { @@ -116,10 +116,8 @@ export function useInputField(params) { }); const { model } = component.props.record; - useBus(model, "WILL_SAVE_URGENTLY", () => commitChanges(true)); - useBus(model, "NEED_LOCAL_CHANGES", (ev) => - ev.detail.proms.push(commitChanges()) - ); + useBus(model.bus, "WILL_SAVE_URGENTLY", () => commitChanges(true)); + useBus(model.bus, "NEED_LOCAL_CHANGES", (ev) => ev.detail.proms.push(commitChanges())); /** * Roughly the same as onChange, but called at more specific / critical times. (See bus events) @@ -154,7 +152,7 @@ export function useInputField(params) { if ((val || false) !== (component.props.record.data[component.props.name] || false)) { await component.props.record.update({ [component.props.name]: val }); lastSetValue = inputRef.el.value; - component.props.record.model.trigger("FIELD_IS_DIRTY", isDirty); + component.props.record.model.bus.trigger("FIELD_IS_DIRTY", isDirty); } } } diff --git a/addons/web/static/src/views/fields/relational_utils.js b/addons/web/static/src/views/fields/relational_utils.js index 9daa8d8e493..0c74bff3b4b 100644 --- a/addons/web/static/src/views/fields/relational_utils.js +++ b/addons/web/static/src/views/fields/relational_utils.js @@ -478,7 +478,7 @@ export class X2ManyFieldDialog extends Component { this.title = this.props.title; useSubEnv({ config: this.props.config }); - useBus(this.record.model, "update", () => this.render(true)); + useBus(this.record.model.bus, "update", () => this.render(true)); this.modalRef = useChildRef(); diff --git a/addons/web/static/src/views/fields/x2many/x2many_field.js b/addons/web/static/src/views/fields/x2many/x2many_field.js index 93b1cdc7f59..62662a4f2fb 100644 --- a/addons/web/static/src/views/fields/x2many/x2many_field.js +++ b/addons/web/static/src/views/fields/x2many/x2many_field.js @@ -250,7 +250,7 @@ export class X2ManyField extends Component { if (editable) { if (this.list.editedRecord) { const proms = []; - this.list.model.trigger("NEED_LOCAL_CHANGES", { proms }); + this.list.model.bus.trigger("NEED_LOCAL_CHANGES", { proms }); await Promise.all([...proms, this.list.editedRecord._updatePromise]); await this.list.editedRecord.switchMode("readonly", { checkValidity: true }); } diff --git a/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.js b/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.js index 7d270a9a2ca..658e43dc34e 100644 --- a/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.js +++ b/addons/web/static/src/views/form/form_status_indicator/form_status_indicator.js @@ -9,7 +9,7 @@ export class FormStatusIndicator extends Component { fieldIsDirty: false, }); useBus( - this.props.model, + this.props.model.bus, "FIELD_IS_DIRTY", (ev) => (this.state.fieldIsDirty = ev.detail) ); diff --git a/addons/web/static/src/views/kanban/kanban_model.js b/addons/web/static/src/views/kanban/kanban_model.js index def5a96589b..e5dce60d784 100644 --- a/addons/web/static/src/views/kanban/kanban_model.js +++ b/addons/web/static/src/views/kanban/kanban_model.js @@ -85,12 +85,12 @@ class KanbanGroup extends Group { }, }); - this.model.addEventListener("record-updated", ({ detail }) => { + this.model.bus.addEventListener("record-updated", ({ detail }) => { const { record, relatedRecords } = detail; const localIds = this.records.map((r) => r.id); const updatedIds = [record, ...relatedRecords].map((r) => r.id); if (localIds.some((id) => updatedIds.includes(id))) { - this.model.trigger("group-updated", { + this.model.bus.trigger("group-updated", { group: this, withProgressBars: true, }); @@ -114,7 +114,7 @@ class KanbanGroup extends Group { */ async deleteRecords() { const records = await super.deleteRecords(...arguments); - this.model.trigger("group-updated", { + this.model.bus.trigger("group-updated", { group: this, withProgressBars: true, }); @@ -207,7 +207,7 @@ class KanbanGroup extends Group { // Do not update progress bars data when filtering on them. await Promise.all([this.list.load()]); - this.model.trigger("group-updated", { group: this, withProgressBars: false }); + this.model.bus.trigger("group-updated", { group: this, withProgressBars: false }); } /** @@ -306,7 +306,7 @@ class KanbanGroup extends Group { await this.model.reloadRecords(record); record.switchMode("readonly"); this.addRecord(record, 0); - this.model.trigger("group-updated", { + this.model.bus.trigger("group-updated", { group: this, withProgressBars: true, }); @@ -353,7 +353,7 @@ export class KanbanDynamicGroupList extends DynamicGroupList { this.groupBy = this.groupBy.slice(0, 1); - this.model.addEventListener("group-updated", async ({ detail }) => { + this.model.bus.addEventListener("group-updated", async ({ detail }) => { if (this.groups.some((g) => g.id === detail.group.id)) { await this.updateGroupProgressData([detail.group], detail.withProgressBars); this.model.notify(); diff --git a/addons/web/static/src/views/list/list_renderer.js b/addons/web/static/src/views/list/list_renderer.js index 8803c3824b1..3046d7b7391 100644 --- a/addons/web/static/src/views/list/list_renderer.js +++ b/addons/web/static/src/views/list/list_renderer.js @@ -154,23 +154,19 @@ export class ListRenderer extends Component { // not very beautiful but works: refactor at some point this.lastCellFocused; - useBus(this.props.list.model, "list-confirmation-dialog-will-open", () => { + useBus(this.props.list.model.bus, "list-confirmation-dialog-will-open", () => { if (this.tableRef.el.contains(document.activeElement)) { this.lastCellFocused = document.activeElement.closest("td"); } }); - useBus(this.props.list.model, "list-confirmation-dialog-closed", () => { + useBus(this.props.list.model.bus, "list-confirmation-dialog-closed", () => { if (this.lastCellFocused) { this.focus(this.lastCellFocused); } }); - useBus( - this.props.list.model, - "FIELD_IS_DIRTY", - (ev) => (this.lastIsDirty = ev.detail) - ); + useBus(this.props.list.model.bus, "FIELD_IS_DIRTY", (ev) => (this.lastIsDirty = ev.detail)); useBounceButton(this.rootRef, () => { return this.showNoContentHelper; diff --git a/addons/web/static/src/views/model.js b/addons/web/static/src/views/model.js index 9844ac8f752..e7dbe19a714 100644 --- a/addons/web/static/src/views/model.js +++ b/addons/web/static/src/views/model.js @@ -11,15 +11,15 @@ import { EventBus, onWillStart, onWillUpdateProps, useComponent } from "@odoo/ow * @typedef {import("@web/search/search_model").SearchParams} SearchParams */ -export class Model extends EventBus { +export class Model { /** * @param {Object} env * @param {Object} services */ constructor(env, params, services) { - super(); this.env = env; this.orm = services.orm; + this.bus = new EventBus(); this.useSampleModel = false; // will be set to true by the "useModel" hook if necessary this.setup(params, services); } @@ -59,7 +59,7 @@ export class Model extends EventBus { } notify() { - this.trigger("update"); + this.bus.trigger("update"); } } Model.services = []; @@ -98,7 +98,7 @@ export function useModel(ModelClass, params, options = {}) { const model = new ModelClass(component.env, params, services); useBus( - model, + model.bus, "update", options.onUpdate || (() => { diff --git a/addons/web/static/src/views/record.js b/addons/web/static/src/views/record.js index 5d44b9f54b0..29dbb85d1dd 100644 --- a/addons/web/static/src/views/record.js +++ b/addons/web/static/src/views/record.js @@ -26,7 +26,7 @@ class _Record extends Component { } this.model = new RelationalModel(this.env, modelParams, modelServices); - useBus(this.model, "update", () => this.render(true)); + useBus(this.model.bus, "update", () => this.render(true)); let loadKey; const load = (props) => { diff --git a/addons/web/static/src/views/relational_model.js b/addons/web/static/src/views/relational_model.js index e39dd36862e..06154c956ba 100644 --- a/addons/web/static/src/views/relational_model.js +++ b/addons/web/static/src/views/relational_model.js @@ -558,7 +558,7 @@ export class Record extends DataPoint { */ async urgentSave() { this._urgentSave = true; - this.model.trigger("WILL_SAVE_URGENTLY"); + this.model.bus.trigger("WILL_SAVE_URGENTLY"); return this._save({ stayInEdition: true, noReload: true }); } @@ -571,7 +571,7 @@ export class Record extends DataPoint { async askChanges() { const proms = []; - this.model.trigger("NEED_LOCAL_CHANGES", { proms }); + this.model.bus.trigger("NEED_LOCAL_CHANGES", { proms }); await Promise.all([...proms, this.model.mutex.getUnlockedDef()]); } @@ -1792,11 +1792,11 @@ class DynamicList extends DataPoint { record, fieldNodes: this.model.fieldNodes, }; - this.model.trigger("list-confirmation-dialog-will-open"); + this.model.bus.trigger("list-confirmation-dialog-will-open"); await new Promise((resolve) => { this.model.dialogService.add(ListConfirmationDialog, dialogProps, { onClose: () => { - this.model.trigger("list-confirmation-dialog-closed"); + this.model.bus.trigger("list-confirmation-dialog-closed"); resolve(); }, }); @@ -3677,7 +3677,7 @@ export class RelationalModel extends Model { await Promise.all([record, ...relatedRecords].map((r) => r.load())); - this.trigger("record-updated", { record, relatedRecords }); + this.bus.trigger("record-updated", { record, relatedRecords }); this.notify(); } } diff --git a/addons/web_editor/static/src/js/backend/html_field.js b/addons/web_editor/static/src/js/backend/html_field.js index d32163d59a1..1373a809377 100644 --- a/addons/web_editor/static/src/js/backend/html_field.js +++ b/addons/web_editor/static/src/js/backend/html_field.js @@ -92,10 +92,10 @@ export class HtmlField extends Component { }); const { model } = this.props.record; - useBus(model, "WILL_SAVE_URGENTLY", () => + useBus(model.bus, "WILL_SAVE_URGENTLY", () => this.commitChanges({ urgent: true }) ); - useBus(model, "NEED_LOCAL_CHANGES", ({ detail }) => + useBus(model.bus, "NEED_LOCAL_CHANGES", ({ detail }) => detail.proms.push(this.commitChanges()) ); @@ -298,7 +298,7 @@ export class HtmlField extends Component { !(!lastValue && stripHistoryIds(value) === "


") && stripHistoryIds(value) !== stripHistoryIds(lastValue) ) { - this.props.record.model.trigger("FIELD_IS_DIRTY", false); + this.props.record.model.bus.trigger("FIELD_IS_DIRTY", false); this.currentEditingValue = value; await this.props.record.update({ [this.props.name]: value }); } @@ -319,7 +319,7 @@ export class HtmlField extends Component { $codeviewButtonToolbar.click(this.toggleCodeView.bind(this)); } this.wysiwyg.odooEditor.editable.addEventListener("input", () => - this.props.record.model.trigger("FIELD_IS_DIRTY", this._isDirty()) + this.props.record.model.bus.trigger("FIELD_IS_DIRTY", this._isDirty()) ); this.isRendered = true;