Angular Directives
Tags: angular, javascript, typescript
Published: 2019-01-28 14:09:15

Una directiva es un atributo que cambia la apariencia o comportamiento de un componente. Imagina que es como un "inyector" de código CSS/JS. Una directiva es una clase decorada con `@Directive`, decorador que acepta un objeto. Entre las propiedades del objeto le pasamos uno escencial, el cual es `selector` y por medio del cual le indicamos el nombre del atributo que inyectaremos en otros componentes. Veamos cómo luce:

```javascript
import { Directive } from '@angular/core'

@Directive({
  selector: '[myAwesomeAttribute]'
})
export class MyAwesomeDirective {

}
```

Si quisiéramos usar ese atributo, sería en el _template_ del componente:

```html
<div myAwesomeAttribute></div>
```

Sin embargo, la directiva por sí misma no realiza nada. Para modificar la apariencia o comportamiento de un elemento debemos hacer uso de algunas herramientas..

### Cambiando la apariencia

Para inyectar clases CSS a un elemento, podemos realizarlo de dos formas:

- Usando `ElementRef`
- Usando `@HostBinding`

**ElementRef**

Nos permite acceder al elemento nativo (DOM) que es host (anfitrión) de nuestra directiva. Si, por ejemplo, el host es un elemento `div`, obtendremos dicho elemento del DOM. Esto se hace de la siguiente manera:

```javascript
constructor(ref: ElementRef) {
  const el = ref.nativeElement as HTMLElement
  el.classList.add('my-awesome-class')
}
```

El código anterior le dice a Angular: "Hey, quiero que me inyectes en el constructor, la referencia al elemento en donde esta directiva está seteada.". Una vez hecho esto, podemos tener acceso al elemento DOM por medio de la propiedad `nativeElement`. A partir de aquí, depende de ti lo que quieras hacer con él.

**HostBinding**

Por medio de esta decoración podemos "bindear" datos en el host. Por ejemplo, podemos decirle qué clases css inyectar sin necesidad de usar el feo `ElementRef`. Esto lo hacemos pasando como atributo del decorador, el nombre de la propiedad/atributo del elemento objetivo. Veamos como se el ejemplo anterior usando este método.

```javascript
// Lo hacemos directamente como propiedad
@HostBinding('class') elementClass = 'my-awesome-class'
```

Y eso es suficiente para inyectar la clase `my-awesome-class` en el elemento.

### Cambiando el comportamiento

Vimos como `@HostBinding` nos permite cambiar la apariencia. Existe otro decorador llamado `@HostListener` que nos permite **escuchar por eventos del elemento** y extender su funcionalidad. Veamos un ejemplo:

```javascript
import { Directive, HostListener } from '@angular/core'

@Directive({
  selector: '[myAwesomeAttribute]'
})
export class MyAwesomeDirective {
  @HostListener('click')
  onElementClick() {
    // hacer algo
  }
}
```

De esta manera, logramos extender la funcionalidad del elemento objetivo. Sencillo, ¿verdad?

## Estableciendo valores

Una directiva también nos permite establecer valores desde el host. Esto, se logra de la misma forma que con componentes: por medio del decorador `@Input`.

```javascript
// como propiedad
@Input() property: string
```

Y lo usaríamos de la siguiente manera:

```html
<div myAwesomeDirective="hi"></div>
```

---

## Ejemplo

Vamos a realizar un ejemplo que incluya las tres características de las directivas que hemos visto: `@HostBinding`, `@HostListener` e `@Input`.

```javascript
@Directive({
  selector: '[appMemoButton]'
})
export class MemoButtonDirective {
  @Input('appMemoButton') key: string
  @HostBinding('class') elementClass = 'btn btn--memo'

  @HostListener('click')
  public onClick() {
    const counter = parseInt(localStorage.getItem(this.key), 10)
    localStorage.setItem(this.key, counter + 1)
  }
}
```

```html
<button appMemoButton="registerButtonClicks">Register</button>
```

El ejemplo anterior es bastante simple. La idea general es guardar en nuestro navegador la cantidad de veces que se haga click sobre un botón.

Para esto, aceptamos que cada botón pase a la directiva su propio identificador para guardarse y que no se confunda con otros botones.

```javascript
@Input('appMemoButton') key: string
```

Lo siguiente es añadir clases CSS que previamente hemos definido.

```javascript
@HostBinding('class') elementClass = 'btn btn--memo'
```

Y por último, cuando se haga click en el botón, interceptamos el evento para agregar al contador de clicks de ese botón, una unidad.

```javascript
@HostListener('click')
public onClick() {
  const counter = parseInt(localStorage.getItem(this.key), 10)
  localStorage.setItem(this.key, counter + 1)
}
```

---

## Conclusiones

Las directivas pueden ser muy útiles; los usos son variados. Podemos usarlos para agregar temas, extender eventos, etc. Por ejemplo, un uso muy común es para los UI Kits, en donde se define, por medio de directivas, los tipos de botones, cajas de texto, dropdowns, etc. El uso que le das depende como siempre de ti. 😉