mirror of
https://github.com/LukeHagar/unicorn-utterances.git
synced 2025-12-06 12:57:44 +00:00
docs: first bit of article finished
This commit is contained in:
240
content/blog/angular-dynamic-host-usage/index.md
Normal file
240
content/blog/angular-dynamic-host-usage/index.md
Normal file
@@ -0,0 +1,240 @@
|
||||
---
|
||||
{
|
||||
title: "Angular Dynamic host Property Usage",
|
||||
description: "",
|
||||
published: '2023-12-27T13:45:00.284Z',
|
||||
authors: ['crutchcorn'],
|
||||
tags: ['angular', 'webdev', javascript'],
|
||||
attached: [],
|
||||
license: 'cc-by-nc-sa-4'
|
||||
}
|
||||
---
|
||||
|
||||
|
||||
Angular is a _powerful_ framework. Most folks know of it as the component framework, but it's much more than that.
|
||||
|
||||
For example, did you know about Angular directives?
|
||||
|
||||
Directives allow you to bind to an element via an attribute and change the behavior of said element.
|
||||
|
||||
```typescript
|
||||
import { Component, Directive } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[doNothing]',
|
||||
standalone: true,
|
||||
})
|
||||
class DoNothingDirective {}
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [DoNothingDirective],
|
||||
template: `
|
||||
<p doNothing>I am currently unchanged.</p>
|
||||
`,
|
||||
})
|
||||
export class App {}
|
||||
```
|
||||
|
||||
Think of them as components without templates. They can use lifecycle methods:
|
||||
|
||||
```typescript
|
||||
@Directive({
|
||||
selector: '[alertOnDestroy]',
|
||||
standalone: true,
|
||||
})
|
||||
class AlertOnDestroyDirective implements OnDestroy {
|
||||
ngOnDestroy() {
|
||||
alert('Element was unrendered!');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [AlertOnDestroyDirective, NgIf],
|
||||
template: `
|
||||
<p *ngIf="render" alertOnDestroy>Unmount me to see an alert!</p>
|
||||
<button (click)="render = !render">Toggle</button>
|
||||
`,
|
||||
})
|
||||
export class App {
|
||||
render = true;
|
||||
}
|
||||
```
|
||||
|
||||
Store state:
|
||||
|
||||
```typescript
|
||||
@Directive({
|
||||
selector: '[listenForEvents]',
|
||||
standalone: true,
|
||||
})
|
||||
class ListenForEventDirective implements OnInit {
|
||||
count = 0;
|
||||
ngOnInit() {
|
||||
document.addEventListener('hello', () => {
|
||||
alert(`You sent this many events: ${++this.count}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [ListenForEventDirective],
|
||||
template: `
|
||||
<p listenForEvents>This paragraph tag listens for events!</p>
|
||||
<button (click)="sendEvent()">Send event</button>
|
||||
`,
|
||||
})
|
||||
export class App {
|
||||
sendEvent() {
|
||||
const event = new CustomEvent('hello');
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use the `inject` function:
|
||||
|
||||
```typescript
|
||||
import { Component, Directive, inject, OnInit } from '@angular/core';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
|
||||
@Directive({
|
||||
selector: '[listenForEvents]',
|
||||
standalone: true,
|
||||
})
|
||||
class ListenForEventDirective implements OnInit {
|
||||
count = 0;
|
||||
|
||||
doc = inject(DOCUMENT);
|
||||
|
||||
ngOnInit() {
|
||||
this.doc.addEventListener('hello', () => {
|
||||
alert(`You sent this many events: ${++this.count}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And do just about anything else a component can do without a template of its own.
|
||||
|
||||
# Accessing a directives' element with `ElementRef`
|
||||
|
||||
Because a directive is attached to an element, a typical usage of a directive is to modify the element it's attached to using `ElementRef` and `inject`; like so:
|
||||
|
||||
```typescript
|
||||
const injectAndGetEl = () => {
|
||||
const el = inject(ElementRef);
|
||||
console.log(el.nativeElement);
|
||||
return el;
|
||||
};
|
||||
|
||||
@Directive({
|
||||
selector: '[logEl]',
|
||||
standalone: true,
|
||||
})
|
||||
class LogElDirective {
|
||||
_el = injectAndGetEl();
|
||||
}
|
||||
```
|
||||
|
||||
While this doesn't do anything yet, it logs the element to the `console.log` method. Let's instead change this code to make the attached element have a red background and white text:
|
||||
|
||||
```typescript
|
||||
import { Component, Directive, ElementRef, inject } from '@angular/core';
|
||||
|
||||
const injectAndMakeRed = () => {
|
||||
const el = inject(ElementRef);
|
||||
el.nativeElement.style.backgroundColor = 'red';
|
||||
el.nativeElement.style.color = 'white';
|
||||
};
|
||||
|
||||
@Directive({
|
||||
selector: '[red]',
|
||||
standalone: true,
|
||||
})
|
||||
class RedDirective {
|
||||
_el = injectAndMakeRed();
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [RedDirective],
|
||||
template: `
|
||||
<p red>This is red</p>
|
||||
`,
|
||||
})
|
||||
export class App {}
|
||||
```
|
||||
|
||||
# `host` property binding
|
||||
|
||||
While the `inject` method works, there's a better way to bind an element: the `host` property.
|
||||
|
||||
```typescript
|
||||
@Directive({
|
||||
selector: '[red]',
|
||||
standalone: true,
|
||||
host: {
|
||||
style: 'background-color: red; color: white;',
|
||||
},
|
||||
})
|
||||
class RedDirective {}
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [RedDirective],
|
||||
template: `
|
||||
<p red>This is red</p>
|
||||
`,
|
||||
})
|
||||
export class App {}
|
||||
```
|
||||
|
||||
Here, `host` refers to the element the directive is attached to. We can use it to then attach new attributes to the parent element like we did above.
|
||||
|
||||
# Dynamic `host` property binding
|
||||
|
||||
`host` isn't just useful for static attribute bindings either, you can use it with attribute binding and event listening using the same `[]` and `()` syntax you're familiar with:
|
||||
|
||||
````typescript
|
||||
@Directive({
|
||||
selector: '[red]',
|
||||
standalone: true,
|
||||
host: {
|
||||
'[style]': `selected ? 'background-color: red; color: white;' : ''`,
|
||||
'(click)': 'selected = !selected',
|
||||
},
|
||||
})
|
||||
class RedDirective {
|
||||
selected = false;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [RedDirective],
|
||||
template: `
|
||||
<p red>This is red when I am selected</p>
|
||||
`,
|
||||
})
|
||||
export class App {}
|
||||
````
|
||||
|
||||
|
||||
|
||||
# Dynamic component `host` property binding
|
||||
|
||||
But what if I told you that these properties were not unique to a directive?
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
As it turns out, **components in Angular are built using the same core APIs as directives**
|
||||
Reference in New Issue
Block a user