diff --git a/addons/web/static/src/js/views/abstract_controller.js b/addons/web/static/src/js/views/abstract_controller.js
index 780b0bcf1b6..70cec99aa86 100644
--- a/addons/web/static/src/js/views/abstract_controller.js
+++ b/addons/web/static/src/js/views/abstract_controller.js
@@ -287,7 +287,7 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
self.handle = handle || self.handle; // update handle if we reloaded
var state = self.model.get(self.handle);
var localState = self.renderer.getLocalState();
- return self.dp.add(self.renderer.updateState(state, params)).then(function () {
+ return self.dp.add(self.updateRendererState(state, params)).then(function () {
if (self._controlPanel && cpUpdateIndex !== self._controlPanel.updateIndex) {
return;
}
@@ -297,6 +297,18 @@ var AbstractController = mvc.Controller.extend(ActionMixin, {
});
},
+ /**
+ * Update the state of the renderer.
+ * This method is required to be overridden in OWL components through the controller
+ * adapter
+ *
+ * @param {Object} state the model state
+ * @param {Object} params will be given to the model and to the renderer
+ */
+ updateRendererState: function(state, params) {
+ return this.renderer.updateState(state, params);
+ },
+
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
diff --git a/addons/web/static/src/js/views/abstract_renderer_owl.js b/addons/web/static/src/js/views/abstract_renderer_owl.js
new file mode 100644
index 00000000000..6071d410d4a
--- /dev/null
+++ b/addons/web/static/src/js/views/abstract_renderer_owl.js
@@ -0,0 +1,8 @@
+odoo.define('web.AbstractRendererOwl', function (require) {
+ "use strict";
+
+ class AbstractRenderer extends owl.Component { }
+
+ return AbstractRenderer;
+
+});
diff --git a/addons/web/static/src/js/views/controller_adapter.js b/addons/web/static/src/js/views/controller_adapter.js
new file mode 100644
index 00000000000..17973cd3b54
--- /dev/null
+++ b/addons/web/static/src/js/views/controller_adapter.js
@@ -0,0 +1,42 @@
+odoo.define('web.ControllerAdapter', function (require) {
+"use strict";
+
+var AbstractController = require('web.AbstractController');
+
+var ControllerAdapter = AbstractController.extend({
+ on_attach_callback: function () {
+ this.renderer.__callMounted();
+ },
+ on_detach_callback: function () {
+ this.renderer.__callWillUnmount();
+ },
+
+ /**
+ * @override
+ * */
+ updateRendererState: async function (props) {
+ let prom;
+ this.renderer.updateProps(props);
+ if (this.renderer.__owl__.isMounted) {
+ prom = this.renderer.render();
+ } else {
+ prom = this.renderer.mount(this.$('.o_content')[0], true);
+ }
+ return prom;
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+ /**
+ * @override
+ * @private
+ */
+ _startRenderer: async function () {
+ return this.renderer.mount(this.$('.o_content')[0]);
+ },
+});
+
+return ControllerAdapter;
+
+});
diff --git a/addons/web/static/src/js/views/renderer_wrapper.js b/addons/web/static/src/js/views/renderer_wrapper.js
new file mode 100644
index 00000000000..7c1c5d6b273
--- /dev/null
+++ b/addons/web/static/src/js/views/renderer_wrapper.js
@@ -0,0 +1,57 @@
+odoo.define('web.RendererWrapper', function (require) {
+ "use strict";
+
+ const { xml } = owl.tags;
+ const AbstractRenderer = require('web.AbstractRendererOwl');
+
+ class RendererWrapper extends owl.Component {
+ /**
+ *
+ * @param {Object} parent
+ * @param {Object} props
+ * @param {Class} concreteRenderer
+ */
+ constructor(parent, props, concreteRenderer) {
+ super(...arguments);
+ this.concreteRenderer = concreteRenderer;
+ this.data = props;
+ }
+
+ /**
+ * Update the props of the renderer before a re-render
+ * called on the initial render as well as when the renderer is re-mounted (view switched)
+ *
+ * @param {Object} props new props
+ */
+ updateProps(props) {
+ this.data = Object.assign(this.data || {}, props);
+ }
+
+ mounted() {
+ for (let x in this.__owl__.children) {
+ this.__owl__.children[x].__callMounted();
+ }
+ }
+
+ willUnmount() {
+ for (let x in this.__owl__.children) {
+ this.__owl__.children[x].__callWillUnmount();
+ }
+ }
+
+ setParent() { }
+
+ getLocalState() { }
+
+ setLocalState() { }
+
+ giveFocus() { }
+ };
+
+ RendererWrapper.template = xml``;
+
+ RendererWrapper.components = { AbstractRenderer };
+
+ return RendererWrapper;
+
+});
diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml
index 8050d18341a..a9f05f97891 100644
--- a/addons/web/views/webclient_templates.xml
+++ b/addons/web/views/webclient_templates.xml
@@ -298,8 +298,11 @@
+
+
+