Override Component UI with an Angular TemplateRef

Isaac Mann
InstructorIsaac Mann
Share this video with your friends

Social Share Links

Send Tweet
Published 6 years ago
Updated 6 years ago

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.

Tey Taghiyev
Tey Taghiyev
~ 6 years ago

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 the react-redux connect() function. Is that whats happening here, where this is favorable over the provider.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.

Isaac Mann
Isaac Manninstructor
~ 6 years ago

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 TemplateRefs.

Compound components give the component author more control over the UI. Using TemplateRefs 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.

Tey Taghiyev
Tey Taghiyev
~ 6 years ago

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.

Zhentian Wan
Zhentian Wan
~ 6 years ago
Andrew Davis
Andrew Davis
~ 6 years ago

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!

Isaac Mann
Isaac Manninstructor
~ 6 years ago

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.

Fabio Bedini
Fabio Bedini
~ 3 years ago

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

Markdown supported.
Become a member to join the discussionEnroll Today