Skip to content
This repository has been archived by the owner on Aug 23, 2020. It is now read-only.

Latest commit

 

History

History
260 lines (197 loc) · 10.4 KB

Clase9_Custom_Pipes_y_Service_Basico.md

File metadata and controls

260 lines (197 loc) · 10.4 KB

Custom Pipes y Service Basico

Custom Pipes: Filtrado en el listado de tareas

Como vimos la clase anterior (Clase 9), Angular provee un conjunto de Pipes que ya vienen integrados y que sirven para transformar los datos de nuestras bound properties antes de mostrarlos en el template (HTML). Ahora veremos como construir nuestros propios, Pipes personalizados, o Custom Pipes.

El código necesario para crearlos seguramente a esta altura ya nos resulte familiar:

import { Pipe, PipeTransform } from '@angular/core'; //0) importamos
import { Homework } from '../models/Homework';

//1) Nos creamos nuestra propia clase HomeworksFilterPipe y la decoramos con @Pipe
@Pipe({
  name: 'homeworksFilter'
})
export class HomeworksFilterPipe implements PipeTransform { //2) Implementamos la interfaz PipeTransform

  transform(list: Array<Homework>, arg: string): Array<Homework> { //3) Método de la interfaz a implementar
        //4) Escribimos el código para filtrar las tareas
        // El primer parametro 'list', es el valor que estamos transformando con el pipe (la lista de tareas)
        // El segundo parametro 'arg', es el criterio a utilizar para transfmar el valor (para filtrar las tareas)
        // Es decir, lo que ingresó el usuario
        // El retorno es la lista de tareas filtrada
  }
}

Como podemos ver, tenemos que crear una clase, y hacerla que implemente la interfaz PipeTransform. Dicha interfaz tiene un método transform que es el que será encargado de filtrar las tareas. A su vez decoramos la clase con un @Pipe que hace que nuestra clase sea un Pipe. Como notamos, la experiencia a la hora de programar en Angular es bastante consistente, esto es muy similar a cuando creamos componentes. Tambien podemos lanzar el comando ng generate pipe "Nombre de la pipe" que nos da como resultado esto mismo.

Luego, para usar este CustomPipe en un template, debemos hacer algo así:

<tr *ngFor='let homework of homeworks | HomeworksFilter:listFilter'> </tr>

Siendo:

  • HomeworksFilter: el pipe que acabamos de crear.
  • listFilter: el string por el cual estaremos filtrando.

Si quisieramos pasar más argumentos además del listFilter, los ponemos separados por :.

También nos falta agregar el Pipe a nuestro módulo. Si queremos que el componente pueda usarlo, entonces debemos decirle a nuestro AppModule que registre a dicho Pipe. Siempre que queremos que un Componente use un Pipe, entonces el módulo del componente debe referenciar al Pipe. Lo haremos definiendo al Pipe en el array declarations del decorador ngModule de nuestro módulo.

Armemos el Pipe!

1) Creamos un archivo para el Pipe

Creamos en la carpeta app/homeworks-list, un ``homeworks-filter.pipe.ts, siguiendo nuestras convenciones de nombre. O lanzamos ng generate pipe HomeworksFilter``` y movemos los archivos a la carpeta.

2) Agregamos la lógica del Pipe:

import { Pipe, PipeTransform } from '@angular/core';
import { Homework } from '../models/Homework';

@Pipe({
  name: 'homeworksFilter'
})
export class HomeworksFilterPipe implements PipeTransform {

  transform(list: Array<Homework>, arg: string): Array<Homework> {
    return list.filter(
      x => x.description.toLocaleLowerCase()
        .includes(arg.toLocaleLowerCase())
    );
  }
}

3) Agregamos el filtrado en el template y sus estilos

Vamos a homeworks-list.component.html y donde usamos *ngFor, agregamos el filtrado tal cual lo vimos arriba:

<tr *ngFor="let aHomework of homeworks | homeworksFilter : listFilter">

4) Agregamos el Pipe a nuestro AppModule

Vamos a app.module.ts y agregamos el pipe:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { HomeworksListComponent } from './homeworks-list/homeworks-list.component';
import { HomeworksFilterPipe } from './homeworks-list/homeworks-filter.pipe';
import { HomeworksService } from './services/homeworks.service';

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

Listo! ya podemos filtar en nuestra lista!

Servicios e Inyección de Dependencias

Los componentes nos permiten definir lógica y HTML para una cierta pantalla/vista en particular. Sin embargo, ¿qué hacemos con aquella lógica que no está asociada a una vista en concreto?, o ¿qué hacemos si queremos reusar lógica común a varios componentes (por ejemplo la lógica de conexión contra una API, lógica de manejo de la sesión/autenticación)?

Para lograr eso, construiremos servicios. Y a su vez, usaremos inyección de dependencias para poder meter/inyectar esos servicios en dichos componentes.

Definiendo servicios, son simplemente clases con un fin en particular. Los usamos para aquellas features que son independientes de un componente en concreto, para reusar lógica o datos a través de componentes o para encapsular interacciones externas. Al cambiar esta responsabilidades y llevarlas a los servicios, nuestro código es más fácil de testear, debuggear y mantener.

Angular trae un Injector built-in, que nos permitirá registrar nuestros servicios en nuestros componentes, y que estos sean Singleton. Este Injector funciona en base a un contenedor de inyección de dependencias, donde una vez estos se registran, se mantiene una única instancia de cada uno.

Supongamos tenemos 3 servicios: svc, log y math. Una vez un componente utilice uno de dichos servicios en su constructor, el Angular Injector le provee la instancia del mismo al componente.

image

Construyamos un servicio

Para armar nuestro servicio precisamos:

  • Crear la clase del servicio.
  • Definir la metadata con un @, es decir un decorador.
  • Importar lo que precisamos. ¿Familiar? Son los mismos pasos que hemos seguido para construir nuestros componentes y nuestros custom pipes :)

1) Creamos nuestro servicio

Vamos a app/services y creamos un nuevo archivo: homeworks.service.ts. O lanzamos ng generate service Homeworks y lo movemos a la carpeta

Luego, le pegamos el siguiente código:

import { Injectable } from '@angular/core';
import { Homework } from '../models/Homework';
import { Exercise } from '../models/Exercise';

@Injectable()
export class HomeworksService {

  constructor() { }

  getHomeworks():Array<Homework> {
    return [
      new Homework('1', 'Una tarea', 0, new Date(), [
        new Exercise('1', 'Un problema', 1),
        new Exercise('2', 'otro problema', 10)
      ]),
      new Homework('2', 'Otra tarea', 0, new Date(), [])
    ];
  }
}

2) Registramos nuestro servicio a través de un provider

Para registrar nuestro servicio en nuestro componente, debemos registrar un Provider. Un provider es simplemente código que puede crear o retornar un servicio, típicamente es la clase del servicio mismo. Esto lo lograremos a través de definirlo en el componente, o como metadata en el Angular Module (AppModule).

  • Si lo registramos en un componente, podemos inyectar el servicio en el componente y en todos sus hijos.
  • Si lo registramos en el módulo de la aplicación, lo podemos inyectar en toda la aplicación.

En este caso, lo registraremos en el Root Component (AppModule). Por ello, vamos a app.module.ts y reemplazamos todo el código para dejarlo así:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { HomeworksListComponent } from './homeworks-list/homeworks-list.component';
import { HomeworksFilterPipe } from './homeworks-list/homeworks-filter.pipe';
import { HomeworksService } from './services/homeworks.service'; //importamos el servicio

@NgModule({
  declarations: [
    AppComponent,
    HomeworksListComponent,
    HomeworksFilterPipe
  ],
  imports: [
    FormsModule,
    BrowserModule
  ],
  providers: [
    HomeworksService //registramos el servicio para que este disponible en toda nuestra app
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

3) Inyectamos el servicio en nuestro HomeworksListComponent

La inyección la logramos a través del constructor de la clase, para ello hacemos en homeworks-list.component.ts:

Primero el import:

import { HomeworksService } from '../services/homeworks.service';

Y luego definimos el constructor que inyecta el servicio a la clase:

constructor(private _serviceHomeworks:HomeworksService) { 
   // esta forma de escribir el parametro en el constructor lo que hace es:
   // 1) declara un parametro de tipo HomeworksService en el constructor
   // 2) declara un atributo de clase privado llamado _serviceHomeworks
   // 3) asigna el valor del parámetro al atributo de la clase
}

Eso el HomeworksService y lo deja disponible para la clase. Ahí mismo podríamos inicializar nuestras homeworks, llamando al getHomeworks del servicio, sin embargo, no es proljo mezclar la lógica de construcción del componente (todo lo que es renderización de la vista), con lo que es la lógica de obtención de datos. Para resover esto usarmoes Hooks particularmente, el OnInit que se ejecuta luego de inicializar el componente.

ngOnInit(): void {
    this.homeworks = this._serviceHomeworks.getHomeworks();
}

Quedando, el código del componente algo así:

import { Component, OnInit } from '@angular/core';
import { Homework } from '../models/Homework';
import { Exercise } from '../models/Exercise';
import { HomeworksService } from '../services/homeworks.service';

@Component({
  selector: 'app-homeworks-list',
  templateUrl: './homeworks-list.component.html',
  styleUrls: ['./homeworks-list.component.css']
})
export class HomeworksListComponent implements OnInit {
  pageTitle:string = 'HomeworksList';
  homeworks:Array<Homework>;
  showExercises:boolean = false;
  listFilter:string = "";

  constructor(private _serviceHomeworks:HomeworksService) { 
    
  }

  ngOnInit() {
    this.homeworks = this._serviceHomeworks.getHomeworks();
  }

  toogleExercises() {
    this.showExercises = !this.showExercises;
  }
}