Dynamic control generation

Controls can create additional foreign controls and use them in their own context. This enables the system to respond to events of these child controls.

Example using the ControlFactory API

In this example the API of the ControlFactory is used. When the control is created, the child control is created and attached to its own representation.

By defining the parent, the child control is embedded in its own life cycle, so that the child control is automatically managed at the same time.

In addition, an event handler is registered to respond to events of the child control. Note that this event registry must be cleaned up in a dedicated Destroy action in order to fix a memory leak.

See also Control life cycle

protected __childOnPressedDestroyEvent: DestroyFunction | undefined; 

public __init() {
   super.__init();

   let childName = this.__id + '_startAction';
   let myButton = TcHmi.ControlFactory.createEx(
      'TcHmi.Controls.Beckhoff.TcHmiButton',
      childName,
      {
         'data-tchmi-top': 25,
         'data-tchmi-left': 250,
         'data-tchmi-width': 200,
         'data-tchmi-height': 50,
         'data-tchmi-text': 'Start Action',
         'data-tchmi-background-color': {
            'color': 'rgba(55, 55, 55, 1)'
         },
         'data-tchmi-state-symbol': '%s%PLC1.MAIN.testBool%/s%' // Binding a symbol to an attribute during control creation
      },
      this // marks our control as the parent
   );
   if (myButton) {
      // Append to our DOM. This will be detected by the framework and its .__attach function will be called automatically.
      this.__element.append(myButton.getElement());
      this.__childOnPressedDestroyEvent = TcHmi.EventProvider.register(childName + '.onPressed', () => {
         // do something
      });

      // Binding a symbol to an attribute after control creation
      Binding.createEx2('%s%PLC1.MAIN.anotherBool%/s%', 'StateSymbol', myButton);
   }
}

public destroy() {
   if (this.__keepAlive) {
      return;
   }
   if(this.__childOnPressedDestroyEvent){
      this.__childOnPressedDestroyEvent();
   }
   super.destroy();
}

Example of a child control embedded in the template

Child controls can also be embedded directly in the Template of a control. The same syntax is used for this as is used in engineering to represent a control in code. The only difference is the {Id} placeholder, which is replaced by the actual ID when the control is compiled.

It is mandatory to use the {Id} placeholder, otherwise it would be impossible to create more than one instance of the control. The compilation of the second instance would run into an error because it would try to create a control with a name already assigned.

<div class="TcHmi_Controls_Example-template">
    <div id="{Id}_startAction" data-tchmi-type="TcHmi.Controls.Beckhoff.TcHmiButton"
        data-tchmi-top="25" data-tchmi-left="250"
        data-tchmi-width="200" data-tchmi-height="50"
        data-tchmi-text="Start Action" data-tchmi-state-symbol="%s%PLC1.MAIN.testBool%/s%"
    >
        <script data-tchmi-target-attribute="data-tchmi-background-color" type="application/json">
            {
                "color": "rgba(55, 55, 55, 1)"
            }
        </script>
    </div>
</div>

The instance of the child control can then be accessed in __previnit or __init of the control:

let button = Controls.get<TcHmi.Controls.Beckhoff.Button>(this.getId() + '_startAction');