Angular

Licencia de Creative Commons mario@mariogl.com

Introducción a Angular

¿Qué es Angular?

  • Framework JS

  • UI reactivas

  • SPA: Single Page Applications

  • Data binding

  • Web components

  • TypeScript

  • Código fuente y código compilado

  • ¿Angular 2? ¿4? ¿11? ¿AngularJS?

  • Microservicios / Jamstack

Entorno de desarrollo

Entorno de desarrollo

Git

Comandos básicos

  • Clonar un repositorio:

        git clone URL
     

  • Descargar última versión del repositorio:

        git pull origin master

Configuración proxy

git config --global http.proxy http://username:password@host:port

git config --global https.proxy http://username:password@host:port

Node.js y npm

npm

  • Instalar última versión después de instalar Node.js
    (configurar proxy si es necesario): npm install -g npm

  • Repositorio de paquetes distribuibles

  • Paquetes globales y paquetes locales

  • La carpeta node_modules

  • El archivo package.json:

    • Registro de dependencias

    • Dependencias de desarrollo y de producción

Comandos npm

  • Instalar un paquete globalmente:
        npm install -g paquete

  • Instalar un paquete de producción:
        npm install paquete

  • Instalar un paquete de desarrollo:
        npm install paquete --save-dev

  • Instalar todas las dependencias:
        npm install

  • Instalar las dependencias de producción:
        npm install --production

  • Listar paquetes instalados:

        npm list --depth=0        (locales)
        npm list -g --depth=0   (globales)

Comandos npm

  • Lanzar el ejecutable de un paquete:
    npx ejecutable

angular-cli

  • Instalación global:
        npm install -g @angular/cli

Configuración proxy

npm config set proxy http://username:password@host:port

npm config set https-proxy http://username:password@host:port

JavaScript

JavaScript

  • Interpretado, compilado y ejecutado en el navegador

  • Cada navegador programa su propio motor de JS

  • Estandarización: ECMAScript

  • La versión ES6 o ES2015

  • Transpiladores: Babel, TypeScript

Organización del código JavaScript: módulos

  • Module loaders: ellos gestionan las dependencias y cargan los módulos (RequireJS, SystemJS)

Organización del código JavaScript: módulos

  • Module bundlers: además de lo anterior, generan un solo código encadenado y minificado (webpack, Parcel)

Organización del código JavaScript: módulos

  • Compatibilidad de los módulos ES6 en navegadores

  • ¡Webpack!

  • Usaremos la sintaxis ES6

  • TS -> ES5 -> webpack -> bundle -> browser =

Angular CLI

ES6

  • let y const

let a = 3;

let a = 10;  // Error
var a = 12;  // Error

const b = 10;

b = 3; // Error

const obj = {
    x: 10,
    y: 12
}

obj.x = 15;  // OK

obj = {    // Error
    x: 15,
    y: 12
}

ES6

  • let y const

  • Template literals

let nombre = "Antonio";

let cuadrado = function(x) {
    return x * x;
}

let n = Math.floor(Math.random() * 10);

let saludo1 = "Hola, " + nombre + ". El cuadrado de " + n + " es " + cuadrado(n) + ".";
let saludo2 = `Hola, ${nombre}. El cuadrado de ${n} es ${cuadrado(n)}.`;

ES6

  • let y const

  • Template literals

  • for ... of

let nombres = ["Patricia", "Zacarías", "Miguel", "Maite"];

for (let i in nombres) {
    console.log(nombres[i]);
}

for (let nombre of nombres) {
    console.log(nombre);
}

let obj = {
    x: 3,
    y: 4
}

for (let i in obj) {
    console.log(obj[i]);
}

let nombre = "Antonio Jesús";

for (let c of nombre) {
    console.log(c);
}

ES6

  • let y const

  • Template literals

  • for ... of

  • Funciones

    • Parámetros por defecto

function potencia(x, y = 2) {
    return Math.pow(x, y);
}

console.log(`10 elevado a 8 es ${potencia(10, 8)}`);
console.log(`El cuadrado de 5 es ${potencia(5)}`);

ES6

  • let y const

  • Template literals

  • for ... of

  • Funciones

    • Parámetros por defecto

    • Función arrow:

      (parámetros) => expresión_devuelta;
const potencia = function (x, y = 2) {
    return Math.pow(x, y);
}

const potencia = (x, y = 2) => Math.pow(x, y);

setTimeout(() => console.log("pausa"), 2000);

ES6

  • let y const

  • Template literals

  • for ... of

  • Funciones

    • Parámetros por defecto

    • Función arrow:

      (parámetros) => expresión_devuelta;
    • Funciones como expresión

ES6

  • Operador spread

    • Meter un array dentro de otro

    • Número variable de argumentos en una función

    • Pasar varios valores a una función

    • Copiar un array en otro

    • Copiar un objeto en otro

    • Mergear objeto con defaults

// function(a, b, c)
function sumar(a, b, c) {
  console.log(a + b + c);
}
let nums = [1, 3, 6];
sumar(...nums);

// function(n parámetros)
function sumar(...nums) {
  let suma = 0;
  for (n of nums) {
    suma += n;
  }
  console.log("La suma es " + suma);
}
let a = 3;
let b = 7;
let c = 8;
sumar(a, b, c);

// push y unshift
let nums1 = [1, 3, 6];
let nums2 = [0, 52, 15, 9];

nums1.push(...nums2);
console.log(nums1);
nums1.unshift(...nums2);
console.log(nums1);

// meter un array en medio de otro
let nums1 = [1, 3, 6];
let nums2 = [0, 52, 15, 9];
nums1.splice(1, 0, ...nums2);
console.log(nums1);

// copiar un array
let nums1 = [1, 3, 6];
let nums2 = [...nums1];

// copiar un objeto
let o1 = {
  x: 3,
  y: 5
}
let o2 = { ...o1 };

// mergear un objeto con defaults (ES2018)
let defaults = {
  ancho: 100,
  alto: 200,
  color: "rojo"
}

let o1 = {
  ancho: 200,
  grosor: 10000
}

o1 = { ...defaults, ...o1 };

Arrays

  • Métodos:

    • map

let nombres = ["juan", "luisa", "amparo", "arturo"];

let nombresMays = nombres.map(nombre => nombre.toUpperCase());

console.log(nombresMays);

Arrays

  • Métodos:

    • map

    • filter

let personas = [
    {
        nombre: "juan",
        edad: 15
    },
    {
        nombre: "luisa",
        edad: 35
    },
    {
        nombre: "amparo",
        edad: 17
    },
    {
        nombre: "arturo",
        edad: 32
    }
];

let mayoresEdad = personas.filter(persona => persona.edad >= 18);

console.log(mayoresEdad);

Arrays

  • Métodos:

    • map

    • filter

    • reduce

let nums = [2, 4, 10, 15, 12];

let suma = nums.reduce((x, y) => x + y);

let objs = [
    {
        x: 3,
        y: 2
    },
    {
        x: 8,
        y: 10
    },
    {
        x: 10,
        y: 15
    }
]

let sumaX = objs.reduce((acc, obj) => acc + obj.x, 0);            // Método 1
let sumaX = objs.map(obj => obj.x).reduce((obj1, obj2) => obj1 + obj2);     // Método 2

Arrays

  • Métodos:

    • map

    • filter

    • reduce

    • find

let notas = [
    {
        nombre: "juan",
        nota: 6
    },
    {
        nombre: "luisa",
        nota: 8
    },
    {
        nombre: "amparo",
        nota: 4
    },
    {
        nombre: "arturo",
        nota: 3
    }
];

let notaArturo = notas.find(n => n.nombre === "arturo");

Arrays

  • Métodos:

    • map

    • filter

    • reduce

    • find

  • Encadenamiento

let notas = [
    {
        nombre: "juan",
        nota: 6
    },
    {
        nombre: "luisa",
        nota: 8
    },
    {
        nombre: "amparo",
        nota: 4
    },
    {
        nombre: "arturo",
        nota: 3
    }
];

let notasAprobados = notas.filter(n => n.nota >= 5).map(n => n.nota);

console.log(notasAprobados);

Arrays

  • El operador spread

  • Destructuring

    • Asignar desde un array

    • Intercambiar variables

function medidasMueble() {
  // ...

  return [100, 70, 20];
}

let [ancho, alto, profundo] = medidasMueble();

console.log(ancho, alto, profundo);

// 100, 70, 20
let a = 10;
let b = 20;
 
[a, b] = [b, a];

console.log(a, b);

// 20, 10

Objetos

  • El operador spread

  • Destructuring

    • Asignar desde un objeto

    • Renombrar variables

    • Valores por defecto

    • Argumentos en las funciones

function getRGB(colorHex) {
  // ...

  return {
    alias: 'deeppink',
    red: 255,
    green: 20,
    blue: 147,
    alpha: 0.8
  }
}

let { red, green, blue } = getRGB("#ff1493");

console.log(red, green, blue);

// 255, 20, 147
let personas = [{
  nombre: "Luis",
  apellido: "Herrera",
  edad: 23
},
{
  nombre: "Marta",
  apellido: "Nieto",
  edad: 29
}];
 
for (let {nombre, edad} of personas) {
  console.log(`Me llamo ${nombre} y tengo ${edad} años`);
}

// Me llamo Luis y tengo 23 años
// Me llamo Marta y tengo 29 años
let notas = {
  mat: 8,
  fis: 6,
  dib: 5,
  tec: 6
}

let { mat: matematicas, fis: fisica, dib: dibujo, tec: tecnologia } = notas;

console.log(matematicas, fisica, dibujo, tecnologia);

// 8, 6, 5, 6
let persona = {
  nombre: "Luis",
  edad: 23
}

let { nombre, edad, estado = "soltero" } = persona;

console.log(nombre, edad, estado);

// Luis, 23, soltero
function area({radio = 0, base = 0, altura = 0, tipo = 'circulo'} = {}) {
  console.log(radio, base, altura, tipo);
}
area({ tipo: 'rectangulo', base: 10, altura: 20 });
// 0, 10, 20, "rectangulo"

area();
// 0, 0, 0, "circulo"

ES6

  • Clases

    • Propiedades y métodos

class A {

    constructor(z) {
        this.x = 3;
        this.y = 10;
        this.z = z;
    }

    suma() {
        return this.x + this.y + this.z;
    }

}

let a = new A(20);

console.log(a.suma());
class A {
  	
  	x = 3;
  	y = 10;
  	z;
    
	constructor(z) {
        this.z = z;
    }

    suma() {
        return this.x + this.y + this.z;
    }

}

let a = new A(20);

console.log(a.suma());

ES6

  • Clases

    • Propiedades y métodos

    • Getters y setters

class A {

    constructor(z) {
        this.x = 3;
        this.y = 10;
        this.z = z;
    }

    suma() {
        return this.x + this.y + this.z;
    }

    set zeta(z) {
        this.z = z * 2;
    }

    get zeta() {
        return this.z / 2;
    }
}

let a = new A(20);

a.zeta = 15;

console.log(a.zeta);

ES6

  • Clases

    • Propiedades y métodos

    • Getters y setters

    • Métodos estáticos

class A {

    constructor(z) {
        this.x = 3;
        this.y = 10;
        this.z = z;
    }

    static getPI() {
        return 3.14159;
    }

    suma() {
        return this.x + this.y + this.z;
    }

    set zeta(z) {
        this.z = z * 2;
    }

    get zeta() {
        return this.z / 2;
    }
}

let a = new A(20);

a.zeta = 15;

console.log(a.zeta);

console.log(A.getPI());

ES6

  • Clases

    • Propiedades y métodos

    • Getters y setters

    • Métodos estáticos

    • Herencia con extends y super()

class A {

    constructor(z) {
        this.x = 3;
        this.y = 10;
        this.z = z;
    }

    static getPI() {
        return 3.14159;
    }

    suma() {
        return this.x + this.y + this.z;
    }

    set zeta(z) {
        this.z = z * 2;
    }

    get zeta() {
        return this.z / 2;
    }
}

class B extends A {
    constructor() {
        super(100);
        this.x = 20;
    }

    suma() {
        return this.x + this.z;
    }

    resta() {
        return this.x - this.z;
    }
}

let b = new B();

console.log(b.suma());
console.log(b.resta());

Timers

  • setTimeout(callback, delay);

  • setInterval(callback, delay);

  • clearTimeout(timer);

  • clearInterval(timer);

ES6

  • Módulos

    • import

      import { literal } from 'ruta_modulo';
      import literal from 'ruta_modulo';
      import * as literal from 'ruta_modulo';
      import 'ruta_modulo';
    • export
      export let a = 3;
      export let class Clase {
          ...
      }
      export default {
          key: value
      }

ES6

  • Módulos

    • import dinámicos

      import('ruta_modulo').then(
      ​    modulo => ...
      );

TypeScript

TypeScript

  • Superconjunto de JavaScript

  • Transpila a ES5 (o a otra versión)

TypeScript

  • Superconjunto de JavaScript

  • Transpila a ES5 (o a otra versión)

  • Tipado

  • Errores en tiempo de compilación

TypeScript

  • Superconjunto de JavaScript

  • Transpila a ES5 (o a otra versión)

  • Tipado

  • Errores en tiempo de compilación

  • tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "./public/js/",
  }
}

tsconfig.json

TypeScript - Tipos

  • Tipos básicos:

    • number

    • string

    • boolean

    • Array

    • any

    • void

let peso: number;
peso = 89.5;

let saludo: string;
saludo = 'Vais a petarlo con TypeScript';

let esVerano: boolean;
esVerano = false;

let nums: Array<number>;
nums = [10, 55, -3, 4.14];

let nombres: string[];
nombres = ['Juan', 'Paqui', 'Lorenzo', 'Alicia'];

let cosas: any[];
cosas = [10, 'Teruel', -5, true, [0, -10, 15], false];

function imprimeSaludo(s: string): void {
  console.log(s);
}
imprimeSaludo('Buenas tardes');

TypeScript - Tipos

  • Tipos básicos:

    • number

    • string

    • boolean

    • Array

    • any

    • void

  • Enum

enum FormasPago {
  TPV,
  PayPal,
  transferencia
}
let pago: FormasPago;

pago = FormasPago.PayPal;
procesarPago(pago);

function procesarPago(formaPago: FormasPago): void {
  switch (formaPago) {
    case FormasPago.TPV:
      // ...
      break;
    case FormasPago.PayPal:
      // ...
      break;
    case FormasPago.transferencia:
      // ...
      break;
  }
}

TypeScript - Tipos

  • Tipos básicos:

    • number

    • string

    • boolean

    • Array

    • any

    • void

  • Enum

  • Union types

let numeros: Array<number | string>;
numeros = ['3', 6, '15.8', 0];

function procesar(a: string | number): void {
  if (typeof a === 'string') {
    console.log(a.toUpperCase());
  } else {
    console.log(a.toFixed(2));
  }
}

TypeScript - Tipos

  • Tipos básicos:

    • number

    • string

    • boolean

    • Array

    • any

    • void

  • Enum

  • Union types

  • Genéricos

function verDoble<T>(elem: T): T[] {
  let elemDoble: T[] = [elem, elem];
  return elemDoble;
}

TypeScript - Tipos

  • Tipos básicos:

    • number

    • string

    • boolean

    • Array

    • any

    • void

  • Enum

  • Union types

  • Genéricos

  • Type assertion

const inputText = document.getElementById("nombre") as HTMLInputElement;

inputText.select();

TypeScript - Tipos

  • Tipos básicos:

    • number

    • string

    • boolean

    • Array

    • any

    • void

  • Enum

  • Union types

  • Genéricos

  • Type assertion

  • Alias

type Posicion = 1 | 2 | 3;

let p: Posicion;

p = 3;  // OK
p = 5;  // Error

TypeScript - Funciones

  • Sin flexibilidad en el número de parámetros

function sumar(a: number, b: number): number {
  return a + b;
}

sumar();              // Error
sumar(3);             // Error
sumar(10, 2);         // OK
sumar(4, -3, 10, 8)   // Error

TypeScript - Funciones

  • Sin flexibilidad en el número de parámetros

  • Parámetros opcionales

function sumar(a: number, b: number, c?: number): number {
  if (c) {
    return a + b + c;
  } else {
    return a + b;
  }
}

sumar(10, 2);
sumar(10, 2, 15);

TypeScript - Funciones

  • Sin flexibilidad en el número de parámetros

  • Parámetros opcionales

  • Sobrecarga

function nChars(a: number): string;
function nChars(a: string): number;
function nChars(a: string | number): number | string {
  if (typeof a === 'number') {
    return '¡Es un número!';
  } else if (typeof a === 'string') {
    return a.length;
  }
}


type RGB = [number, number, number];

function convierteColor(color: string): RGB;
function convierteColor(color: RGB): string;
function convierteColor(color: string | RGB): string | RGB {
  if (typeof color === 'string') {
    return [0, 128, 0];
  } else {
    return '#006600';
  }
}

const colorRGB = convierteColor('#006600');
const colorHEX = convierteColor([0, 128, 0]);

TypeScript - Funciones

  • Sin flexibilidad en el número de parámetros

  • Parámetros opcionales

  • Sobrecarga

  • Function types

function transformaNumero(x: number, callback: (n: number) => void) {
  callback(x);
}

let a = 10;

transformaNumero(a, m => console.log(m * 2));

TypeScript - Módulos

  • Sintaxis ES6:
    • import { literal } from 'ruta_modulo';

      import literal from 'ruta_modulo';
      import * as literal from 'ruta_modulo';
      import 'ruta_modulo';
    • export let a = 3;
      export class Clase {
          ...
      }
      export default {
          key: value
      }

TypeScript - Módulos

  • Módulos

    • import dinámicos

      import('ruta_modulo').then(
      ​    modulo => ...
      );

TypeScript - Módulos

  • Sintaxis ES6
  • Se omite la extensión .ts
  • Importar de paquetes npm: nombre del paquete
    import { } from 'paquete';
  • Importar de nuestros módulos: rutas relativas
    import { } from './modulo';

TypeScript - Clases

  • Propiedades fuera del constructor
class Factura {
  numero: string;
  base: number;
  tipoIva: number;

  constructor(numero: string, base: number, tipoIva: number = 21) {
    this.numero = numero;
    this.base = base;
    this.tipoIva = tipoIva;
  }
}

TypeScript - Clases

  • Propiedades fuera del constructor
  • Visibilidad de los miembros
  • Modificador readonly
  • Propiedades estáticas
class Factura {
  private static caracteresSerie = 2;
  public num: string;
  public serie: string;
  public base: number;
  private readonly intTipoIva: number;

  constructor(base: number, tipoIva: number = 21) {
    this.base = base;
    this.intTipoIva = tipoIva;
  }

  get numero(): string {
    return this.serie + this.num;
  }

  set numero(n: string) {
    this.serie = n.slice(0, Factura.caracteresSerie - 1);
    this.num = n.slice(Factura.caracteresSerie);
  }
}

let f = new Factura(100);

f.numero = 'AB600';
console.log(f.numero);

TypeScript - Clases

  • Propiedades fuera del constructor
  • Visibilidad de los miembros
  • Getters y setters
  • Modificador readonly
  • Propiedades estáticas
  • Métodos abstractos
abstract class Vehiculo {

  public manual: boolean;

  constructor(public ruedas: number, public motor: Motor) {
    this.manual = this.motor === Motor.ninguno;
  }

  public abstract arrancar(): void;

}

class Bici extends Vehiculo {
  public arrancar(): void {
    console.log('Me pongo de pie y pedaleo');
  }
}

TypeScript - Clases

  • Propiedades fuera del constructor
  • Visibilidad de los miembros
  • Getters y setters
  • Modificador readonly
  • Propiedades estáticas
  • Métodos abstractos
  • Interfaces
interface Arrancable {
  arrancar(): void;
  apagar(): void;
}

class Vehiculo {

  public manual: boolean;

  constructor(public ruedas: number, public motor: Motor) {
    this.manual = this.motor === Motor.ninguno;
  }

}

class Bici extends Vehiculo implements Arrancable {
  public arrancar(): void {
    console.log('Me pongo de pie y pedaleo');
  }
  public apagar(): void {
    console.log('Me bajo de la bici');
  }
}
interface Cliente {
  id: number;
  login: string;
  nombre: string;
  tipo: TiposCliente;
  fechaAlta: Date;
}

function getClientes(): Cliente[] {
  let clientes: Cliente[] = conectaBD('clientes');
  return clientes;
}

TypeScript - Decoradores

  • @

  • Asignar metadatos

  • Muy utilizados en Angular

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

@Component({
  selector: 'app-factura',
  templateUrl: './factura.component.html',
  styleUrls: ['./factura.component.css']
})
export class FacturaComponent {
  @Input()
  facturaId: number;
}

Angular

Primeros pasos

Primeros pasos

  • Generar la app:
    ng new <nombre-app> --prefix <prefijo> --skip-tests

  • El comando crea un nuevo directorio y lanza npm install dentro

  • Opciones de ng new:
    --prefix: prefijo de la app
    --skip-tests: no genera los archivos *.spec.ts
    --skip-install: se salta el paso de npm install

Primeros pasos

  • Ejecutar la app y verla en el navegador:

    ng serve -o
  • Entornos dev y prod

  • Archivos de configuración

  • Módulos: contenedores lógicos

  • Componentes

    • Divisiones de la UI

    • Tienen clase y template (controlador y vista)

    • Estilos propios (:host para el propio componente)

  • Creando piezas:
    ng generate <tipo-pieza> <nombre-pieza> [params]

Esqueleto de una pieza en Angular

  • clase =>

  • => clase exportada =>

  • => clase decorada =>

  • => dependencias

Examinando un módulo

  • Metadata

    • declarations:
      - componentes, directivas y pipes del módulo

    • imports:
      - otros módulos cuyos componentes, directivas o pipes exportados queremos usar

    • exports:
      - componentes, directivas o pipes que exponemos para que los usen otros módulos

    • providers:
      - objetos inyectables que están disponibles para el inyector del módulo

    • bootstrap:
      - componente(s) inicial de la app

Examinando un componente

  • Metadata

    • selector:
      Selector CSS que se corresponde con una etiqueta HTML

    • template / templateUrl:
      String con el HTML / fichero con el HTML

    • styles / styleUrls:
      Strings con los estilos / ficheros con los estilos

  • ngOnInit
    Componente inicializado (con su vista renderizada y sus valores cargados), se usa para los procesos iniciales (no usar el constructor).

  • ngOnDestroy

Examinando un template

  • Custom elements

  • Data binding

  • Interpolation

  • Property binding

  • Class & style binding

  • Event binding

  • Two-way binding

Examinando un template

  • Directivas de atributo

    • ngClass

    • ngStyle

  • Directivas estructurales

    • ngIf

    • ngFor

  • Pipes

Servicios

  • Dependency Injection:
    Proveedores y jerarquía de inyectores

  • Injectable()

  • Singleton: tiene como ámbito su inyector y todos sus inyectores hijos.

Formularios

  • Template driven y Reactive forms

  • [(ngModel)]: Two-way binding

  • Importar el módulo FormsModule

  • Variables de template con #:
    #formulario="ngForm"
    #control="ngModel"

  • Validaciones: ng-invalid, ng-dirty y ng-touched

  • Capturar el envío: ngSubmit

  • Resetear los estados

Despliegue a producción

  • Pruebas con ng build

  • ng build:

    • --prod: código optimizado para producción

    • --base-href=: cambia el directorio base

    • --sourcemaps: genera los source maps

  • lite-server --baseDir="dist/project-name"

  • Entornos propios

Links

Angular y TypeScript

By mariogl

Angular y TypeScript

Curso Iniciación Angular y TypeScript 17-20 mayo 2021

  • 446