Search code examples
odatasapui5

OData V2 SetProperty in onInit


I need to set the property to DataSet during onInit, to change the visiblity of some controls in my View. I could solve this problem with setting the visibility dynamically in controller. But I want to use the expression binding and the visible property in the View to set visibilites.

I'm getting an error in the function OnStartSetVisibilites. self.getView().getBindingContext() returns UNDEFINED. Without the sPath, I can't use setProperty. And without setProperty, my View-Controls don't know the visible-value.

How to fix this?

View:

<uxap:ObjectPageSubSection visible="{= ${Responsible} === '1'}" id="leader">
                    
</uxap:ObjectPageSubSection>

OnInit in View-Controller:

    onInit: function () {
        var startupParameters = this.getOwnerComponent().getComponentData().startupParameters;

        var sWorkitem = startupParameters.TASK[0];

        this.setModel(this.getOwnerComponent().getModel());
        this.getModel().metadataLoaded().then(function () {
            var sObjectPath = this.getModel().createKey("DataSet", {  
                Workitem: sWorkitem 
            });
            this.getView().bindElement({
                path: "/" + sObjectPath
            });
        }.bind(this));

        var self = this;
        var model = this.getOwnerComponent().getModel();

        this.getModel().read("/CharSet", {
            success: function (response) {
                $.sap.Chars = response.results;

                self.onStartSetVisibilities(model, self);

            }
        });

        // self.getView().attachAfterRendering(function () {
        //  self.onStartSetVisibilities(model, self);
        // });
        
    }

OnStartSetVisibilities:

    onStartSetVisibilities: function (model, self) {
        var char = model.getProperty(„GeneralData/Char");

        if (char !== "" || char !== null) {
          model.setProperty(self.getView().getBindingContext().sPath + "/Responsible", 
          this.getResponsibleForChar(char));
        }
    }

Solution

  • I put together some code which might solve your issue (it's untested so it may contain syntax errors!).

    I introduced the concept of Promises which simplifies the asynchronous behavior of JS. I also replaced some of the inner functions with Arrow functions so you don't have to deal with that or self. Arrow functions basically use the this of the scope they are defined within.

    Your app should now have a proper flow.

    1. First you bind the view.
    2. After the view is bound you read the CharSet.
    3. After the data is read you set the visibility stuff
    onInit: function () {
        const startupParameters = this.getOwnerComponent().getComponentData().startupParameters;
        const sWorkitem = startupParameters.TASK[0];
        
        this._bindView(sWorkitem)
            .then(() => this._readCharSet())
            .then(() => this._setVisibilities())
    },
        
    _bindView: function (sWorkitem) {
        return new Promise((resolve) => {
            const oModel = this.getOwnerComponent().getModel();
            oModel.metadataLoaded().then(() => {
                const sPath = "/" + oModel.createKey("DataSet", {  
                    Workitem: sWorkitem 
                });
                this.getView().bindElement({
                    path: sPath,
                    events: {
                        change: resolve,
                        dataReceived: resolve
                    }
                });
            });
        });
    },
        
    _readCharSet: function () {
        return new Promise((resolve) => {
            const oModel = this.getOwnerComponent().getModel();
            oModel.read("/CharSet", {
                success: resolve
            });
        });
    },
        
    _setVisibilities: function () {
        const oModel = this.getOwnerComponent().getModel();
        const sChar = oModel.getProperty("GeneralData/Char");
    
        if (sChar) {
            // ...
        }
    }