Handling of symbol expressions in objects

A control setter can contain objects that contain symbol expressions. The control must enable this for each attribute individually via the property allowSymbolExpressionsInObject. In this case this is shown in Visual Studio as follows:

Handling of symbol expressions in objects 1:

For this purpose, the source code that deals with attribute handling must be adapted accordingly.

First an existing ObjectResolver has to be located. If successful, this will be destroyed. Then a new ObjectResolver with the new object (which may contain the symbol expression) is created and saved. At the same time a watch function is linked, which is called initially and when changes in referenced symbols occur.

This watch function checks whether the control is currently visible. If this is not the case, the ObjectResolver is put into a waiting state. If the control is made visible later, all ObjectResolvers are reactivated by the system. If an error occurs, processing is aborted with an error message.
Processing is terminated when the new value is identical to the old value. If the value is changed, the new value is saved, and the system is notified about the change and responds to it.

Instead of the generic type object, a more specific TypeScript type definition can be used.

protected __value: object | undefined;

/**
* @description Setter function for 'data-tchmi-value' attribute.
* @param valueNew the new value or null 
*/
public setValue(valueNew: object | null): void {
    // convert the value with the value converter
    let convertedValue = TcHmi.ValueConverter.toObject<object>(valueNew);

    // check if the converted value is valid
    if (convertedValue === null) {
        // if we have no value to set we have to fall back to the defaultValueInternal from description.json
        convertedValue = this.getAttributeDefaultValueInternal('Value') as object;
    }

    let resolverInfo = this.__objectResolvers.get('value');

    if (resolverInfo) {
        if (resolverInfo.watchDestroyer) {
            resolverInfo.watchDestroyer();
        }
        resolverInfo.resolver.destroy();
    }

    let resolver = new Symbol.ObjectResolver(convertedValue);

    this.__objectResolvers.set('value', {
        resolver: resolver,
        watchCallback: this.__onResolverForMyAttributeWatchCallback,
        watchDestroyer: resolver.watch(this.__onResolverForMyAttributeWatchCallback)
    });
}

/**
* The watch callback for the value object resolver.
*/
protected __onResolverForMyAttributeWatchCallback = (data: Symbol.ObjectResolver.IWatchResultObject<Transform[]>) => {
    if (this.__isAttached === false) { // While not attached attribute should only be processed once during initializing phase.
        this.__suspendObjectResolver('value');
    }

    if (data.error !== TcHmi.Errors.NONE) {
        TcHmi.Log.error('[Source=Control, Module=TcHmi.Controls.System.TcHmiControl, Id=' + this.getId() + ', Attribute=Value] Resolving symbols from object failed with error: ' + TcHmi.Log.buildMessage(data.details));
        
        return;
    }

    if (tchmi_equal(data.value, this.__value)) {
        return;
    }

    this.__value = data.value;

    TcHmi.EventProvider.raise(this.__id + '.onPropertyChanged', { propertyName: 'Value' });


    this.__processValue();
};

public __processValue() {
    // process actions with Value
    // ...
}