Angular 4: Directivas – Parte 3

Directivas

Las directivas son una forma rápida de modificar nuestro HTML, y tenemos 3 tipos:

  • Componentes: Son directivas que siempre tienen asignados templates de HTML.
  • Estructurales: Nos permiten modificar el DOM pudiendo manipular los elementos del HTML.
  • Atributos: Con estas directivas en nuestro HTML podemos indicar como se deben comportar de acuerdo a ciertas condiciones.

A continuación un poco de código para entender algunas directivas estructurales, ngFor (bucles) y ngIf (condiciones):

app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <hr>
  <h1>Ejemplo de Data binding - Parte 2</h1>
  <hr>
  <p><b>String interpolation {{ title }}</b></p>
  <p>{{v1}} + {{v2}} = {{v1 + v2}}</p>
  <p>{{v1}} - {{v2}} = {{v1 - v2}}</p>
  <p>{{v1}} * {{v2}} = {{v1 * v2}}</p>
  <p>{{v1}} / {{v2}} = {{v1 / v2}}</p>
  <p>Property Binding</p>
  <p><button [disabled]="!activate">click</button></p>
  <p>Event Binding</p>
  <p><button (click)="pulsame()">click</button></p>
  <p>Two way data Binding</p>
  <p>Título: <input type="text" placeholder="Cambiar el título" [(ngModel)]="title" /></p>
  <hr>
  <h1>Directivas - Parte 3</h1>
  <hr>
  <p><b>ngFor</b></p>
  <ul>
    <li *ngFor="let pelicula of peliculas">
      <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
    </li>
  </ul>
  <p>
    Puntuación
    <input type="range" min="1" max="10" step="1" [ngModel]="puntuacion" (ngModelChange)="actualizarPuntuacion($event)">
  </p>

  <p><b>ngIf (puntuación >= {{puntuacion}})</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li *ngIf="pelicula.puntuacion >= puntuacion">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>

</div>

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  // Uso general de la aplicación
  title: String = 'my-app';

  // Data binding - Parte 2
  v1: Number = 10;
  v2: Number = 5;
  activate: Boolean = false;

  // Directivas - Parte 3
  peliculas: any = [
    {titulo: 'Hotel transilvania', puntuacion: 4},
    {titulo: 'Los goonies', puntuacion: 6},
    {titulo: 'Indiana Jones y el arca perdida', puntuacion: 9},
    {titulo: 'Coco', puntuacion: 10}
  ];
  puntuacion: Number = 0;

  constructor() {
    // Data binding - Parte 2
    setTimeout(() => this.activate = true, 3000);
  }

  // Data binding - Parte 2
  pulsame() {
    alert('Click');
  }

  actualizarPuntuacion(value) {
    this.puntuacion = value;
  }
}

Como hemos podido comprobar en el código, estas directivas son muy simples de usar. Usando la directiva ngFor realizamos un bucle for por los elementos y con la directiva ngIf realizamos una condición para que se muestre el contenido.

Ahora vamos a usar otras directivas con las que vamos a poder modificar los estilos. Las directivas son ngStyle (estilos css) ngClass (clases css):

app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <hr>
  <h1>Ejemplo de Data binding - Parte 2</h1>
  <hr>
  <p><b>String interpolation {{ title }}</b></p>
  <p>{{v1}} + {{v2}} = {{v1 + v2}}</p>
  <p>{{v1}} - {{v2}} = {{v1 - v2}}</p>
  <p>{{v1}} * {{v2}} = {{v1 * v2}}</p>
  <p>{{v1}} / {{v2}} = {{v1 / v2}}</p>
  <p>Property Binding</p>
  <p><button [disabled]="!activate">click</button></p>
  <p>Event Binding</p>
  <p><button (click)="pulsame()">click</button></p>
  <p>Two way data Binding</p>
  <p>Título: <input type="text" placeholder="Cambiar el título" [(ngModel)]="title" /></p>
  <hr>
  <h1>Directivas - Parte 3</h1>
  <hr>
  <p><b>ngFor</b></p>
  <ul>
    <li *ngFor="let pelicula of peliculas">
      <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
    </li>
  </ul>
  <p>
    Puntuación
    <input type="range" min="1" max="10" step="1" [ngModel]="puntuacion" (ngModelChange)="actualizarPuntuacion($event)">
  </p>
  <p><b>ngIf (puntuación >= {{puntuacion}})</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li *ngIf="pelicula.puntuacion >= puntuacion">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>ngStyle (rojo para puntuación < 5 y verde para >= 5)</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li [ngStyle]="{color: (pelicula.puntuacion) < 4 ? '#ff0000' : '#00aa00'}">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>ngClass (la puntuación con mas de 8 puntos le cambiamos el tamaño de la fuente)</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index">
      <li [ngClass]="pelicula.puntuacion > 8 ? 'topPelicula' : ''">
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
</div>

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  // Uso general de la aplicación
  title: String = 'my-app';

  // Data binding - Parte 2
  v1: Number = 10;
  v2: Number = 5;
  activate: Boolean = false;

  // Directivas - Parte 3
  peliculas: any = [
    {titulo: 'Hotel transilvania', puntuacion: 4},
    {titulo: 'Los goonies', puntuacion: 6},
    {titulo: 'Indiana Jones y el arca perdida', puntuacion: 9},
    {titulo: 'Coco', puntuacion: 10}
  ];
  puntuacion: Number = 0;

  constructor() {
    // Data binding - Parte 2
    setTimeout(() => this.activate = true, 3000);
  }

  // Data binding - Parte 2
  pulsame() {
    alert('Click');
  }

  actualizarPuntuacion(value) {
    this.puntuacion = value;
  }
}

Con las anteriores directivas estamos aplicando estilos a nuestra página. Cuando usamos la directiva ngStyle estamos aplicando un estilo css directamente, pero cuando usamos la directiva ngClass aplicamos una clase de css.

Con la directiva ngSwitch podemos mostrar u ocultar nuestro contenido en base a una expresión.

Veamos algo de código para entenderlo mejor:

app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <hr>
  <h1>Ejemplo de Data binding - Parte 2</h1>
  <hr>
  <p><b>String interpolation {{ title }}</b></p>
  <p>{{v1}} + {{v2}} = {{v1 + v2}}</p>
  <p>{{v1}} - {{v2}} = {{v1 - v2}}</p>
  <p>{{v1}} * {{v2}} = {{v1 * v2}}</p>
  <p>{{v1}} / {{v2}} = {{v1 / v2}}</p>
  <p>Property Binding</p>
  <p><button [disabled]="!activate">click</button></p>
  <p>Event Binding</p>
  <p><button (click)="pulsame()">click</button></p>
  <p>Two way data Binding</p>
  <p>Título: <input type="text" placeholder="Cambiar el título" [(ngModel)]="title" /></p>
  <hr>
  <h1>Directivas - Parte 3</h1>
  <hr>
  <p><b>ngFor</b></p>
  <ul>
    <li *ngFor="let pelicula of peliculas">
      <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
    </li>
  </ul>
  <p>
    Puntuación
    <input type="range" min="1" max="10" step="1" [ngModel]="puntuacion" (ngModelChange)="actualizarPuntuacion($event)">
  </p>
  <p><b>ngIf (puntuación >= {{puntuacion}})</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li *ngIf="pelicula.puntuacion >= puntuacion">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>ngStyle (rojo para puntuación < 5 y verde para >= 5)</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li [ngStyle]="{color: (pelicula.puntuacion) < 4 ? '#ff0000' : '#00aa00'}">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>ngClass (la puntuación con mas de 8 puntos le cambiamos el tamaño de la fuente)</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index">
      <li [ngClass]="pelicula.puntuacion > 8 ? 'topPelicula' : ''">
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>ngSwitch</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index" [ngSwitch]="pelicula.edad">
      <li>
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
        <span *ngSwitchCase="6"> - Película no apta para menores de 6 años</span>
        <span *ngSwitchCase="12"> - Película no apta para menores de 12 años</span>
        <span *ngSwitchCase="18"> - Película no apta para menores de 18 años</span>
      </li>
    </ng-container>
  </ul>
</div>

app.component.ts

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <hr>
  <h1>Ejemplo de Data binding - Parte 2</h1>
  <hr>
  <p><b>String interpolation {{ title }}</b></p>
  <p>{{v1}} + {{v2}} = {{v1 + v2}}</p>
  <p>{{v1}} - {{v2}} = {{v1 - v2}}</p>
  <p>{{v1}} * {{v2}} = {{v1 * v2}}</p>
  <p>{{v1}} / {{v2}} = {{v1 / v2}}</p>
  <p>Property Binding</p>
  <p><button [disabled]="!activate">click</button></p>
  <p>Event Binding</p>
  <p><button (click)="pulsame()">click</button></p>
  <p>Two way data Binding</p>
  <p>Título: <input type="text" placeholder="Cambiar el título" [(ngModel)]="title" /></p>
  <hr>
  <h1>Directivas - Parte 3</h1>
  <hr>
  <p><b>ngFor</b></p>
  <ul>
    <li *ngFor="let pelicula of peliculas">
      <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
    </li>
  </ul>
  <p>
    Puntuación
    <input type="range" min="1" max="10" step="1" [ngModel]="puntuacion" (ngModelChange)="actualizarPuntuacion($event)">
  </p>
  <p><b>ngIf (puntuación >= {{puntuacion}})</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li *ngIf="pelicula.puntuacion >= puntuacion">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>ngStyle (rojo para puntuación < 5 y verde para >= 5)</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li [ngStyle]="{color: (pelicula.puntuacion) < 4 ? '#ff0000' : '#00aa00'}">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>ngClass (la puntuación con mas de 8 puntos le cambiamos el tamaño de la fuente)</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index">
      <li [ngClass]="pelicula.puntuacion > 8 ? 'topPelicula' : ''">
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>ngSwitch</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index" [ngSwitch]="pelicula.edad">
      <li>
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
        <span *ngSwitchCase="6"> - Película no apta para menores de 6 años</span>
        <span *ngSwitchCase="12"> - Película no apta para menores de 12 años</span>
        <span *ngSwitchCase="18"> - Película no apta para menores de 18 años</span>
      </li>
    </ng-container>
  </ul>
</div>

Con los ejemplos anteriores hemos visto como usar algunas de las directivas que nos ofrece Angular.

También voy añadir un enlace a la documentación oficial de Angular, por si quieres profundizar en el tema de las directivas en Angular.

Bien, como en este punto ya hemos asimilado el concepto de directiva ahora vamos a crear las nuestras propias.

Creando nuestra directiva

La forma mas simple de crear nuestra directiva es usando el CLI de Angular, aunque también podemos crear nuestra directiva manualmente.

Para evitar problemas y aprovechar la potencia que ofrece el CLI de Angular vamos a usar las herramientas que nos ofrece Angular.

Usando el comando ng generate directive nombreDirectiva crearemos los ficheros y la estructura necesaria.

Para el curso he usado este comando: ng generate directive directives/alquilada

import { Directive } from '@angular/core';

@Directive({
  selector: '[appAlquilada]'
})
export class AlquiladaDirective {

  constructor() { }

}

Este comando nos genera una carpeta dentro de nuestro proyecto y los archivos necesarios :

  • src/app/directives/alquilada.directive.ts 
  • src/app/directives/alquilada.directive.spec.ts

Veamos el código terminado y su uso:

app.modules.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule} from '@angular/forms';
import { AppComponent } from './app.component';
import { AlquiladaDirective } from './directives/alquilada.directive';

@NgModule({
  declarations: [
    AppComponent,
    AlquiladaDirective,
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  // Uso general de la aplicación
  title: String = 'my-app';

  // Data binding - Parte 2
  v1: Number = 10;
  v2: Number = 5;
  activate: Boolean = false;

  // Directivas - Parte 3
  peliculas: any = [
    {alquilada: false, genero: 'Animaciòn', edad: 6, titulo: 'Hotel transilvania', puntuacion: 4},
    {alquilada: true, genero: 'Aventura', edad: 12, titulo: 'Los goonies', puntuacion: 6},
    {alquilada: false, genero: 'Aventura', edad: 18,  titulo: 'Indiana Jones y el arca perdida', puntuacion: 9},
    {alquilada: true, genero: 'Animaciòn', edad: 6, titulo: 'Coco', puntuacion: 10}
  ];
  puntuacion: Number = 0;

  constructor() {
    // Data binding - Parte 2
    setTimeout(() => this.activate = true, 3000);
  }

  // Data binding - Parte 2
  pulsame() {
    alert('Click');
  }

  actualizarPuntuacion(value) {
    this.puntuacion = value;
  }
}

alquilada.directive.ts

import { Directive, OnInit, ElementRef, Renderer2, Input } from '@angular/core';

@Directive({
  selector: '[appAlquilada]'
})
export class AlquiladaDirective implements OnInit {
  constructor(private el: ElementRef, private render: Renderer2) { }

  @Input('appAlquilada') alquiler:Boolean;
  ngOnInit() {
    if (this.alquiler) {
      this.render.setStyle(this.el.nativeElement, 'color', 'red');
    }
  }

}

app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <hr>
  <h1>Ejemplo de Data binding - Parte 2</h1>
  <hr>
  <p><b>String interpolation {{ title }}</b></p>
  <p>{{v1}} + {{v2}} = {{v1 + v2}}</p>
  <p>{{v1}} - {{v2}} = {{v1 - v2}}</p>
  <p>{{v1}} * {{v2}} = {{v1 * v2}}</p>
  <p>{{v1}} / {{v2}} = {{v1 / v2}}</p>
  <p>Property Binding</p>
  <p><button [disabled]="!activate">click</button></p>
  <p>Event Binding</p>
  <p><button (click)="pulsame()">click</button></p>
  <p>Two way data Binding</p>
  <p>Título: <input type="text" placeholder="Cambiar el título" [(ngModel)]="title" /></p>
  <hr>
  <h1>Directivas - Parte 3</h1>
  <hr>
  <p><b>---- ngFor ----</b></p>
  <ul>
    <li *ngFor="let pelicula of peliculas">
      <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
    </li>
  </ul>
  <p>
    Puntuación
    <input type="range" min="1" max="10" step="1" [ngModel]="puntuacion" (ngModelChange)="actualizarPuntuacion($event)">
  </p>
  <p><b>---- ngIf (puntuación >= {{puntuacion}}) ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li *ngIf="pelicula.puntuacion >= puntuacion">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>---- ngStyle (rojo para puntuación < 5 y verde para >= 5) ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li [ngStyle]="{color: (pelicula.puntuacion) < 4 ? '#ff0000' : '#00aa00'}">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>---- ngClass (la puntuación con mas de 8 puntos le cambiamos el tamaño de la fuente) ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index">
      <li [ngClass]="pelicula.puntuacion > 8 ? 'topPelicula' : ''">
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>---- ngSwitch ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index" [ngSwitch]="pelicula.edad">
      <li>
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}} - Alquilada: {{pelicula.alquilada}}
        <span *ngSwitchCase="6"> - Película no apta para menores de 6 años</span>
        <span *ngSwitchCase="12"> - Película no apta para menores de 12 años</span>
        <span *ngSwitchCase="18"> - Película no apta para menores de 18 años</span>
      </li>
    </ng-container>
  </ul>
  <p><b>---- Directiva propia - Muestra peliculas alquiladas en color rojo ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index">
      <li [appAlquilada]="pelicula.alquilada">
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
</div>

Para entender el código anterior voy añadir una breve explicación de los elementos que estamos usando en nuestra directiva:

  • @Directive: Con este decorador se especifica el selector de la directiva.
  • ElementRef: Nos permite crear una referencia de elemento de HTML para poder manipularla en TypeScript.
  • Renderer2: También permite manipular un elemento HTML pero enfocado a css, esto se hace también para que el mismo código sirva en las diferentes plataformas (servidor, browser, mobile, etc).
  • ngOnInit: Añadimos toda la lógica del negocio, es decir, lo que nuestra directiva ejecutará.
  • setStyle(): Recibiendo tres parámetros (Elemento nativo, Atributo de css que queremos modificar, Valor para el atributo de css).

Como hemos podido ver, cuando usamos el CLI de Angular ademas de crearnos los archivos necesarios también realiza otras tareas.

Vamos a comprobar todo lo que nos ha creado y configurado usando el CLI de Angular:

  • En el fichero app.module.ts, se ha importado y añadido la declaración de nuestra directiva.
  • En el fichero alquilada.directive.ts, se ha creado la estructura básica para crear nuestra directiva.

Aunque estas modificaciones son muy simples y las podemos crear nosotros mismos, cuando las creamos con el CLI de Angular nos aseguramos que tanto la declaración, importación y definición de las mismas no entren en conflicto con el resto.

Host listeners y host binders

Ya hemos creado nuestra directiva que nos cambia el color de las películas que están alquiladas.

Ahora lo interesante seria llevar un control de las veces que se han alquilado y así tener unas estadísticas sobre esa película.

Bien, si queremos hacer click, focus o cualquier otro evento para interactuar con nuestra directiva, Angular ofrece los host listeners y los host binders.

Para crear un host listeners Angular tiene el decorador @HostListener que nos permite escuchar los eventos de una forma sencilla:

  • El evento: podemos usar click, mouseover, focus, blur, etc…
  • El código a ejecutar: El código que se ejecutara

A continuación el código para ver su integración:

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule} from '@angular/forms';
import { AppComponent } from './app.component';
import { AlquiladaDirective } from './directives/alquilada.directive';
import { ClickAlquilerDirective } from './directives/click-alquiler.directive';

@NgModule({
  declarations: [
    AppComponent,
    AlquiladaDirective,
    ClickAlquilerDirective,
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

click-alquiler.directive.ts

import { Directive, HostListener, Renderer2, ElementRef } from '@angular/core';

@Directive({
  selector: '[appClickAlquiler]'
})
export class ClickAlquilerDirective {
  clickAlquiler: any = 0;
  constructor(private el: ElementRef, private render: Renderer2) { }
  @HostListener('click') onclick() {
    console.log('CLICK', this.clickAlquiler++);
  }
  @HostListener('mouseover') mouseOver() {
    console.log('MOUSEOVER');
    this.peliculaSeleccionada(true);
  }
  @HostListener('mouseout') out() {
    console.log('MOUSEOOUT');
    this.peliculaSeleccionada(false);
  }
  peliculaSeleccionada(estado) {
    this.render.setStyle(this.el.nativeElement, 'color', estado ? 'blue' : 'black');
  }
}

app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <hr>
  <h1>Ejemplo de Data binding - Parte 2</h1>
  <hr>
  <p><b>String interpolation {{ title }}</b></p>
  <p>{{v1}} + {{v2}} = {{v1 + v2}}</p>
  <p>{{v1}} - {{v2}} = {{v1 - v2}}</p>
  <p>{{v1}} * {{v2}} = {{v1 * v2}}</p>
  <p>{{v1}} / {{v2}} = {{v1 / v2}}</p>
  <p>Property Binding</p>
  <p><button [disabled]="!activate">click</button></p>
  <p>Event Binding</p>
  <p><button (click)="pulsame()">click</button></p>
  <p>Two way data Binding</p>
  <p>Título: <input type="text" placeholder="Cambiar el título" [(ngModel)]="title" /></p>
  <hr>
  <h1>Directivas - Parte 3</h1>
  <hr>
  <p><b>---- ngFor ----</b></p>
  <ul>
    <li *ngFor="let pelicula of peliculas">
      <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
    </li>
  </ul>
  <p>
    Puntuación
    <input type="range" min="1" max="10" step="1" [ngModel]="puntuacion" (ngModelChange)="actualizarPuntuacion($event)">
  </p>
  <p><b>---- ngIf (puntuación >= {{puntuacion}}) ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li *ngIf="pelicula.puntuacion >= puntuacion">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>---- ngStyle (rojo para puntuación < 5 y verde para >= 5) ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li [ngStyle]="{color: (pelicula.puntuacion) < 4 ? '#ff0000' : '#00aa00'}">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>---- ngClass (la puntuación con mas de 8 puntos le cambiamos el tamaño de la fuente) ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index">
      <li [ngClass]="pelicula.puntuacion > 8 ? 'topPelicula' : ''">
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>---- ngSwitch ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index" [ngSwitch]="pelicula.edad">
      <li>
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}} - Alquilada: {{pelicula.alquilada}}
        <span *ngSwitchCase="6"> - Película no apta para menores de 6 años</span>
        <span *ngSwitchCase="12"> - Película no apta para menores de 12 años</span>
        <span *ngSwitchCase="18"> - Película no apta para menores de 18 años</span>
      </li>
    </ng-container>
  </ul>
  <p><b>---- Directiva propia - Muestra peliculas alquiladas en color rojo ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index">
      <li [appAlquilada]="pelicula.alquilada">
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>---- Directiva propia con Listener - Eventos click para contabilizar veces que se alquila, eventos mouseover y mouseout para cambiar el color ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index">
      <a appClickAlquiler>
        <li [appAlquilada]="pelicula.alquilada">
          <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
        </li>
      </a>
    </ng-container>
  </ul>
</div>

Bien para finalizar la parte de directivas y con ello esta entrada, vamos a ver que es un host binder.

El host binder nos permite editar elementos del DOMHTML al que esté asignado desde nuestra directiva, recibiendo el atributo del HTML que queremos resaltar.

Para el siguiente ejemplo he aprovechado nuestra directiva ya creada y ademas del host listener e añadido host binder.

Para este caso he aprovechado para hacer algo muy visual, modificando la opacidad del elemento cada vez que hacemos click, aunque también podemos usar cualquier clase (css), propiedad o atributo.

click-alquiler.directive.ts

import { Directive, HostListener, Renderer2, ElementRef, HostBinding } from '@angular/core';

@Directive({
  selector: '[appClickAlquiler]'
})
export class ClickAlquilerDirective {
  clickAlquiler: any = 0;
  constructor(private el: ElementRef, private render: Renderer2) { }

  @HostBinding('style.opacity') opacity: any = .2;

  @HostListener('click') onclick() {
    console.log('CLICK', this.clickAlquiler++);
    this.peliculaSeleccionada();
  }
  @HostListener('mouseover') mouseOver() {
    console.log('MOUSEOVER');
  }
  @HostListener('mouseout') out() {
    console.log('MOUSEOOUT');
  }
  peliculaSeleccionada() {
    if (this.opacity < 1) {
      this.opacity += .1;
    } else {
      this.opacity = 1;
    }
  }


}

app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <hr>
  <h1>Ejemplo de Data binding - Parte 2</h1>
  <hr>
  <p><b>String interpolation {{ title }}</b></p>
  <p>{{v1}} + {{v2}} = {{v1 + v2}}</p>
  <p>{{v1}} - {{v2}} = {{v1 - v2}}</p>
  <p>{{v1}} * {{v2}} = {{v1 * v2}}</p>
  <p>{{v1}} / {{v2}} = {{v1 / v2}}</p>
  <p>Property Binding</p>
  <p><button [disabled]="!activate">click</button></p>
  <p>Event Binding</p>
  <p><button (click)="pulsame()">click</button></p>
  <p>Two way data Binding</p>
  <p>Título: <input type="text" placeholder="Cambiar el título" [(ngModel)]="title" /></p>
  <hr>
  <h1>Directivas - Parte 3</h1>
  <hr>
  <p><b>---- ngFor ----</b></p>
  <ul>
    <li *ngFor="let pelicula of peliculas">
      <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
    </li>
  </ul>
  <p>
    Puntuación
    <input type="range" min="1" max="10" step="1" [ngModel]="puntuacion" (ngModelChange)="actualizarPuntuacion($event)">
  </p>
  <p><b>---- ngIf (puntuación >= {{puntuacion}}) ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li *ngIf="pelicula.puntuacion >= puntuacion">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>---- ngStyle (rojo para puntuación < 5 y verde para >= 5) ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas">
      <li [ngStyle]="{color: (pelicula.puntuacion) < 4 ? '#ff0000' : '#00aa00'}">
        <b>Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>---- ngClass (la puntuación con mas de 8 puntos le cambiamos el tamaño de la fuente) ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index">
      <li [ngClass]="pelicula.puntuacion > 8 ? 'topPelicula' : ''">
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>---- ngSwitch ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index" [ngSwitch]="pelicula.edad">
      <li>
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}} - Alquilada: {{pelicula.alquilada}}
        <span *ngSwitchCase="6"> - Película no apta para menores de 6 años</span>
        <span *ngSwitchCase="12"> - Película no apta para menores de 12 años</span>
        <span *ngSwitchCase="18"> - Película no apta para menores de 18 años</span>
      </li>
    </ng-container>
  </ul>
  <p><b>---- Directiva propia - Muestra peliculas alquiladas en color rojo ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index">
      <li [appAlquilada]="pelicula.alquilada">
        <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
      </li>
    </ng-container>
  </ul>
  <p><b>---- Directiva propia con Listener - Eventos click para contabilizar veces que se alquila, eventos mouseover y mouseout para cambiar el color ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index">
      <a appClickAlquiler>
        <li [appAlquilada]="pelicula.alquilada">
          <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
        </li>
      </a>
    </ng-container>
  </ul>
  <p><b>---- Directiva propia con Binding - Eventos click ahora contabiliza y cambia la opacidad ----</b></p>
  <ul>
    <ng-container *ngFor="let pelicula of peliculas; let i = index">
      <a>
        <li [appAlquilada]="pelicula.alquilada" appClickAlquiler>
          <b>{{i}} - Título:</b> {{pelicula.titulo}} - <b>Puntuación:</b> {{pelicula.puntuacion}}
        </li>
      </a>
    </ng-container>
  </ul>
</div>