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.
Extension Types¶
These are the available extension types in the Mobile Development Kit:
-
- Page Control Extension is used to render a control on page level. It's useful if you want to support a full page extension such as full screen Map or full screen Chart.
-
-
Section Extension is used to render an entire custom section inside a Sectioned Table Control.
-
Object Collection Extension is used to render a custom collection of cells inside an Object Collection Section.
-
Object Header Detail Container Extension is used to render custom controls in the
DetailContentContainer
view of an Object Header Section.
-
-
- Form Cell Extension is used to render an entire custom cell inside a Form Cell Container.
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 anyPodfile
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
andi18n
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
. - Ensure that the name and path of the additional modules that you have imported in your extension are added to the Mdk: Bundler Externals settings under SAP Business Application Studio preferences or MDK Settings under Visual Studio Code Preferences or the Externals list in the
MDKProject.json
(for custom client).
- For app targeting client with mobile development kit 4.2 or newer, the core classes should be referenced from the
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 fromBaseControl
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 fromIControl
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 fromIView
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'sview()
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 returntrue
. - If the extension's
view()
returns a NativeScript view, then override this function to returnfalse
. You should always returnfalse
if you are creating Control Extensions.
- If the extension's
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 typeBaseObservable
. Implementing observable allow your extension to support binding on theValue
property and supportingOnValueChange
event.setValue()
- This abstract function must be implemented, it is called when "Value" property is set in metadata or viaFormCellControlProxy.setValue
and it is recommended to set pass the value into the observable'ssetValue
function.
Helper functions you can call:
-
valueResolver()
- this function return avalueResolver
instance, it has aresolveValue(value, context)
function that can be used by your extension to resolve binding value that's passed to the extension from the app. TheValueResolver
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 more information about marshalling, please refer to the following NativeScript guides:
Additional information on inline extending of classes in Android via marshalling:
If the base class is an abstract class
, then you'll have to use extend
:
// take note of the brackets after ‘new’, as you need to wrap your extended class before initializing its instance.
let eventHandler = new (com.my.EventListenerAbstractClass.extend({
SomeAbstractFunction: (value: any) => {
//Do something here
}
}));
If the base class is an interface
, then use this format:
let eventHandler = new com.my.EventListenerInterface({
SomeAbstractFunction: (value: any) => {
//Do something here
}
});
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 {
// ...
}
Tutorials¶
Create Extension Controls in an Mobile Development Kit App
Implement VideoView
and AVPlayer
as Extension control
Implement CalendarView
as Extension Control
Create NativeScript view in an extension control for Form Cell Extension