# Templates

## About

{% embed url="<https://angular.dev/guide/templates>" %}

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.

{% hint style="info" %}

* 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.
  {% endhint %}

## 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

{% embed url="<https://angular.dev/guide/templates/expression-syntax>" %}
expression-syntax
{% endembed %}

### Whitespaces

Angular collapse multiple consecutive whitespaces in Templates.

{% hint style="info" %}
Preserving the whitespaces as written in the template would result in many unnecessary nodes and increase page rendering overhead.
{% endhint %}

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

```typescript
@Component({
    preserveWhitespaces: true
})
```

## [`<ng-content>`](https://angular.dev/guide/components/content-projection)

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.

{% hint style="info" %}
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>`.
{% endhint %}

{% hint style="danger" %}
Don't conditionally include `<ng-content>` , for conditional rendering use [#less-than-ng-template-greater-than](#less-than-ng-template-greater-than "mention") instead.
{% endhint %}

{% hint style="warning" %}
`<ng-content>` itself is not rendered at the resulting DOM.
{% endhint %}

```typescript
@Component({
    selector: "custom-card",
    template: '<div class="card-shadow"><ng-content></ng-content></div>',
})
export class CustomCard { ... }
```

```html
<!-- Using the component -->
<custom-card>
    <p>This is the projected content</p>
</custom-card>
```

```html
<!-- 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.

{% hint style="info" %}
Depending on the Angular version the IDE might give errors about this, even though it will work.
{% endhint %}

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

```html
<!-- Using the component -->
<custom-card></custom-card>
```

```html
<!-- 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.*

```typescript
@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>
    `,
})
```

```html
<!-- 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>
```

```html
<!-- 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`.

```typescript
@Component({
    selector: "form-group",
    template: `
        <div class="form-group">
            <ng-content select="input, textarea"></ng-content>
        </div>
    `,
})
```

```html
<!-- 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.**

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

## [`<ng-template>`](https://angular.dev/guide/templates/ng-template)

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

{% hint style="danger" %}
`<ng-template>` **contents are NOT automatically rendered to the page.**

You will need to programatically render it or use specific directives like `ngTemplateOutlet`.
{% endhint %}

Create a template fragment with:

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

### [How to Reference a `ng-template`](https://angular.dev/guide/templates/ng-template#referencing-a-template-fragment-with-queries)

Get a reference to a template fragment by:

* Declaring a [Broken link](https://kdongs.gitbook.io/kdocs/angular/broken-reference "mention") on the `<ng-template #variable>`.
* 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>`.

```html
<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.

```html
<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`.

```typescript
@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.

```html
<ng-template #myFragment let-pizzaTopping="topping">
    <p>This is the fragment with data: {{ pizzaTopping }}</p>
</ng-template>
```

```typescript
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.

{% hint style="warning" %}
Angular ignores all attribute bindings and event listeners applied to `<ng-container>`.
{% endhint %}

```html
<section>
    <ng-container>
        <h3>User bio</h3>
        <p>Here's some info about the user</p>
    </ng-container>
</section>
```

```html
<!-- 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>`.

```typescript
@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>`.

```typescript
@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());
}
```
