kdocs
GitHub
Lang - Web
Lang - Web
  • Base
    • Css
    • Javascript
    • Typescript
      • New Project
  • Frameworks
    • Angular
      • Directives
      • Components
      • Templates
        • Bindings
        • Control Flow
        • Variables
      • Signals
      • Pipes
      • Services
        • Dependency Injection
      • Forms
        • Reactive Form
        • Template-Driven Form
      • Router
      • HTTP Client
      • Observables RxJS
      • Testing
        • Components
        • Directives
        • Pipes
        • Services
      • Optimization & Performance
      • Security
Powered by GitBook
On this page
  • About
  • How they work
  • Best Practices
  • Supported Values & Operators
  • Whitespaces
  • <ng-content>
  • Fallback Content on <ng-content>
  • Muliple content placeholder - with select=""
  • Using select="" for restricting content
  • Aliasing content for projection ngProjectAs
  • <ng-template>
  • How to Reference a ng-template
  • Rendering the Fragments
  • <ng-container>
  • Rendering dynamic components
  • Rendering template fragments
  1. Frameworks
  2. Angular

Templates

PreviousComponentsNextBindings

Last updated 5 months ago

About

Every Angular Component has a template that defines the DOM that the component renders onto the page.

They are usually inside:

  • *.component.html file.

  • template property of a *.component.ts file.

How they work

Templates are based on HTML syntax, bu with additional features that are interpreted by Angular.

Angular complies templates into JavaScript in order to build up an internal understanding of your application.

  • Comments in the template are not included in the rendered output.

  • Since @ has special meaning to Angular, use HTML entity code &commat; or &#64; to print @ in the HTML page.

  • Angular ignores and collapses unecessary whitespaces characters.

Best Practices

  • With Template Statements:

    • Template statements cannot refer to anything in the global namespace like windows or document.

    • They cannot call console.log() or Math.max().

    • Use short expressions, keep application and business logic in the component.

    • They should have quick execution, since Angular executes a template expression after every change detection cycle.

      • Consider caching values when their computation requires greater resources.

  • When property binding:

    • Avoid side effects, evaluation of a template expression should have no visible side effects.

    • Return the proper type, a template expression should result in the type of value that the target property expects.

  • Keep variables names unique to avoid name collisions or variables shadowing variables in another context.

  • For event binding:

    • Prefer to use code for binding to keyboard events.

Supported Values & Operators

Whitespaces

Angular collapse multiple consecutive whitespaces in Templates.

Preserving the whitespaces as written in the template would result in many unnecessary nodes and increase page rendering overhead.

To force Angular to preserve these whitespaces specify preserveWhitespaces: true in the @Component decorator.

@Component({
    preserveWhitespaces: true
})

You use <ng-content> element as a placeholder to mark where content should go.

It works similarly to <slot /> the native component, but with some Angular specific functionality.

They are processed at built-time, so cannot be inserted, removed or modified at run time.

You cannot add directives, styles, or arbitrary attributes to <ng-content>.

Don't conditionally include <ng-content> , for conditional rendering use <ng-template> instead.

<ng-content> itself is not rendered at the resulting DOM.

@Component({
    selector: "custom-card",
    template: '<div class="card-shadow"><ng-content></ng-content></div>',
})
export class CustomCard { ... }
<!-- Using the component -->
<custom-card>
    <p>This is the projected content</p>
</custom-card>
<!-- The rendered DOM -->
<custom-card>
    <div class="card-shadow">
        <p>This is the projected content</p>
    </div>
</custom-card>

Fallback Content on <ng-content>

Any added content between <ng-content></ng-content> tags will be used as fallback content if none was passed.

Depending on the Angular version the IDE might give errors about this, even though it will work.

@Component({
    selector: "custom-card",
    template: `
        <div class="card-shadow">
            <ng-content>
                <span>Nothing was passed!</span>
            </ng-content>
        </div>
    `,
})
export class CustomCard { ... }
<!-- Using the component -->
<custom-card></custom-card>
<!-- The rendered DOM -->
<custom-card>
    <div class="card-shadow">
        <span>Nothing was passed!</span>
    </div>
</custom-card>

Muliple content placeholder - with select=""

You may project multiple different elements into different <ng-content> placeholders based on CSS selectors.

If a component does not include an <ng-content> placeholder without a select attribute, any elements that don't match one of the component's placeholders do not render into the DOM.

@Component({
    selector: "custom-card",
    template: `
        <div class="card-shadow">
            <ng-content select="card-title"></ng-content>
            <div class="card-divider"></div>
            <ng-content select="card-body"></ng-content>
            
            <!-- capture anything else except "card-title" and "card-body" -->
            <ng-content></ng-content>
        </div>
    `,
})
<!-- Using the component -->
<custom-card>
    <p>This content</p>
    <card-title>Hello</card-title>
    <p>This middle content</p>
    <card-body>Welcome to the example</card-body>
    <p>This end content</p>
</custom-card>
<!-- Rendered DOM -->
<custom-card>
    <div class="card-shadow">
        <card-title>Hello</card-title>
        <div class="card-divider"></div>
        <card-body>Welcome to the example</card-body>
        <p>This content</p>
        <p>This middle content</p>
        <p>This end content</p>
    </div>
</custom-card>

Using select="" for restricting content

The use of select is not restricted to having multiple <ng-content>, it can be used to be more restrictive to what can go inside <ng-content>.

In fact you may also specify multiple selectors.

@Component({
    selector: "form-group",
    template: `
        <div class="form-group">
            <ng-content select="input, textarea"></ng-content>
        </div>
    `,
})
<!-- Using the component -->
<form-group>
    <input type="text" />
</form-group>

<!-- Or -->
<form-group>
    <textarea></textarea>
</form-group>

<!-- Anything else passed will not be projected -->
<form-group>
    <span>Something</span>
</form-group>

Aliasing content for projection ngProjectAs

ngProjectAs is a special attribute, that allows you to specify a CSS selector on any element.

It will be checked against an <ng-content select=""> with the same CSS selector given.

ngProjectAs supports only static values and cannot be bound to dynamic expressions.

<!-- Using the component -->
<custom-card>
    <h3 ngProjectAs="card-title">Hello</h3>
    <card-body>Welcome to the example</card-body>
</custom-card>

This lets you declare a template fragment. (A section of content that you can dynamically or programmatically render)

<ng-template> contents are NOT automatically rendered to the page.

You will need to programatically render it or use specific directives like ngTemplateOutlet.

Create a template fragment with:

<p>This is a normal element</p>
<ng-template>
    <p>This is a template fragment</p>
</ng-template>

Get a reference to a template fragment by:

  • By quering for the frament with viewChild(TemplateRef).

  • By injecting the fragment in a directive that is applied directly to an <ng-template> element.

In all three cases, the fragment is represented by a TemplateRef object.

Rendering the Fragments

Once you have the reference to the fragment's TemplateRef object, you can render the fragment in two ways.

Using NgTemplateOutlet

A native directive from Angular that accepts a TemplateRef and renders the fragment as a sibling to the element with the outlet.

The *ngTemplateOutlet is used with a <ng-container>.

<p>This is a normal element</p>
<ng-template #myFragment>
    <p>This is a template fragment</p>
</ng-template>
<ng-container *ngTemplateOutlet="myFragment"></ng-container>

This way the content inside <ng-template> is rendered inside <ng-container>.

You can additionally pass parameters accepted by the frament, through a context object corresponding to these paramenters.

And each paramenter is accessed as an attribute prefixed with let- with a value matching a property name in the context object.

<ng-template #myFragment let-pizzaTopping="topping">
  <p>You selected: {{ pizzaTopping }}</p>
</ng-template>
<ng-container
  [ngTemplateOutlet]="myFragment"
  [ngTemplateOutletContext]="{topping: 'onion'}"
/>

Using ViewContainerRef

A ViewContainer is a node in Angular's component tree that can contain content.

Any component or directive can inject ViewContainerRef to get a reference to a view container corresponding to that component or directive's location in the DOM.

You then use the createEmbeddedView Angular method on ViewContainerRef to dynamically render a template fragment as the next sibling of the component or directive that injected the ViewContainerRef.

@Component({
    template: `
        <h2>Component with a fragment</h2>
        <ng-template #myFragment>
            <p>This is the fragment</p>
        </ng-template>
        <my-outlet [fragment]="myFragment" />
    `,
})
export class ComponentWithFragment { ... }

@Component({
    selector: 'my-outlet',
    template: `<button (click)="showFragment()">Show</button>`,
})
export class MyOutlet {
    private viewContainer = inject(ViewContainerRef);
    fragment = input<TemplateRef<unknown>>();
    
    showFragment() {
        if (this.fragment) {
            this.viewContainer.createEmbeddedView(this.fragment);
        }
    }
}

You can additionally pass parameters accepted by the frament, through createEmbeddedView.

And each paramenter is passed as an attribute prefixed with let- with a value matching a property name in the context object.

<ng-template #myFragment let-pizzaTopping="topping">
    <p>This is the fragment with data: {{ pizzaTopping }}</p>
</ng-template>
this.viewContainer.createEmbeddedView(this.fragment, {topping: 'onion'});

<ng-container>

It is a special element in Angular that groups multiple elements together or marks a location in a template without rendering a real element in the DOM.

You may apply directives to <ng-container> to add behaviors or configuration to a part of your template.

Angular ignores all attribute bindings and event listeners applied to <ng-container>.

<section>
    <ng-container>
        <h3>User bio</h3>
        <p>Here's some info about the user</p>
    </ng-container>
</section>
<!-- Rendered DOM -->
<section>
    <h3>User bio</h3>
    <p>Here's some info about the user</p>
</section>

Rendering dynamic components

Use Angular's built-in NgComponentOutlet directive to dynamically render components to the <ng-container>.

@Component({
    template: `
        <h2>Your profile</h2>
        <ng-container [ngComponentOutlet]="profileComponent()" />
    `
})
export class UserProfile {
    isAdmin = input(false);
    // Where `AdminProfile` and `BasicUserProfile` are other Component Classes
    profileComponent = computed(() => this.isAdmin() ? AdminProfile : BasicUserProfile);
}

Rendering template fragments

Use Angular's built-in NgComponentOutlet directive to dynamically render components to the <ng-container>.

@Component({
    template: `
        <h2>Your profile</h2>
        <ng-container [ngTemplateOutlet]="profileTemplate()" />
        <ng-template #admin>This is the admin profile</ng-template>
        <ng-template #basic>This is the basic profile</ng-template>
    `
})
export class UserProfile {
    isAdmin = input(false);
    adminTemplate = viewChild('admin', { read: TemplateRef });
    basicTemplate = viewChild('basic', { read: TemplateRef });
    profileTemplate = computed(() => this.isAdmin() ? this.adminTemplate() : this.basicTemplate());
}

Declaring a on the <ng-template #variable>.

<ng-content>
<ng-template>
How to Reference a ng-template
AngularAngular
expression-syntax
AngularAngular
Logo
Logo
Template Reference Variables