Skip to content

Extensions

Extensions give the application designer the ability to customize the look-and-feel and behavior of the client via their own Typescript and native classes. In the case that an extension throws an exception while being built or returning its View, the app will show a the error text in place of the extension.

Table of Contents:

Extension Types

These are the available extension types in the Mobile Development Kit:

Please visit each page for particular details and examples.

Creating Extensions

Each extension should have base directories with the following structure:

  • ExtensionModuleName - The name for the extension module.
    • controls - The TypeScript code for extension controls.
    • i18n - The localization resource file for extension module (See Internationalization for Extension Module for more details)
    • plugin - NativeScript plugins used by the extension.
      • NativeScript plugins are only supported for Extension in Branded Client
      • NativeScript plugins are optional but it's recommended that you implement your extension's platform-specific codes as NativeScript plugins in the .ios.ts and .android.ts files, and consume the plugin in your extension class in the controls folder. including TypeScript codes and/or any Podfile and Swift frameworks for iOS and .gradle files and Android Libraries (AAR) for Android.

A typical extension module directory would look like this:

ExtensionModule1/
  controls/
    Extension1Control.ts
  i18n/
    i18n.properties
    i18n_de.properties
  plugin/ (Optional)
    MyNSModule1/
      index.js
      MyNSPlugin1.ios.ts
      MyNSPlugin1.android.ts
      MyNSPlugin1.ts
      package.json
      platforms/ (Optional)
        android/
          include.gradle
          MyAndroidLib1.aar
        ios/
          Podfile
          MySwiftFramework1.framework

Extension in Branded Client vs Extension in Metadata Project

You may choose to put the extension module in one of the following two locations:

Extension in Branded Client

extensions folder under branded client .mdkproject

Putting your extension in your custom branded client. This will include the extension to be built as part of the branded client. The advantage of this approach you can include additional NativeScript plugin, iOS framework or Android libraries in the plugin folder. If your extension requires additional libraries, it is highly recommended that you use this approach.

  • Place the extension module and its content into .mdkproject/extensions folder:

    <projectname>.mdkproject/extensions/<ExtensionModuleName>/
    
  • Notes:

    • For branded client built using Mobile Development Kit 4.2 or newer, the core classes should be referenced from the mdk-core module e.g. mdk-core/controls/IControl.

    • For branded client built using Mobile Development Kit 4.1 or older, the code classes are 1 level above the extension so they must be referenced 1 level up e.g. ../controls/IControl.

Extension in Metadata Project

This refers to extensions that's stored in the Extensions folder in metadata project.

Putting your extension inside the metadata project will include your extension as part of the project bundling and update it via app update. The benefit of this approach is that it does not require updating mobile development kit client. The limitation with this approach is you cannot add include additional NativeScript plugin, iOS framework or Android libraries.

  • plugin folder is not supported with this approach.
  • Place the extension module with controls and i18n folders into metadata definition folder:

    <MyApp>/Extensions/<ExtensionModuleName>/controls/[..]
    
  • Notes:

    • For app targeting client with mobile development kit 4.2 or newer, the core classes should be referenced from the mdk-core module e.g. mdk-core/controls/IControl.
    • For app targeting client with mobile development kit 4.1 or older, the core classes is in the level of the bundle that include the extension, so they can be referenced as same level e.g. ./controls/IControl.
    • Make sure to add the additional modules name or path that you have imported in your extension to the Externals field in the SAP Web IDE deployment dialog or MDK Settings in Visual Studio Code Preferences or the "Externals" list in the MDKProject.json (for custom client).

When the extension control classes exist on both .mdkproject/extensions and metadata/Extensions, Mobile Development Kit would prioritize the one from metadata/Extensions.

When i18n files exist on both mdkproject/extensions and metadata/Extensions, Mobile Development Kit would use the i18n files based on which control class is being used to render the extension view.

Implementing Extensions

After creating the necessary module folders and class .ts file for your extension, you must implement a class that extends one of the interfaces, depends on the type of extension you want to create. Here are the 2 interfaces and 1 class that can be extended from:

  • BaseControl You should extend from BaseControl if you are implement a control that can get/set value. Base control provide observable on "Value" property that can be set via <BaseControl>.observable().setValue() function.
  • IControl You should extend from IControl if you are implement a control and you want to have complete control on the observable.
  • IView. IView is the base class of all views, you can extend from IView if you need only basic read-only type extension.

BaseControl and IControl are recommended if you are creating a control that has values like other FormCell controls.

Some of the good-to-know functions in IView and IControl:

Functions you need to overrides for all base class types:

  • initialize() - This function is called to initialize the extension. Implement this function and initialize the extension and its root layout or class. This function is called only once when the extension is created in the page.
  • view() - The extension must implement this abstract function to return the UI view that is rendered. This function is called when the app requires a reference to the extension view. Avoid creating your controls here, because this function might be called multiple times in a single pass.
  • viewIsNative() - This function informs the app if the view returned by the extension's view() function is native view or not, by default it returns false. If the extension's view() returns a native view, then override this function to return true. You should always return true unless you are creating Control Extensions.

Abstract functions you need to implement for base class IControl:

  • observable() - This function should be overridden to return an observable object (IObservable). Typical use case is to return a member variable of type BaseObservable. Implementing observable allow your extension to support binding on the Value property and supporting OnValueChange event.
  • setValue() - This abstract function must be implemented, it is called when "Value" property is set in metadata or via FormCellControlProxy.setValue and it is recommended to set pass the value into the observable's setValue function.

Helper functions you can call:

  • valueResolver() - this function return a valueResolver instance, it has a resolveValue(value, context) function that can be used by your extension to resolve binding value that's passed to the extension from the app. The ValueResolver class spec can be found here. For more details about handling data binding in extensions, see Data Binding in Extension

  • androidContext() - this function return the current page's context that'll be needed to create an Android native view.

Writing Native Code in TypeScript via Marshalling

NativeScript provide the ability to access platform-specific objects, class, and types in TypeScript / JavaScript via marshalling.

NativeScript handles the conversion between JavaScript and native data types implicitly.

For examples:

Accessing iOS Objective-C objects such as NSMutableArray, UIButton:

  var array = new NSMutableArray();
  var buttonClass = UIButton;
  var button = new buttonClass();
  array.setObjectAtIndex(buttonClass, 0);
  array.setObjectAtIndex(button, 1);

Accessing Android Java classes such as android.widget.Button, UIButton:

var button = new android.widget.Button(this.androidContext());
var enabled = button.isEnabled();

For details on the topic of marshalling, please go to the following NativeScript guides:

Consuming Extension in Metadata

After you have created your extension, application can consume it from within application metadata definition by defining the extension at the extension point of the Extension Types that you want to consume. Please refer to each extension types documentation for more details.

All Extension types have the same configuration properties: Module, Control, Class and ExtensionProperties.

Property Value
Module The path to the extension module / folder under <mdkproject>/extensions/.
Control The name of the file under the <mdkproject>/extensions/<Module>/controls that contains the extension class. If not specified, module name would be used as the value for this property.
Class The name of the exported class in the module that is to be used for the extension
ExtensionProperties Additional custom properties to be passed to the extension

In the .mdkproject directory, the same structure is expected under the extensions/ directory.

Example

{
  "Extension": {
    "Module": "MyExtensionFolder",
    "Control": "MyExtensionFileName",
    "Class": "MyExtensionClass",
    "ExtensionProperties": {
      "Prop1": {
        "Value": "My Sample Extension Display Value"
      }
    }
  }
}

This means that inside /<mdkproject>/extensions/MyExtensionFolder/controls/MyExtensionFileName.ts, there should be a class declared as:

// file: MyExtensionFileName.ts
import { IView } from 'mdk-core/IView';

export class MyExtensionClass extends IView {
  // ...
}

Tutorial

Create Extension Controls in an Mobile Development Kit App


Last update: September 29, 2020