Lumira 2.1 contains a very powerful feature. While in former Design Studio and Lumira version an app had constant number of components, in 2.1 you can also create and delete components via scripting. This give you – the app developer – much more freedom to react on external factors, e.g. specific data from your data sources or preferences of the user. We have developed it in 2.0 for Discovery: The Discovery tools is mainly a Designer app and required such a feature, e.g. to create data sources, charts and crosstabs on user interaction or to load and unload stories. In version 2.1 you are free to use it, but be warned: while it is a very powerful feature it is also dangerous: if you don’t exactly know what, you are doing, you could make your app slow or create an unsupported state.

Most of the dynamic features are exposed with the new technical component called “COMPONENTS”. Its methods are also called the Components-API.

Creating a Component

Try it out: simply create an App or a Composite and add a “COMPONENTS” technical component. Then start scripting, e.g. adding a button’s ON_CLICK event.

var myNewText = COMPONENTS.createComponent(ComponentType.Text); myNewText.setText("Hello world"); myNewText.setLeftMargin(100); myNewText.setTopMargin(100);

What is special with this script expect that you have one component more in your app after it is executed? Maybe the fact that type of the component returned by COMPONENTS.createComponent depends on the argument.

The parameter “type” is an entry from a special enumeration “ComponentType” – which automatically contains all available component types, including SDK components and Composites. The return type is – according to the documentation “GenericComponentBase” – which is only a placeholder for the concrete type specified by the “type” argument. If you hover, you will see that the function really returned a “Text” component – so you can afterward use all script APIs that are available on a Text component.

If you want to create the new component inside of some container component, e.g. in a Panel, pass the container as second argument.

Deleting a Component

If you later want to delete the component again, you should store it in a Global Script variable: Create a global script variable of type “Text” and modify your creation script like this:

gMyNewText = COMPONENTS.createComponent(ComponentType.Text); gMyNewText.setText("Hello world"); gMyNewText.setLeftMargin(100); gMyNewText.setTopMargin(100);

Later in another script you can simply delete the new component:

COMPONENTS.deleteComponent(gMyNewText);

Creating and Deleting Multiple Components

In most cases, you will create multiple components in a loop, e.g. using some data as input:

var dims = DS_1.getDimensions(); dims.forEach(function(dim, index) { var text = COMPONENTS.createComponent(ComponentType.Text); text.setLeftMargin(20); text.setTopMargin(20 + index*30); text.setWidth(200); text.setText(dim.name + " (" + dim.text + ")"); });

This is fine as long as you don’t need to delete the created components later.

If you need to delete the components, e.g. to create new ones with nested data, there are two typical strategies:

  • Keep all created components in a global array
  • Create a container, keep it and later delete the container.

Strategy 1

Create a global variable array – best using the base type “Component”, as it allows you to mix components of multiple types:

// Delete all old dynamic components gaCreatedComponents.forEach(function(oldComp) { COMPONENTS.deleteComponent(oldComp); }); // Trick: make gaCreatedComponents empty gaCreatedComponents = [me]; // add a dummy component gaCreatedComponents.pop(); // remove the dummy // Now create new components var dims = DS_1.getDimensions(); dims.forEach(function(dim, index) { var text = COMPONENTS.createComponent(ComponentType.Text); text.setLeftMargin(20); text.setTopMargin(20 + index*30); text.setWidth(200); text.setText(dim.name + " (" + dim.text + ")"); gaCreatedComponents.push(text); });

Strategy 2

Create a global script variable with a container component, e.g. a Panel.

Now the first thing you do it to create the panel. All other components will go into it. Later you can completely delete the Panel to implicitly delete all other components as well:

if (gPanel != undefined) { // This will delete the old panel and all old componnents contained in it COMPONENTS.deleteComponent(gPanel); } // Create a new Panel gPanel = COMPONENTS.createComponent(ComponentType.Panel); // Now create new components var dims = DS_1.getDimensions(); dims.forEach(function(dim, index) { var text = COMPONENTS.createComponent(ComponentType.Text, gPanel); text.setLeftMargin(20); text.setTopMargin(20 + index*30); text.setWidth(200); text.setText(dim.name + " (" + dim.text + ")"); }); gPanel.setWidth(200); gPanel.setHeight(dims.length * 30 + 20);

Creating and Deleting Composites

You can also create instances of Composites. This is the way how Discovery loads and unloads Stories. If you create a composite called “COMP_1” in you document (LUMX file), your will find an entry like “LUM_CC762C8C979A2EEEAFE788A0760955D9_COMP1” in your ComponentType enumeration. The long “number” is internal ID for the document in which the composite is contained. For local documents, it is derived from the LUMX file name. For BIP documents it uses the document’s CUID. Composites from the same document as your current APP can be always created. If you want to create instances of a Composite from a different document, you must have at least one Composite from that document statically contained in your app – else we would not know that there is a reference to the document.

What’s next?

In my next blog I will show you another interesting new API for dynamic app: DS.getDataSelections() – which allows you to iterate over result sets.

I’m looking forward for you feedback, questions, proposals etc.