Herramientas de usuario

Herramientas del sitio


clase:daw:diw:1eval:tema03

Diferencias

Muestra las diferencias entre dos versiones de la página.


Revisión previa
clase:daw:diw:1eval:tema03 [2025/11/05 16:48] (actual) – [Filtros] Lorenzo
Línea 1: Línea 1:
 +====== 3. Creación de componentes ======
 +En este tema vamos a crear nuevo componentes de Angular
 +
 +===== Crear tu nuevo componente botón =====
 +Vamos a crear nosotros un nuevo componente ''boton''.
 +
 +
 +Para ello vamos a crear los 3 ficheros que necesitas todo componente:
 +  * ''boton/boton.ts'': El código TypeScript que tiene la lógica del botón
 +  * ''boton/boton.html'': El HTML correspondiente al botón
 +  * ''boton/boton.scss'': Los estilos en SASS del botón. Por ahora es simplemente estilos CSS.
 +
 +
 +Los 3 ficheros los crearás dentro de la carpeta ''src/app/components/ui''
 +
 +<sxh text;gutter:false>
 +src/
 + └─ minecraft/
 +     └─ components/            
 +         ├─ ui/
 +         │   ├─ boton/
 +         │   │   ├─ boton.ts
 +         │   │   ├─ boton.html
 +         │   │   └─ boton.scss
 +         │   └─ panel/
 +         └─ paginas/
 +             ├─ minecraft-main/
 +             │   ├─ minecraft-main.ts
 +             │   ├─ minecraft-main.html
 +             │   └─ minecraft-main.scss
 +             └─ productos/
 +                 ├─ productos.ts
 +                 ├─ productos.html
 +                 └─ productos.scss
 +</sxh>
 +
 +El contenido de cada uno de ellos será:
 +
 +
 +<sxh typescript; title: boton.ts>
 +import { Component  } from '@angular/core';
 +import {CommonModule} from '@angular/common';
 +
 +@Component({
 +  selector: 'boton',
 +  imports: [CommonModule],
 +  templateUrl: './boton.html',
 +  styleUrl: './boton.scss'
 +})
 +export class Boton  {
 +
 +}
 +</sxh>
 +
 +La parte más importante es el objeto que se le pasa al decocador ''@Componente''. Veamos sus propiedades:
 +  * ''selector'': Es la etiqueta que usaremos cuando queramos usar el componente. ''<boton></boton>''
 +  * ''imports'': Importamos otros componentes para poder usarlos dentro del HTML del componente.
 +  * ''templateUrl'': El nombre del fichero HTML
 +  * ''styleUrl'': El nombre del fichero SCSS de estilos
 +
 +
 +<sxh typescript; title: boton.html>
 +<button class="boton">Aceptar</button>
 +</sxh>
 +
 +
 +<sxh css; title: boton.scss>
 +.boton {
 +  font-family: sans-serif;
 +  font-size: 16px;
 +  padding: 6px;
 +
 +  border-radius: 6px;
 +  border-width: 1px;
 +  border-style: solid;
 +
 +  display: inline-block;
 +  cursor: pointer;
 +  text-decoration: none;
 +
 +
 +  border-color: #0056b8;
 +  background-color: #0056b8;
 +  color: #ffffff;
 +}
 +</sxh>
 +
 +
 +
 +
 +
 +
 +
 +===== Usando un componente =====
 +Como ya hicimos en el tema anterior, hay que importarlo en el ''.ts'' donde quedamos usarlo y añadirlo al array de ''imports''
 +
 +Primero lo importamos en el ''app.ts''.
 +
 +
 +Primero lo importamos en el ''app.ts''.
 +
 +<sxh typescript;highlight: [4,8]; title:app.ts;>
 +import { Component, signal } from '@angular/core';
 +import { RouterOutlet } from '@angular/router';
 +import {MatButtonModule} from '@angular/material/button';
 +import { Boton } from './shared/ui/boton/boton';
 + 
 +@Component({
 +  selector: 'app-root',
 +  imports: [RouterOutlet, MatButtonModule,Boton],
 +  templateUrl: './app.html',
 +  styleUrl: './app.scss'
 +})
 +export class App {
 +  protected readonly title = signal('EjemploComponentesAngular');
 + 
 + 
 + 
 +}
 +</sxh>
 +
 +Y ahora ya podemos usar la etiquetya ''<boton></boton>'' en el ''app.html''
 +
 +<sxh typescript;highlight: [13]; title:app.html;>
 +<style>
 +</style>
 +  
 +<h1 >Hola mundo</h1>
 +  
 +<button matButton="filled">
 +  Aceptar
 +</button>
 +
 +<br>
 +<br>
 +
 +<boton></boton>
 +  
 +<router-outlet />
 +</sxh>
 +
 +===== Contenido de la etiqueta =====
 +Nuestros botones es muy pobre porque no permite indicar el texto del botón. Para poder hacerlo simplemente hay que usar la etiqueta ''<ng-content></ng-content>'' en el ''boton.html''
 +
 +
 +<sxh typescript;highlight: [2];  title: boton.html>
 +<button class="boton">
 +  <ng-content></ng-content>
 +</button>
 +</sxh>
 +
 +Y ahora ya podremos cambiar el texto:
 +
 +<sxh typescript;highlight: [13]; title:app.html;>
 +<style>
 +</style>
 +  
 +<h1 >Hola mundo</h1>
 +  
 +<button matButton="filled">
 +  Aceptar
 +</button>
 +
 +<br>
 +<br>
 +
 +<boton>Hola mundo</boton>
 +  
 +<router-outlet />
 +</sxh>
 +
 +===== Personalizar del componente =====
 +Vamos ahora añadir atributos a nuestro componente para poder personalizarlo. Para ello vamos a añadir propiedades a la clase TypeScript del componente con el decorador ''Input()''
 +
 +
 +Nuestro objetivo es poder hacer lo siguiente:
 +<sxh html>
 +<boton [backgroundColor]="'#FF0000'" [color]="'#00FF00'">Hola mundo</boton>
 +</sxh>
 +
 +
 +Lo primero es añadir las propiedades ''backgroundColor'' y ''color'' a la clase ''Boton''.
 +
 +<sxh typescript;highlight: [11,12]; title: boton.ts>
 +import { Component, Input } from '@angular/core';
 +import {CommonModule} from '@angular/common';
 +
 +@Component({
 +  selector: 'boton',
 +  imports: [CommonModule],
 +  templateUrl: './boton.html',
 +  styleUrl: './boton.scss'
 +})
 +export class Boton  {
 +  @Input() backgroundColor: string = '#0B5CD5';
 +  @Input() color: string = '#F5F7FF';
 +}
 +</sxh>
 +
 +Para usarlas en el ''boton.html'' hay 3 formas distintas:
 +
 +  * Directamente interpolando los valores:
 +
 +<sxh typescript;highlight: [1];  title: boton.html>
 +<button class="boton" style="color:{{color}};background-color:{{backgroundColor}}">
 +  <ng-content></ng-content>
 +</button>
 +</sxh>
 +
 +  * Usando ''[style]'' de Angular:
 +
 +<sxh typescript;highlight: [1];  title: boton.html>
 +<button class="boton" [style.color]="color" [style.backgroundColor]="backgroundColor">
 +  <ng-content></ng-content>
 +</button>
 +</sxh>
 +
 +  * Usando ''[ngStyle]'' de Angular:
 +
 +<sxh typescript;highlight: [1];  title: boton.html>
 +<button class="boton" [ngStyle]="{'color': color, 'background-color': backgroundColor}">
 +  <ng-content></ng-content>
 +</button>
 +</sxh>
 +
 +  * Por último lo ideal siempre es usar clases CSS y evitar usar ''style'':
 +
 +<sxh typescript;highlight: [1];  title: boton.html>
 +<button class="boton" [ngClass]="[colorClass, backgroundClass]">
 +  <ng-content></ng-content>
 +</button>
 +</sxh>
 +
 +<sxh typescript;highlight: [1];  title: boton.html>
 +<button class="boton color--{{color}} background--{{backgroundColor}}">
 +  <ng-content></ng-content>
 +</button>
 +</sxh>
 +
 +<sxh typescript;highlight: [1];  title: boton.html>
 +<button class="boton" [ngClass]="['color--'+color, 'background--'+backgroundColor]">
 +  <ng-content></ng-content>
 +</button>
 +</sxh>
 +
 +Pero en ese caso hay que limitar los posibles valores, definir las clases, relacionarlas con las propiedades TypeScript. Esto lo veremos en el siguiente apartado.
 +
 +===== Limitando los valores =====
 +En el apartado anterior hemos visto como definir los colores de los botones pero como vimos en el primer tema, realmente hay únicamente unos valores de colores que puede haber
 +
 +{{:clase:daw:diw:1eval:primary.png|}}
 +{{:clase:daw:diw:1eval:alt-primary.png|}}
 +{{:clase:daw:diw:1eval:rojo-primary.png|}}
 +
 +
 +Así que realmente lo que queremos es solo definir la función del botón.De forma que se use de la siguiente manera:
 +
 +<sxh html;title:app.html>
 +<boton [funcion]="'peligrosa'">Hola mundo</boton>
 +</sxh>
 +
 +
 +  * Por lo tanto el atributo del botón va a ser ''funcion'' con los posibles valores de '''normal' | 'alternativa' | 'peligrosa''' .
 +
 +<sxh typescript;highlight: [11];  title: boton.html>
 +import {Component, Input} from '@angular/core';
 +import {CommonModule} from '@angular/common';
 +
 +@Component({
 +  selector: 'boton',
 +  imports: [CommonModule],
 +  templateUrl: './boton.html',
 +  styleUrl: './boton.scss'
 +})
 +export class Boton  {
 +  @Input() funcion:'normal' | 'alternativa' | 'peligrosa'='normal';
 +
 +}
 +</sxh>
 +
 +
 +  * Ahora hay que definir las clases CSS en el archivo ''boton.scss''
 +
 +<sxh css;highlight: [20-24,26-30,32-36];  title: boton.scss>
 +.boton {
 +  font-family: sans-serif;
 +  font-size: 16px;
 +  padding: 6px;
 +
 +  border-radius: 6px;
 +  border-width: 1px;
 +  border-style: solid;
 +
 +  display: inline-block;
 +  cursor: pointer;
 +  text-decoration: none;
 +
 +
 +  border-color: #0056b8;
 +  background-color: #0056b8;
 +  color: #ffffff;
 +}
 +
 +.funcion--normal {
 +  border-color: #0056b8;
 +  background-color: #0056b8;
 +  color: #ffffff;
 +}
 +
 +.funcion--alternativa {
 +  border-color: #ed8936;
 +  background-color: #ed8936;
 +  color: #ffffff;
 +}
 +
 +.funcion--peligrosa {
 +  border-color: #c53030;
 +  background-color: #c53030;
 +  color: #ffffff;
 +}
 +
 +
 +</sxh>
 +
 +
 +  * Y por último relacionamos la propiedad TypScript ''funcion'' con las clases CSS en el HTML de la siguiente forma:
 +
 +<sxh html;highlight: [1];title:boton.html>
 +<button class="boton funcion--{{funcion}}">
 +  <ng-content></ng-content>
 +</button>
 +</sxh>
 +
 +<note tip>
 +Notar el truco de ''class="funcion--<nowiki>{{funcion}}</nowiki>"''
 +</note>
 +
 +  * Otra forma alternativa sería:
 +
 +<sxh html;highlight: [1];title:boton.html>
 +<button class="boton" [ngClass]="['funcion--'+funcion]">
 +  <ng-content></ng-content>
 +</button>
 +</sxh>
 +
 +===== Acción de href =====
 +Por último vamos a añadir acciones al botón, como el ''onClick'' y el ''href''
 +
 +
 +Para crear el atributo ''href'' hay que hacer 3 cambios:
 +  * Añadir la propiedad ''href'' a la clase ''Boton''
 +  * Usar la propiedad en el HTML
 +  * Cambiar de tipo ''<button>'' a ''<a>''
 +
 +
 +<sxh TypeScript;highlight: [12]; title:boton.ts>
 +import {Component, Input} from '@angular/core';
 +import {CommonModule} from '@angular/common';
 +
 +@Component({
 +  selector: 'boton',
 +  imports: [CommonModule],
 +  templateUrl: './boton.html',
 +  styleUrl: './boton.scss'
 +})
 +export class Boton  {
 +  @Input() funcion:'normal' | 'alternativa' | 'peligrosa'='normal';
 +  @Input() href:string="";
 +
 +}
 +
 +</sxh>
 +
 +<sxh html;highlight: [1,3]; title:boton.html>
 +  <a class="boton funcion--{{funcion}}" [attr.href]="href || null">
 +    <ng-content></ng-content>
 +  </a>
 +</sxh>
 +
 +Hay que destacar que además de usar ahora ''href'' también hemos cambiado la etiqueta a ''<a>''.
 +
 +Hemos puesto 
 +  [attr.href]="href || null"
 +en vez de simplemente 
 +  href={{ href }}
 +porque si no hay nada en href, no se añadirá el atributo
 +
 +===== Acción onClick =====
 +Queremos que se ejecute la función ''alerta()'' de nuestra aplicación al pulsar en el botón.
 +
 +
 +<sxh typescript;highlight: [14-16]; title:app.ts>
 +import { Component, signal } from '@angular/core';
 +import { RouterOutlet } from '@angular/router';
 + 
 + 
 +@Component({
 +  selector: 'app-root',
 +  imports: [RouterOutlet],
 +  templateUrl: './app.html',
 +  styleUrl: './app.scss'
 +})
 +export class App {
 +  protected readonly title = signal('prueba');
 + 
 +  alerta() : void {
 +    alert('Hola mundo');
 +  }
 +
 +}
 +</sxh>
 +
 +En el HTML lo indicaremos así
 +
 +<sxh html;highlight: [13]; title:app.html>
 +<style>
 +</style>
 +   
 +<h1 >Hola mundo</h1>
 +   
 +<button matButton="filled">
 +  Aceptar
 +</button>
 + 
 +<br>
 +<br>
 + 
 +<boton [funcion]="'peligrosa'" (onClick)="alerta()"  >Hola mundo</boton>
 +   
 +<router-outlet />
 +</sxh>
 +
 +
 +Para ello tenemos que hacer 3 cosas en el botón:
 +  * Decir que vamos a emitir un evento
 +  * Emitir el evento desde TypeScript 
 +  * Enlazar el click de ''<a>'' con el TS
 +
 +
 +
 +<sxh typescript;highlight: [1,13,15-17]; title:boton.ts>
 +import {Component, EventEmitter, Input, Output} from '@angular/core';
 +import {CommonModule} from '@angular/common';
 +
 +@Component({
 +  selector: 'boton',
 +  imports: [CommonModule],
 +  templateUrl: './boton.html',
 +  styleUrl: './boton.scss'
 +})
 +export class Boton  {
 +  @Input() funcion:'normal' | 'alternativa' | 'peligrosa'='normal';
 +  @Input() href:string="";
 +  @Output() onClick = new EventEmitter<void>();
 +
 +  handleOnClick(): void {
 +    this.onClick.emit();
 +  }
 +
 +}
 +
 +
 +</sxh>
 +
 +
 +
 +<sxh html;highlight: [1]; title:boton.html>
 +  <a class="boton funcion--{{funcion}}" [attr.href]="href || null" (click)="handleOnClick()">
 +    <ng-content></ng-content>
 +  </a>
 +</sxh>
 +
 +
 +
 +===== Filtros =====
 +Angular permite modificar los datos cuando se interpolan en el html.
 +
 +
 +  * [[https://angular.dev/api/common/CurrencyPipe|currency]] : Permite mostrar un número como una moneda. En nuestro caso mostrará el símbolo del "€"
 +
 +<sxh html>
 +<p>{{precio | currency}}</p>
 +</sxh>
 +
 +  * [[https://angular.dev/api/common/DatePipe|date]] : Permite formatear una fecha
 +
 +<sxh html>
 +<p>{{fechaCompra | date:'shortDate'}}</p>
 +</sxh>
 +
 +
 +
 +Los formatos predefinidos de fecha se pueden ver en [[https://angular.dev/api/common/DatePipe#pre-defined-format-options|Pre-defined format options]]
 +===== Ejercicios =====
 +
 +==== Ejercicio 1 ====
 +
 +Usando el elemento HTML de [[https://developer.mozilla.org/es/docs/Web/HTML/Reference/Elements/progress|<progress>]].
 +
 +<sxh html>
 +<label for="file">File progress:</label>
 +
 +<progress id="file" max="100" value="70">70%</progress>
 +</sxh>
 +
 +Crea el componente ''<progreso>''
 +<sxh html>
 +<progreso  [title]="'File progress:'" [porcentajeRealizado]="70" />
 +
 +<boton (onClick)="incrementar()">Incrementar 10</boton>
 +
 +</sxh>
 +
 +y que muestre:
 +
 +{{:clase:daw:diw:1eval:file_progress.png?400|}}
 +
 +Añade en la página un botón de forma que al pulsarlo se incremente en 10 el valor.
 +
 +<note tip>
 +Para generar los ''id'' se usa la librería uuid
 +
 +<sxh bash>
 +npm install uuid
 +npm install --save-dev @types/uuid
 +</sxh>
 +
 +
 +<sxh typescript>
 +import { v4 as uuidv4 } from 'uuid';
 +
 +class MiClase{
 +  uniqueId: string;
 +
 +  constructor() {
 +    this.uniqueId =uuidv4();
 +  }
 +}
 +</sxh>
 +
 +</note>
 +
 +==== Ejercicio 2 ====
 +Crea un componente llamado panel que permita hacer los siguientes paneles
 +
 +{{:clase:daw:diw:1eval:ejercicio_paneles.png?400|}}
 +
 +
 +Y crea una página HTML donde se vean los paneles.
 +
 +
 +==== Ejercicio 3 ====
 +
 +Crea ahora un componente para crear los siguientes botones:
 +
 +^    Importancia  ^^^
 +^  Función  |  Primaria  |  Secundaria  |  Terciaria  |
 +|  Normal  |  {{:clase:daw:diw:1eval:primary.png|}}  |  {{:clase:daw:diw:1eval:secondary.png|}}  |  {{:clase:daw:diw:1eval:tertiary.png|}}  |
 +|  Alternativa  |  {{:clase:daw:diw:1eval:alt-primary.png|}}  |  {{:clase:daw:diw:1eval:alt-secondary.png|}}  |  {{:clase:daw:diw:1eval:alt-tertiary.png|}}  |
 +|  Peligrosa  |  {{:clase:daw:diw:1eval:rojo-primary.png|}}  |  {{:clase:daw:diw:1eval:rojo-secondary.png|}}  |  {{:clase:daw:diw:1eval:rojo-tertiary.png|}}  |