Search code examples
angularnative-web-component

Set callback of native webcomponent from Angular


I have a native webcomponent with a callback function 'myCallback'.

<script>
    class MyComponent extends HTMLElement {
        constructor() {
            super();
            this.myCallback = this.getAttribute("my-callback");
        }

        connectedCallback() {
            this.innerHTML = "MyComponent";

            const me = this;
            setTimeout(function () {
                const cb = me.myCallback;
                if (typeof cb === "string") {
                    new Function(cb).call(window);
                } else if (typeof cb === "function") {
                    cb.call(window);
                } else {
                    console.log("not a function: " + cb);
                }
            }, 1000);
        }

    }

    customElements.define("my-component", MyComponent);
</script>

I want to use this webcomponent in Angular and assign a callback to it but it doesn't seem to work. This is what I have tried so far:

<my-component my-callback="angularCallback()"></my-component>
<my-component my-callback="{{angularCallback}}"></my-component>
<my-component [my-callback]="angularCallback"></my-component>
<my-component [my-callback]="angularCallback()"></my-component>
<my-component (my-callback)="angularCallback()"></my-component>

The first line above throws the error "angularCallback is not a function" because it is not defined in the window, but in Angular. The other lines are never called and do not throw any error.

As a simple test I tried the following and it works fine:

<my-component my-callback="console.log('test-callback');"></my-component>

Is there a way to assign the callback in Angular via the template?

Update with solution

The mistake that I made was that I tried [my-callback] instead of [myCallback]

So the solution is the following:

<my-component [myCallback]="angularCallback"></my-component>

Solution

  • Attributes are passed down to a custom element as a string. When passing a function, it will be easier to pass it as a property on the custom element.

    You could pass the property using the [] syntax in Angular.

    <my-component [mycallback]="callbackMethod"></my-component>
    

    The callbackMethod being a simple function in the ts code.

    callbackMethod = () => console.log('callback method called');
    

    Then in the custom web component, you can directly access the property value.

    setTimeout(function () {
        // directly access the callback property on the component
        const cb = this.mycallback;
    
        if (typeof cb === "string") {
            new Function(cb).call(window);
        } else if (typeof cb === "function") {
            cb.call(window);
        } else {
            console.log("not a function: " + cb);
        }
    }, 1000);
    

    I've created a StackBlitz example to illustrate this.