Search code examples
javascriptwebsapui5sap-fioriui5-webcomponents

Custom Card Component with individual Controllers in SAPUI5 Application


I want to create a FIORI Overview Page with SAPUI5 1.84 and ​I followed this Tutorial to add custom cards to the application. After adding the first custom card, everything worked just fine. It showed the card with its custom UI elements and used the custom controller. The Component.js looked like this:

(function () {
    "use strict";

    jQuery.sap.declare("myApp.ext.customcard1.Component");
    jQuery.sap.require("sap.ovp.cards.generic.Component");

    sap.ovp.cards.generic.Component.extend("myApp.ext.customcard1.Component", {
        metadata: {
            properties: {
                "contentFragment": {
                    "type": "string",
                    "defaultValue": "myApp.ext.customcard1.customcard1"
                }
            },
            version: "1.44.10",
            library: "sap.ovp",
            includes: [],
            dependencies: {
                libs: ["sap.m"],
                components: []
            },
            config: {},
            customizing: {
                "sap.ui.controllerExtensions": {
                    "sap.ovp.cards.generic.Card": {
                        controllerName: "myApp.ext.customCard1.customcard1"
                    }
                }
            }
        }
    });
})();

The custom card files look like this:

webapp
-ext
--customcard1
---Component.js
---customcard1.controller.js
---customcard1.fragment.xml

Then I wanted to add a second custom card to the application. I created it just liked the first custom card. When starting the application, the second card is shown, but I soon realized that both cards use the same controller class. The customcard1.controller.js was also used as the controller for the customcard2 instead of customcard2.controller.js. The sources tab in chrome debug tools also shows that only code from the files customcard1/Component.js, customcard2/Component.js and customcard1.controller.js is being executed.

I tried debugging into the framework and soon realized that somewhere in the code it looks up the controller for the custom components by the class name sap.ovp.cards.generic.Card which returned an instance of the customcard1.controller.js for both custom cards.

I then found the Sample Code for custom cards and realized that the controller is linked to the custom component differently than in the tutorial I originally used.

Instead of the customizing:

customizing: {
  "sap.ui.controllerExtensions": {
    "sap.ovp.cards.generic.Card": {
      controllerName: "myApp.ext.customCard1.customcard1"
    }
  }
}

they set the controller as a metadata property in the Component.js:

metadata: {
  properties: {
    "controllerName": {
      "type": "string",
      "defaultValue": "test.testovp.ext.myCustomCard.MyCustomCard"
}

I tried changing my Component.js and removed the customizing section and added the controller name in the metadata like this:

(function () {
    "use strict";

    jQuery.sap.declare("myApp.ext.customcard2.Component");
    jQuery.sap.require("sap.ovp.cards.generic.Component");

    sap.ovp.cards.generic.Component.extend("myApp.ext.customcard2.Component", {
        metadata: {
            properties: {
                "contentFragment": {
                    "type": "string",
                    "defaultValue": "myApp.ext.customcard2.customcard2"
                },
                "controllerName": {
                    "type": "string",
                    "defaultValue": "myApp.ext.customcard2.customcard2"
                },
            },
            version: "1.44.10",
            library: "sap.ovp",
            includes: [],
            dependencies: {
                libs: ["sap.m"],
                components: []
            },
            config: {},
        }
    });
})();

With both Component.js setup like this, both custom cards use their correct individual controllers.

However, I now realized that I do not extend the generic custom card class sap.ovp.cards.generic.Card anymore. This means that I do not have access to any properties of this class in my custom controllers.

What am I doing wrong and how can I setup my application with multiple custom cards that each use their individual controller that still inherits all functions and properties from the generic card class?


Solution

  • I got it to work by manually extending the parent class in my controller. You need to call the parents class onInit() function to get access to all the relevant properties.

    Instead of defining your controller like this:

    Component.js

    sap.ovp.cards.generic.Component.extend("myapp.ext.customcard.Component", {
        metadata: {
            ...
            customizing: {
                "sap.ui.controllerExtensions": {
                    "sap.ovp.cards.generic.Card": {
                        controllerName: "myapp.ext.customCard.customcard"
                    }
                }
            }
        }
    });
    

    customcard.controller.js

    (function () {
        "use strict";
    
        sap.ui.controller("myapp.cards.customcard.customcard", {
            
            onInit: function() 
            {
                
            }
        });
    })();
    

    This is the approach to get as many custom cards with custom controllers as you want:

    Component.js

    sap.ovp.cards.generic.Component.extend("myapp.ext.customcard.Component", {
        metadata: {
            properties: {
                "controllerName": {
                    "type": "string",
                    "defaultValue": "myapp.ext.customcard.customcard"
                }
            }
        });
    

    customcard.controller.js

    (function () {
        "use strict";
    
        sap.ui.define(["sap/ovp/cards/generic/Card.controller"], function (CardController) 
        {
            return CardController.extend("myapp.ext.customcard.customcard", 
            {
                onInit: function() 
                {
                    //Call parent class onInit()
                    CardController.prototype.onInit.apply(this, arguments);
                },
            }
        });
    })();