Bindon: lesser known Angular template features

Post Editor

Angular 12 just came out which added a nullish coalescing operator (‘??’) to templates. But what are other features Angular templates have that you might have never heard of? Let’s find out!

4 min read
0 comments
post

Bindon: lesser known Angular template features

Angular 12 just came out which added a nullish coalescing operator (‘??’) to templates. But what are other features Angular templates have that you might have never heard of? Let’s find out!

post
post
4 min read
0 comments
0 comments

Angular 12 just came out which added a nullish coalescing operator (‘??’) to templates. But what are other features Angular templates have that you might have never heard of? Let’s find out!

ngProjectAs
Link to this section

Angular content projection is a bit like the Web Components slots system. While you can just write <ng-content></ng-content> and everything you put inside your component tag would get projected there — you can also add multiple content tags with “select” attribute for targeted projection. Like the example below:

<>Copy
@Component({ selector: 'layout', template: ` <ng-content select="header"></ng-content> <main> <ng-content select="aside"></ng-content> <ng-content></ng-content> </main> <ng-content select="footer"></ng-content> `, styles: [` :host { height: 100%; display: flex; flex-direction: column; } main { display: flex; flex: 1; } `] }) export class LayoutComponent {}

Now you can pass sections to this layout component with content projection:

<>Copy
<layout> <header>Header</header> <aside>Sidebar</aside> <footer>Footer</footer> I am content </layout>

But what if you want to project a block of content without wrapping it into an actual DOM element? Like several tags to be projected into a single slot or project some plain text. You can use the ng-container tag and mark it with ngProjectAs. See example below:

https://stackblitz.com/edit/angular-content-selection

This was recently added to angular.io under content projection guide so if you want to know more, you definitely should read it!

ngNonBindable
Link to this section

Let’s assume you want to show an example of interpolation with handlebars and display the following as is: <div>Hello, {{userName}}</div>. But if you want to have actual handlebars in the template you need to escape them somehow. Otherwise Angular would think you try to process them. Try adding single ‘{‘ to the template. You will see the following message:

Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.

Writing 5 handlebars instead of one is suboptimal 🙂 You can try using a custom interpolation symbol for this component but this won’t work — this option doesn’t replace the original symbol, just adds an alternative.

This is where ngNonBindable comes to the rescue. It’s a compiler directive (like i18n) which tells Angular compiler to treat a section of template as plain html: <div ngNonBindable>Hello, {{userName}}</div>

ngPreserveWhitespaces
Link to this section

As you might know, Angular saves you the troubles of unnecessary whitespace symbols in your templates by removing them during compilation. If you do not need that you can turn this off in angularCompilerOptions of your tsconfig.json for the whole project or in @Component decorator for a particular component. Generally this is a very helpful feature but sometimes you want to disable it for some specific sections. Another Angular compiler directive can help you with that — just put ngPreserveWhitespaces on an element in your template and the compiler would leave all the whitespace intact.

&ngsp;
Link to this section

Related to the previous block — non breaking space character is treated as a whitespace and can be removed by the compiler. If you want it to remain there’s a special symbol that came from Angular Dart believe it or not —  &ngsp. It survives whitespace purge and gets replaced by &nbsp during compilation.

$any()
Link to this section

Did you ever work with a 3rd party library that has type issues? Say you operate immutable data and want to feed your array to a component input. But the type is set to be an Array even though there are no mutations allowed. TypeScript will tell you your readonly instance is missing a few mutable methods and throw an error at you. Or the interface might be written plain wrong if maintainer is not a TypeScript enthusiast 🙂 Here are two steps that you can take:

  1. Wrap your object with $any() special function: [value]=”$any(value)”
  2. Go to that 3rd party library GitHub and make a PR fixing the type

Don’t forget that it only works if both steps are taken! 😉

Bindon
Link to this section

Does this look hazy to you due to all the special characters?

<>Copy
<my-component #component [@animation]=”animation” [value]=”value” [(banana)]=”twoWayBoundValue” (output)=”onOutput($event)” (click)=”onClick()” ></my-component>

There’s an alternative syntax to those binding and you can use it to write the same template without all those brackets:

<>Copy
<my-component ref-component bind-animate-animation=”animation” bind-value=”value” bindon-banana=”twoWayBoundValue” on-output=”onOutput($event)” on-click=”onClick()” ></my-component>

Admittedly I’m not sure why you would want to do that. Maybe to easily sort bindings by their type? Anyway, you can do that so now you know how 🙂

Bonus
Link to this section

A small bonus — did you know there are other built-in tags you can use in Angular templates besides ng-template, ng-container and ng-content? There’s also the ng-component. It’s not a template feature per se, but I’ve seen it in DevTools while inspecting templates and was quite surprised to find it, so I’ll explain it in this article too.

Maybe you can guess for yourself why it appears in the HTML after Angular rendered the page?  Here’s a small tip for you: selector is optional for @Component decorator:

<>Copy
@Component({ selector: 'app-root',---- this thing is optional ... })

So think about what Angular can use for components that don’t have this property defined. You probably guessed that it’s exactly where we can see ng-component — it’s an auto-generated tag for components that are lacking selectors. How can components appear in HTML but not have a selector? Well, you might dynamically instantiate a component with *ngComponentOutlet or the component can be inserted after router-outlet by the Router!

Do you know any other template special cases that are missing in this article? Be sure to drop them in comments because I have never seen a comprehensive list of those and this article is just what I gather from experience. Thank you for reading!

Comments (0)

Be the first to leave a comment

Share

About the author

author_image

I’m a devoted Angular developer and a musician. I use one thing to help me not to starve while doing another and I love doing both. I work on Taiga UI at Tinkoff and I like sharing my findings.

author_image

About the author

Alex Inkin

I’m a devoted Angular developer and a musician. I use one thing to help me not to starve while doing another and I love doing both. I work on Taiga UI at Tinkoff and I like sharing my findings.

About the author

author_image

I’m a devoted Angular developer and a musician. I use one thing to help me not to starve while doing another and I love doing both. I work on Taiga UI at Tinkoff and I like sharing my findings.

Featured articles