Skip to content

Sectioned Table Extension

All Sectioned Table extension classes must extend the IView interface.

For Mobile Development Kit version 6.2 or older, the return value of the view() method must be a subclass of a native view (i.e. a subclass of UIView for iOS or android.view.View for Android) for section/table and form cell extensions. The viewIsNative() must also be overridden to return true.

With Mobile Development Kit version 6.3 or newer, it is possible to return a NativeScript view through the view() method. In this case, the viewIsNative() must also be overridden to return false.

Example - Creating Extension Using Native Views

Implementing the Extension control to return native iOS / Android views using TypeScript Marshalling.

You have the following the file in the Extensions folder: /MyMDKExtensionModule/controls/MyExtensionViewControl.ts:

//file: MyExtensionViewControl.ts
import * as app from '@nativescript/core/application';
import { Color } from '@nativescript/core/color';
import { IView } from 'mdk-core/IView';

export class MyExtensionView extends IView {
  private _label: any;
  private _value: any;

  public initialize(props) {
    super.initialize(props);
    const color = new Color('yellow');
    if (this.definition().data.ExtensionProperties.Prop1) {
      this._value = this.definition().data.ExtensionProperties.Prop1.Value;
    }
    else {
      this._value = "Default Value";
    }
    if (app.ios) {
      this._label = UILabel.alloc().init();
      this._label.numberOfLines = 3;
      this._label.backgroundColor = color.ios;
    } else {
      this._label = new android.widget.TextView(this.androidContext());
      this._label.setBackgroundColor(color.android);
    }
  }

  public view() {
    //Use provided ValueResolver to resolve value to support bindings, rules, etc in your extension
    this.valueResolver().resolveValue(this._value).then((resolvedValue)=> {
      if (app.ios) {
        this._label.text = resolvedValue;
      } else {
        this._label.setText(resolvedValue);
      }
    });
    return this._label;
  }

  public viewIsNative() {
    return true;
  }
}

Consuming the extension control in your app e.g. in a Section of a Sectioned Table:

{
  "Module": "MyMDKExtensionModule",
  "Control": "MyExtensionViewControl",
  "Class": "MyExtensionView",
  "Height": 200,
  "ExtensionProperties": {
      "MinValue": 0,
      "MaxValue": 100,
      "Unit": "mi",
      "Title": "Distance"
  },
  "Value": 10,
  "OnPress": "/MDKApp/Actions/ShowMessage.action",
  "_Type": "Section.Type.Extension",
  "_Name": "MyNSProgress2"
}

Example - Creating Extension Using NativeScript Views

Implementing the Extension control to return NativeScript views.

You have the following the file in the Extensions folder: /MyMDKExtensionModule/controls/NSProgressControl.ts:

//file: NSProgressControl.ts
import { GridLayout, Label, Progress, Button, PropertyChangeData } from '@nativescript/core';
import { IView } from 'mdk-core/IView';

function toNumber(value: any, defaultIfNaN = 0): number {
  if ((typeof value !== 'number' && typeof value !== 'string') || (typeof value === 'string' && value.trim().length === 0)) {
    return defaultIfNaN;
  }
  const n = +value;
  return isNaN(n) ? defaultIfNaN : n;
}

function toNumberGenerator(defaultIfNaN = 0): (value: any) => number {
  return (value: any) => toNumber(value, defaultIfNaN);
}


export class NSProgress extends IView {
  private _nsView: GridLayout;
  private _progress: Progress;
  private _progressValueText: Label;
  private _unit = '';

  public view(): any {
    const defaultMaxValue = 10000;
    if (!this._nsView) {
      this._nsView = new GridLayout();
      (this._nsView as any).rows = 'auto,auto';
      (this._nsView as any).columns = 'auto,*,auto';
      const headerGrid = new GridLayout();
      const progressLabel = new Label();
      progressLabel.className = 'progress-label';
      const progressValue = new Label();
      this._progressValueText = progressValue;
      progressValue.className = 'progress-value';
      headerGrid.colSpan = 3;
      headerGrid.addChild(progressLabel);
      headerGrid.addChild(progressValue);
      headerGrid.className = 'header-wrapper';

      const progress = new Progress();
      this._progress = progress;
      progress.row = 1;
      progress.col = 1;
      progress.className = 'progress';
      progress.on('valueChange', (evt: PropertyChangeData) => {
        this.setValue(evt.value, true, false);
      });
      const progressMinValueLabel = new Label();
      progressMinValueLabel.row = 1;
      progressMinValueLabel.col = 0;
      progressMinValueLabel.className = 'progress-min-value-label';
      progressMinValueLabel.text = "0";
      const progressMaxValueLabel = new Label();
      progressMaxValueLabel.row = 1;
      progressMaxValueLabel.col = 2;
      progressMaxValueLabel.className = 'progress-max-value-label';

      this._nsView.className = 'wrapper';
      this._nsView.addChild(headerGrid);
      this._nsView.addChild(progress);
      this._nsView.addChild(progressMinValueLabel);
      this._nsView.addChild(progressMaxValueLabel);
      this._nsView.addCss(`
      .wrapper {
        padding: 20;
      }
      .progress-label {
        horizontal-align: left;
        font-weight: bold;
      }
      .progress-value {
        horizontal-align: right;
      }

      .header-wrapper {
        margin-bottom: 10;
      }
      .progress {
        margin-left: 10;
        margin-right: 10;
        vertical-align: middle;
      }
      .progress-min-value-label, .progress-max-value-label {
        vertical-align: middle;
      }

      `);

      // set the unit
      this.resolve(this.definition().data.ExtensionProperties.Unit).then((unit) => {
        this._unit = unit ?? '';
        // update the text
        this._setProgressValueText(this._progress.value);
      });

      // set title
      this.resolve(this.definition().data.ExtensionProperties.Title).then((v) => {
        progressLabel.text = `${v ?? ''}`;
      });

      // set max value
      this.resolve(this.definition().data.ExtensionProperties.MaxValue)
        .then(toNumberGenerator(defaultMaxValue))
        .then((v) => {
          progress.maxValue = v;
          progressMaxValueLabel.text = `${v}`;
        });

      // set start value
      this.resolve(this.definition().data.Value).then((v) => {
        this.setValue(v, false, false);
      });
    }
    return this._nsView;
  }

  public viewIsNative() {
    return false;
  }

  public setContainer(container: IControl) {
    // do nothing
  }

  public async setValue(value: any, notify: boolean, isTextValue?: boolean): Promise<any> {
    let finalNumber = toNumber(value, NaN);
    if (isNaN(finalNumber)) {
      throw new Error('Error: Value is not a number');
    }
    finalNumber = Math.max(0, Math.min(this._progress.maxValue, finalNumber));
    this._setProgressValue(finalNumber);
    this._setProgressValueText(finalNumber);
  }

  private resolve(v: any): Promise<any> {
    return this.valueResolver().resolveValue(v, this.context);
  }

  private _setProgressValue(value: number) {
    this._progress.value = value;
  }
  private _setProgressValueText(value: number) {
    const roundValue = Math.round(value);
    this._progressValueText.text = `${roundValue}${this._unit ? ' ' + this._unit : ''}`;
  }
}

Consuming the extension control in your app e.g. in a Section of a Sectioned Table:

{
  "Module": "MyMDKExtensionModule",
  "Control": "NSProgressControl",
  "Class": "NSProgress",
  "Height": 200,
  "ExtensionProperties": {
      "MinValue": 0,
      "MaxValue": 100,
      "Unit": "mi",
      "Title": "Distance"
  },
  "Value": 10,
  "OnPress": "/MDKApp/Actions/ShowMessage.action",
  "_Type": "Section.Type.Extension",
  "_Name": "MyNSProgress2"
}

Last update: September 15, 2022