Lab Introducción Angular - ArgentinaPrograma
Autor: Mg. Ing. Hernán Borré
657 |
658 |
Precondición:
Instalar nodejs y Visual Studio Code
659 |
Página Oficial de Angular (todo en inglés solamente): https://angular.io/
Ejemplo completo subido en https://github.com/hernanborre/mc-angular-tutorial
660 |
Creamos una carpeta nueva llamada “clase-angular”
- Creamos una carpeta nueva donde queramos: (yo la voy a llamar “clase-angular”
661 |
- Abrimos el VS Code y arrastramos y tiramos esta carpeta nueva en el VS Code
(deberías ver algo así)

662 |
Abrimos una nueva Terminal y ejecutamos:
npm -v
Si esto nos devuelve un número de versión en vez de un error, podemos continuar (sino fijate que puede estar fallando)
663 |
(ejemplo de npm
andando bien en tu Terminal )

664 |
- Ahora si, vamos a instalar Angular:
npm install -g @angular/cli
665 |
Luego para asegurarnos que Angular se instaló bien, ejecutamos
ng v
666 |
(debería aparecer algo así )

667 |
- Ahora tenemos que hacer dos cosas, una es crear un nuevo proyecto de angular y la otra es hacer
cd
y meternos dentro de la carpeta recién creada (esto es MUY importante sino no te van a andar los siguientes comandos). Entonces:
ng new mi-primera-app
Le damo y
’ a la opción del router y elegimos soloi CSS
en la segunda pregunta y listo!

(esperamos un rato que se cree todo, puede tardar y luego ejecutarmos)
cd mi-primera-app
(deberíamos tener algo así)

En la carpeta creada en el Explorer de VS Code, si abrís la nueva carpeta deberías ver varios archivos generados por angular!
668 |
Lo importante está dentro de la carpeta src
- Ahora ejecutemos la app a ver si está andando (puede tardar un poco, no te desesperes)
ng serve

669 |
Deberías ver algo similar a lo que hay acá arriba, además abrí en un browser de Chrome, la url que me indica Angular ( http://localhost:4200 )
670 |
- Vamos a agregar Bootstrap 5, así ya nos queda listo para usar
De dónde sacar Bootstrap 5?
Una de las opciones (la más rápida para practicar por ahora, es copiar y pegar los links a los CDN de Bootstrap 5, con lo cual, vamos a la página oficial y copiamos tanto los links a css como a los de js
https://getbootstrap.com/docs/5.0/getting-started/introduction/
671 |
(acordate que esto va pegado dentro de la etiqueta <head> de tu html principal
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
672 |
Esto va antes de cerrar la etiqueta </body> de tu html principal
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
673 |
Listo! ya tenemos Boostrap 5 en nuestro proyecto Angular integrado!
674 |
Listo! ahora podemos crear nuestro primer componente
675 |
Todo comienza en app.module.ts
y esto nos dice que todo apunta a que su primer y único componente por ahora es el “AppComponent” así que vamos a ver qué tiene!

676 |
Todo componente podría tener tres archivos principales (fijate más sobre esto en la página oficial de buenas prácticas y convenciones de Angular)
app.component.ts
app.component.html
app.component.css
677 |
Como te podrías imaginar, los de html y css son la vista (View) de nuestro componente llamado app en este caso! Y el código javascript (typescript) con la lógica y algunas cosas más, lo vamos a estar escribiendo en el archivo .ts
678 |
679 |
680 |
De la imagen anterior podemos inferir en 3 partes importantes que hacen a un componente en Angular y que se encuentran dentro del decorador @Component()
:
selector
, le dice a Angular que cree e inserte una instancia de este componente siempre que encuentre la etiqueta en el html. Por ejemplo, si el HTML de una aplicación contiene <app-root></app-root>, entonces Angular inserta una instancia de la vista HeaderComponent entre esas etiquetas.
templateUrl
, la dirección relativa al template HTML del componente. Alternativamente, se puede escribir código html.
stylesUrl
, la dirección relativa al archivo CSS del componente.
En pocas palabras, el selector es el nombre con el cual luego invocamos al componente en el html como se puede observar en el index.html en este caso!:

681 |
La manera más fácil y rápida de crear un componente es través de la consola de comandos, ayudados por angular cli!
ng generate component <nombre-del-componente> [opciones]
682 |
Entonces vamos a ejecutar en nuestra Terminal, estas dos líneas por separado
Una va a crear el componente personas y el otro el componente persona (ya te estarás imaginando qué va a hacer cada uno!
ng generate component componentes/personas --skip-tests=true
ng generate component componentes/persona --skip-tests=true
683 |
También vamos a crear una carpeta nueva dentro de src
que vamos a llamar models
y adentro vamos a crear un nuevo archivo llamado Persona.ts
En persona, vamos a definir la clase Persona y va a ser muy importante para poder usar desde la vista y empezar a separar la Vista del Model (Model / View / Controller) También deberíamos hacer un Servicio Persona para tener así el Controller pero por el momento lo vamos a dejar para más adelante
684 |
(debería quedar algo así)

685 |
Entonces dentro de Persona.ts
vamos a crear su clase!
export class Persona {
686 |
687 | nombre:string
688 | apellido: string
689 | edad: number
690 |
691 | constructor(nombre:string = "" , apellido:string = "", edad:number = 0 ){
692 | this.nombre = nombre
693 | this.apellido = apellido
694 | this.edad = edad
695 | }
696 |
697 | cumplirAnios(){
698 | this.edad++
699 | }
700 | }
701 |

Borramos absolutamente todo del app.component.html
y vamos a escribir nuestra etiqueta del nuevo componente creado de Personas!
En nuestro personas.component.html
vamos a escribir el siguiente código para mostrar una lista de personas usando la directiva de angular *ngFor
<div class="personas">
702 | <h3> Esta es nuestra lista de personas!</h3>
703 |
704 | <div *ngFor="let persona of personas">
705 | <h4>Nueva Persona</h4>
706 | <p>Nombre: {{persona.nombre}}</p>
707 | <p>Apellido: {{persona.apellido}}</p>
708 | <p>Edad: {{persona.edad}}</p>
709 | </div>
710 | </div>
711 |
También en el app.component.html
vamos a borrar todo y dejar solo el siguiente código (llamado al componente de personas y su selector definido
<div class="container">
712 | <app-personas></app-personas>
713 | </div>
714 |
Todavía esto no va a andar porque necesitamos definir en el archivo de lógica del componente de dónde está saliendo esa lista o array de personas
que le estamos pidiendo iterar en en el html, no?
715 |

716 |
Entonces en el personas.component.ts
vamos a importarlo y definirlo, quedando así
import { Component, OnInit } from '@angular/core';
717 | import { Persona } from 'src/app/models/Persona';
718 |
719 | @Component({
720 | selector: 'app-personas',
721 | templateUrl: './personas.component.html',
722 | styleUrls: ['./personas.component.css']
723 | })
724 | export class PersonasComponent implements OnInit {
725 |
726 | personas: Persona[] = []
727 |
728 | constructor() { }
729 |
730 | ngOnInit(): void {
731 | let persona1 = new Persona("Hernan", "Borre", 28 )
732 | this.personas.push(persona1)
733 | this.personas.push(new Persona("Alejandro", "Fantino", 55 ))
734 | this.personas.push(new Persona("Nicky", "Nicole", 22 ))
735 | }
736 |
737 | }
738 |
Genial ahora debería estar andando todo!!! 😊
739 |

740 |
Ahora vamos a componentizar más esto y a poner nuestra funcionalidad de mostrar una persona, dentro de su propio componente en singular persona.components.ts
Te animás a hacer solo/a???
741 |
Acá te debe estar fallando un truco y nos introduce a otra cosa importantísima de Angular que es cómo pasar information (props o properties
) de un componente padre a un hijo, eso es super fácil y se hace con el decorator de @Input
delante de la variable de clase creada en nuestro componente hijo y así cuando llamamos a nuestro selector desde el padre, solo hacemos referencia a ese nombre definido como Input
, mirá:
742 |
<div class="persona">
743 | <h6>Nueva Persona</h6>
744 | <p>Nombre: {{persona.nombre}}</p>
745 | <p>Apellido: {{persona.apellido}}</p>
746 | <p>Edad: {{persona.edad}}</p>
747 | </div>

748 |
import { Component, Input, OnInit } from '@angular/core';
749 | import { Persona } from 'src/app/models/Persona';
750 |
751 | @Component({
752 | selector: 'app-persona',
753 | templateUrl: './persona.component.html',
754 | styleUrls: ['./persona.component.css']
755 | })
756 | export class PersonaComponent implements OnInit {
757 |
758 | @Input() persona: Persona = new Persona()
759 |
760 | constructor() { }
761 |
762 | ngOnInit(): void {
763 | }
764 |
765 | }

766 |
Finalmente, nuestro componente padre personas.component.html
va a quedar así (usando los corchetes para indicar que ese elemento será “pasado” a su componente hijo

<div class="personas">
767 | <h3> Esta es nuestra lista de personas!</h3>
768 |
769 | <div *ngFor="let persona of personas">
770 | <app-persona [persona]="persona"></app-persona>
771 | </div>
772 | </div>
773 |
Cómo venis? Ya falta poco!!!
774 |
Ahora vamos a intentar hacer lo que se conoce como two-way binding y para esto vamos a usar un input de html y una cosa especial de angular que es combinar los corchetes y los paréntesis, entonces!
En persona.component.html,
agregamos el input
<input type="text" [(ngModel)]="persona.nombre">
(quedando así)

775 |
Y en el app.module.ts
necesitamos agregar dentro de los imports el FormsModule
así:

776 |
Ahora si! Deberíamos poder modificar y ver el cambio en vivo de los nombres de nuestras
personas con el input!!

Para capturar eventos de los elementos html, los ponemos entre paréntesis y de la manera que angular nos inidique en su documentación oficial.
Por ejemplo en el evento onClick
se captura con (click)
Creemos un botón para cada persona que cuando se clickee, llame a una función que a su vez llamará al método cumplirAnios() de la clase persona, es decir del objeto persona que estamos modificando. Y su edad se actualizará en vivo (reactividad)
777 |
- Agregamos en
persona.component.html
<button class="btn btn-primary mb-3" (click)="festejo(persona)" >Cumplir Años</button>

778 |
Y en persona.component.ts
el método que estamos llamando desde el html (festejo). Vale aclarar que le pasamos la persona como parámetro así, nuestra aplicación sabe exactamente a qué persona estamos queriendo modificar su atributo (estado).
779 |
festejo(persona:Persona) {
780 | persona.cumplirAnios()
781 | }

782 |
783 |
Bueno vamos por el Service! Lo vamos a crear dentro de la carpeta de services con la ayuda de angular, vamos a la Terminal de nuevo y ejecutamos:
ng generate service services/persona
Dentro de la nueva carpeta, services, en el archivo persona.service.ts
debería quedar algo así:
import { Injectable } from '@angular/core';
784 | import { Persona } from '../models/Persona';
785 |
786 | @Injectable({
787 | providedIn: 'root'
788 | })
789 | export class PersonaService {
790 |
791 | personas: Persona[] = []
792 | constructor() { }
793 |
794 | getAllPersonas(): Persona[] {
795 | let persona1 = new Persona("Hernan", "Borre", 28 )
796 | this.personas.push(persona1)
797 | this.personas.push(new Persona("Alejandro", "Fantino", 55 ))
798 | this.personas.push(new Persona("Nicky", "Nicole", 22 ))
799 |
800 | return this.personas
801 | }
802 |
803 | // solo un ejemplo de los servicios que se pueden ofrecer
804 | removePersona(persona:Persona):void {
805 | // TODO implement logic to remove a Persona
806 | }
807 | }
808 |
Entonces personas.component.ts
va a quedar así:
import { Component, OnInit } from '@angular/core';
809 | import { Persona } from 'src/app/models/Persona';
810 | import { PersonaService } from 'src/app/services/persona.service';
811 |
812 | @Component({
813 | selector: 'app-personas',
814 | templateUrl: './personas.component.html',
815 | styleUrls: ['./personas.component.css']
816 | })
817 | export class PersonasComponent implements OnInit {
818 |
819 | personas: Persona[] = []
820 |
821 | constructor(private personaService:PersonaService) { }
822 |
823 | ngOnInit(): void {
824 | this.getAllPersonas()
825 | // let persona1 = new Persona("Hernan", "Borre", 28 )
826 | // this.personas.push(persona1)
827 | // this.personas.push(new Persona("Alejandro", "Fantino", 55 ))
828 | // this.personas.push(new Persona("Nicky", "Nicole", 22 ))
829 | }
830 |
831 | getAllPersonas():void {
832 | this.personas = this.personaService.getAllPersonas()
833 | }
834 | }
835 |
836 |
‘Bueno nos queda el decorator de @Output
para pasar eventos y datos desde nuestros componentes hijos a nuestros padres
Vamos a borrar una persona
837 |
Esto involucra cuatro pasos, agregar el botón de Eliminar en el componente hijo, agregar el @Output
en el mismo componente hijo y luego hacer la definición del mismo para que emita el evento hacia los componentes padres.
Por último debemos capturar el evento desde el componente padre (personas) en este tutorial y así finalmente, definir el evento capturado y hacer lo necesario con él desde el padre - en este caso será eliminar de la lista de todas las persona, la seleccionada para ser eliminada
838 |
Veamos como queda todo el código, en orden de implementación:
839 |
persona.component.html
<div class="persona">
840 | <h6>Nueva Persona</h6>
841 | <input type="text" [(ngModel)]="persona.nombre">
842 | <p>Nombre: {{persona.nombre}}</p>
843 | <p>Apellido: {{persona.apellido}}</p>
844 | <p>Edad: {{persona.edad}}</p>
845 | <button class="btn btn-danger mb-3" (click)="borrarPersona(persona)" >Eliminar!</button>
846 | <button class="btn btn-primary mb-3" (click)="festejo(persona)" >Cumplir Años</button>
847 | </div>
848 |
persona.component.ts
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
849 | import { Persona } from 'src/app/models/Persona';
850 |
851 | @Component({
852 | selector: 'app-persona',
853 | templateUrl: './persona.component.html',
854 | styleUrls: ['./persona.component.css']
855 | })
856 | export class PersonaComponent implements OnInit {
857 |
858 | @Input() persona: Persona = new Persona()
859 | @Output() deletePersona:EventEmitter<Persona> = new EventEmitter()
860 |
861 | constructor() { }
862 |
863 | ngOnInit(): void {
864 | }
865 |
866 | festejo(persona:Persona) {
867 | persona.cumplirAnios()
868 | }
869 |
870 | borrarPersona(personaParaBorrar:Persona) {
871 | this.deletePersona.emit(personaParaBorrar)
872 | }
873 | }
874 |
personas.component.html
<div class="personas">
875 | <h3> Esta es nuestra lista de personas!</h3>
876 |
877 | <div *ngFor="let persona of personas">
878 | <app-persona [persona]="persona"
879 | (deletePersona)="borrarPersonaDeLista($event)"></app-persona>
880 | </div>
881 | </div>
882 |
personas.component.ts
import { Component, OnInit } from '@angular/core';
883 | import { Persona } from 'src/app/models/Persona';
884 | import { PersonaService } from 'src/app/services/persona.service';
885 |
886 | @Component({
887 | selector: 'app-personas',
888 | templateUrl: './personas.component.html',
889 | styleUrls: ['./personas.component.css']
890 | })
891 | export class PersonasComponent implements OnInit {
892 |
893 | personas: Persona[] = []
894 |
895 | constructor(private personaService:PersonaService) { }
896 |
897 | ngOnInit(): void {
898 | this.getAllPersonas()
899 | // let persona1 = new Persona("Hernan", "Borre", 28 )
900 | // this.personas.push(persona1)
901 | // this.personas.push(new Persona("Alejandro", "Fantino", 55 ))
902 | // this.personas.push(new Persona("Nicky", "Nicole", 22 ))
903 | }
904 |
905 | getAllPersonas():void {
906 | this.personas = this.personaService.getAllPersonas()
907 | }
908 |
909 | borrarPersonaDeLista(personaParaBorrar: Persona) {
910 | // con este truco borramos a la persona pero en realidad deberíamos
911 | // llamar al service
912 | this.personas = this.personas.filter(p => p.nombre !== personaParaBorrar.nombre)
913 | }
914 | }
915 |
Ahora si, deberíamos poder borrar personas desde nuestra lista en la página que hemos creado! 🙂