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.
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
Yep, it's actually a feature of ES6. The equivalent ES5 would be:
var toggleProvider = changes.toggleProvider;
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;
}
@felikf
toggle: ToggleDirective = this.toggleDirective;
is exactly the same as doing this.toggle = this.toggleDirective
at the end of your constructor.
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
.
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 @Input
s in the logic.
I think I answered all your questions. Let me know if I missed anything.
Isaac, thank you for taking time to clarify all my questions.
Why am I getting this error?
ERROR TypeError: Cannot assign to read only property 'toggleProvider' of object '[object Object]'
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...
In the toggle directive, you emit the state. Why is emitting an event a better option than using 2-way binding?
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