Adding Method On Custom Module

Hi Dear,

I am trying to understand on how the perspective custom module works. I took example perspective- component and try to do several modification in order to understand how thing works. So on messenger.props.json i had added extra props member which i named it as value. It is a string.

Then i made some changes on Messenger.tsx:

on getPropsReducer i added valueConfig: tree.read(“value”) so i can get the value of value props member to be used somewhere else in the code later on. Also i need to have a method to update the value of value whick i would trigger later on by clicking another additional button. So i added a new method called as setValueConfig on getPropsReducer section , and this method which is accepting a string as parameter.

But when i compile the project i got this error message:

Type “void” is not assignable to type “string”.

Just wondering what i did wrong ? is there any way other than this to allow me adding a new method so i can change the value of any props properties ?

messenger.props.json

{
    "type": "object",
    "properties": {
        "value": {
          "type": "string",
          "description": "",
          "propertyNames": {},
          "default": "untouched"
        },
        "messageConfig": {
            "type": "object",
            "description": "A set of key:value pairs where the key is the cutoff point for when a message should be displayed, and the value is a string containing the message displayed when the 'key' number of messages is reached.",
            "propertyNames": {
                "pattern": "^[0-9][0-9]*$"
            },
            "default": {
              "0": "None",
              "1": "Messages!",
              "5": "Lots of Messages!",
              "10": "Literally ten+ messages!",
              "25": "Carpal Tunnel Warning!",
              "50": "Zero Patient Message!"
            }
        },
        "style": {
            "$ref": "urn:ignition-schema:schemas/style-properties.schema.json",
            "default": {
                "classes": ""
            }
        }
    }
}

Messenger.txt

/**
 * Example of a component that utilizes the ComponentStoreDelegate API to send and receive payload-containing messages
 * between the browser's instance of a component/store and the Gateway's model.
 *
 * This is the Client-Side implementation of a ComponentStoreDelegate. The API provides a convenient way to send
 * messages over the websocket established by the Perspective runtime.  A mirror API on the Gateway provides a
 * corresponding opportunity to send/receive messages from the Gateway.  Together, they provide a seamless 'realtime'
 * message channel for sending state to/from your client stores and server models.
 */

import * as React from 'react';
import {
    AbstractUIElementStore,
    Component,
    ComponentMeta,
    ComponentProps,
    ComponentStoreDelegate,
    JsObject,
    makeLogger,
    PComponent,
    PropertyTree,
    SizeObject
} from '@inductiveautomation/perspective-client';
import { bind } from 'bind-decorator';

// The 'key' or 'id' for this component type.  Component must be registered with this EXACT key in the Java side as well
// as on the client side.  In the client, this is done in the index file where we import and register through the
// ComponentRegistry provided by the perspective-client API.
export const COMPONENT_TYPE = "au.com.deloitte.srt.modules.common.component.display.messenger";

/**
 * Name of the message config prop defined in the json schema defined in common/src/main/resources/messenger.props.json
 */
export const MESSAGE_CONFIG_PROP = "messageConfig";

interface MessagePropConfig {
    [key: string]: string;
}

// Default configuration in component props.  Added here just as a useful reference.  Generally only defined in
// the prop schema (props.json file).
export const DEFAULT_MESSAGE_CONFIG: MessagePropConfig = {
    "0": "None",
    "1": "Messages!",
    "5": "Lots of Messages!",
    "10": "Literally ten+ messages!",
    "25": "Carpal Tunnel Warning!",
    "50": "Zero Patient Message!",
};

const logger = makeLogger("radcomponents.Messenger");

interface MessengerDelegateState {
    messagePending: boolean;
    messageCount: number;
}

// These match events in the Gateway side component delegate.
enum MessageEvents {
    MESSAGE_REQUEST_EVENT = "messenger-component-message-event",
    MESSAGE_RESPONSE_EVENT = "messenger-component-message-response-event"
}

/**
 * ComponentStoreDelegate provides a way to communicate with a Gateway side implementation of ComponentModelDelegate.
 * Communication is done over Perspective's websocket connection. Incoming messages are mapped to implemented handlers 
 * in the abstract method `handleEvent`. The ComponentStoreDelegate can also be used to store local state if needed.
 * 
 * Tip: Avoid frequently sending large payloads over the websocket connection.  The websocket connection is
 * the primary way the front-end communicates with the backend (Gateway).  Sending large payloads often will cause traffic 
 * over the network potentially slowing other tasks relying on communication. Consider fetching the payload of a websocket 
 * message over HTTP instead to help alleviate some websocket traffic for large and frequent payloads.
 * 
 * Since the ComponentStoreDelegate is intended to be paired with a corresponding Perspective component implementation, 
 * its state, if any, is meant to be passed to the paired component via the component's `props`.  This is done by invoking 
 * `mapStateToProps` of the ComponentStoreDelegate by the paired component's Higher Order Component (HOC) 
 *  once the delegate notifies listeners of state change.
 * 
 * The MessageComponentGateway delegate inherits these methods from the ComponentStoreDelegate abstract class:
 * 
 * @method `public subscribe(listener: Function): Function` - public methods used by listeners to subscribe to state changes or notifications.  
 * Returns a disposer function that should be use to unsubscribe and remove reference to the listener.  Use this to prevent memory leaks where necessary.
 * @method `protected notify(): void` - called by the delegate as a means to notify any subscribed listeners of state changes. Listeners will then read the new values.
 * @method `abstract handleEvent(eventName: string, eventObject: PlainObject): void` - receives model events from the Gateway side delegate over the websocket connection.
 * @method `fireEvent(eventName: string, eventObject: PlainObject): void` - fires model event that is received by the Gateway side delegate over the websocket connection.
 * @method `mapStateToProps(): PlainObject` - invoked by the HOC wrapper component to map any delegate state to component props when the HOC is notified of state changes.
 * 
 */
export class MessageComponentGatewayDelegate extends ComponentStoreDelegate {
    /**
     * Value that is set when a message should be pending.
     */
    private messagePending: boolean = false;

    /**
     * The number of messages that have occurred between the Gateway and this delegate.
     */
    private messageCount: number = 0;
    
    constructor(componentStore: AbstractUIElementStore) {
        // Required initialization of super.
        super(componentStore);
    }
    
    /**
     * Maps our delegate state to component props. Invoked by the components HOC
     * Wrapper component in the ComponentStore and passed to the Perspective 
     * component in props, i.e. `this.props.props.delegate`.  Will do so
     * whenever this delegate notifies listeners of state changes.
     */
    mapStateToProps(): MessengerDelegateState {
        return {
            messageCount: this.messageCount,
            messagePending: this.messagePending
        };
    }

    public fireDelegateEvent(eventName: string, eventObject: JsObject): void {
        // log messages left intentionally
        logger.info(() => `Firing ${eventName} event with message body ${eventObject}`);
        // Inherited from the ComponentStoreDelegate abstract class.
        this.fireEvent(eventName, eventObject);
        
        // Set messagePending to true since an event has been sent to the Gateway.
        // Will be set to false when a response event is received from the Gateway.
        this.messagePending = true;
        // Notify listeners of changes to message pending. This will update the props 
        // (this.props.delegate) being passed to the paired component (MessengerComponent).
        this.notify();
    }

    // Used by our component to fire a message to the gateway.
    public fireGatewayMessage(): void {
        this.fireDelegateEvent(MessageEvents.MESSAGE_REQUEST_EVENT, { count: this.messageCount });
    }

    /**
     * Implements `handleEvent` of abstract class ComponentStoreDelegate.
     * 
     * Will automatically be invoked whenever a message is sent down from the corresponding delegate 
     * on the backend. Here we map messages and their payloads to the appropriate handlers.
     */
    handleEvent(eventName: string, eventObject: JsObject): void {
        console.log("handleEvent starts")
        const {
            MESSAGE_RESPONSE_EVENT
        } = MessageEvents;

        console.log("MESSAGE_RESPONSE_EVENT => " + MESSAGE_RESPONSE_EVENT);

        switch (eventName) {
            case MESSAGE_RESPONSE_EVENT:
                this.handleComponentResponseEvent(eventObject);
                break;
            default:
                logger.warn(() => `No delegate event handler found for event: ${eventName} in MessageComponentGatewayDelegate`);
        }
        console.log("handleEvent stops")
    }

    handleComponentResponseEvent(eventObject: JsObject): void {
        console.log("handleComponentResponseEvent starts")

        if (eventObject && !isNaN(eventObject.count)) {
            if (
                this.messageCount !== eventObject.count ||
                this.messagePending !== false
            ) {
                // The count sent to the Gateway side component delegate where it is 
                // incremented by one and then returned in this response event.
                this.messageCount = eventObject.count;
                // A response event has been received.  Message is no longer pending. Reset state to false.
                this.messagePending = false;
                // Notify listeners of changes to internal state. This will update the props 
                // (this.props.delegate) being passed to the paired component (MessengerComponent).
                this.notify();
            }

        } else if (eventObject && eventObject.error) {
            logger.error(() => `Error MessageDelegate Response: ${eventObject.error}`);
        } else {
            logger.error(() => `Detected no payload in response!`);
        }
        console.log("handleComponentResponseEvent stops")
    }
}

/**
 * Our Perspective component, written as a React class component.  Functional components are
 * also acceptable if you choose to make use of React's Hook API, or simply prefer writing functional components. 
 * 
 * MessengerDelegateState is optional and only useful when a component actually has
 * a registered ComponentDelegate as it does in this example.
 */
export class MessengerComponent extends Component<ComponentProps<MessagePropConfig, MessengerDelegateState>, {}> {

    rootElementRef: Element | void;

    componentDidMount() {
        // If a reference to the root element is needed, it can be achieved using `this.props.store.element` after the
        // component has mounted.
        this.rootElementRef = this.props.store.element;
    }

    @bind
    fireUpdateToGateway(): void {
        console.log("fireUpdateToGateway starts")
        // We know a delegate exists because we've set implemented `createDelegate` in 
        // our components meta definition. Access the delegate directly via this component's 
        // ComponentStore in order to invoke methods of our delegate.  Passing methods via 
        // props (this.props.delegate.someMethodOrCallback) can also work, and is a common React pattern.
        logger.info(() => "Firing message to the Gateway!");
        (this.props.store.delegate! as MessageComponentGatewayDelegate).fireGatewayMessage();
        console.log("fireUpdateToGateway stops")
    }

    @bind
    fireUpdatePropsOnDesigner(): void {
        console.log("fireUpdatePropsOnDesigner starts")

        console.log("fireUpdatePropsOnDesigner stops")
    }

    @bind
    renderMsg(): String {
        console.log("renderMsg starts")
        console.log(this.props.props.valueConfig)
        const messageCount = this.props.delegate!.messageCount;
        // Get the correct message based on the number of messages that have been sent to/from the Gateway.
        const msgItem = Object.entries(this.props.props.messageConfig)
            .filter(([k, v]: [string, string]) => /^[0-9]+$/.test(k))
            .map(([k, v]: [string, string]) => [Number(k), v])
            .sort((pair, pair2) => (pair[0] as number) - (pair2[0] as number))
            .reduce((accum, next) => {
                if (messageCount >= (next[0] as number)) {
                    return next;
                } else {
                    return accum;
                }
            }, [-1, "Default Message!"]);

        console.log("renderMsg stops")
        return msgItem[1] as string;
    }

    render() {
        const buttonText: string = this.props.delegate!.messagePending ? "Waiting" : "Send Message";
        const buttonFire: string = this.props.delegate!.messagePending ? "Reload" : "Fire !!";
        return (
            // Note that the topmost piece of DOM requires the use of the 'emitter' provided by props in order for
            // containers to appropriately position them in addition to attaching an element reference, event listeners, and styles.
            // Adding your own events here or styles here outside of the emitter will cause an override depending on 
            // the order in which they are declared relative to the invocation of the emitter. Add inline styles and classes
            // as shown below.  Add event listeners by using `this.props.domEvents.addListener` in the constructor or on mount,
            // and be sure to remove these listeners to prevent memory leaks on un-mount.  If a reference to the root element
            // is needed, this can be done by using `this.props.store.element` as opposed to declaring a `ref` on the
            // root element.  The later will override the emitted ref and will not allow your component to properly
            // display any changes to the state of its qualities and may cause the component to throw an error.  It is
            // also highly recommended that the root element does not change throughout the lifecycle of the component.
            <div {...this.props.emit({ classes: ["messenger-component"] })}>
                <h3 className="counter">
                    {this.props.delegate!.messageCount}
                </h3>
                <span className="message">{this.renderMsg()}</span>
                <button
                    className="messenger-button"
                    onClick={this.fireUpdateToGateway}
                    disabled={this.props.delegate!.messagePending}
                >
                    {buttonText}
                </button>
                <button
                    className="messenger-button"
                    onClick={this.fireUpdatePropsOnDesigner}
                    disabled={this.props.delegate!.messagePending}
                >
                    {buttonFire}
                </button>
            </div>
        );
    }
}


// This is the actual thing that gets registered with the component registry.
export class MessengerComponentMeta implements ComponentMeta {

    getComponentType(): string {
        return COMPONENT_TYPE;
    }

    getDefaultSize(): SizeObject {
        return ({
            width: 250,
            height: 250
        });
    }

    createDelegate(component: AbstractUIElementStore): ComponentStoreDelegate | undefined {
        return new MessageComponentGatewayDelegate(component);
    }

    getViewComponent(): PComponent {
        return MessengerComponent;

    }

    getPropsReducer(tree: PropertyTree): MessagePropConfig {
        console.log(JSON.stringify(DEFAULT_MESSAGE_CONFIG));
        return {
            messageConfig: tree.read("messageConfig", DEFAULT_MESSAGE_CONFIG),
            valueConfig: tree.read("value"),
            setValueConfig: (value: string) => tree.write("value",value)
        };
    }
}

This looks like just a Typescript error, adjust your MessagePropConfig type to allow setValueConfig to be a function.

Right now all string keys (including setValueConfig) must have a string value:

interface MessagePropConfig {
    [key: string]: string;
}
1 Like

Thanks for replying. This is what i did:

  1. Because MessagePropConfig interface was used as a reference by DEFAULT_MESSAGE_CONFIG as well, i created another interface : resultPropConfig.

Then i modified the getPropsReducer method by replacing its output type reference:

After that i compiled the code on terminal and it was successful

image

Now the issue is : when i tried to execute this new setValueConfig method, i got an error :

image

And this is the method which call that new method :

The following below is the complete code: Messenger.tsx

/**
 * Example of a component that utilizes the ComponentStoreDelegate API to send and receive payload-containing messages
 * between the browser's instance of a component/store and the Gateway's model.
 *
 * This is the Client-Side implementation of a ComponentStoreDelegate. The API provides a convenient way to send
 * messages over the websocket established by the Perspective runtime.  A mirror API on the Gateway provides a
 * corresponding opportunity to send/receive messages from the Gateway.  Together, they provide a seamless 'realtime'
 * message channel for sending state to/from your client stores and server models.
 */

import * as React from 'react';
import {
    AbstractUIElementStore,
    Component,
    ComponentMeta,
    ComponentProps,
    ComponentStoreDelegate,
    JsObject,
    makeLogger,
    PComponent,
    PropertyTree,
    SizeObject
} from '@inductiveautomation/perspective-client';
import { bind } from 'bind-decorator';

// The 'key' or 'id' for this component type.  Component must be registered with this EXACT key in the Java side as well
// as on the client side.  In the client, this is done in the index file where we import and register through the
// ComponentRegistry provided by the perspective-client API.
export const COMPONENT_TYPE = "au.com.deloitte.srt.modules.common.component.display.messenger";

/**
 * Name of the message config prop defined in the json schema defined in common/src/main/resources/messenger.props.json
 */
export const MESSAGE_CONFIG_PROP = "messageConfig";

interface MessagePropConfig {
    [key: string]: string;
}

interface resultPropConfig {
    messageConfig: {};
    valueConfig: string;
    setValueConfig(value:string):void;
}


// Default configuration in component props.  Added here just as a useful reference.  Generally only defined in
// the prop schema (props.json file).
export const DEFAULT_MESSAGE_CONFIG: MessagePropConfig = {
    "0": "None",
    "1": "Messages!",
    "5": "Lots of Messages!",
    "10": "Literally ten+ messages!",
    "25": "Carpal Tunnel Warning!",
    "50": "Zero Patient Message!",
};

const logger = makeLogger("radcomponents.Messenger");

interface MessengerDelegateState {
    messagePending: boolean;
    messageCount: number;
}

// These match events in the Gateway side component delegate.
enum MessageEvents {
    MESSAGE_REQUEST_EVENT = "messenger-component-message-event",
    MESSAGE_RESPONSE_EVENT = "messenger-component-message-response-event"
}

/**
 * ComponentStoreDelegate provides a way to communicate with a Gateway side implementation of ComponentModelDelegate.
 * Communication is done over Perspective's websocket connection. Incoming messages are mapped to implemented handlers 
 * in the abstract method `handleEvent`. The ComponentStoreDelegate can also be used to store local state if needed.
 * 
 * Tip: Avoid frequently sending large payloads over the websocket connection.  The websocket connection is
 * the primary way the front-end communicates with the backend (Gateway).  Sending large payloads often will cause traffic 
 * over the network potentially slowing other tasks relying on communication. Consider fetching the payload of a websocket 
 * message over HTTP instead to help alleviate some websocket traffic for large and frequent payloads.
 * 
 * Since the ComponentStoreDelegate is intended to be paired with a corresponding Perspective component implementation, 
 * its state, if any, is meant to be passed to the paired component via the component's `props`.  This is done by invoking 
 * `mapStateToProps` of the ComponentStoreDelegate by the paired component's Higher Order Component (HOC) 
 *  once the delegate notifies listeners of state change.
 * 
 * The MessageComponentGateway delegate inherits these methods from the ComponentStoreDelegate abstract class:
 * 
 * @method `public subscribe(listener: Function): Function` - public methods used by listeners to subscribe to state changes or notifications.  
 * Returns a disposer function that should be use to unsubscribe and remove reference to the listener.  Use this to prevent memory leaks where necessary.
 * @method `protected notify(): void` - called by the delegate as a means to notify any subscribed listeners of state changes. Listeners will then read the new values.
 * @method `abstract handleEvent(eventName: string, eventObject: PlainObject): void` - receives model events from the Gateway side delegate over the websocket connection.
 * @method `fireEvent(eventName: string, eventObject: PlainObject): void` - fires model event that is received by the Gateway side delegate over the websocket connection.
 * @method `mapStateToProps(): PlainObject` - invoked by the HOC wrapper component to map any delegate state to component props when the HOC is notified of state changes.
 * 
 */
export class MessageComponentGatewayDelegate extends ComponentStoreDelegate {
    /**
     * Value that is set when a message should be pending.
     */
    private messagePending: boolean = false;

    /**
     * The number of messages that have occurred between the Gateway and this delegate.
     */
    private messageCount: number = 0;
    
    constructor(componentStore: AbstractUIElementStore) {
        // Required initialization of super.
        super(componentStore);
    }
    
    /**
     * Maps our delegate state to component props. Invoked by the components HOC
     * Wrapper component in the ComponentStore and passed to the Perspective 
     * component in props, i.e. `this.props.props.delegate`.  Will do so
     * whenever this delegate notifies listeners of state changes.
     */
    mapStateToProps(): MessengerDelegateState {
        return {
            messageCount: this.messageCount,
            messagePending: this.messagePending
        };
    }

    public fireDelegateEvent(eventName: string, eventObject: JsObject): void {
        // log messages left intentionally
        logger.info(() => `Firing ${eventName} event with message body ${eventObject}`);
        // Inherited from the ComponentStoreDelegate abstract class.
        this.fireEvent(eventName, eventObject);
        
        // Set messagePending to true since an event has been sent to the Gateway.
        // Will be set to false when a response event is received from the Gateway.
        this.messagePending = true;
        // Notify listeners of changes to message pending. This will update the props 
        // (this.props.delegate) being passed to the paired component (MessengerComponent).
        this.notify();
    }

    // Used by our component to fire a message to the gateway.
    public fireGatewayMessage(): void {
        this.fireDelegateEvent(MessageEvents.MESSAGE_REQUEST_EVENT, { count: this.messageCount });
    }

    /**
     * Implements `handleEvent` of abstract class ComponentStoreDelegate.
     * 
     * Will automatically be invoked whenever a message is sent down from the corresponding delegate 
     * on the backend. Here we map messages and their payloads to the appropriate handlers.
     */
    handleEvent(eventName: string, eventObject: JsObject): void {
        console.log("handleEvent starts")
        const {
            MESSAGE_RESPONSE_EVENT
        } = MessageEvents;

        console.log("MESSAGE_RESPONSE_EVENT => " + MESSAGE_RESPONSE_EVENT);

        switch (eventName) {
            case MESSAGE_RESPONSE_EVENT:
                this.handleComponentResponseEvent(eventObject);
                break;
            default:
                logger.warn(() => `No delegate event handler found for event: ${eventName} in MessageComponentGatewayDelegate`);
        }
        console.log("handleEvent stops")
    }

    handleComponentResponseEvent(eventObject: JsObject): void {
        console.log("handleComponentResponseEvent starts")

        if (eventObject && !isNaN(eventObject.count)) {
            if (
                this.messageCount !== eventObject.count ||
                this.messagePending !== false
            ) {
                // The count sent to the Gateway side component delegate where it is 
                // incremented by one and then returned in this response event.
                this.messageCount = eventObject.count;
                // A response event has been received.  Message is no longer pending. Reset state to false.
                this.messagePending = false;
                // Notify listeners of changes to internal state. This will update the props 
                // (this.props.delegate) being passed to the paired component (MessengerComponent).
                this.notify();
            }

        } else if (eventObject && eventObject.error) {
            logger.error(() => `Error MessageDelegate Response: ${eventObject.error}`);
        } else {
            logger.error(() => `Detected no payload in response!`);
        }
        console.log("handleComponentResponseEvent stops")
    }
}

/**
 * Our Perspective component, written as a React class component.  Functional components are
 * also acceptable if you choose to make use of React's Hook API, or simply prefer writing functional components. 
 * 
 * MessengerDelegateState is optional and only useful when a component actually has
 * a registered ComponentDelegate as it does in this example.
 */
export class MessengerComponent extends Component<ComponentProps<MessagePropConfig, MessengerDelegateState>, {}> {

    rootElementRef: Element | void;

    componentDidMount() {
        // If a reference to the root element is needed, it can be achieved using `this.props.store.element` after the
        // component has mounted.
        this.rootElementRef = this.props.store.element;
    }

    @bind
    fireUpdateToGateway(): void {
        console.log("fireUpdateToGateway starts")
        // We know a delegate exists because we've set implemented `createDelegate` in 
        // our components meta definition. Access the delegate directly via this component's 
        // ComponentStore in order to invoke methods of our delegate.  Passing methods via 
        // props (this.props.delegate.someMethodOrCallback) can also work, and is a common React pattern.
        logger.info(() => "Firing message to the Gateway!");
        (this.props.store.delegate! as MessageComponentGatewayDelegate).fireGatewayMessage();
        console.log("fireUpdateToGateway stops")
    }

    @bind
    fireUpdatePropsOnDesigner(): void {
        console.log("valueConfig => " + this.props.props.valueConfig)
        this.props.props.setValueConfig("touched")
    }

    @bind
    renderMsg(): String {
        console.log("renderMsg starts")
        console.log(this.props.props.valueConfig)
        const messageCount = this.props.delegate!.messageCount;
        // Get the correct message based on the number of messages that have been sent to/from the Gateway.

        const msgItem = Object.entries(this.props.props.messageConfig)
            .filter(([k, v]: [string, string]) => /^[0-9]+$/.test(k))
            .map(([k, v]: [string, string]) => [Number(k), v])
            .sort((pair, pair2) => (pair[0] as number) - (pair2[0] as number))
            .reduce((accum, next) => {
                if (messageCount >= (next[0] as number)) {
                    return next;
                } else {
                    return accum;
                }
            }, [-1, "Default Message!"]);

        console.log("renderMsg stops")
        return msgItem[1] as string;
    }

    render() {
        const buttonText: string = this.props.delegate!.messagePending ? "Waiting" : "Send Message";
        const buttonFire: string = this.props.delegate!.messagePending ? "Reload" : "Fire !!";
        return (
            // Note that the topmost piece of DOM requires the use of the 'emitter' provided by props in order for
            // containers to appropriately position them in addition to attaching an element reference, event listeners, and styles.
            // Adding your own events here or styles here outside of the emitter will cause an override depending on 
            // the order in which they are declared relative to the invocation of the emitter. Add inline styles and classes
            // as shown below.  Add event listeners by using `this.props.domEvents.addListener` in the constructor or on mount,
            // and be sure to remove these listeners to prevent memory leaks on un-mount.  If a reference to the root element
            // is needed, this can be done by using `this.props.store.element` as opposed to declaring a `ref` on the
            // root element.  The later will override the emitted ref and will not allow your component to properly
            // display any changes to the state of its qualities and may cause the component to throw an error.  It is
            // also highly recommended that the root element does not change throughout the lifecycle of the component.
            <div {...this.props.emit({ classes: ["messenger-component"] })}>
                <h3 className="counter">
                    {this.props.delegate!.messageCount}
                </h3>
                <span className="message">{this.renderMsg()}</span>
                <button
                    className="messenger-button"
                    onClick={this.fireUpdateToGateway}
                    disabled={this.props.delegate!.messagePending}
                >
                    {buttonText}
                </button>
                <button
                    className="messenger-button"
                    onClick={this.fireUpdatePropsOnDesigner}
                    disabled={this.props.delegate!.messagePending}
                >
                    {buttonFire}
                </button>
            </div>
        );
    }
}


// This is the actual thing that gets registered with the component registry.
export class MessengerComponentMeta implements ComponentMeta {

    getComponentType(): string {
        return COMPONENT_TYPE;
    }

    getDefaultSize(): SizeObject {
        return ({
            width: 250,
            height: 250
        });
    }

    createDelegate(component: AbstractUIElementStore): ComponentStoreDelegate | undefined {
        return new MessageComponentGatewayDelegate(component);
    }

    getViewComponent(): PComponent {
        return MessengerComponent;

    }

    getPropsReducer(tree: PropertyTree): resultPropConfig {
        return {
            messageConfig: tree.read("messageConfig", DEFAULT_MESSAGE_CONFIG),
            valueConfig: tree.read("value"),
            setValueConfig: (value: string):void => {tree.write("value",value);}
        };
    }
}

After hours i figured out the answer. I had to modified MessengerComponent by replacing MessagePropConfig with resultPropConfig.

image

Now it works like a charm. By anyway thank you for helping me out.


1 Like

What's the advantage of providing setValueConfig function through getPropsReducer?

You already have access to the props PropertyTree from your component via props.store.props.