View SDK Widget Examples
Simple code snippets for view SDK widget examples
1. Simple To Do Widget
This example widget code creates a simple To Do widget with HTML Elements.
class Todo extends cWidgetBase
{
readonly Items:IProperty<string[]> = this.Properties.Add(
"Items", cPropertyType.Array,
{
ItemType: cPropertyType.Text,
DefaultValue: [],
IsBrowsable: true,
IsSerializable: true
}
);
// Todo list elements
private header : HTMLHeadingElement = this.DOM.Create("h3", {innerHTML: "TODO"});
private list : HTMLUListElement = this.DOM.Create("ul");
private form : HTMLFormElement = this.DOM.Create("form",
{
onsubmit: (ev:Event) =>
{
ev.preventDefault(); //Prevent page reload!
const items:string[] = this.Items.Get();
items.push(this.input.value); //Add a new item in Items property
this.Items.Set(items); //Triggers Changed event in Items property
this.input.value = "";
}
});
private input : HTMLInputElement = this.DOM.Create("input");
private button : HTMLButtonElement = this.DOM.Create("button");
constructor(
container : HTMLElement,
config : IWidgetBaseConfig)
{
super(container);
this.Properties.Read(config); //Read initial property values
//Add CSS class for the widget
container.classList.add("Examples_cTodo");
//Create Element structure
container.append(
this.header,
this.list,
this.form
);
this.form.append(
this.input,
this.button
);
//Listen changes in Items property
this.Items.OnChange(() => this.renderList());
//Render list the first time
this.renderList();
}
private renderList()
{
const items:string[] = this.Items.Get();
this.list.innerHTML = "";
items.forEach((item:string) => {
this.list.innerHTML += "<li>" + item + "</li>";
});
this.button.textContent="Add #" + (items.length + 1);
}
}
//Register the widget class ("Class" in todo.mdw.json)
Components.Add(Todo, "Examples.TS.Todo");Below is the sample code snippet of '.mdw.json' file
{
"Name": "Todo list",
"Category": "Widget Development - Examples",
"Class": "Examples.TS.Todo",
"Icon": "../exampleicon.svg",
"IsBrowsable": true,
"Width": 270,
"Height": 230
}2. Context API - How to use context API to drag and drop Variable or Equipment Property in the custom widget
This example explains how to use Context API to drag and drop Variable or Equipment property into the custom widget. And also this example provides a way to Listen Context of another widget from this widget.
import { Components } from "componentbase/componentregistry";
import { IComponentConfig, IProperty } from "componentbase/propertyapi";
import { cWidgetBase } from "widgetbase/widgetbase";
import { cPropertyType } from "componentbase/propertytype";
import { cCurrentValueAccessor } from "valueaccessors/currentvalueaccessor";
import { cValueRecord } from "client/data";
import { cContextBuilder, ContextItemType, IContext, IContextBuilder, IContextItem } from "componentbase/context";
class cContextSensitiveWidget extends cWidgetBase
{
public readonly Accessor:IProperty<cCurrentValueAccessor> = this.Properties.Add
(
"Accessor", cPropertyType.Component,
{
DefaultValue: new cCurrentValueAccessor(),
IsSerializable: true, IsBrowsable: true, IsReadOnly: true,
ComponentType: "ABB.Mia.cSingleValueAccessorBase"
}
);
private contextType:ContextItemType=ContextItemType.DbClassInstance;
public constructor(private container:HTMLElement, config:IComponentConfig)
{
super(container);
this.Properties.Read(config);
//Add incoming context handler
this.Context.AddHandler((context:IContext)=>{
//Handle incoming context
for(const item of context.Context)
{//context.Context may contain multiple items
if(this.handleContextItem(item)) break; //If data class received, skip others
}
});
//Fire (outgoing) context event when the user clicks in the widget
container.onclick=this.fireContext;
//Use data accessor as an example for showing values from data context
const accessor:cCurrentValueAccessor=this.Accessor.Get();
accessor.Changed.AddListener(()=>{
if(this.contextType===ContextItemType.DbClassInstance)
{
const vr:cValueRecord=accessor.ValueRecord.Get();
container.innerHTML=`${accessor}: ${vr.GetValueString()} ${accessor.Unit.Get()}`;
}
});
this.container.innerHTML=`
<div>Drag'n'drop data here (for example from a Variable list)</div>
<div>Use <i>Listen Context</i> of another widget to listen the context from this widget.</div>
`;
}
//Context In
private handleContextItem(item:IContextItem)
{
this.contextType=item.ItemType;
switch(this.contextType)
{
case ContextItemType.Value:
this.container.textContent=`Got context value ${item.Value} and the value type is ${cPropertyType[item.Type]}`;
break;
case ContextItemType.Empty:
this.container.textContent=`Got an empty context`;
break;
case ContextItemType.Object:
this.container.textContent=`Got a context object ${JSON.stringify(item.Value)}`;
break;
case ContextItemType.DbClassInstance:
{//Item contains a data class instanace
//can be used for example with value accessors
const accessor=this.Accessor.Get();
accessor.Source.Set(item.Class);
if(item.Property) accessor.Property.Set(item.Property);
accessor.Instance.Set(item.Id);
return true; //break context handling
}
}
return false;
}
//Context Out
private fireContext=(_:MouseEvent)=>{
this.Context.Fire(); //Fires the Context event (automatically calls GetContextBuilder)
};
//Build data context object
public GetContextBuilder():IContextBuilder
{
const ctxbuilder:IContextBuilder=new cContextBuilder(this);
//Create data context item using the data accessor
const accessor=this.Accessor.Get();
if(accessor.Property.Get())
{//Data class instance with the specific data property
ctxbuilder.AddClassInstanceProperty(
accessor.Connection.Get(), //connection
accessor.Source.Get(), //class
accessor.Instance.Get(), //instance id
accessor.Property.Get() //instance property
);
}
else
{//Otherwise same but omits the specific property information
ctxbuilder.AddClassInstance(
accessor.Connection.Get(), //connection
accessor.Source.Get(), //class
accessor.Instance.Get(), //instance id
);
}
return ctxbuilder;
}
}
Components.Add(cContextSensitiveWidget, "Examples.TS.ContextSensitiveWidget");
3. How to create a custom component and use it as a widget property?
The below example provides a way to create a custom component and add that component as a widget property.
//custom component
class ColorAndValue extends cComponentBase {
public Name: IProperty<string> = this.Properties.Add("Name", cPropertyType.Text, { Description: "Color palette name for heatmap items.", IsSerializable: true, IsBrowsable: true, ItemType: cPropertyType.Text });
public Color: IProperty<TColor> = this.Properties.Add("Color", cPropertyType.Color, { Description: "Color palette for heatmap items.", IsSerializable: true, IsBrowsable: true, ItemType: cPropertyType.Color });
//The stops is an array of tuples, where the first item is a float between 0 and 1 assigning the relative position in the gradient, and the second item is the color.
public ColorStop: IProperty<number> = this.Properties.Add("Color Stop", cPropertyType.Number, { Description: "Color stops for the gradient of a scalar color axis. It's a float between 0 and 1.", IsSerializable: true, IsBrowsable: true, ItemType: cPropertyType.Number });
public Value: IProperty<number> = this.Properties.Add("Value", cPropertyType.Number, { Description: "Value range for heatmap, the value between 0 to 1", IsSerializable: true, IsBrowsable: true, ItemType: cPropertyType.Number });
constructor(config: IWidgetBaseConfig) {
super();
this.Properties.Read(config);
}
}
Components.Add(ColorAndValue, "CustomComponent.ColorAndValue");
//Widget code
class Widget extends ReactWidget {
private readonly ColorAndValue: IProperty<ColorAndValue[]> = this.Properties.Add(
"ColorAndValue", cPropertyType.Array, {
Category: 'General',
IsBrowsable: true,
IsSerializable: true,
ItemType: cPropertyType.Component,
ComponentType: 'CustomComponent.ColorAndValue'
}
);
private readonly SeriesColors: IProperty<cPropertyType.Color[]> = this.Properties.Add(
"SeriesColors", cPropertyType.Array, {
Category: "General",
DefaultValue: ["red", "blue", "green", "yellow", "black", "white"],
ItemType: cPropertyType.Color,
IsBrowsable: true,
IsSerializable: true
});
private readonly parentHtmlElement: HTMLElement;
constructor(container: HTMLElement, config: IComponentConfig) {
super(container);
this.Properties.Read(config);
this.parentHtmlElement = container;
}
}
4. How to use Array types in custom widget properties?
The below example explains how to use Array types in custom widget properties.
public Data = this.Properties.Add<cGraphDataAccessor[]>(
"DataAccessor",
cPropertyType.Array,
{
Description: "Graph Data Accessor",
Category: "General",
ComponentType: "ABB.Mia.cGraphDataAccessor",
IsSerializable: true,
IsBrowsable: true,
IsReadOnly: false,
ItemType: cPropertyType.Component,
DefaultValue: []
}
);
private readonly SeriesColors: IProperty<cPropertyType.Color[]> = this.Properties.Add(
"SeriesColors", cPropertyType.Array, {
Category: "General",
DefaultValue: ["red", "blue", "green", "yellow", "black", "white"],
ItemType: cPropertyType.Color,
IsBrowsable: true,
IsSerializable: true
});5. How to add custom events to a widget ?
Example below explains how to add a custom event to a widget.
public readonly Clicked: IEvent<void> = this.Events.Add("Clicked", { Description: "Fires when the Details button in the widget is clicked", Category: "Events", IsBrowsable: true });6. How to perform a click operation in a custom component in the widget ?
Below code snippet explains how a user click on a button available in the custom component in the custom widget.
if (!this.DesignMode.Get()) {
this.parentHtmlElement.addEventListener('focusin', () => {
var childElement = Array.from(this.parentHtmlElement.querySelectorAll('*')).find(function (element) {
return element.id && element.id.includes('-Details-Button');
}) as HTMLButtonElement;
if (childElement != undefined) {
childElement.onclick = this.mLeftMouseDown;
}
}, false);
}
private mLeftMouseDown = (ev: MouseEvent) => {
// Fire the "Clicked" event
if (this.Accessor != undefined) {
const accessor = this.Accessor.Get() as cCurrentValueAccessor;
const config = accessor.GetContextItem();
const ctxBuilder = new cContextBuilder(this);
ctxBuilder.AddContext(this.Context.Builder());
ctxBuilder.AddClassInstanceProperty(this.connection, config.Class, config.Id, config.Property);
if (this.TargetDashboard.Get() != undefined) {
const navreq = CreateNavigationRequest(`{` + this.TargetDashboard.Get() + `}`, this.connection.GetUri(), false, ctxBuilder.GetContext(), false, true);
(this.ResolveDashboard() as IGUIDashboard)?.NavigateTo.Fire(navreq);
this.Clicked.Fire();
}
}
};7. How to Open Dashboard as a Dialog in the custom Widget ?
This section demonstrates how to open a configured dashboard as a dialog upon a button click, within a custom widget. When the user clicks the button, the specified dashboard opens as a dialog.
private OpenDashboardDialog = async (parentdashboard: IGUIDashboard, ctx: IContext) => {
const defaultWidth = 500;
const defaultHeight = 500;
const windowFrameWidthExtra = 16;
const windowFrameHeightExtra = 82;
let inst = (this.connection as IDbCacheConnection).Classes.TreeNode.Instances;
let node = await inst.GetById(this.TargetDashboard.Get());
const nodeid = node.Get('NodeId');
const fetchnode = JS.API("ABB.Mia.GUI.cDashboard.FetchNode") as (display: string, uri: string | undefined, callback: (node: unknown) => void, onfail: () => void) => void;
const createwindow: TCreateCustomEditorHandle = parentdashboard.CreateCustomEditorHandle.Get();
fetchnode(`{${nodeid}}`, undefined, (node: TNavigationNode) => {
const rootelement = document.createElement('div');
const createdashboard = JS.API("ABB.Mia.GUI.cDashboard") as (parentelement: HTMLElement, config: unknown) => IGUIDashboard;
const dashboard = createdashboard(rootelement, { CreateCustomEditorHandle: createwindow });
const buttonconfig: IButtonConfig = { OK: false, Cancel: true, Apply: false };
const win: IWindow = createwindow(this.getDashboardTitle(node), buttonconfig, defaultWidth, defaultHeight);
const windowelement = win.Element.Get()
windowelement.appendChild(rootelement);
windowelement.style.overflow = "hidden";
if (buttonconfig.Cancel) {
win.Cancel.AddListener(() => win.Dispose());
}
dashboard.LoadDisplay(node, false, undefined, true);
dashboard.Loaded.Then(() => {
const parentelement: HTMLElement = dashboard.ParentElement.Get();
const displaydef = dashboard.GetDisplayDef();
if (displaydef.UseFixedSize.Get()) {
const width: number = displaydef.FixedWidth.Get();
const height: number = displaydef.FixedHeight.Get();
this.setElementSize(parentelement, width, height);
win.DesiredSize.Set({
Width: width + windowFrameWidthExtra,
Height: height + windowFrameHeightExtra
});
}
else {
this.setElementSize(parentelement, defaultWidth, defaultHeight);
win.Refresh.AddListener(() => {
this.setElementSize(parentelement, windowelement.offsetWidth, windowelement.offsetHeight);
dashboard.RefreshEvent.Fire();
});
}
dashboard.Context.Fire(ctx);
dashboard.RefreshEvent.Fire();
return;
});
}, () => {
//this.showErrorNotification(`Error loading dashboard with id ${nodeid}.`);
console.log(`Error loading dashboard with id ${nodeid}.`);
});
}8. How to use Enums as dropdown values in custom widgets ?
Below code snippet defines a Enum with static values and use it as a dropdown values.
//Create an enum with required types
enum TimeUnit {
cSeconds = 1,
cMinutes = 2,
cHours = 3,
cShifts = 4,
cDays = 5,
cWeeks = 6,
cMonths = 7,
cYears = 8,
}
//Create a constant with INLSEnum type by adding to Enums and this can be used while creating Enum type widget property
const TimeUnitInstance: INLSEnum = Enums.Add("DEMO_WIDGETS.TimeUnit", TimeUnit);
//Widget property of Enum Type
public readonly SelectedTimeUnit: IProperty<TimeUnit> = this.Properties.Add("Selected Time", cPropertyType.Enum, { DefaultValue: TimeUnit.cSeconds, IsSerializable: true, IsBrowsable: true, EnumType: TimeUnitInstance });Updated 4 months ago
