Consuming a RESTful web service with Angular

duration 20 minutes

Prerequisites:

Explore how to access a simple RESTful web service and consume its resources with Angular in Open Liberty.

What you’ll learn

Angular is a framework for creating interactive web applications. Angular applications are written in HTML, CSS, and TypeScript, a variant of JavaScript. Angular helps you create responsive and intuitive applications that download once and run as a single web page. Consuming REST services with your Angular application allows you to request only the data and operations that you need, minimizing loading times.

You will learn how to access a REST service and deserialize the returned JSON that contains a list of artists and their albums by using an Angular service and the Angular HTTP Client. You will then present this data using an Angular component.

The REST service that provides the artists and albums resource was written for you in advance and responds with the artists.json.

The Angular application was created and configured for you in the frontend directory. It contains the default starter application. There are many files that make up an Angular application, but you only need to edit only a few to consume the REST service and display its data.

Angular applications must be compiled before they can be used. The Angular compilation step was configured as part of the Maven build. You can use the start folder of this guide as a template for getting started with your own applications built on Angular and Open Liberty.

artists.json

 1[
 2    {
 3        "name": "foo",
 4        "albums": [
 5            {
 6                "title": "album_one",
 7                "artist": "foo",
 8                "ntracks": 12
 9            },
10            {
11                "title": "album_two",
12                "artist": "foo",
13                "ntracks": 15
14            }
15        ]
16    },
17    {
18        "name": "bar",
19        "albums": [
20            {
21                "title": "foo walks into a bar",
22                "artist": "bar",
23                "ntracks": 12
24            }
25        ]
26    },
27    {
28        "name": "dj",
29        "albums": [
30        ]
31    }
32]

You will implement an Angular client that consumes this JSON and displays its contents at the http://localhost:9080/app URL.

To learn more about REST services and how you can write them, see Creating a RESTful web service.

Getting started

The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:

git clone https://github.com/openliberty/guide-rest-client-angular.git
cd guide-rest-client-angular

The start directory contains the starting project that you will build upon.

The finish directory contains the finished project that you will build.

Starting the service

Before you begin the implementation, start the provided REST service so that the artist JSON is available to you.

To start the REST service, navigate to the start directory and run the Maven install and liberty:start-server goals:

mvn install liberty:start-server

After you start the server, you can find your artist JSON at the http://localhost:9080/artists URL.

You can rebuild the front end at any time. To do so, run Maven with the generate-resources goal:

mvn generate-resources

Any local changes to your TypeScript and HTML are picked up when you build the front end. After you start the Open Liberty server, you don’t need to restart it.

Importing the HTTP client

Navigate to the start directory to begin.

Your application needs a way to communicate with RESTful web services to retrieve their resources. In this case, the provided Angular application needs to communicate with the artists service to retrieve the artists JSON. While there are various ways to perform this task, Angular contains a built-in HTTP Client module that you can use.

Begin by importing the Angular HTTP Client module into the root module.

app.module.ts

 1import { BrowserModule } from '@angular/platform-browser';
 2import { NgModule } from '@angular/core';
 3// tag::importHttpClientModule[]
 4import { HttpClientModule } from '@angular/common/http';
 5// end::importHttpClientModule[]
 6import { AppComponent } from './app.component';
 7
 8// tag::atNgModule[]
 9@NgModule({
10  declarations: [
11    AppComponent
12  ],
13  // tag::importsArray[]
14  imports: [
15    BrowserModule,
16    // tag::httpClientModule[]
17    HttpClientModule,
18    // end::httpClientModule[]
19  ],
20  // end::importsArray[]
21  providers: [],
22  bootstrap: [AppComponent]
23})
24// end::atNgModule[]
25export class AppModule { }

Angular applications consist of modules, which are groups of classes that perform specific functions. The Angular framework provides its own modules for applications to use. One of these modules, the HTTP Client module, includes convenience classes that make it easier and quicker for you to consume a RESTful API from your application.

The provided application is organized into a module, called the root module, and defined in the app.module.ts file. You must import the HTTP Client module into the root module for its classes to be accessible from the application’s code.

Update the app.module.ts file.
src/main/frontend/src/app/app.module.ts

Import the HttpClientModule class into the file, then update the imports array within the @NgModule declaration to include HttpClientModule.

Creating the Angular component

You need to create the component that is used in the application to acquire and display data from the REST API. The component file contains two classes: the service, which handles data access, and the component itself, which handles the presentation of the data. The provided Angular application already contains a component.

app.component.ts

 1// tag::importOnInitAndAngularCorePackage[]
 2import { Component, OnInit } from '@angular/core';
 3// end::importOnInitAndAngularCorePackage[]
 4// tag::importHttpClient[]
 5import { HttpClient } from '@angular/common/http';
 6// end::importHttpClient[]
 7// tag::importInjectable[]
 8import { Injectable } from '@angular/core';
 9// end::importInjectable[]
10
11// tag::atInjectable[]
12@Injectable()
13// end::atInjectable[]
14// tag::artistsServiceClass[]
15export class ArtistsService {
16  // tag::httpClientInstance[]
17  constructor(private http: HttpClient) { }
18  // end::httpClientInstance[]
19
20  // tag::artistsUrl[]
21  private static ARTISTS_URL = '/artists';
22  // end::artistsUrl[]
23
24  // tag::fetchArtistsMethod[]
25  // tag::asyncFeature[]
26  async fetchArtists() {
27  // end::asyncFeature[]
28    try {
29      // tag::httpInstanceAndAwaitFeatureAndHttpGetAndToPromise[]
30      const data: any = await this.http.get(ArtistsService.ARTISTS_URL).toPromise();
31      // end::httpInstanceAndAwaitFeatureAndHttpGetAndToPromise[]
32      return data;
33    } catch (error) {
34      console.error(`Error occurred: ${error}`);
35    }
36  }
37  // end::fetchArtistsMethod[]
38}
39// end::artistsServiceClass[]
40
41// tag::atComponent[]
42@Component({
43  selector: 'app-root',
44  // tag::templateUrl[]
45  templateUrl: './app.component.html',
46  // end::templateUrl[]
47  // tag::providersProperty[]
48  providers: [ ArtistsService ],
49  // end::providersProperty[]
50  // tag::styleUrls[]
51  styleUrls: ['./app.component.css']
52  // end::styleUrls[]
53})
54// end::atComponent[]
55// tag::appComponentClass[]
56// tag::onInitInterface[]
57export class AppComponent implements OnInit {
58// end::onInitInterface[]
59  // tag::artistsClassMember[]
60  artists: any[] = [];
61  // end::artistsClassMember[]
62
63  // tag::artistsServiceInstanceDeclaration[]
64  constructor(private artistsService: ArtistsService) { }
65  // end::artistsServiceInstanceDeclaration[]
66
67  // tag::ngOnInitMethod[]
68  ngOnInit() {
69    // tag::thenClause[]
70    // tag::artistsServiceInstance[]
71    this.artistsService.fetchArtists().then(data => {
72    // end::artistsServiceInstance[]
73      this.artists = data;
74    });
75    // end::thenClause[]
76  }
77  // end::ngOnInitMethod[]
78}
79// end::appComponentClass[]

Defining a service to fetch data

Services are classes in Angular that are designed to share their functionality across entire applications. A good service performs only one function, and it performs this function well. In this case, the ArtistsService class requests artists data from the REST service.

Update the app.component.ts file.
src/main/frontend/src/app/app.component.ts

Create the entire ArtistsService class with the @Injectable annotation. Add the HttpClient and Injectable import statements at the top.

The file imports the HttpClient class and the Injectable decorator.

The ArtistsService class is defined. While it shares the file of the component class AppComponent, it can also be defined in its own file. The class is annotated by @Injectable so instances of it can be provided to other classes anywhere in the application.

The class injects an instance of the HttpClient class, which it uses to request data from the REST API. It contains the ARTISTS_URL constant, which points to the API endpoint it requests data from. The URL does not contain a host name because the artists API endpoint is accessible from the same host as the Angular application. You can send requests to external APIs by specifying the full URL. Finally, it implements a fetchArtists() method that makes the request and returns the result.

To obtain the data for display on the page, the fetchArtists() method tries to use the injected http instance to perform a GET HTTP request to the ARTISTS_URL constant. If successful, it returns the result. If an error occurs, it prints the error message to the console.

The fetchArtists() method uses a feature of JavaScript called async/ await to make requests and receive responses without preventing the application from working while it waits. For the result of the HttpClient.get() method to be compatible with this feature, it must be converted to a Promise by invoking its toPromise() method. A Promise is how JavaScript represents the state of an asynchronous operation. If you want to learn more, check out https://promisejs.org for an introduction.

Defining a component to consume a service

Components are the basic building blocks of Angular application user interfaces. Components are made up of a TypeScript class annotated with the @Component annotation and the HTML template file (specified by templateUrl) and CSS style files (specified by styleUrls.)

Update the AppComponent class to use the artists service to fetch the artists data and save it so the component can display it.

Update the app.component.ts file.
src/main/frontend/src/app/app.component.ts

Replace the entire AppComponent class with the @Component annotation. Add OnInit to the list of imported classes at the top.

The providers property on the @Component annotation indicates that this component provides the ArtistsService to other classes in the application.

AppComponent implements OnInit, which is a special interface called a lifecycle hook. When Angular displays, updates, or removes a component, it calls a specific function, the lifecycle hook, on the component so the component can run code in response to this event. This component responds to the OnInit event via the ngOnInit method, which fetches and populates the component’s template with data when it is initialized for display. The file imports the OnInit interface from the @angular/core package.

artists is a class member of type any[] that starts out as an empty array. It holds the artists retrieved from the service so the template can display them.

An instance of the ArtistsService class is injected into the constructor and is accessible by any function that is defined in the class. The ngOnInit function uses the artistsService instance to request the artists data. Since the fetchArtists() method is an async function, it returns a Promise. To retrieve the data from the request, ngOnInit calls the then() method on the Promise which takes in the data and stores it to the artists class member.

Creating the Angular component template

Now that you have a service to fetch the data and a component to store it in, you will create a template to specify how the data will be displayed on the page. When you visit the page in the browser, the component populates the template to display the artists data with formatting.

Replace the component's template file app.component.html.
src/main/frontend/src/app/app.component.html

app.component.html

 1<!-- tag::artistsDiv[] -->
 2<div *ngFor="let artist of artists">
 3  <!-- tag::artistNameAndAlbumsLengthPlaceholders[] -->
 4  <p>{{ artist.name }} wrote {{ artist.albums.length }} albums: </p>
 5  <!-- end::artistNameAndAlbumsLengthPlaceholders[] -->
 6  <!-- tag::albumDiv[] -->
 7  <div *ngFor="let album of artist.albums">
 8    <p style="text-indent: 20px">
 9      Album titled <b>{{ album.title }}</b> by
10                   <b>{{ album.artist }}</b> contains
11                   <b>{{ album.ntracks }}</b> tracks
12    </p>
13  </div>
14  <!-- end::albumDiv[] -->
15</div>
16<!-- end::artistsDiv[] -->

The template contains a div element that is enumerated using the *ngFor directive. The artist variable is bound to the artists member of the component. The div element itself and all elements contained within it are repeated for each artist, and the {{ artist.name }} and {{ artist.albums.length }} placeholders are populated with the information from each artist. The same strategy is used to display each album by each artist.

The Open Liberty server is already started, and the REST service is running. You can recompile the front end by running the following command:

mvn generate-resources

Then, point your browser to the web application root http://localhost:9080/app to see the following output:

foo wrote 2 albums:
    Album titled album_one by foo contains 12 tracks
    Album tilted album_two by foo contains 15 tracks
bar wrote 1 albums:
    Album titled foo walks into a bar by bar contains 12 tracks
dj wrote 0 albums:

Testing the Angular client

No explicit code directly uses the consumed artist JSON, so you don’t need to write any test cases.

Whenever you change and build your Angular implementation, the application root at http://localhost:9080/app reflects the changes automatically. You can visit the root to manually check whether the artist JSON was consumed correctly. You can rebuild only your Angular front end by running the following command:

mvn generate-resources

When you are done checking the application root, stop the Open Liberty server by running the following command:

mvn liberty:stop-server

Although the Angular application that this guide shows you how to build is simple, when you build more complex Angular applications, testing becomes a crucial part of your development lifecycle. If you need to write test cases, follow the official unit testing and end-to-end testing documentation on the official Angular page.

Great work! You’re done!

You just accessed a simple RESTful web service and consumed its resources by using Angular in Open Liberty.

Guide Attribution

Consuming a RESTful web service with Angular by Open Liberty is licensed under CC BY-ND 4.0

Copied to clipboard
Copy code block
Copy file contents

Prerequisites:

Nice work! Where to next?

What did you think of this guide?

Extreme Dislike Dislike Like Extreme Like

What could make this guide better?

Raise an issue to share feedback

Create a pull request to contribute to this guide

Need help?

Ask a question on Stack Overflow

Like Open Liberty? Star our repo on GitHub.

Star