1. Introduction
What you'll build
In this codelab, you'll build a housing app with Angular. The completed app will feature the ability to view home listings based on user search, and view details of a housing location.
You'll build everything with Angular using Angular's powerful tooling and great browser integration.
This is the app you'll be building today
What you'll learn
- How to use the Angular CLI to scaffold a new project.
 - How to use Angular components to build a user interface.
 - How to share data in components and other parts of an app.
 - How to use event handlers in Angular.
 - How to deploy the app to Firebase Hosting using the Angular CLI.
 
What you'll need
- Basic knowledge of HTML, CSS, TypeScript (or JavaScript), Git, and the command line.
 
2. Environment setup
Set up your local environment
To complete this codelab, you need the following software installed on your local machine:
- Node version ^12.20.2, ^14.15.5 , or ^16.10.0.
 - Code Editor - VS Code or another code editor of your choice.
 - Angular Language Service Plugin for VS Code.
 
Install the Angular CLI
Once all of your dependencies are configured, you can install the Angular CLI from a command-line window on your computer:
npm install -g @angular/cli
To confirm that your configuration is correct, run this command from your computer's command line:
ng –version
If the command works successfully, you'll find a message similar to the screenshot below.

Get the code
The code for this codelab contains the intermediate steps and the final solution in different branches. To get started, download the code from GitHub
- Open a new browser tab and go to  
https://github.com/angular/introduction-to-angular. - From a command-line window fork and clone the repository and 
cd introduction-to-angular/into the repository. - From the starter code branch, enter 
git checkout get-started. - Open the code in your preferred code editor, and open the 
introduction-to-angularproject folder. - From the command-line window, run 
npm installto install the dependencies required to run the server. - To run the  Angular web server in the background, open a separate command-line window and run 
ng serveto start the server. - Open a browser tab to 
http://localhost:4200. 
With the app up and running, you can start building the Fairhouse app.
3. Create your first component
Components are the core building blocks for Angular apps. Think of components as bricks used for construction. When starting out, a brick doesn't have much power, but when combined with other bricks you can build amazing structures.
The same goes for apps built with Angular.
Components have 3 main aspects:
- An HTML file for the template.
 - A CSSfile for the styles.
 - A TypeScript file for the behavior of the app.
 
The first component you're going to update is AppComponent.
- Open 
app.component.htmlin your code editor; this is the template file for theAppComponent. - Delete all the code in this file and replace it with this:
 
<main>
  <header><img src="../assets/logo.svg" alt="fairhouse">Fairhouse</header>
  <section>
  </section>
</main>
- Save the code and check the browser. With the development server running, the changes are reflected in the browser when we save.
 
Congratulations, you successfully updated your first Angular app. Look for more great things ahead. Let's continue.
Next, you'll add a text field for search, and a button to the UI.
Components have many benefits, with one being the ability to organize the UI. You're going to create a component that contains the text field, the button for search, and eventually the list of locations.
To create this new component, you'll use the Angular CLI. The Angular CLI is the set of command-line tools that help with scaffolding, deployment, and more.
- From the command line, enter:
 
ng generate component housing-list
Here are the parts of this command:
ngis the Angular CLI.- The command generates the type of action to perform. In this case, generate scaffolding for something.
 - The component represents the "what" we want to create.
 - housing-list is the name of the component.
 
- Next, add the new component to the 
AppComponenttemplate. Inapp.component.html, update the template code: 
<main>
  <header><img src="../assets/logo.svg" alt="fairhouse">Fairhouse</header>
 <section>
   <app-housing-list></app-housing-list>
 </section>
</main>
- Save all files and return to the browser to confirm that the message 
housing-list worksis displayed. - In your code editor, navigate to 
housing-list.component.html, remove the existing code, and replace it with: 
<label for="location-search">Search for a new place</label>
<input id="location-search" placeholder="Ex: Chicago"><button>Search</button>
- In 
housing-list.component.css, add the following styles: 
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;
}
- Save the files, then return to the browser. 

 
Our Angular app is starting to take shape.
4. Event Handling
The app has an input field and button but it is missing the interaction. On the web, you typically interact with controls and invoke the use of events and event handlers. You'll use this strategy to build your app.
You'll make these changes in housing-list.component.html.
To add a click handler, you'll need to add the event listener to the button. In Angular, the syntax is to surround the name of the event in parentheses and assign it a value. Here, you name the method that is called when the button is clicked. Let's call it searchHousingLocations. Don't forget to add the parentheses to the end of this function name to call it.
- Update the button code to match this code:
 
<button (click)="searchHousingLocations()">Search</button>
- Save this code and check the browser. There is now a compilation error:
 

The app throws this error because the searchHousingLocations method does not exist, so you'll need to change that.
- In 
housing-list.component.ts, add a new method at the end of the body of theHousingListComponentclass: 
 searchHousingLocations() {}
You'll fill in the details for this method shortly.
- Save this code to update the browser and resolve the error.
 
Our next step is to get the value of the input field and pass it as an argument to the searchHousingLocations method. You'll use an Angular feature called a template variable, which provides a way to get a reference to an element in a template and interact with it.
- In 
housing-list.component.html, add an attribute calledsearch, with a hashtag as a prefix to the input. 
<label for="location-search">Search for a new place</label>
<input id="location-search" #search><button (click)="searchHousingLocations()">Search</button>
Now, we have  a reference to the input. We have access to the .value property of the input as well.
- Pass the value of the input to the 
searchHousingLocationsmethod, 
<input id="location-search" #search><button (click)="searchHousingLocations(search.value)">Search</button>
Until now, you've been passing the value as a parameter, but let's update the method to use the parameter. Right now, the parameter is used in a console.log command; later, it's used as a search parameter.
- In 
housing-list.component.ts, add this code: 
 searchHousingLocations(searchText: string) {
   console.log(searchText);
 }
- Save the code and then, in the browser, open Chrome DevTools and navigate to the Console tab. Enter any value into the input. Choose Search and verify that the value displays in the Console tab of Chrome DevTools.
 

You've successfully added an event handler and your app can take input from users.
5. Search results
The next step is to display results based on the user input. Each location has string properties for name, city, state, photo, a number property for availableUnits, and two boolean properties for laundry and wifi:
name: "Location One",
city: "Chicago",
state: "IL",
photo: "/path/to/photo.jpg",
availableUnits: 4,
wifi: true,
laundry: true
You can represent this data as a plain JavaScript object, but it's better to use the TypeScript support in Angular. Use types to help avoid errors during build time.
We can use types to define the characteristics of the data, also known as "shaping the data." In TypeScript, interfaces are commonly used for this purpose. Let's create an interface representing our housing location data. In the editor's terminal, use the Angular CLI to create a HousingLocation type.
- To do this, enter:
 
ng generate interface housing-location
- In 
housing-location.ts, add the type details for our interface. Give each property the appropriate type based on our design: 
export interface HousingLocation {
  name: string,
  city: string,
  state: string,
  photo: string,
  availableUnits: number,
  wifi: boolean,
  laundry: boolean,
}
- Save the file and open 
app.component.ts. - To create an array containing data that represents the housing locations by importing the housing location interface from 
./housing-location. 
import { HousingLocation } from './housing-location';
- Update the 
AppComponentclass to include a property calledhousingLocationListof typeHousingLocation[]. Populate the array with the following values: 
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,
  }
];
You don't have to instantiate new instances of a class to get objects; we can take advantage of the type information provided by the interface. The data in our objects has to be the same "shape"; that is, it has to match the properties defined on the interface.
The data is stored in app.component.ts but we need to share it with other components. One solution is to use  services in Angular but to reduce the complexity of the app, we'll use the  Input decorator provided by Angular. The input decorator allows a component to receive a value from a template. You'll use it to share the housingLocationList array with the HousingListComponent.
- In 
housing-list.component.ts, importinputfrom@angular/coreas well asHousingLocationfrom./housingLocation. 
import { Component, OnInit, Input } from '@angular/core';
import {HousingLocation } from '../housing-location';
- Create a property called 
locationListin the body of the component class. You're going to useInputas a decorator forlocationList. 
export class HousingListComponent implements OnInit {
  @Input() locationList: HousingLocation[] = [];
  ...
}
The type of this property is set to HousingLocation[].
- In 
app.component.html, update theapp-housing-listelement to include an attribute calledlocationListand set the value tohousingLocationList. 
<main>
 ...
 <app-housing-list [locationList]="housingLocationList"></app-housing-list>
</main>
The locationList attribute must be enclosed in square brackets ( [ ] ) so that Angular can  dynamically bind the value of the locationList property to a variable or expression. Otherwise, Angular treats the value on the right-hand side of the equals sign as a string.
If you are getting any errors at this point, check that:
- The input attribute name spelling matches the spelling in the property in the TypeScript class. Case matters here, as well.
 - The property name on the right-hand side of the equal sign is spelled correctly.
 - The input property is enclosed in square brackets.
 
The data-sharing configuration is complete! The next step is to display the results in the browser. Since the data is in array format, we need to use an Angular feature that lets you loop over data and repeat of elements in templates, *ngFor.
- In 
housing-list.component.html, update the article element in the template to use*ngForso that you can display the array entries in the browser: 
<article *ngFor="let location of locationList"></article>
The value assigned to the ngFor attribute is Angular template syntax. It creates a local variable in the template. Angular uses the local variable in the scope of the article element between the open and closing tags.
To learn more about ngFor and template syntax, refer to the Angular Documentation.
The ngFor repeats an article element for each entry of the locationList array. Next, you'll display values from the location variable.
- Update the template to add a paragraph element(
<p>) element. The child of the paragraph element is an interpolated value from the location property: 
<input #search><button (click)="searchHousingLocations(search.value)">Search</button>
<article *ngFor="let location of locationList">
   <p>{{location.name}}</p>
</article>
In Angular templates, you can use  text interpolation to display values with the double curly bracket ( {{ }}) syntax.
- Save and return to the browser. Now, the app will display one label for each array entry in the 
locationListarray. 

The data is shared from the app component to the housing list component, and we're iterating over each of those values to display them in the browser.
We've just covered some ways to share data between components, used some new template syntax and the ngFor directive.
6. Filter search results
Currently, the app displays all results instead of results based on a user's search. To change that, you need to update the HousingListComponent so that the app can function as intended.
- In 
housing-list.component.tsupdate theHousingListComponentto have a new property calledresultsthat is of typeHousingLocation[]: 
export class HousingListComponent implements OnInit {
 
 @Input() locationList: HousingLocation[] = [];
 results: HousingLocation[] = [];
 ...
The results array represents the housing locations that match the user search. The next step is to update the searchHousingLocations method to filter the values.
- Remove the 
console.logand update the code to assign the results property to the output of filtering thelocationList, filtered bysearchText: 
searchHousingLocations(searchText: string) {
  this.results = this.locationList.filter(
  (location: HousingLocation) => location.city
    .toLowerCase()
    .includes(
        searchText.toLowerCase()
  ));
}
In this code, we're using the array filter method and only accepting values that contain the searchText. All of the values are compared using the lowercase versions of the strings.
Two things to note:
- The 
thisprefix must be used when referencing properties of a class inside methods. That's why we're usingthis.resultsandthis.locationList. - The search function here only matches against the city property of a location, but you can update the code to include more properties.
 
Although this code works as is, you can improve it.
- Update the code to prevent searching through the array if 
searchTextis blank: 
searchHousingLocations(searchText: string) {
  if (!searchText) return;
  ...
}
The method has been updated and there's a template change that you need to make before the results are displayed in the browser.
- In 
housing-location.component.html, replacelocationListwithresultsin thengFor: 
<article *ngFor="let location of results">...</article>
- Save the code and return to the browser. Using the input, search for a location from the sample data (for example, Chicago).
 
The app displays only the matching results:

You've just completed the additional functionality required to fully link the user input to the search results. The app is nearly complete.
Next, you'll display more details about the app to finish it.
7. Display the details
The app needs to support clicking on a search result and displaying the information in a details panel. The HousingListComponent knows which result has been clicked since the data is displayed in that component. We need a way to share the data from the HousingListComponent to the parent component AppComponent.
In Angular, @Input() sends data from parent to child, while @Output() lets components send an event with data from the child to their parent component. The  Output decorator uses an EventEmitter to notify any listeners of any events. In this case, you want to emit an event representing the click of a search result. Along with the selection event, you want to send the selected item as a part of the payload.
- In 
housing-list.component.ts, update theimportto includeOutputandEventEmitterfrom@angular/coreandHousingLocationfrom its location: 
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { HousingLocation } from '../housing-location';
- In the body of 
HousingListComponent, update the code to add a new property calledlocationSelectedEventof typeEventEmitter<HousingLocation>();: 
@Output() locationSelectedEvent = new EventEmitter<HousingLocation>();
The locationSelectedEvent property is decorated with @Output(), which makes this a part of the API of this component. With EventEmitter, you take advantage of the generics API for the class by providing it the type HousingLocation. When an event is emitted by the locationSelectedEvent, listeners to the event can expect any accompanying data to be of type HousingLocation. This is the type safety supporting our development and reducing the potential for some errors.
We need to trigger the locationSelectedEvent whenever a user clicks on a location from the list.
- Update 
HousingListComponentto add a new method calledselectLocationthat accepts a value of typehousingLocationas a parameter: 
selectHousingLocation(location: HousingLocation) { }
- In the body of the method, emit a new event from the 
locationSelectedEventemitter. The value that is emitted is the location selected by the user. 
selectHousingLocation(location: HousingLocation) {
  this.locationSelectedEvent.emit(location);
}
Let's link this to the template.
- In 
housing-list-component.htmlupdate the article element to have a new button child with aclick event. This event calls theselectHousingLocationmethod in the TypeScript class and passes a reference to the clickedlocationas an argument. 
<article *ngFor="let location of results" >
  <p>{{location.name}}</p>
  <button (click)="selectHousingLocation(location)">View</button>
</article>
The housing locations now have a clickable button and you're passing the values back into the component.
The final step in this process is to update the AppComponent to listen to the event and update the display accordingly.
- In 
app.component.html, update theapp-housing-listelement to listen to thelocationSelectedEventand to handle the event with theupdateSelectedLocationmethod: 
<app-housing-list [locationList]="housingLocationList" (locationSelectedEvent)="updateSelectedLocation($event)"></app-housing-list>
The $event is provided by Angular when dealing with event handlers in templates. The $event argument is an object of type HousingLocation because that is what we set the EventEmitter's type parameter to be. Angular handles all this for you. You just need to confirm that your templates are correct.
- In 
app.component.ts, update the code to include a new property calledselectedLocationof typeHousingLocation | undefined. 
selectedLocation: HousingLocation | undefined;
This uses a TypeScript feature called a  Union Type. Unions let variables accept one of multiple types. In this case, you want the value of selectedLocation to be HousingLocation or undefined because you're not specifying a default value for selectedLocation.
You need to implement updateSelectedLocation.
- Add a new method called 
updateSelectionwith a parameter calledlocationand with a type ofHousingLocation. 
updateSelectedLocation(location: HousingLocation) { } searchHousingLocations() {}
- In the body of the method, set the value of 
selectedLocationto be thelocationparameter: 
updateSelectedLocation(location: HousingLocation) {
  this.selectedLocation = location;
}
With this part complete, the last step is to update the template to display the selected location.
- In 
app.component.html, add a new<article>element that we'll use to display the properties of the selected location. Update the template with the following code: 
<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>
Since the selectedLocation can be undefined, you use the  optional chaining operator to retrieve the values from the property. You're also using ternary syntax for the wifi and laundry boolean values. This provides the opportunity to display a custom message, depending on the value.
- Save the code and check the browser. Search for a location and click on one to reveal the details:
 

This looks great, but there is still an issue to resolve. When the page loads initially, there are some text artifacts from the details panel that should not be displayed. Angular has some ways to conditionally display content that you'll use in the next step.

For now, be excited with how far the app has come. Here's what you've implemented so far:
- You can share data from the child components to the parent components using the Output decorator and the EventEmitter.
 - You've also successfully allowed your users to enter a value and search using that value.
 - The app can display the search results and users can click to see more details.
 
This is excellent work so far. Let's update the templates and complete the app.
8. Polish the templates
The UI currently contains text artifacts from the details panel that should be conditionally displayed. We're going to use two Angular features, ng-container and *ngIf.
If you apply the  ngIf directive to the article element directly, it causes a layout shift when the user makes the first selection. To improve this experience, you can wrap the location details in another element that is a child of the article. This element has no styling or function and just adds weight to the DOM. To avoid that, you can use  ng-container. You can apply directives to it, but it won't show up in the final DOM.
- In 
app.component.html, update thearticleelement to match this code: 
<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>
- Next, add the 
*ngIfattribute to theng-containerelement. The value should beselectedLocation. 
<article>
  <ng-container *ngIf="selectedLocation">
  ...
  </ng-container>
</article>
Now the app only displays the content of the ng-container element if selectedLocation is  Truthy.
- Save this code and confirm that the browser no longer displays the text artifacts when the page loads.
 
There's one final update we can make to our app. The search results in housing-location.component.html should display more details.
- In 
housing-location.component.html, update the code to: 
<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>
- Save the code and return to the browser to reveal the completed app.
 

The app looks great now, and is fully functional. Well done.
9. Congratulations
Thanks for taking this journey and using Angular to build Fairhouse.
You created a user interface using Angular. Using the Angular CLI, you created components and interfaces. Then, you used the powerful template features in Angular to build a functional app that displays images, handles events and more.
What's next?
If you want to continue to build functionality, here are some ideas:
- The data is hard-coded in the app. A great refactor is to add a service to contain the data.
 - The details page is currently displayed on the same page, but it would be cool to move the details to their own page and take advantage of Angular routing.
 - One other update would be to host the data at a rest endpoint and use the HTTP package in Angular to load the data at runtime.
 
Plenty of opportunities for fun.