To fetch data in Angular, we can use Observables with http to fetch data. An angular application will use http via a GET request to fetch data from a back-end server. The back-end could be powered by any number of server-side technologies such as Java, PHP, Python, or even server-side JavaScript in the form of Node.js. An http response is sent from the back-end web service to the client side angular application. In this tutorial, we’ll learn about things like Observables, Reactive Extensions, HTTP Requests, and how they integrate into an angular application.
Observables and Reactive Extensions
Reactive Extensions, or RxJS, represent a data sequence as an Observable Sequence. The Observable Sequence is simply referred to as an Observable. The purpose of an Observable is to manage asynchronous data, such as what you might get when waiting for data to arrive over a network using HTTP. An Observable treats events as a collection. One way to think of an Observable is that of an array whose items populate asynchronously over time. In an Observable, you would have a method that subscribes to receive notifications as new data arrives. The Angular framework uses Observables to do its asynchronous work.
Angular Observables
Observables offer significant benefits over other techniques for event handling, asynchronous programming, and handling multiple values. Observables are declarative—that is, you define a function for publishing values, but it is not executed until a consumer subscribes to it.
Observable Operators
Functions like map, filter, take, and merge are examples of observable operators. They transform the source observable and compose a new observable while processing each value as it is emitted. One way to better understand Observables is to become familiar with Marble Diagrams. Here, we see an example of the map function which transforms each value to 10 times its original state.
How to compose Observable Operators
Observable operators are composed using a pipe method known as Pipeable Operators. Here is an example.
import {Observable, range} from 'rxjs';
import {map, filter} from 'rxjs/operators';
const source$: Observable<number> = range(0, 10);
source$.pipe(
map(x => x * 2),
filter(x => x % 3 === 0)
).subscribe(x => console.log(x));
The output for this in the console would be the following:
0 6 12 18
So what happens in the code above? Well, first we import two common packages when working with Observables. Those are the rxjs and rxjs/operators packages. Then, the line of const source$: Observable<number> = range(0, 10) creates an observable stream of numbers in the range of 0 to 10. Note the dollar sign suffix which indicates that a particular variable is holding an Observable. For any variable now holding an observable, note that we can now use the .pipe()
method to pass in one or multiple operator functions that can work on and transform each item in the observable collection. So this example takes each number in the range of 0 thru 10 and multiplies it by 2. Then, we use the filter function to filter the result down to only the odd numbers. Once that is complete, we subscribe to the observable and log out the result. Pretty cool!
Promises vs Observables
A Promise is used to handle a single event when an async operation completes or fails while an Observable is like a Stream and allows to pass zero or more events where the callback is called for each event. Here is a table that shows some of the differences.
Promise |
Observable |
Provides a single future value
|
Emits multiple values over time
|
Not Lazy
|
Lazy
|
Can not cancel
|
Able to cancel
|
Can use with map, filter, reduce, and other operators
|
Send HTTP Request in Angular
Now that we know a bit about Observables, let’s configure our games application to fetch the game’s data from an API.
Import HttpClientModule
First up, we need to import the HttpClientModule module into the application. We can do this in app.module.ts like so.
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {FormsModule} from '@angular/forms';
import {HttpClientModule} from '@angular/common/http';
import {AppComponent} from './app.component';
import {GameListComponent} from './games/game-list.component';
import {ThumbComponent} from './shared/thumb.component';
@NgModule({
declarations: [
AppComponent,
GameListComponent,
ThumbComponent
],
imports: [
BrowserModule,
FormsModule,
HttpClientModule
],
bootstrap: [AppComponent]
})
export class AppModule {
}
Use Http Client In Game Service
In the Angular Service Dependency Injection tutorial, we had hard-coded the games data into the game.service.ts file directly. Now, we can remove that data and update the service to make an HTTP request to a webserver to fetch the data. Below we highlight the updated code where we import the modules we need, inject the HTTP module into the constructor, set up an API url to send the request to, and define the getGames() and handleError() methods.
import {Injectable} from '@angular/core';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {Observable, throwError} from 'rxjs';
import {catchError, tap, map} from 'rxjs/operators';
import {IGame} from './game';
@Injectable({
providedIn: 'root'
})
export class GameService {
private gameUrl = 'http://lpg.io/api/angulargames';
constructor(private http: HttpClient) {
}
getGames(): Observable<IGame[]> {
return this.http.get<IGame[]>(this.gameUrl).pipe(
tap(data => console.log('All: ' + JSON.stringify(data))),
catchError(this.handleError)
);
}
private handleError(err: HttpErrorResponse) {
let errorMessage = '';
if (err.error instanceof ErrorEvent) {
errorMessage = `An error occurred: ${err.error.message}`;
} else {
errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
}
console.error(errorMessage);
return throwError(errorMessage);
}
}
Configure Server Side API
For this demonstration, we are sending the HTTP request to http://lpg.io/api/angulargames which is a server running on a virtual machine. This server is using Laravel, a popular PHP framework that is very capable of providing dedicated JSON API web service features. Here is the code we use on the server-side using Laravel.
api.php
<?php
use IlluminateHttpRequest;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:api');
Route::get('/angulargames', function () {
return [["gameId" => 1,
"gameName" => "Castlevania",
"gameCode" => "AAA-0101",
"releaseDate" => "September 26, 1986",
"description" => "Action-adventure game series created and developed by Konami.",
"price" => 19.99,
"thumbRating" => 5.0,
"imageUrl" => "./assets/images/castlevania.png"
],
[
"gameId" => 2,
"gameName" => "Dr Mario",
"gameCode" => "AAA-1100",
"releaseDate" => "July 27, 1990",
"description" => "Action puzzle game produced by Gunpei Yokoi and published by Nintendo.",
"price" => 15.99,
"thumbRating" => 3,
"imageUrl" => "./assets/images/drmario.png"
],
[
"gameId" => 3,
"gameName" => "Kid Icarus",
"gameCode" => "AAA-0048",
"releaseDate" => "December 19, 1986",
"description" => "Kid Icarus revolves around protagonist Pit's quest for three sacred treasures.",
"price" => 20.99,
"thumbRating" => 4,
"imageUrl" => "./assets/images/kidicarus.png"
],
[
"gameId" => 4,
"gameName" => "Legend Of Zelda",
"gameCode" => "AAA-0049",
"releaseDate" => "February 21, 1986",
"description" => "Link is often given the task of rescuing Princess Zelda and the kingdom of Hyrule from Ganon.",
"price" => 29.95,
"thumbRating" => 5,
"imageUrl" => "./assets/images/legendofzelda.png"
],
[
"gameId" => 5,
"gameName" => "Mega Man",
"gameCode" => "AAA-1042",
"releaseDate" => "December 17, 1987",
"description" => "Mega Man is an android originally named Rock, created as a lab assistant by the scientist Dr. Light.",
"price" => 15.95,
"thumbRating" => 4.5,
"imageUrl" => "./assets/images/megaman.png"
],
[
"gameId" => 6,
"gameName" => "Metroid",
"gameCode" => "AAA-4511",
"releaseDate" => "August 6, 1986",
"description" => "Metroid follows space-faring bounty hunter Samus Aran, who protects the galaxy from the Space Pirates.",
"price" => 10.95,
"thumbRating" => 3,
"imageUrl" => "./assets/images/metroid.png"
],
[
"gameId" => 7,
"gameName" => "Super Mario Bros",
"gameCode" => "AAA-3325",
"releaseDate" => "September 13, 1985",
"description" => "Mario finds power-ups and items that give him special magic powers such as fireball-throwing and size-changing into giant and miniature sizes.",
"price" => 40.95,
"thumbRating" => 5,
"imageUrl" => "./assets/images/supermariobros.png"]];
});
Note: You will also need to add the below snippets to publicindex.php to enable CORS functionality. If you do not include these, you may encounter errors such as Server returned code: 0, error message is: Http failure response for (unknown url): 0 Unknown Error or perhaps something like Access to XMLHttpRequest at ‘http://lpg.io/api/angulargames’ from origin ‘http://localhost:4200’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
Update Game List Component
With everything now in place, we can update the game-list.component.ts file. Specifically, we will write new code for the ngOnInit()
life cycle hook so that it now makes use of Observables to make an http get request to our api using our updated game service.
import {Component, OnInit} from '@angular/core';
import {IGame} from './game';
import {GameService} from './game.service';
@Component({
selector: 'game-list',
templateUrl: './game-list.component.html',
styleUrls: ['./game-list.component.css']
})
export class GameListComponent implements OnInit {
pageTitle = 'Dynamic! Game List';
imageWidth = 45;
imageMargin = 1;
showImage = true;
_listFilter = '';
get listFilter(): string {
return this._listFilter;
}
set listFilter(value: string) {
this._listFilter = value;
this.filteredGames = this.listFilter ? this.doFilter(this.listFilter) : this.games;
}
filteredGames: IGame[] = [];
games: IGame[] = [];
constructor(private gameService: GameService) {
this.listFilter = '';
}
onRatingClicked(message: string): void {
this.pageTitle = 'Game List: ' + message;
}
doFilter(filterBy: string): IGame[] {
filterBy = filterBy.toLocaleLowerCase();
return this.games.filter((game: IGame) =>
game.gameName.toLocaleLowerCase().indexOf(filterBy) !== -1);
}
toggleImage(): void {
this.showImage = !this.showImage;
}
ngOnInit(): void {
this.gameService.getGames().subscribe(
games => {
this.games = games;
this.filteredGames = this.games;
},
error => this.errorMessage = <any>error
);
}
}
Our new Game Service which uses Observables to make an HTTP request to a Laravel back-end server is now working!
You can also see the API response data in the console section of chrome developer tools.
How To Make HTTP Requests In Angular Using Observables Summary
Here are some key points to remember when dealing with Observables and HTTP Requests in Angular.
Setting up HTTP in Angular
- Add HttpClientModule to the imports array of one of the applications Angular Modules
- Import what is needed for the data service
- Define a dependency for the http client service using the constructor
- Create a method for each http request type you would like to use
- Call the http method(s) as needed (for example GET)
- Use Generics to specify the return type
Subscribing To Observables
- Call the subscribe method of the returned observable
- Create a function to handle emitted items
- Assign property data to returned JSON object
- Create a function to handle errors