Refactor Angular Component State Logic into Directives

Share this video with your friends

Send Tweet

Allow the base toggle to be a tag (<toggle>) or attribute (<div toggle>). The <toggle> component has become less opinionated about the view, but has now taken on some responsibilities managing state. We’ll decouple the state management piece by moving it into the toggleProvider directive. The toggleProvider directive provides state for all the <toggle-off>, <toggle-on> and <toggle-button> components inside it.

Jessy
Jessy
~ 6 years ago

I haven't seen this syntax usage before const {toggleProvider} = changes; Is there anything special about wrapping toggleProvider in curly brackets?

-- edit: I've found it in the documentation of TypeScript :) pretty neat! https://www.typescriptlang.org/docs/handbook/variable-declarations.html#object-destructuring

Isaac Mann
Isaac Mann(instructor)
~ 6 years ago

Yep, it's actually a feature of ES6. The equivalent ES5 would be:

var toggleProvider = changes.toggleProvider;
felikf
felikf
~ 6 years ago

Hi Isaac, thank you for the course...

I have some questions inlined in code:

export class ToggleProviderDirective implements OnChanges {
 ...
  // When this assignment happens? I would expect that assignment in the constructor.. I am not sure if this is the same
  toggle: ToggleDirective = this.toggleDirective;

  constructor(@Host() @Optional() private toggleDirective: ToggleDirective) {}

  ngOnChanges(changes: SimpleChanges) {
    const {toggleProvider} = changes;

  // this is kind of tricky for me
  // you are testing `toggleProvider` (passed as function argument) - but then you assign 
  // `this.toggleProvider` - class member - injected; is it that toggleProvider === this.toggleProvider ?

    if(toggleProvider) {
      this.toggle = this.toggleProvider || this.toggleDirective;
    }
  }
}

And why do we need the OnChanges lifecycle hook at all? We are injecting the @Input() toggleProvider: ToggleDirective so it should be already available as class member.

I see .. we dont need OnChanges but the @Inject is available later, not in the constructor. I think this is more implicit:

constructor(@Host() @Optional() private toggleDirective: ToggleDirective) {
    this.toggle = toggleDirective;
  }

  ngOnInit() {
    this.toggle = this.toggleProvider || this.toggle;
  }
Isaac Mann
Isaac Mann(instructor)
~ 6 years ago

@felikf

  1. toggle: ToggleDirective = this.toggleDirective; is exactly the same as doing this.toggle = this.toggleDirective at the end of your constructor.

  2. The toggleProvider that I pulled off the changes object is a SimpleChange type. Which means it has a previousValue property and a currentValue property. At the line you're referring to toggleProvider.currentValue === this.toggleProvider.

  3. The reason I used ngOnChanges instead of ngOnInit is to support the ability to change the toggleProvider input after the component has been initialized. If you used ngOnInit, you would need to make a new component any time the provider changed. I've had to refactor ngOnInit logic into ngOnChanges enough times to choose ngOnChanges by default any time I'm using @Inputs in the logic.

I think I answered all your questions. Let me know if I missed anything.

felikf
felikf
~ 6 years ago

Isaac, thank you for taking time to clarify all my questions.

Andrew Davis
Andrew Davis
~ 6 years ago

Why am I getting this error?

ERROR TypeError: Cannot assign to read only property 'toggleProvider' of object '[object Object]'
Andrew Davis
Andrew Davis
~ 6 years ago

Why am I getting this error?

ERROR TypeError: Cannot assign to read only property 'toggleProvider' of object '[object Object]'

It seems I was missing a decorator...

Alex
Alex
~ 6 years ago

In the toggle directive, you emit the state. Why is emitting an event a better option than using 2-way binding?

Kyle Westendorf
Kyle Westendorf
~ 3 years ago

Playing around with the code...why is it that even though I remove the toggle directive that is being referenced, the toggle buttons still are in sync and work? https://stackblitz.com/edit/adv-ng-patterns-03a-compound-comp-inject-parent-m9flqg?file=app/app.component.html