1. Introducción
Qué crearás
En este codelab, compilarás una app de vivienda con Angular. La app completada ofrecerá la posibilidad de ver fichas de casas en función de la búsqueda del usuario y de consultar los detalles de la ubicación de una vivienda.
Compilarás todo con Angular a través de las potentes herramientas de Angular y una excelente integración del navegador.
Esta es la app que compilarás hoy.
Qué aprenderás
- Cómo usar la CLI de Angular para crear el andamiaje de un proyecto nuevo
- Cómo usar componentes de Angular para compilar una interfaz de usuario
- Cómo compartir datos en componentes y otras partes de una app
- Cómo usar controladores de eventos en Angular
- Cómo implementar la app en Firebase Hosting con la CLI de Angular
Requisitos
- Conocimientos básicos de HTML, CSS, TypeScript (o JavaScript), Git y la línea de comandos
2. Configuración del entorno
Cómo configurar tu entorno local
Para completar este codelab, debes instalar el siguiente software en tu máquina local:
- Node versión ^12.20.2, ^14.15.5 o ^16.10.0.
- Editor de código: VS Code o cualquier otro editor de código que elijas
- Complemento del servicio de lenguaje angular para VS Code.
Instala la CLI de Angular
Una vez que todas tus dependencias estén configuradas, puedes instalar la CLI de Angular desde una ventana de línea de comandos en la computadora:
npm install -g @angular/cli
Para confirmar que tu configuración sea correcta, ejecuta este comando desde la línea de comandos de tu computadora:
ng –version
Si el comando funciona correctamente, encontrarás un mensaje similar al de la siguiente captura de pantalla.
Obtén el código
El código de este codelab contiene los pasos intermedios y la solución final en diferentes ramas. Para comenzar, descarga el código desde GitHub.
- Abra una nueva pestaña del navegador y vaya a
https://github.com/angular/introduction-to-angular
. - Desde una bifurcación de ventana de la línea de comandos, clona el repositorio y
cd introduction-to-angular/
en el repositorio. - En la rama de código de inicio, ingresa
git checkout get-started
. - Abre el código en tu editor de código preferido y abre la carpeta del proyecto
introduction-to-angular
. - Desde la ventana de línea de comandos, ejecuta
npm install
para instalar las dependencias necesarias a fin de ejecutar el servidor. - Para ejecutar el servidor web de Angular en segundo plano, abre una ventana de línea de comandos aparte y ejecuta
ng serve
para iniciar el servidor. - Abra una pestaña del navegador para
http://localhost:4200
.
Una vez que la app esté en funcionamiento, puedes comenzar a compilarla.
3. Cómo crear tu primer componente
Los componentes son los componentes fundamentales de las apps de Angular. Los componentes son ladrillos que se usan para la construcción. Un ladrillo no tiene mucha potencia, pero cuando se combina con otros ladrillos, puedes construir estructuras increíbles.
Lo mismo sucede con las apps compiladas con Angular.
Los componentes tienen 3 aspectos principales:
- Un archivo HTML para la plantilla.
- Un archivo CSS para los estilos
- Un archivo TypeScript para el comportamiento de la app
El primer componente que actualizarás es AppComponent
.
- Abre
app.component.html
en el editor de código. Este es el archivo de plantilla paraAppComponent
. - Borra todo el código de este archivo y reemplázalo por:
<main>
<header><img src="../assets/logo.svg" alt="fairhouse">Fairhouse</header>
<section>
</section>
</main>
- Guarda el código y revisa el navegador. Con el servidor de desarrollo en ejecución, los cambios se reflejarán en el navegador cuando los guardemos.
¡Felicitaciones! Actualizaste tu primera app de Angular correctamente. Busca más cosas increíbles por delante. Continuemos.
A continuación, agregarás un campo de texto para la búsqueda y un botón a la IU.
Los componentes tienen muchos beneficios, y uno es la capacidad de organizar la IU. Vas a crear un componente que contenga el campo de texto, el botón de búsqueda y, finalmente, la lista de ubicaciones.
Para crear este componente nuevo, usarás la CLI de Angular. La CLI de Angular es el conjunto de herramientas de línea de comandos que ayudan con la estructura, la implementación y mucho más.
- En la línea de comandos, ingresa lo siguiente:
ng generate component housing-list
Estas son las partes de este comando:
ng
es la CLI de Angular.- El comando genera el tipo de acción que se debe realizar. En este caso, genera un andamiaje para algo.
- El componente representa lo que queremos crear.
- La lista de viviendas es el nombre del componente.
- Luego, agrega el componente nuevo a la plantilla
AppComponent
. Enapp.component.html
, actualiza el código de la plantilla:
<main>
<header><img src="../assets/logo.svg" alt="fairhouse">Fairhouse</header>
<section>
<app-housing-list></app-housing-list>
</section>
</main>
- Guarda todos los archivos y regresa al navegador para confirmar que se muestra el mensaje
housing-list works
. - En el editor de código, navega a
housing-list.component.html
, quita el código existente y reemplázalo por:
<label for="location-search">Search for a new place</label>
<input id="location-search" placeholder="Ex: Chicago"><button>Search</button>
- En
housing-list.component.css
, agrega los siguientes estilos:
input, button {
border: solid 1px #555B6E;
border-radius: 2px;
display: inline-block;
padding: 0;
}
input {
width: 400px;
height: 40px;
border-radius: 2px 0 0 2px;
color: #888c9c;
border: solid 1px #888c9c;
padding: 0 5px 0 10px;
}
button {
width: 70px;
height: 42px;
background-color: #4468e8;
color: white;
border: solid 1px #4468e8;
border-radius: 0 2px 2px 0;
}
article {
margin: 40px 0 10px 0;
color: #202845;
}
article, article > p {
color: #202845;
}
article> p:first-of-type {
font-weight: bold;
font-size: 22pt;
}
img {
width: 490px;
border-radius: 5pt;
}
label {
display: block;
color: #888c9c;
}
- Guarda los archivos y, luego, regresa al navegador.
Nuestra app de Angular está comenzando a tomar forma.
4. Manejo de eventos
La app tiene un campo de entrada y un botón, pero le falta la interacción. Por lo general, en la Web interactúas con los controles y, además, invocas el uso de eventos y controladores de eventos. Usarás esta estrategia para compilar tu app.
Se realizarán estos cambios en housing-list.component.html
.
Para agregar un controlador de clics, debes agregar el objeto de escucha de eventos al botón. En Angular, la sintaxis implica rodear el nombre del evento entre paréntesis y asignarle un valor. Aquí, asigna un nombre al método al que se llama cuando se hace clic en el botón. Llamémoslo searchHousingLocations
. No olvides agregar el paréntesis al final de este nombre de función para llamarlo.
- Actualiza el código del botón para que coincida con este código:
<button (click)="searchHousingLocations()">Search</button>
- Guarda este código y revisa el navegador. Ahora se muestra un error de compilación:
La app muestra este error porque el método searchHousingLocations
no existe, por lo que deberás cambiarlo.
- En
housing-list.component.ts
, agrega un nuevo método al final del cuerpo de la claseHousingListComponent
:
searchHousingLocations() {}
En breve, completarás los detalles de este método.
- Guarda este código para actualizar el navegador y resolver el error.
El próximo paso es obtener el valor del campo de entrada y pasarlo como argumento al método searchHousingLocations
. Usarás una función de Angular llamada variable de plantilla, que proporciona una manera de obtener una referencia a un elemento en una plantilla y también interactuar con ella.
- En
housing-list.component.html
, agrega un atributo llamadosearch
, con un hashtag como prefijo en la entrada.
<label for="location-search">Search for a new place</label>
<input id="location-search" #search><button (click)="searchHousingLocations()">Search</button>
Ahora, tenemos una referencia a la entrada. También tenemos acceso a la propiedad .value
de la entrada.
- Pasa el valor de la entrada al método
searchHousingLocations
,
<input id="location-search" #search><button (click)="searchHousingLocations(search.value)">Search</button>
Hasta ahora, pasaste el valor como parámetro, pero actualicemos el método para usarlo. En este momento, el parámetro se usa en un comando console.log
; más adelante, se usa como parámetro de búsqueda.
- En
housing-list.component.ts
, agrega este código:
searchHousingLocations(searchText: string) {
console.log(searchText);
}
- Guarde el código y, en el navegador, abra las Herramientas para desarrolladores de Chrome y navegue hasta la pestaña Console. Ingrese cualquier valor en la entrada. Seleccione Search y verifique que el valor aparezca en la pestaña Console de las Herramientas para desarrolladores de Chrome.
Agregaste correctamente un controlador de eventos, y tu app puede recibir entradas de los usuarios.
5. Resultados de la búsqueda
El siguiente paso consiste en mostrar resultados basados en las entradas del usuario. Cada ubicación tiene propiedades de strings para nombre, ciudad, estado, foto, una propiedad numérica para availableUnits y dos propiedades booleanas para lavandería y Wi-Fi:
name: "Location One",
city: "Chicago",
state: "IL",
photo: "/path/to/photo.jpg",
availableUnits: 4,
wifi: true,
laundry: true
Puedes representar estos datos como un objeto de JavaScript simple, pero es mejor usar la compatibilidad con TypeScript en Angular. Usa tipos para ayudar a evitar errores durante el tiempo de compilación.
Podemos usar tipos para definir las características de los datos, que también se conocen como tipos de datos. En TypeScript, las interfaces se usan comúnmente para este fin. Creemos una interfaz que represente los datos de ubicación de nuestras viviendas. En la terminal del editor, usa la CLI de Angular para crear un tipo de HousingLocation.
- Para ello, ingresa lo siguiente:
ng generate interface housing-location
- En
housing-location.ts
, agrega los detalles del tipo para nuestra interfaz. Asigna el tipo adecuado a cada propiedad en función de nuestro diseño:
export interface HousingLocation {
name: string,
city: string,
state: string,
photo: string,
availableUnits: number,
wifi: boolean,
laundry: boolean,
}
- Guarda el archivo y abre
app.component.ts
. - Para crear un array que contenga datos que representen las ubicaciones de las viviendas, importa la interfaz de esas ubicaciones de
./housing-location
.
import { HousingLocation } from './housing-location';
- Actualiza la clase
AppComponent
para incluir una propiedad llamadahousingLocationList
de tipoHousingLocation[]
. Propaga el array con los siguientes valores:
housingLocationList: HousingLocation[] = [
{
name: "Acme Fresh Start Housing",
city: "Chicago",
state: "IL",
photo: "../assets/housing-1.jpg",
availableUnits: 4,
wifi: true,
laundry: true,
},
{
name: "A113 Transitional Housing",
city: "Santa Monica",
state: "CA",
photo: "../assets/housing-2.jpg",
availableUnits: 0,
wifi: false,
laundry: true,
},
{
name: "Warm Beds Housing Support",
city: "Juneau",
state: "AK",
photo: "../assets/housing-3.jpg",
availableUnits: 1,
wifi: false,
laundry: false,
}
];
No es necesario crear una instancia de instancias nuevas de una clase para obtener objetos; podemos aprovechar la información de tipo que proporciona la interfaz. Los datos de nuestros objetos deben ser de la misma forma; es decir, deben coincidir con las propiedades definidas en la interfaz.
Los datos se almacenan en app.component.ts
, pero debemos compartirlos con otros componentes. Una solución es usar servicios en Angular, pero para reducir la complejidad de la app, usaremos el decorador de entrada proporcionado por Angular. El decorador de entrada permite que un componente reciba un valor de una plantilla. Lo usarás para compartir el array de housingLocationList
con HousingListComponent
.
- En
housing-list.component.ts
, importainput
de@angular/core
yHousingLocation
de./housingLocation
.
import { Component, OnInit, Input } from '@angular/core';
import {HousingLocation } from '../housing-location';
- Crea una propiedad llamada
locationList
en el cuerpo de la clase del componente. UsarásInput
como decorador paralocationList
.
export class HousingListComponent implements OnInit {
@Input() locationList: HousingLocation[] = [];
...
}
El tipo de esta propiedad se establece en HousingLocation[]
.
- En
app.component.html
, actualiza el elementoapp-housing-list
para que incluya un atributo llamadolocationList
y establece el valor enhousingLocationList
.
<main>
...
<app-housing-list [locationList]="housingLocationList"></app-housing-list>
</main>
El atributo locationList
debe encerrarse entre corchetes ( [ ]
) para que Angular pueda vincular dinámicamente el valor de la propiedad locationList con una variable o expresión. De lo contrario, Angular trata el valor a la derecha del signo igual como una string.
Si recibe algún mensaje de error en este punto, compruebe lo siguiente:
- La ortografía del nombre del atributo de entrada coincide con la ortografía en la propiedad en la clase TypeScript. El caso también es importante aquí.
- El nombre de la propiedad, en el lado derecho del signo igual, está escrito correctamente.
- La propiedad de entrada se encierra entre corchetes.
Se completó la configuración de uso compartido de datos. El siguiente paso consiste en mostrar los resultados en el navegador. Dado que los datos están en formato de arreglo, necesitamos usar una función de Angular que te permita repetir datos y repetir elementos en plantillas, *ngFor
.
- En
housing-list.component.html
, actualiza el elemento del artículo en la plantilla para usar*ngFor
de modo que puedas mostrar las entradas del array en el navegador:
<article *ngFor="let location of locationList"></article>
El valor asignado al atributo ngFor
es la sintaxis de la plantilla de Angular. Crea una variable local en la plantilla. Angular utiliza la variable local en el alcance del elemento article
entre las etiquetas de apertura y de cierre.
Para obtener más información sobre ngFor y la sintaxis de plantilla, consulta la documentación de Angular.
ngFor
repite un elemento de artículo para cada entrada del arreglo locationList
. A continuación, mostrarás los valores de la variable de ubicación.
- Actualiza la plantilla para agregar un elemento de párrafo (
<p>
). El elemento secundario del elemento de párrafo es un valor interpolado de la propiedad de ubicación:
<input #search><button (click)="searchHousingLocations(search.value)">Search</button>
<article *ngFor="let location of locationList">
<p>{{location.name}}</p>
</article>
En las plantillas de Angular, puedes usar la interpolación de texto para mostrar valores con la sintaxis de llave doble ( {{ }}
).
- Guarde y regrese al navegador. Ahora, la app mostrará una etiqueta para cada entrada de arreglo en el arreglo
locationList
.
Los datos se comparten desde el componente de la app hasta el componente de la lista de viviendas, y estamos iterando sobre cada uno de esos valores para mostrarlos en el navegador.
Acabamos de analizar algunas formas de compartir datos entre los componentes. Ahora utilizamos la nueva sintaxis de la plantilla y la directiva ngFor.
6. Filtrar resultados de la búsqueda
Actualmente, la app muestra todos los resultados en lugar de los basados en la búsqueda del usuario. Para cambiar eso, debes actualizar el HousingListComponent
de modo que la app pueda funcionar según lo previsto.
- En
housing-list.component.ts
, actualiza elHousingListComponent
para tener una nueva propiedad llamadaresults
que sea del tipoHousingLocation[]
:
export class HousingListComponent implements OnInit {
@Input() locationList: HousingLocation[] = [];
results: HousingLocation[] = [];
...
El array de resultados representa las ubicaciones de viviendas que coinciden con la búsqueda del usuario. El siguiente paso es actualizar el método searchHousingLocations
para filtrar los valores.
- Quita el
console.log
y actualiza el código para asignar la propiedad de resultados al resultado de filtrarlocationList
, filtrado porsearchText
:
searchHousingLocations(searchText: string) {
this.results = this.locationList.filter(
(location: HousingLocation) => location.city
.toLowerCase()
.includes(
searchText.toLowerCase()
));
}
En este código, usamos el método de filtro de arreglos y solo aceptamos valores que contengan searchText
. Todos los valores se comparan con las versiones en minúsculas de las strings.
Dos puntos a tener en cuenta:
- Se debe usar el prefijo
this
cuando se hace referencia a propiedades de una clase dentro de los métodos. Por eso, usamosthis.results
ythis.locationList
. - La función de búsqueda aquí solo coincide con la propiedad de ciudad de una ubicación, pero puedes actualizar el código para incluir más propiedades.
Aunque este código funciona tal como está, puedes mejorarlo.
- Actualiza el código para evitar buscar en el arreglo si
searchText
está en blanco:
searchHousingLocations(searchText: string) {
if (!searchText) return;
...
}
El método se actualizó y hay un cambio de plantilla que debes hacer antes de que los resultados se muestren en el navegador.
- En
housing-location.component.html
, reemplazalocationList
porresults
enngFor
:
<article *ngFor="let location of results">...</article>
- Guarde el código y regrese al navegador. Con la entrada, busca una ubicación a partir de los datos de muestra (por ejemplo, Chicago).
La app solo muestra los resultados que coinciden:
Acabas de completar la funcionalidad adicional necesaria para vincular completamente la entrada del usuario con los resultados de la búsqueda. La app está casi completa.
A continuación, verás más detalles sobre la app para finalizarla.
7. Mostrar los detalles
La app debe admitir que se haga clic en un resultado de la búsqueda y que se muestre la información en un panel de detalles. HousingListComponent
sabe en qué resultado se hizo clic desde que se muestran los datos en ese componente. Necesitamos una forma de compartir los datos de HousingListComponent
con el componente superior AppComponent
.
En Angular, @Input()
envía datos de superior a secundario, mientras que @Output()
permite que los componentes envíen un evento con datos del elemento secundario a su componente superior. El decorador de salida usa un EventEmitter
para notificar a los objetos de escucha sobre cualquier evento. En este caso, desea emitir un evento que represente el clic de un resultado de la búsqueda. Junto con el evento de selección, quieres enviar el elemento seleccionado como parte de la carga útil.
- En
housing-list.component.ts
, actualiza elimport
para incluirOutput
yEventEmitter
de@angular/core
yHousingLocation
desde su ubicación:
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { HousingLocation } from '../housing-location';
- En el cuerpo de
HousingListComponent
, actualiza el código para agregar una nueva propiedad llamadalocationSelectedEvent
de tipoEventEmitter<HousingLocation>();
:
@Output() locationSelectedEvent = new EventEmitter<HousingLocation>();
La propiedad locationSelectedEvent
está decorada con @Output()
, que forma parte de la API de este componente. Con EventEmitter
, aprovechas la API de elementos genéricos para la clase y le proporcionas el tipo HousingLocation
. Cuando locationSelectedEvent
emite un evento, los objetos de escucha de este pueden esperar que los datos correspondientes sean del tipo HousingLocation
. Esta es la seguridad de tipo que respalda nuestro desarrollo y reduce la posibilidad de algunos errores.
Debemos activar el locationSelectedEvent
cada vez que un usuario haga clic en una ubicación de la lista.
- Actualiza
HousingListComponent
para agregar un método nuevo llamadoselectLocation
que acepte un valor de tipohousingLocation
como parámetro:
selectHousingLocation(location: HousingLocation) { }
- En el cuerpo del método, emite un evento nuevo desde el emisor
locationSelectedEvent
. El valor que se emite es la ubicación que selecciona el usuario.
selectHousingLocation(location: HousingLocation) {
this.locationSelectedEvent.emit(location);
}
Vinculemos esto a la plantilla.
- En
housing-list-component.html
, actualiza el elemento de artículo para tener un nuevo elemento secundario del botón con unclick event
. Este evento llama al métodoselectHousingLocation
en la clase TypeScript y pasa una referencia allocation
en el que se hizo clic como argumento.
<article *ngFor="let location of results" >
<p>{{location.name}}</p>
<button (click)="selectHousingLocation(location)">View</button>
</article>
Las ubicaciones de las viviendas ahora tienen un botón en el que se puede hacer clic y se pasan los valores de nuevo al componente.
El último paso de este proceso es actualizar AppComponent
para escuchar el evento y actualizar la pantalla según corresponda.
- En
app.component.html
, actualiza el elementoapp-housing-list
para escuchar el elementolocationSelectedEvent
y controlar el evento con el métodoupdateSelectedLocation
:
<app-housing-list [locationList]="housingLocationList" (locationSelectedEvent)="updateSelectedLocation($event)"></app-housing-list>
Angular proporciona la $event
cuando se trata de los controladores de eventos en las plantillas. El argumento $event es un objeto de tipo HousingLocation
porque establecimos ese parámetro para el tipo EventEmitter
. Angular se encarga de todo esto por ti. Solo debe confirmar que sus plantillas sean correctas.
- En
app.component.ts
, actualiza el código para incluir una nueva propiedad llamadaselectedLocation
de tipoHousingLocation | undefined
.
selectedLocation: HousingLocation | undefined;
Esto utiliza una función de TypeScript llamada Union Type. Las uniones permiten que las variables acepten uno de varios tipos. En este caso, deseas que el valor de selectedLocation
sea HousingLocation
o undefined
porque no especificas un valor predeterminado para selectedLocation
.
Debes implementar updateSelectedLocation
.
- Agrega un nuevo método llamado
updateSelection
con un parámetro llamadolocation
y con un tipo deHousingLocation
.
updateSelectedLocation(location: HousingLocation) { } searchHousingLocations() {}
- En el cuerpo del método, establece el valor de
selectedLocation
para que sea el parámetrolocation
:
updateSelectedLocation(location: HousingLocation) {
this.selectedLocation = location;
}
Con esta parte completa, el último paso es actualizar la plantilla para mostrar la ubicación seleccionada.
- En
app.component.html
, agrega un nuevo elemento<article>
que usaremos para mostrar las propiedades de la ubicación seleccionada. Actualiza la plantilla con el siguiente código:
<article>
<img [src]="selectedLocation?.photo">
<p>{{selectedLocation?.name}}</p>
<p>{{selectedLocation?.availableUnits}}</p>
<p>{{selectedLocation?.city}}, {{selectedLocation?.state}}</p>
<p>{{selectedLocation?.laundry ? "Has laundry" : "Does Not have laundry"}}</p>
<p>{{selectedLocation?.wifi ? "Has wifi" : "Does Not have wifi"}}</p>
</article>
Dado que selectedLocation
puede ser undefined
, usa el operador de encadenamiento opcional para recuperar los valores de la propiedad. Además, usas sintaxis ternaria para los valores booleanos wifi
y laundry
. Esto brinda la oportunidad de mostrar un mensaje personalizado, según el valor.
- Guarda el código y revisa el navegador. Busca una ubicación y haz clic en una para ver los detalles:
Se ve increíble, pero aún queda un problema por resolver. Cuando la página se carga inicialmente, hay algunos artefactos de texto del panel de detalles que no se deben mostrar. Angular tiene algunas maneras de mostrar condicionalmente contenido que usarás en el paso siguiente.
Por ahora, muéstrale cuánto has alcanzado la app. Esto es lo que implementaste hasta ahora:
- Puedes compartir datos de los componentes secundarios con los superiores mediante el decorador de salida y el EventEmitter.
- También permitiste correctamente que tus usuarios ingresen un valor y realicen búsquedas con ese valor.
- La app puede mostrar los resultados de la búsqueda y los usuarios pueden hacer clic para ver más detalles.
Hasta ahora, ese es un excelente trabajo. Actualicemos las plantillas y completemos la app.
8. Optimiza las plantillas
Actualmente, la IU contiene artefactos de texto del panel de detalles que deben mostrarse de forma condicional. Usaremos dos funciones de Angular: ng-container
y *ngIf
.
Si aplicas directamente la directiva ngIf
al elemento article
, se generará un cambio de diseño cuando el usuario realice la primera selección. Para mejorar esta experiencia, puedes unir los detalles de la ubicación en otro elemento que sea un elemento secundario de article
. Este elemento no tiene estilo ni función, y solo agrega peso al DOM. Para evitar que esto suceda, puedes usar ng-container
. Puedes aplicarle directivas, pero no se mostrará en el DOM final.
- En
app.component.html
, actualiza el elementoarticle
para que coincida con este código:
<article>
<ng-container>
<img [src]="selectedLocation?.photo">
<p>{{selectedLocation?.name}}</p>
<p>{{selectedLocation?.city}}, {{selectedLocation?.state}}</p>
<p>Available Units: {{selectedLocation?.availableUnits}}</p>
<p>{{selectedLocation?.laundry ? "Has laundry" : "Does Not have laundry"}}</p>
<p>{{selectedLocation?.wifi ? "Has wifi" : "Does Not have wifi"}}</p>
</ng-container>
</article>
- A continuación, agrega el atributo
*ngIf
al elementong-container
. El valor debe serselectedLocation
.
<article>
<ng-container *ngIf="selectedLocation">
...
</ng-container>
</article>
Ahora, la app solo muestra el contenido del elemento ng-container
si selectedLocation
es Truthy.
- Guarda este código y confirma que el navegador ya no muestre los artefactos de texto cuando se cargue la página.
Hay una última actualización que podemos hacer en nuestra app. Los resultados de la búsqueda en housing-location.component.html
deben mostrar más detalles.
- En
housing-location.component.html
, actualiza el código a lo siguiente:
<label for="location-search">Search for a new place</label>
<input id="location-search" #search placeholder="Ex: Chicago"><button
(click)="searchHousingLocations(search.value)">Search</button>
<article *ngFor="let location of results" (click)="selectHousingLocation(location)">
<img [src]="location.photo" [alt]="location.name">
<p>{{location.name}}</p>
<p>{{location.city}}, {{location.state}}</p>
<button (click)="selectHousingLocation(location)">View</button>
</article>
- Guarda el código y regresa al navegador para revelar la app completada.
Ahora, la app se ve muy bien y es completamente funcional. ¡Bien hecho!
9. Felicitaciones
Gracias por realizar este viaje y usar Angular para crear Fairhouse.
Creaste una interfaz de usuario con Angular. Con la CLI de Angular, creaste interfaces y componentes. Luego, usaste las potentes funciones de plantilla en Angular a fin de compilar una app funcional que muestra imágenes, controla eventos y mucho más.
Próximos pasos
Si quieres seguir compilando funciones, estas son algunas ideas:
- Los datos están codificados en la app. Una gran refactorización es agregar un servicio para contener los datos.
- La página de detalles se muestra actualmente en la misma página, pero sería bueno mover los detalles a su propia página y aprovechar el enrutamiento de Angular.
- Otra actualización sería alojar los datos en un extremo de reposo y usar el paquete HTTP en Angular para cargar los datos en el entorno de ejecución.
Hay muchas oportunidades para divertirse.