1

I have a directive that helps with adding toggle effects to various elements.

export class AlloyToggleDirective {
    private toggled = false;

    @Input('alloyToggled')
    @HostBinding('class.alloy-toggled')
    get isToggled() { return this.toggled; }
    set isToggled(value: boolean) {
      this.toggled = value;
      this.alloyToggledChange.emit(this.toggled);
    }
    @Output() alloyToggledChange: EventEmitter<boolean> = new EventEmitter();

    toggle() {
      this.toggled = !this.toggled;
    }

    @HostListener('click')
    onclick() {
        this.toggled = !this.toggled;
        this.alloyToggledChange.emit(this.toggled);
    }
}

It works fine when toggled, however the initial bound value is ignored:

<button [alloyToggled]="booleanValue">

The HTML will reflect that initial value, but the class is only applied after toggling programmatically or via mouse. Is there a strange interaction when @HostBinding is on an @Input?

Anthony
  • 7,638
  • 3
  • 38
  • 71
  • I'd guess that `alloyToggled` and `toggled` are fighting from the start. Since the **get** applies **false** from the start you will always receive **false**. Is there a reason you're not using `alloyToggled` throughout, and have the `private toggled`? – Z. Bagley Feb 07 '20 at 20:45
  • 1
    Check out this stackblitz, I think it'll help clear up Directives for you: https://stackblitz.com/angular/naavjopgege?file=src%2Fapp%2Fapp.component.html (also, help with @Input syntax) – Z. Bagley Feb 07 '20 at 20:50
  • If that were true, the `onClick` and changes to `booleanValue` wouldn't work. However, they do. It's _only_ the initial value of `booleanValue` that is ignored. – Anthony Feb 07 '20 at 21:01
  • Are you actually manually changing "booleanValue" within your component? At second glance it appears both "alloyToggled" and "booleanValue" are ignored and not even part of the equation here. – Z. Bagley Feb 07 '20 at 21:04
  • For testing I swap `booleanValue` programmatically. It start `true`, but doesn't apply `alloy-toggled`. Clicking once does nothing, it switches to `false` and hence should not have the class. A second click applies the class, since the internal boolean is now true. So the initial value _does_ get set in the directive, however, `HostBinding` simply ignores it. However, it's content to listen to all following changes. Same behavior from `host` metadata. – Anthony Feb 07 '20 at 21:18
  • Hope that answer helps – Z. Bagley Feb 07 '20 at 21:33

1 Answers1

2

There are a few problems:

  • @Input definition

  • Variable syntax mixups

directive.ts

@Directive({
  selector: '[alloyToggled]'
})
export class HighlightDirective {
    @Input('alloyToggled') alloyToggled: boolean;
    @HostBinding('class.alloy-toggled')
    get isToggled() { return this.alloyToggled; }
    set isToggled(value: boolean) {
      this.alloyToggled = value;
      this.alloyToggledChange.emit(this.alloyToggled);
    }
    @Output() alloyToggledChange: EventEmitter<boolean> = new EventEmitter();

    toggle() {
      this.alloyToggled = !this.alloyToggled;
    }

    @HostListener('click')
    onclick() {
        this.alloyToggled = !this.alloyToggled;
        this.alloyToggledChange.emit(this.alloyToggled);
    }
}

html

<button [alloyToggled]="booleanValue">Toggle Alloy</button>

component.ts

export class AppComponent {
  booleanValue = true;
}

HighlightDirective edited to mirror you expected code behavior. https://stackblitz.com/edit/angular-lrmveu

Created from the Angular Highlight Example: https://stackblitz.com/angular/naavjopgege?file=src%2Fapp%2Fapp.component.html

Z. Bagley
  • 8,942
  • 1
  • 40
  • 52
  • 1
    Interestingly, plugging my code into your stackblitz also works. It must involve the interaction with the host component then... – Anthony Feb 07 '20 at 22:21
  • Turns out another dev had added some logic related to menus that sets `alloy-toggled` in the button. Since I wrote most of the code in the library it didn't even occur to me to check internally to button..._sigh_ Thanks for the help, marking it as the answer since it ended up pointing me at the tertiary culprit. – Anthony Feb 10 '20 at 15:11