diff --git a/addons/web/static/src/core/dialog/dialog_container.js b/addons/web/static/src/core/dialog/dialog_container.js
index daf0f96cc99..c71610d6bf7 100644
--- a/addons/web/static/src/core/dialog/dialog_container.js
+++ b/addons/web/static/src/core/dialog/dialog_container.js
@@ -1,17 +1,8 @@
/** @odoo-module **/
-const { Component, tags } = owl;
+import { ErrorHandler, NotUpdatable } from "../utils/components";
-class ErrorHandler extends Component {
- catchError(error) {
- this.props.callback();
- throw error;
- }
- shouldUpdate() {
- return false;
- }
-}
-ErrorHandler.template = tags.xml``;
+const { Component, tags } = owl;
export class DialogContainer extends Component {
setup() {
@@ -24,16 +15,24 @@ export class DialogContainer extends Component {
}
}
- errorCallBack(id) {
- return () => this.close(id);
+ handleError(error, dialogId) {
+ this.close(dialogId);
+ Promise.resolve().then(() => {
+ throw error;
+ });
}
}
-DialogContainer.components = { ErrorHandler };
+DialogContainer.components = { ErrorHandler, NotUpdatable };
DialogContainer.template = tags.xml`
-
+
+
+
+
+
`;
diff --git a/addons/web/static/src/core/utils/components.js b/addons/web/static/src/core/utils/components.js
index eb86323c1cf..eedfab693e7 100644
--- a/addons/web/static/src/core/utils/components.js
+++ b/addons/web/static/src/core/utils/components.js
@@ -2,14 +2,18 @@
const { Component, tags } = owl;
-export function makeNonUpdatableComponent(Comp) {
- class NoUpdate extends Component {
- shouldUpdate() {
- return false;
+export class NotUpdatable extends Component {
+ shouldUpdate() {
+ return false;
+ }
+}
+NotUpdatable.template = tags.xml``;
+
+export class ErrorHandler extends Component {
+ catchError(error) {
+ if (this.props.onError) {
+ this.props.onError(error);
}
}
- NoUpdate.template = tags.xml``;
- NoUpdate.components = { Comp };
-
- return NoUpdate;
}
+ErrorHandler.template = tags.xml``;
diff --git a/addons/web/static/src/webclient/navbar/navbar.js b/addons/web/static/src/webclient/navbar/navbar.js
index 73d2aa98de3..e635feef055 100644
--- a/addons/web/static/src/webclient/navbar/navbar.js
+++ b/addons/web/static/src/webclient/navbar/navbar.js
@@ -6,6 +6,7 @@ import { useService } from "../../core/service_hook";
import { useEffect } from "../../core/effect_hook";
import { registry } from "../../core/registry";
import { debounce } from "../../core/utils/timing";
+import { ErrorHandler, NotUpdatable } from "../../core/utils/components";
const { Component, hooks } = owl;
const { useExternalListener, useRef } = hooks;
@@ -69,6 +70,14 @@ export class NavBar extends Component {
this.env.bus.off("MENUS:APP-CHANGED", this);
}
+ handleItemError(error, item) {
+ // remove the faulty component
+ item.isDisplayed = () => false;
+ Promise.resolve().then(() => {
+ throw error;
+ });
+ }
+
get currentApp() {
return this.menuService.getCurrentApp();
}
@@ -183,4 +192,4 @@ export class NavBar extends Component {
}
}
NavBar.template = "web.NavBar";
-NavBar.components = { MenuDropdown, MenuItem };
+NavBar.components = { MenuDropdown, MenuItem, NotUpdatable, ErrorHandler };
diff --git a/addons/web/static/src/webclient/navbar/navbar.xml b/addons/web/static/src/webclient/navbar/navbar.xml
index 81564d4794d..f2da688d5c9 100644
--- a/addons/web/static/src/webclient/navbar/navbar.xml
+++ b/addons/web/static/src/webclient/navbar/navbar.xml
@@ -31,7 +31,11 @@
diff --git a/addons/web/static/src/webclient/webclient.js b/addons/web/static/src/webclient/webclient.js
index 5a43cf9ff33..90bb8359cc5 100644
--- a/addons/web/static/src/webclient/webclient.js
+++ b/addons/web/static/src/webclient/webclient.js
@@ -6,7 +6,7 @@ import { useBus } from "../core/bus_hook";
import { ActionContainer } from "./actions/action_container";
import { NavBar } from "./navbar/navbar";
import { useEffect } from "@web/core/effect_hook";
-import { makeNonUpdatableComponent } from "../core/utils/components";
+import { ErrorHandler, NotUpdatable } from "../core/utils/components";
const { Component, hooks } = owl;
const { useExternalListener } = hooks;
@@ -20,11 +20,7 @@ export class WebClient extends Component {
this.router = useService("router");
this.user = useService("user");
useService("legacy_service_provider");
- this.Components = mainComponentRegistry.getEntries().map(([name, elem]) => {
- const { Component, props } = elem;
- const NonUpdatableComp = makeNonUpdatableComponent(Component);
- return [name, { Component: NonUpdatableComp, props }];
- });
+ this.Components = mainComponentRegistry.getEntries();
this.title.setParts({ zopenerp: "Odoo" }); // zopenerp is easy to grep
useBus(this.env.bus, "ROUTE_CHANGE", this.loadRouterState);
useBus(this.env.bus, "ACTION_MANAGER:UI-UPDATED", (mode) => {
@@ -47,6 +43,19 @@ export class WebClient extends Component {
this.env.bus.trigger("WEB_CLIENT_READY");
}
+ handleComponentError(error, C) {
+ // remove the faulty component
+ this.Components.splice(this.Components.indexOf(C), 1);
+ /**
+ * we rethrow the error to notify the user something bad happened.
+ * We do it after a tick to make sure owl can properly finish its
+ * rendering
+ */
+ Promise.resolve().then(() => {
+ throw error;
+ });
+ }
+
async loadRouterState() {
let stateLoaded = await this.actionService.loadState();
let menuId = Number(this.router.current.hash.menu_id || 0);
@@ -106,5 +115,5 @@ export class WebClient extends Component {
}
}
}
-WebClient.components = { ActionContainer, NavBar };
+WebClient.components = { ActionContainer, NavBar, ErrorHandler, NotUpdatable };
WebClient.template = "web.WebClient";
diff --git a/addons/web/static/src/webclient/webclient.xml b/addons/web/static/src/webclient/webclient.xml
index fb30583aadf..4617f6cbe00 100644
--- a/addons/web/static/src/webclient/webclient.xml
+++ b/addons/web/static/src/webclient/webclient.xml
@@ -3,11 +3,19 @@
-
-
+
+
+
+
+
+
-
+
+
+
+
+
diff --git a/addons/web/static/tests/core/dialog_service_tests.js b/addons/web/static/tests/core/dialog_service_tests.js
index ac292b6375b..39417385478 100644
--- a/addons/web/static/tests/core/dialog_service_tests.js
+++ b/addons/web/static/tests/core/dialog_service_tests.js
@@ -250,6 +250,7 @@ QUnit.test("dialog component crashes", async (assert) => {
env.services.dialog.add(FailingDialog);
await prom;
+
assert.verifySteps(["error"]);
assert.containsOnce(pseudoWebClient, ".modal");
assert.containsOnce(pseudoWebClient, ".modal .o_dialog_error");
diff --git a/addons/web/static/tests/core/utils/component_tests.js b/addons/web/static/tests/core/utils/component_tests.js
index 0c90101c44b..9acb0643773 100644
--- a/addons/web/static/tests/core/utils/component_tests.js
+++ b/addons/web/static/tests/core/utils/component_tests.js
@@ -1,6 +1,6 @@
/** @odoo-module **/
-import { makeNonUpdatableComponent } from "@web/core/utils/components";
+import { NotUpdatable, ErrorHandler } from "@web/core/utils/components";
import { makeTestEnv } from "../../helpers/mock_env";
import { getFixture } from "../../helpers/utils";
@@ -9,7 +9,7 @@ const { Component, mount } = owl;
QUnit.module("utils", () => {
QUnit.module("components");
- QUnit.test("makeNonUpdatableComponent", async function (assert) {
+ QUnit.test("NotUpdatable component", async function (assert) {
class Child extends Component {
mounted() {
assert.step("mounted");
@@ -20,8 +20,12 @@ QUnit.module("utils", () => {
}
Child.template = owl.tags.xml`hey
`;
class Parent extends Component {}
- Parent.template = owl.tags.xml`
`;
- Parent.components = { Child1: Child, Child2: makeNonUpdatableComponent(Child) };
+ Parent.template = owl.tags.xml`
+
+
+
+
`;
+ Parent.components = { Child, NotUpdatable };
const target = getFixture();
const parent = await mount(Parent, { env: makeTestEnv(), target });
@@ -31,4 +35,36 @@ QUnit.module("utils", () => {
assert.verifySteps(["willupdateprops"]);
parent.destroy();
});
+
+ QUnit.test("ErrorHandler component", async function (assert) {
+ class Boom extends Component {}
+ Boom.template = owl.tags.xml`
`;
+
+ class Parent extends Component {
+ setup() {
+ this.flag = true;
+ }
+ handleError() {
+ this.flag = false;
+ this.render();
+ }
+ }
+ Parent.template = owl.tags.xml`
+
+
+
+
+
+
+
+ not boom
+
+
`;
+ Parent.components = { Boom, ErrorHandler };
+
+ const target = getFixture();
+ const parent = await mount(Parent, { env: makeTestEnv(), target });
+ assert.strictEqual(target.innerHTML, " not boom
");
+ parent.destroy();
+ });
});