Introducción a Angular

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:

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.

Resultado de la CLI de Angular que muestra la versión de Angular

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.

  1. Abra una nueva pestaña del navegador y vaya a https://github.com/angular/introduction-to-angular.
  2. Desde una bifurcación de ventana de la línea de comandos, clona el repositorio y cd introduction-to-angular/ en el repositorio.
  3. En la rama de código de inicio, ingresa git checkout get-started.
  4. Abre el código en tu editor de código preferido y abre la carpeta del proyecto introduction-to-angular.
  5. Desde la ventana de línea de comandos, ejecuta npm install para instalar las dependencias necesarias a fin de ejecutar el servidor.
  6. 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.
  7. 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.

  1. Abre app.component.html en el editor de código. Este es el archivo de plantilla para AppComponent.
  2. 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>
  1. 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.

  1. 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.
  1. Luego, agrega el componente nuevo a la plantilla AppComponent. En app.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>
  1. Guarda todos los archivos y regresa al navegador para confirmar que se muestra el mensaje housing-list works.
  2. 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>
  1. 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;
}

  1. Guarda los archivos y, luego, regresa al navegador. La app ahora tiene un cuadro de búsqueda y un botón

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.

  1. Actualiza el código del botón para que coincida con este código:
<button (click)="searchHousingLocations()">Search</button>
  1. Guarda este código y revisa el navegador. Ahora se muestra un error de compilación:

46a528b5ddbc7ef8.png

La app muestra este error porque el método searchHousingLocations no existe, por lo que deberás cambiarlo.

  1. En housing-list.component.ts, agrega un nuevo método al final del cuerpo de la clase HousingListComponent:
 searchHousingLocations() {}

En breve, completarás los detalles de este método.

  1. 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.

  1. En housing-list.component.html, agrega un atributo llamado search, 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.

  1. 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.

  1. En housing-list.component.ts, agrega este código:
 searchHousingLocations(searchText: string) {
   console.log(searchText);
 }
  1. 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.

Resultado de la búsqueda de coincidencias de la consola de las Herramientas para desarrolladores de Chrome desde la IU

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.

  1. Para ello, ingresa lo siguiente:
ng generate interface housing-location
  1. 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,
}
  1. Guarda el archivo y abre app.component.ts.
  2. 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';
  1. Actualiza la clase AppComponent para incluir una propiedad llamada housingLocationList de tipo HousingLocation[]. 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.

  1. En housing-list.component.ts, importa input de @angular/core y HousingLocation de ./housingLocation.
import { Component, OnInit, Input } from '@angular/core';
import {HousingLocation } from '../housing-location';
  1. Crea una propiedad llamada locationList en el cuerpo de la clase del componente. Usarás Input como decorador para locationList.
export class HousingListComponent implements OnInit {

  @Input() locationList: HousingLocation[] = [];
  ...
}

El tipo de esta propiedad se establece en HousingLocation[].

  1. En app.component.html, actualiza el elemento app-housing-list para que incluya un atributo llamado locationList y establece el valor en housingLocationList.
<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.

  1. 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.

  1. 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 ( {{ }}).

  1. Guarde y regrese al navegador. Ahora, la app mostrará una etiqueta para cada entrada de arreglo en el arreglo locationList.

ficha de 3 ubicaciones de viviendas mostradas

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.

  1. En housing-list.component.ts, actualiza el HousingListComponent para tener una nueva propiedad llamada results que sea del tipo HousingLocation[]:
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.

  1. Quita el console.log y actualiza el código para asignar la propiedad de resultados al resultado de filtrar locationList, filtrado por searchText:
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, usamos this.results y this.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.

  1. 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.

  1. En housing-location.component.html, reemplaza locationList por results en ngFor:
<article *ngFor="let location of results">...</article>
  1. 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:

resultados de la búsqueda que coinciden con el texto ingresado en el campo de búsqueda

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.

  1. En housing-list.component.ts, actualiza el import para incluir Output y EventEmitter de @angular/core y HousingLocation desde su ubicación:
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { HousingLocation } from '../housing-location';
  1. En el cuerpo de HousingListComponent, actualiza el código para agregar una nueva propiedad llamada locationSelectedEvent de tipo EventEmitter<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.

  1. Actualiza HousingListComponent para agregar un método nuevo llamado selectLocation que acepte un valor de tipo housingLocation como parámetro:
selectHousingLocation(location: HousingLocation) { }
  1. 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.

  1. En housing-list-component.html, actualiza el elemento de artículo para tener un nuevo elemento secundario del botón con un click event. Este evento llama al método selectHousingLocation en la clase TypeScript y pasa una referencia al location 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.

  1. En app.component.html, actualiza el elemento app-housing-list para escuchar el elemento locationSelectedEvent y controlar el evento con el método updateSelectedLocation:
<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.

  1. En app.component.ts, actualiza el código para incluir una nueva propiedad llamada selectedLocation de tipo HousingLocation | 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.

  1. Agrega un nuevo método llamado updateSelection con un parámetro llamado location y con un tipo de HousingLocation.
updateSelectedLocation(location: HousingLocation) { } searchHousingLocations() {}
  1. En el cuerpo del método, establece el valor de selectedLocation para que sea el parámetro location:
updateSelectedLocation(location: HousingLocation) {
  this.selectedLocation = location;
}

Con esta parte completa, el último paso es actualizar la plantilla para mostrar la ubicación seleccionada.

  1. 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.

  1. Guarda el código y revisa el navegador. Busca una ubicación y haz clic en una para ver los detalles:

diseño de dos columnas; hacia la izquierda se muestran los resultados de la búsqueda, hacia la derecha que muestran los detalles de ubicación seleccionados

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.

IU predeterminada con artefactos que se muestran de manera incorrecta en la pantalla

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.

  1. En app.component.html, actualiza el elemento article 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>
  1. A continuación, agrega el atributo *ngIf al elemento ng-container. El valor debe ser selectedLocation.
<article>
  <ng-container *ngIf="selectedLocation">
  ...
  </ng-container>
</article>

Ahora, la app solo muestra el contenido del elemento ng-container si selectedLocation es Truthy.

  1. 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.

  1. 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>
  1. Guarda el código y regresa al navegador para revelar la app completada.

aplicación de dos columnas: el lado izquierdo muestra los resultados de la búsqueda, el lado derecho muestra los detalles del resultado de la búsqueda seleccionado

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.