# Directives

## About

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

They are classes that add additional behavior to elements in your applications.

Use Angular's built-in directives to manage forms, lists, styles and what users see.

{% hint style="info" %}
[#components](#components "mention") are type of directive that have [Broken link](https://kdongs.gitbook.io/kdocs/angular/broken-reference "mention").
{% endhint %}

### [Generate Directive (CLI)](https://angular.dev/cli/generate/directive)

```bash
ng g directive [directive-name] [options]
```

## Best Practices

* Avoid changing elements directly through the DOM like `*.nativeElement.style.*`, since angular is able to render the Templates without a DOM, for instance when using service workers, then these properties may not be available.

## Attribute Directives

Change the appearance or behavior of an element, component, or another directive.

*They listen to and modify the behavior of other HTML elements, attributes, properties, and components.*

### **Built-in Attribute Directives**

{% hint style="info" %}
Built-in directives use only public APIs.

They do not have special access to any private APIs that other directives can't access.
{% endhint %}

#### `NgClass`

An old way of adding and removing CSS classes in elements.

{% hint style="success" %}
Preffer to use [Broken link](https://kdongs.gitbook.io/kdocs/angular/broken-reference "mention").
{% endhint %}

```typescript
@Component({
    template: `
        <!-- Adding 'special' class based on 'isSpecial' value -->
        <div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>

        <!-- Adding multiple classes with object 'multipleClasses', where each key is the class that will be applied based on its value -->
        <div [ngClass]="multipleClass">Div of multiple classes</div>
    `
})
export class Component {
    protected isSpecial = true;

    protected multipleClasses = {
        'className1': true,
        'className2': false,
    }
}
```

#### `NgStyle`

An old way of adding and removing HTML styles in elements.

{% hint style="success" %}
Preffer to use [Broken link](https://kdongs.gitbook.io/kdocs/angular/broken-reference "mention").
{% endhint %}

```typescript
@Component({
    template: `
        <!-- Adding single inline style -->
        <div [ngStyle]="'font-size': true ? '10px' : '20px'">This div is special</div>

        <!-- Adding multiple classes with object 'multipleClasses', where each key is the class that will be applied based on its value -->
        <div [ngStyle]="multipleStyles">Div of multiple classes</div>
    `
})
export class Component {
    protected isSpecial = true;

    // You may use regular style names, or camelCase names without quotes
    protected multipleStyles = {
        'font-weight': true ? 'bold': 'normal',
        fontSize: false ? '10px' : '20px',
    }
}
```

#### `NgModel`

Use it to display a data property and update that property when the user makes changes. *(Adds two-way data binding to HTML form elements)*

```html
<label for="example-ngModel">[(ngModel)]:</label>
<input [(ngModel)]="currentItem.name" id="example-ngModel">
```

```html
<input [ngModel]="currentItem.name" (ngModelChange)="setUppercaseName($event)" id="example-uppercase">
```

More examples in [Broken link](https://kdongs.gitbook.io/kdocs/angular/broken-reference "mention").

{% hint style="info" %}
To apply `[(ngModel)]` to a non-form built-in element or a thid-party custom component, you have to write a value accessor.
{% endhint %}

### [**Building an Attribute Directive**](https://angular.dev/guide/directives/attribute-directives#building-an-attribute-directive)

Create the new directive with the [CLI command](#generate-directive-cli).

By default a directive will have a `[appDirective]` attribute seletor, but it could be a class or component selector as well.

{% hint style="info" %}
Inject `ElementRef` to get access to the host `DOM` element.
{% endhint %}

```typescript
@Directive({
    standalone: true,
    selector: "[appNew]",
})
export class NewDirective {
    // Inject the `ElementRef` to get the reference to the Host DOM element
    constructor(private el: ElementRef) {
        this.el.nativeElement.style.backgroundColor = "yellow";
    }
}
```

```html
<p appNew>This will have yellow background.</p>
```

#### Passing values into an attribute directive

Pass values to the Directive with `input()` using the same selector name as the variable name.

```typescript
export class NewDirective {
    // This uses the same variable name as the directive's selector
    appNew = input("yellow");

    constructor(private el: ElementRef) {
        this.el.nativeElement.style.backgroundColor = this.appNew;
    }
}
```

```html
<p [appNew]="white">This will override the color to white.</p>
```

#### Handling user events

You can handle user events or subscribe to event of the `DOM` with `@HostListener()`.

{% hint style="success" %}
But prefer to use [Broken link](https://kdongs.gitbook.io/kdocs/angular/broken-reference "mention").
{% endhint %}

```typescript
export class NewDirective {
    constructor(private el: ElementRef) {}

    @HostListener("mouseenter") onMouseEnter() {
        this.highlight(true);
    }

    @HostListener("mouseleave") onMouseLeave() {
        this.highlight();
    }

    private highlight(state = false) {
        this.el.nativeElement.style.backgroundColor = state ? "yellow" : "transparent";
    }
}
```

## Structural Directives

Change the `DOM` layout by adding and removing `DOM` elements.

Structural directives can be raw applied to `<ng-template>` only.

```html
<ng-template [ngIf]=""><p>Paragraph</p></ng-template>
<ng-template newStructuralDirective let-item [selectFrom]="items">
    <p>Paragraph</p>
</ng-template>
```

Angular expanded the use of them to other HTML elements by the use of **shorthand syntax** (`*`).

```html
<p *ngIf="">Paragraph</p>
<p *newStructuralDirective="let item from items">Paragraph</p>
```

{% hint style="info" %}
Angular transforms the `*` into a `<ng-template>` that hosts the directive and surrounds the element and its descendants.
{% endhint %}

{% hint style="danger" %}
You can only apply one structural directive per element when using the **shorthand syntax** (`*`).

If multiple directives need to be applied, apply them in a `<ng-container>`.
{% endhint %}

### **Built-in Structural Directives**

#### [`NgIf`](https://angular.dev/api/common/NgIf)

Conditionally creates or disposes of subviews from the template. **(Frees up memory and resources)**

By default, `ngIf` prevents display of an element bound to a null value.

```html
<div *ngIf="isActive">Div</div>
```

To use an `else` block, must use a `<ng-template>` block with the help of a `LocalReference`.

```html
<div *ngIf="isActive; else elseBlock">Div</div>
<ng-template #elseBlock>Div otherwise</ng-template>
```

Or with a more descriptive way.

```html
<ng-template [ngIf]="isActive" [ngIfElse]="elseBlock">
    <div>Div</div>
</ng-template>

<ng-template #elseBlock>
    <div>Div otherwise</div>
</ng-temaplate>
```

#### `NgFor`

Repeat a node for each item in a list.

```typescript
@Component({
    template: `
        <div *ngFor="let item of items">
            <span>{{ item.value }}</span>
        </div>
    `,
})
export class Component {
    protected items = [{ value: "Value1" }, { value: "Value2" }];
}
```

You may pass the `item` value to a sub component like.

```html
<app-sub-component *ngFor="let item of items" [itemData]="item" />
```

You can get the list `index` with `let i=index` like.

```html
<div *ngFor="let item of items; let i=index">
    <span [number]="i">{{ item.value }}</span>
</div>
```

You can track items with `trackBy`.

**Angular can change and re-render only those items that have changed, rather than reloading the entire list of items.**

The item list must have some sort of unique `id` field.

```typescript
@Component({
    template: `
        <div *ngFor="let item of items; trackBy: item.id">
            <span>{{ item.value }}</span>
        </div>
    `,
})
export class Component {
    protected items = [
        { id: 1, value: "Value1" },
        { id: 2, value: "Value2" },
    ];
}
```

#### `NgSwitch`

To display one element among several possible elements, based on a switch condition.

```typescript
@Component({
    template: `
        <ng-template [ngFor]="let item of items; trackBy: item.id">
            <div [ngSwitch]="item.value">
                <app-sub-component *ngSwitchCase="'Value1'" [itemData]="item" />
                <app-sub-component *ngSwitchCase="'Value2'" [itemData]="item" />
                <span *ngSwitchDefault >Unknown</span>
            </div
        </ng-template>
    `,
})
export class Component {
    protected items = [
        { id: 1, value: "Value1" },
        { id: 2, value: "Value2" },
    ];
}
```

### [**Building a Structural Directive**](https://angular.dev/guide/directives/structural-directives)

Create the new directive with the [CLI command](#generate-directive-cli).

{% hint style="info" %}
When you write your own structural directives, use the [following syntax](https://angular.dev/guide/directives/structural-directives#structural-directive-syntax-reference).
{% endhint %}

```typescript
@Directive({
    selector: "[select]",
})
export class SelectDirective {
    private templateRef = inject(TemplateRef);
    private viewContainerRef = inject(ViewContainerRef);
    selectFrom = input.required<DataSource>();
    
    async ngOnInit() {
        const data = await this.selectFrom.load();
        this.viewContainerRef.createEmbeddedView(this.templateRef, {
            /*
                Create the embedded view with a context object that contains
                the data via the key `$implicit`.
            */
            $implicit: data,
        });
    }
}
```

```html
<ng-template selector let-item [selectFrom]="items">
    <p>The data is: {{ item.value }}</p>
</ng-template>
```

## Adding Directives to a Component with `hostDirectives`

{% hint style="danger" %}
Only usable in `standalone` components.
{% endhint %}

You can assign directives to a component through the `hostDirectives` property inside the component's decorator.

```typescript
@Component({
    hostDirectives: [MenuBehavior],
})
```

{% hint style="warning" %}
**Angular applies host directives statically at compile time.**

You cannot dynamically add directives at runtime.
{% endhint %}

{% hint style="warning" %}
Angular ignores the `selector` of directives applied in the `hostDirectives` property.
{% endhint %}

{% hint style="info" %}
By default, host directive inputs and outputs are not exposed as part of the component's public API.
{% endhint %}

#### Execution order

{% hint style="danger" %}
Host directives always execute their constructor, lifecycle hooks, and binding **before** the component or directive on which they are applied.

This means that components with `hostDirectives` can override any host binding specified by a host directive.
{% endhint %}

### Including inputs and outputs

By explicitly specifying the inputs and outputs, consumers of the component with `hostDirective` can bind them in a template.

```typescript
@Component({
    hostDirectives: [{
        directive: MenuBehavior,
        inputs: ['menuId'],
        outputs: ['menuClosed']
    }],
})
export class AdminMenu { ... }
```

```html
<admin-menu menuId="top-menu" (menuClosed)="logMenuClosed()">
```

#### Alias the inputs and outputs

You can also alias them to customize the API of your component.

```typescript
@Component({
    hostDirectives: [{
        directive: MenuBehavior,
        inputs: ['menuId: id'],
        outputs: ['menuClosed: closed']
    }],
})
export class AdminMenu { ... }
```

```html
<admin-menu id="top-menu" (closed)="logMenuClosed()">
```

## Adding Directives to other Directives with `hostDirectives`

You can also add `hostDirectives` to other directives, in addition to components.

This enables the transitive aggregation of multiple behaviors *(chainned directives)*.

```typescript
@Directive({ ... })
export class Menu { ... }

@Directive({ ... })
export class Tooltip { ... }

// MenuWithTooltip can compose behaviors from multiple other directives
@Directive({
    hostDirectives: [Tooltip, Menu],
})
export class MenuWithTooltip { ... }

// CustomWidget can apply the already-composed behaviors from MenuWithTooltip
@Directive({
    hostDirectives: [MenuWithTooltip],
})
export class SpecializedMenuWithTooltip { }
```
