angularweb-componentcustom-elementangular-elementsangular-signals

How to properly manage a boolean signal input in a custom Angular element (v19)?


I have a web component created with Angular 19 prerelease version. It has a liked signal input, which can be set from a parent but can't be modified from within because signal inputs are read only. To manage toggling the state, I introduced a local signal _liked, which I can toggle and emit its value to the parent. This feels like an overkill, I'm not sure if this is a good way of handling the state.

I need to transform the input value into a boolean, because when the custom element is used outside Angular context, say in a Vue app, the resulting html will render my input as a string and strings are always truthy.

Do you have any suggestions on improving the following code?

export class LikeButtonComponent {
    liked = input(false, { transform: booleanAttribute });
    _liked = signal(this.liked());
    
    likedChange = output<boolean>({ alias: 'likedchange' });
    
    constructor() {
        effect(() => {
            this._liked.set(this.liked());
        });
    }
    
    toggleLike() {
        this._liked.update(value => !value);
        this.likedChange.emit(this._liked());
    }
}

I looked into the model() and it doesn't seem to be suitable here because it doesn't expose a transform option. The problem is that when values are passed into a web component they are passed as strings, like this <like-button liked="true"></like-button>.


Solution

  • The latest prerelease added a new experimental linkedSignal() function which is basically a computed that is writable at the same time. It allows you to skip the effect:

    export class LikeButtonComponent {
        liked = input(false, { transform: booleanAttribute });
        _liked = linkedSignal(() => this.liked());
        
        likedChange = output<boolean>({ alias: 'likedchange' });
        
        toggleLike() {
            this._liked.update(value => !value);
            this.likedChange.emit(this._liked());
        }
    }