Forum Discussion

Anonymous's avatar
Anonymous
Not applicable
6 years ago
Solved

Angular custom visual

Can we use angular 8+ to create custom visulas. I could see links for creating custom visual in React. Similarly can we create a custom visual in angular
  • dm-p's avatar
    dm-p
    6 years ago

    Hi Anonymous,

    I haven't personally unpacked this partcular pattern until you asked, as it's just works for me, but having done so (and thanks very much for asking, as it was quite a fun exercise), my understanding is as follows:

    • We have two objects available to us in a React component that we can use to manage it: props and state.
    • props are passed in upon creation/re-render, whereas state is managed internally to the component.
    • In this example, the React component is instantiated in the visual's constructor method, meaning that there is only a chance to set its props when the visual is added to the canvas or its page is opened.
    • This will be done for efficiency, i.e. it makes sense to only create the "app"-level component once rather than re-render it each time the visual updates.
    • However, this is too soon in the visual's lifecyle to be able to get the info we need from the update method, such as the dataView, viewport etc.
    • It's also not really the "React way" to destroy and create elements on changes.
    • So in this example, the component props are inaccessible in the visual's update method, so the only way to get objects into our component is to manipulate the state externally. Which we techincally should not be doing. But we have to.

    Your posted code snippet doesn't include the overloaded React methods, so let's add them from the example so we can round this out:

     

        private static updateCallback: (data: object) => void = null;
    
        public static update(newState: State) {
            if(typeof ReactCircleCard.updateCallback === 'function'){
                ReactCircleCard.updateCallback(newState);
            }
        }
    
        public componentWillMount() {
            ReactCircleCard.updateCallback = (newState: State): void => { this.setState(newState); };
        }
    
        public componentWillUnmount() {
            ReactCircleCard.updateCallback = null;
        }

     

    If we think about this in terms of component lifecycle, what's happening here is as follows:

    1. If called statically, the updateCallback method doesn't do anything if invoked, because the component will have no state.
    2. When the component is about to mount (i.e. when the visual constructor method calls it) and React invokes componentWillMount, this overloads the previously void/null updateCallback method to now trigger the component's setState method from within by assigning it the fat arrow function.
    3. The component's update function will now pass through the state we supply from our visual's update method into the overloaded updateCallback function and this will update the component state correctly. Similarly, if this function is called statically, it won't do anything as the updateCallback is void/null at this point.
    4. When the visual is unloaded/destroyed, the componentWillUnmount method (triggered by React) will reset the overloaded updateCallback function to null again.

    If you want to unpack this further or get an explanation for the rationale, it'll be best to follow up with the team on the previously supplied email and hopefully they can clarify further. Please share if you find anything out 🙂


    One thing I have found with using this in the real world is, if you need to add properties to the component State interface that are not part of the visual's update method, it's handy to modify the update function's signature to accept <Partial>State as follows:

     

        public static update(newState: <Partial>State) {
            /** function code as before */
        }

     

    The alternative is to declare properties that aren't available to be supplied from the visual's update method as optional (with a ?) in the State interface, e.g.:

     

    export interface State {
        textLabel: string,
        textValue: string,
        internalStateValue?: string
    }

     

    The latter way adds the overhead of having to check each optional property whenever you need to use it, so I prefer the former as I at least know that my initial state is always going to be correct when the visual instantiates.

    Regards,

    Daniel