Dividir en componentes

app-root

app-formulario

app-listado

Diagrama de elementos

Generemos dos componentes: formulario y listado.

Agregarlos a app.component

<!-- app.component.html -->

<app-formulario></app-formulario>

<app-listado></app-listado>

app-root

app-formulario

app-listado

Diagrama de elementos

app-tarea

Generemos un tercer componente: tarea

Agregarlo a listado.component

<!-- listado.component.html -->

<app-tarea></app-tarea>

app-root

app-formulario

app-listado

El flujo de información ahora va desde "formulario" hacia el app.component y éste a su vez envía la información a "listado" y "listado" la envía a "tarea"

app-tarea

app-root

app-formulario

app-listado

Cuando "formulario" agrega una tarea, emite la señal "onNuevaTarea"

app-tarea

(onNuevaTarea)

app-root

app-formulario

app-listado

"app" envía la nueva tarea a través de la propiedad "tareas" del componente "listado"

app-tarea

(onNuevaTarea)

[tareas]

app-root

app-formulario

app-listado

"listado" envía la tarea al componente "tarea"

app-tarea

(onNuevaTarea)

[tareas]

[tarea]

El evento o señal "onNuevaTarea" ejecuta el método "grabar" al cual le pasa como parámetro la tarea creada.

<!-- app.component.html -->

<app-formulario (onNuevaTarea)="grabar($event)"></app-formulario>

<app-listado [tareas]="tareas"></app-listado>

@Input

El decorador Input permite crear nuestras propias entradas.

import { Input } from "@angular/core"

El Input debe importarse de la librería "@angular/core"

@Input() tareas: ITarea[]

Podemos usar el Input especificando un nombre. Este nombre se convierte en una propiedad.

Es decir, podemos llamar a esta propiedad usando el objeto "this".

this.tareas

Podemos usarla para interpolar o en *ngFor, *ngIf, etc.

<div *ngFor="let tarea of tareas">
    {{tarea.titulo}}
</div>
<!-- app.component.html -->

<app-listado [tareas]="tareas"></app-listado>

Los Inputs se pueden manejar con alias

<!-- listado.component.ts -->

...
    @Input("tareas") listaTareas: ITarea[]
...

En este caso "tareas" es el alias del Input.

El Input es asignado a la propiedad "listaTareas".

@Output

El decorador Output permite crear nuestras propias salidas (eventos, señales)

import { Output } from "@angular/core"

El Output debe importarse de la librería "@angular/core"

@Ouput() onNuevaTarea = new EventEmitter<ITarea>()

Podemos usar el Output especificando un nombre. Este nombre se convierte en una propiedad de tipo "EventEmitter".

Es decir, podemos llamar a esta propiedad usando el objeto "this".

this.onNuevaTarea

La clase "EventEmitter" tiene varios métodos. Uno de ellos se llama "emit"

this.onNuevaTarea.emit(tarea)
<!-- app.component.html -->

<app-formulario (onNuevaTarea)="grabar($event)"></app-formulario>

Los Outputs se pueden manejar con alias

<!-- formulario.component.ts -->

...
    @Output("onNuevaTarea") agregarTarea = new EventEmitter<ITarea>()
...

En este caso "onNuevaTarea" es el alias del Output.

El Output es asignado a la propiedad "agregarTarea".

Tarea 1

Agregar un botón "Eliminar" a cada tarea.

 

Usar los métodos de arreglos "indexOf" para ubicar el índice de un elemento, y "splite" para eliminar uno o más elementos.

Encapsulation

La encapsulación tiene que ver con el ámbito o alcance de las reglas de CSS

@Component({
    selector: ...
    templateUrl: ...
    styleUrls: ...
    encapsulation: ...
})

Encapsulation es una propiedad del JSON que define un componente a través del decorador @Component

import { ViewEncapsulation } from "@angular/core"

Para utilizar "Encapsulation" necesitamos importar el template "ViewEncapsulation" desde "@angular/core"

ViewEncapsulation.Emulated

ViewEncapsulation.Native

ViewEncapsulation.None

"Encapsulation" tiene tres posibles valores.

ViewEncapsulation.Emulated

<!-- formulario.component.html -->

<p>Formulario para agregar</p>

Agreguemos un título al componente "formulario" usando una <p>.

<!-- listado.component.html -->

<p>Fecha: {{tarea.fecha}}</p>
<p>Solicitante: {{tarea.solicitante}}</p>
<p>Estado: {{tarea.estado}}</p>

Agreguemos <p> al componente "listado".

<!-- listado.component.css -->

p {
    font-style: italic
}

Agreguemos una regla de estilos para las <p>

El cambio se refleja únicamente en el componente "listado"

¿Por qué?

En el inspector de elementos encontramos la respuesta.

Angular le agregó automáticamente el atributo "_ngcontent-c2".

Por defecto, Angular utiliza "ViewEncapsulation.Emulated" así no definamos la propiedad "encapsulation".

ViewEncapsulation.Native

Native y Emulated dan resultados iguales.

La diferencia está en la técnica usada.

 

Native usa "shadow-root", que es parecido a usar un iframe

"shadow-root" no es soportado por todos los navegadores aún.

ViewEncapsulation.None

Usar None significa que cualquier estilo definido fuera del componente puede afectar a las etiquetas html definidas dentro.

Referencias Locales

Las referencias locales son las que he llamado antes variables de controles

<!-- formulario.component.html -->

...
<input type="text" id="titulo" placeholder="Ingrese el título" #titulo>

<input type="date" id="fecha" placeholder="Ingrese la fecha" #fecha>

<input type="text" id="solicitante" placeholder="Ingrese el solicitante" #solicitante>

<select style="display: block" #estado>
    <option value="" disabled selected>Elija un estado</option>
    <option value="sin-asignar">Sin asignar</option>
    <option value="asignada">Asignada</option>
    <option value="en-proceso">En proceso</option>
    <option value="terminada">Terminada</option>
    <option value="cancelada">Cancelada</option>
</select>
...

Reemplacemos el "ngModel" y "name" por referencias locales.

<!-- formulario.component.html -->

...
<button 
    class="btn waves-effect waves-light" 
    (click)="grabar(titulo, fecha, solicitante, estado)">
    Grabar
</button>
...

El botón "Grabar" ejecutará el método "grabar" pero tendrá como parámetros a las referencias locales.

<!-- formulario.component.ts-->
import * as moment from "moment";

...
grabar(titulo, fecha, solicitante, estado) {
    this.modelo.titulo = titulo.value
    this.modelo.fecha = moment(fecha.value).toDate()
    this.modelo.solicitante = solicitante.value
    this.modelo.estado = estado.value

    this.onNuevaTarea.emit(this.modelo)
}
...

El botón "Grabar" ejecutará el método "grabar" pero tendrá como parámetros a las referencias locales.

@ViewChild

@ViewChild es un decorador que nos permite acceder a elementos HTML

Mantengamos en mente a las referencias locales.

<!-- formulario.component.html -->

...
<input type="text" id="titulo" placeholder="Ingrese el título" #titulo>

<input type="date" id="fecha" placeholder="Ingrese la fecha" #fecha>

<input type="text" id="solicitante" placeholder="Ingrese el solicitante" #solicitante>

<select style="display: block" #estado>
    <option value="" disabled selected>Elija un estado</option>
    <option value="sin-asignar">Sin asignar</option>
    <option value="asignada">Asignada</option>
    <option value="en-proceso">En proceso</option>
    <option value="terminada">Terminada</option>
    <option value="cancelada">Cancelada</option>
</select>
...

Podemos acceder a ellas desde el typescript usando el ViewChild

<!-- formulario.component.ts -->
import { ViewChild } from "@angular/core"

...
@ViewChild("titulo") tituloTarea
@ViewChild("fecha") fechaTarea
@ViewChild("solicitante") solicitanteTarea
@ViewChild("estado") estadoTarea
...
<!-- formulario.component.ts -->

grabar() {
    console.log(this.tituloTarea)	
}

Modificamos el método "grabar" para analizar el ViewChild

Observamos que lo devuelto es un "ElementRef"

Modifiquemos el typescript para reflejar el tipo de dato correcto

<!-- formulario.component.ts -->
import { ViewChild, ElementRef } from "@angular/core"

...
@ViewChild("titulo") tituloTarea: ElementRef
@ViewChild("fecha") fechaTarea: ElementRef
@ViewChild("solicitante") solicitanteTarea: ElementRef
@ViewChild("estado") estadoTarea: ElementRef
...
// devuelve el valor del input
this.titulo.nativeElement.value 

// nos permite modificar los estilos de la etiqueta
this.titulo.nativeElement.style 

this.titulo.nativeElement.style.backgroundColor = "red"

"ElementRef" contiene una propiedad "nativeElement" que nos da acceso a modificar las características y valores de la etiqueta HTML.

Tarea 2

1. Crear tres componentes: "GeneradorNumero", "Par", "Impar"

2. El componente "GenerarNumero" contendrá dos botones: "Iniciar" y "Detener".

3. El botón "Iniciar" hará que el componente "GenerarNumero" emita un número incremental desde 1 cada segundo (setInterval).

4. El botón "Detener" hará que el componente deje de emitir (clearInterval)

5. Los componentes "Par" e "Impar" se repetirán tantas veces se hayan emitido números pares e impares respectivamente.

6. Los componentes "Par" e "Impar" deben mostrar el número que los generó.

3 Angular: Componentes, Inputs, Outputs, Encapsulation y ElementRef

By Sergio Hidalgo

3 Angular: Componentes, Inputs, Outputs, Encapsulation y ElementRef

División en componentes, Entradas y Salidas, Alcance de CSS, Referencias Locales, Acceso a elementos HTML

  • 800