Instructor: Here, we have a toggle compound component that leverages dependency injection to communicate between the different parts of the compound component system.
If we look at the toggle component definition, you'll notice that the template is simply an ng-content tag, which implies that we don't even need a template at all. A component without a template is called a directive. Let's refactor this toggle component into a toggle directive.
Remove the template. Change it from a component to a directive. We'll update the selector to use the existing HTML tag selector or use an attribute selector.
After going through and changing all the toggle component references -- the toggle directive references -- we see it's working just as it was before. We can also change the tag here to a div and put the toggle here as an attribute. It's also working as it was before.
Another thing you may notice about this toggle directive is that it's actually doing two jobs. The first job is receiving input and sending output to the parent component. The second job is to provide the toggle state to all the child components.
Any time you have two jobs being accomplished by one directive, you have an opportunity to split those jobs out into two different directives. Let's write a toggle provider directive to handle the communication with the child components.
We'll create a toggle.toggle provider file, rider.directive.ts. We paste in some scaffolding here for a directive, call it toggle provider directive. Let's give it the same selectors as the toggle directive.
What this will do is, any time the toggle directive is instantiated, Angular will also put on the toggle provider directive. We'll have both directives in the same HTML element. In order to reference the toggle directive, we are going to inject it like so.
We actually only want to inject the toggle directive that's on the exact same element. Angular provides us a handy decorator called host, which says, "Only look on this element. Don't look at ancestors."
We'll save a reference to this toggle directive, set it to the injected toggle directive. Now, let's update all the child components to use the toggle provider instead of the toggle directive directly. Now that we have all that set up, our button works exactly as it did before. Wait, why did we do that? Let's look at that toggle provider directive again.
Since the toggle provider directive is decoupled from the actual toggle directive itself, we can reference it directly, giving it its own selector and giving input with the same name that will allow us to specify which toggle directive we want to provide.
To do this, we'll implement the onChanges lifecycle hook, add ngOnChanges, and when the change is coming, we will look to see if the toggle provider is one of the changes. If so, we will set the this.toggle to the this.toggle provider input if it's provided, or the this.toggle directive that is on the host element.
One thing we need to watch out for is if we use this selector to reference the toggle provider, we will not have a toggle directive on the host element. We need to tell Angular that that's OK by saying this is optional.
We also need to give Angular a way to get a reference to this directive from the template, by giving it a name to export as. We'll call it toggle provider, and we'll export the toggle directive as toggle. Now we can do some pretty interesting things in our template.
Let's say you wanted a different part of your template to be associated with the same toggle state, so something else here. These two toggle components are completely independent of each other, but if we use a toggle provider directive here, and set that to the first toggle, lets' make that an actual reference.
We have to name the first toggle here. Now both toggles are referencing the same toggle state.