What if we want to use some of the state in the toggle component inside of the customized view we’re providing. Content components don’t have an easy way of accomplishing that. However, TemplateRefs were specifically designed for this purpose. TemplateRefs serve the same function as React’s Render Prop pattern.
Instructor: [00:00] This compound toggle component gives you three child components that let you interact with the state of the toggle. This toggle button lets you toggle the value on or off.
[00:10] The toggle-on component displays something when the toggle is on. The toggle-off component displays something when the toggle is off. These three child components fully cover all the possible values of the toggle.
[00:22] Say we had a component with state that was a bit more complicated. What we'd really want is for a toggle component to just give us the state that's inside of it and let us fill in the details of how to display that.
[00:34] The way we'll do that is using the ng-template tag. You can explicitly specify which part of the state you're interested in and then use that state inside of your template.
[00:45] This template isn't wired up at all. Let's go do that. First off, we can get rid of these child components. You see our toggle module gets a lot simpler. We'll need to change this toggle directive back into a component.
[01:03] Let's set up the TemplateRef. We'll get a reference to the TemplateRef using ContentChild. We'll call it layout template.
[01:11] Let's define the template for this component. In order to use the TemplateRef, we need an element to drop it on, but we don't need a real HTML element. We'll use the ng-container placeholder.
[01:24] To instantiate the TemplateRef, we'll use the ngTemplateOutlet structural directive. We'll tell it to use the layout template. We also want to specify a context, which is how we can pass in the state of this toggle component.
[01:38] If we go back to the app component, you see we're passing in the on state. The value is false. We're displaying off here. We'd also like to be able to update the state.
[01:49] We'll add in a switch component and then use the click event to trigger some function that will update the toggle state for us. We need the TemplateRef to provide us with that toggle function that will update the state when we call it.
[02:01] We need to hook that up in the toggle component. All we need to do is add to the context object a toggle property that references a toggle function. We'll define that toggle function in the component.
[02:13] We'll define the toggle function using an arrow function to automatically bind the this context to the toggle component. We can do this.setOnState with not this.on.
[02:26] We need to update this output event to be toggled so the name doesn't conflict with this function here. When we click the switch, it changes the state.
[02:35] Let's compare the before and after here. There's just one toggle component. We've gotten rid of the child components. The app component simply uses the state that's passed in and uses a function that's provided here to change that state.
[02:51] The parent component is both more flexible, because we're dealing with the raw state, and simpler, because we don't need to learn a new set of child components for each new compound component that we use.
[03:02] There are a few items to tidy up with this implementation. We should change this div tag to a toggle tag, so that people don't expect it to behave like a directive, since it's really a component.
[03:12] This red squiggle here is actually a false negative. It doesn't really hurt anything, but if you really want to get rid of it, you can work around doing something like this.
[03:22] Pass in a functions object that has functions.toggle defined on it and then hook it up in your toggle component. Instead of putting toggle here, you rename it to functions.
[03:33] Make it an object that has toggle defined on it. If you go back to the TemplateRef, it no longer has a validation error. The component works just as it did before.
I can't speak to your comparison with mapStateToProps
, but here's how I would compare the pros and cons of the compound component pattern vs. using TemplateRef
s.
Compound components give the component author more control over the UI. Using TemplateRef
s gives the user of the component control over the whole UI. If there are parts of your component UI that you want to let the user do whatever they want with, use a TemplateRef
. If there are parts that really need to be rendered in a certain way and you don't want to let the user mess it up, use Compound Components (or inside of a user's TemplateRef
use Content Directives).
It all comes down to who should be in control.
Yesss, thats exactly the type of answer I was looking for, really appreciate you understanding the context of the question, which was about decision making.
Just to clarify, the mapToStateProps
part of my comment was to highlight something I see many tutorials do. They take a concept, say 'state management' and over the course of say 5 'lessons' they will start with the most vanilla version of implementation, often from scratch. The instructor then introduces tools to make the process easier, like reacts connect()
method. Sometimes its not clear if a lesson is 'a different way of doing the previous' or a 'better way of doing the previous', which is what spawned my original question to you.
I'm getting this error:
compiler.js:2427 Uncaught Error: Template parse errors:
Binding to event property 'on' is disallowed for security reasons, please use ()=...
If 'on' is a directive input, make sure the directive is imported by the current module. ("is off" }}
<!-- The red squiggles on toggle() can be ignored, but are annoying. -->
<switch [ERROR ->][on]="on" (click)="toggle()"></switch>
</ng-template>
Please help!
You can look at a working example here: https://stackblitz.com/edit/adv-ng-patterns-06-use-template-refs-pdwded
Check to see if you have the SwitchComponent
in the declarations
list of the AppModule
.
Hi there, very nice lessons. Just an other approach clearer imo using bind https://stackblitz.com/edit/adv-ng-patterns-06-use-template-refs-b3qetu
This refactor particularly has been one of the more confusing to me, both here and in its original React context. Are there any rule-of-thumbs of when to use this over the previous (provider binding methods). Ive noticed that sometimes (as in Dans original React course) these lessons sets will describe a concept in a 'vanilla' raw implementation form, and then go on to teach us the abstracted shortcut, e.g. doing a
mapStateToProps
manually, and then teaching us thereact-redux connect()
function. Is that whats happening here, where this is favorable over theprovider.directive
pattern? If not, do you have any use case examples of when you'd use which.Thanks for this lesson set, Ive been googling for this 'translation' to angular for a while now.