'getOwnerComponent() returns undefined in controller of which view was embedded on-demand

I want to nest a second view into my root view if some requirements are met. This works fine by creating a XMLView object and adding it to the <page> aggregation of the <App> element. But when I try to access the default model (which is created by the manifest.json file) via this.getOwnerComponent().getModel(), it throws:

Uncaught TypeError: Cannot read property 'getModel' of undefined

It worked in the root controller but in the nested controller, this.getOwnerComponent() returned undefined again.

It even works if the view is added right at the beginning in the root view. Maybe I'm adding it the wrong way to the view in my controller?


RootView.view.xml:

<mvc:View controllerName="test.demo.controller.RootView" xmlns:mvc="sap.ui.core.mvc">
    <App id="app" xmlns="sap.m">
        <Page id="page" title="{i18n>title}">
            <Button text="load nested view" press=".onLoadButtonPress"/>
        </Page>
    </App>
</mvc:View>

RootView.controller.js:

sap.ui.define([
    "sap/ui/core/mvc/Controller",
    "sap/ui/core/mvc/XMLView"
], function (Controller, XMLView) {
    "use strict";

    return Controller.extend("test.demo.controller.RootView", {
        onLoadButtonPress: function () {
            this.mDefault = this.getOwnerComponent().getModel(); // works just fine
            alert(this.mDefault);
            XMLView.create({
                viewName: "test.demo.view.NestedView"
            }).then(function(oView) {
                this.byId("page").addContent(oView);
            }.bind(this));
        }
    });
});

NestedView.view.xml:

<mvc:View xmlns:mvc="sap.ui.core.mvc" controllerName="test.demo.controller.NestedView">
  <!-- ... -->
</mvc:View>

NestedView.controller.js:

sap.ui.define([
    "sap/ui/core/mvc/Controller"
], function (Controller) {
    "use strict";

    return Controller.extend("test.demo.controller.NestedView", {
        onInit: function () {
            this.mDefault = this.getOwnerComponent().getModel(); // throws Uncaught TypeError: this.getOwnerComponent() is undefined
            alert(this.mDefault);
        }
    });
});

In the manifest.json, I added the GWSAMPLE_BASIC OData service as the default model.



Solution 1:[1]

When the caller in the application code creates on-demand a view or any other instance of ManagedObject, from which the owner component information needs to be accessible, it should be done within the method runAsOwner from your target sap.ui.core.Component. For example:

// XMLView required from "sap/ui/core/mvc/XMLView"
onLoadButtonPress: async function() {
  const fn = () => XMLView.create({ viewName: "test.demo.view.NestedView" });
  const oView = await this.getOwnerComponent().runAsOwner(fn);
  this.byId("page").addContent(oView);
},

Sample: https://embed.plnkr.co/29UrnDHpTHquNWuH

According to the API reference:

Ownership for objects is only checked by the framework at the time when they are created. It is not checked or updated afterwards. And it can only be detected while the Component.runAsOwner function is executing. Without further action, this is only the case while the content of a UIComponent is constructed or when a Router creates a new View and its content. (source)

Then in the controller of the nested view, calling this.getOwnerComponent() returns the correctly assigned component instead of undefined, and thus this.getOwnerComponent().getModel() works.


Tip: when loading fragments, favor the controller API loadFragment over Fragment.load since loadFragment automatically calls Fragment.load with the controller's component.runAsOwner.src

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1