inheritancesapui5

UI5 extension of controller and lifecycle methods


I'm trying to create a base controller that has common methods and common onInit logic.

Using the extend method adds the methods from the base controller to the child controller, but the lifecycle methods get overwritten completely.

As far as I understood, the extension feature should replace common methods that have been overridden, but it should execute the lifecycle methods from both the base and extension controller on this respective order.

So my question are the following:

  1. Is this behavior possible to achieve? If so, what am I doing wrong?
  2. Is there a better way to implement it?

Example code:

Parent controller

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

  return Controller.extend("my.namespace.controller.App", {
    onInit: function () {
      console.log("reusable init");
    },

    // ...
  });
});

Child controller

sap.ui.define([
  "my/namespace/controller/App.controller"
], function (Controller) {
  "use strict";

  return Controller.extend("my.namespace.child.controller.App", {
    onInit: function () {
      console.log("extend init");
    },

    // ...
  });
});

This example logs only "extend init" when I run the application. I expected it to log both "reusable init" and "extend init" in this order.

I've omitted some code, but the main idea is represented on these two controllers. The only relevant metadata is that the base controller is abstract.


Solution

  • In many cases, when developing applications from scratch, the BaseController approach is sufficient as Benedikt explained, or you could try composition with many tiny modules, depending on your project.

    The topic controller extension is lesser-known, yet worth to get to know what it's about and how extensions can also help us in application development. The documentation does hint that the extension concepts usually target developers who want to extend existing applications with additional features. However, extensions can also be used:

    [...] as a reusable part that is added to the original application. (Source)

    Currently, there are two main approaches:

    1. The older concept Component Configuration
      Extensions are declared in manifest.json (or formerly in Component.js) and return a plain object {}.

      • Supported by older UI5 versions.
      • Lifecycle methods are called in addition to the original ones. (See table below)
      • Non-lifecycle methods with the same name are fully overwritten by the extension.
      • Simple and declarative but lacks a clear interface for extension authors.
      • Extending extensions with this approach is not allowed.

        [...] Only one level of controller extension is allowed; nested controller extension is not supported. (Source)


      Example: embed.plnkr.co/7jnWdezkHueq3esS



    2. The newer concept Controller Extension
      Defines methods metadata and the extension returns a sap.ui.core.mvc.ControllerExtension with overrides.

      • Extensions can be either declared in manifest.json (same as above) or added as dependency to your own application development as reusable parts.
      • Lifecycle methods are called in addition to the original ones same as above. (See table)
      • Allows defining a clear interface on all methods for extension authors:
        • public: true | false (default: true)
        • final: true | false (default: false)
        • overrideExecution: "After" | "Before" | "Instead" (default: "Instead")
      • Supports overriding even extension methods.
      • Supported by SAP Fiori elements apps.
      • Can access the base controller via this.base from the extension (Extensions of SAP Fiori elements apps should call this.base.getExtensionAPI()).

      Example: embed.plnkr.co/xnVMDtx8f2IgI91A



    Lifecycle execution order

    In either case, UI5 executes the lifecycle methods in the following order by default:

    Execution order Original controller code Extension code
    1. onInit
    2. onInit
    3. onBeforeRendering
    4. onBeforeRendering
    5. onAfterRendering
    6. onAfterRendering
    7. onExit
    8. onExit