There are 2 ways to build a web application. The 1st way – and certainly the oldest way – is to handle the web application on the server and just send back HTML to the browser. It’s basically like Wikipedia, Amazon, or LinkedIn. Almost every interaction triggers a page change or a page reload.
Then, comes the 2nd way. The principle is to give to the browser all HTML layouts of every screen in the App and a lot of JavaScript code. Upon user interaction, JavaScript code will be in charge of taking data from a REST API and generate the HTML itself. It’s how Gmail or Facebook works but it’s also how Angular works – and that’s why you’re reading this article, right?!
Without calling an API, let’s say it, your Angular App will just be isolated from the rest of the world – just like Tom Hanks – and you won’t be a good friend if you let that happen. At the end of this article, your App will get access to Cloud Power. But for now, we need to know what code we will need to change.
That will be probably your first question: what TypeScript file will I need to open? The problem is that your file isn’t created yet.
Angular helps your App organization by providing a set of rules and conventions that will encourage you to split your code into a couple of pre-defined categories:
At this stage, you probably know components and their templates, also maybe you’ve seen or touched once a module but not much more. Components are really focused to represent a screen or a checkbox or a text field. It only cares about how to behave if the text field is empty or if the checkbox is effectively checked.
It won’t know what are you going to put inside the field or the checkbox is meant to accept Terms & Conditions, or not. For this reason, components should not call REST APIs – otherwise, you couldn’t re-use the component in another content or with another API.
The API part can’t be a module as well. Modules are just a package of Angular Components and Services that can be shared between multiple applications. Nothing related to API here.
The right place where we should put API code is Services. Services’ job is to focus more on a feature you may need in your Components but this feature isn’t directly linked to a Component. For example, there could be a SendEmail
Service and then, a SendButton
component would, upon click, call the Service to send the e-mail. However, if you suddenly prefer to trigger the button by voice, you would certainly need to change the Component. But does Gmail or Outlook works differently because of that change? No, that’s why the Service can be reused without making any change to it.
In other words, Services is that reusable LEGO you can use to build a castle or a bridge. And that reusability will allow you to code once and reuse it many times. You now understand better why Angular is so popular in businesses and companies.
Now you need a way to make HTTP requests to your server. That’s how we call a REST API, right?
Before you start writing inXMLHttpRequest
your code, maybe you should look for an Angular way to do the task instead of working around it. In general, things go smoother when you follow the Angular way, and surprise, there’s Angular’s HttpClient
Service that exactly allows you to do HTTP requests. And even better, it’s cross-browser and has some great features that will help you with REST APIs.
OK but now, how to use that piece of technology? Well, you first need to edit your AppModule
(too bad!) to import the HttpClientModule
. The module isn’t imported in all Angular projects by default to make the app lighter if you wouldn’t use it. You need to import this module after the BrowserModule
. Module imports are almost the only place of Angular sensitive to the ordering.
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; // Import components... @NgModule({ declarations: [ // Declare components... ], imports: [ BrowserModule, HttpClientModule ], // providers, bootstrap ... }) export class AppModule { }
Then, you can use simply import and add the HttpClient
to your Service’s constructor. Angular will handle the creation of the HttpClient
and all the like.
However, now it’s time to tell you something you need to learn about JavaScript & TypeScript in general: it’s an asynchronous language. That will change how you have to code HTTP requests compared to other languages like C or Python.
I invite you to take a look at another article where I explain what’s asynchronous programming in JavaScript, but if you’re in a hurry, here’s the short explanation: When you can’t get something right now (like time or a random number) but instead you need to fetch it from the outside where it might take a few seconds to get the result, you’ll need to provide a function that will be called when that result is ready.
For the HTTP request, it means that you’ll launch the HTTP request and you’ll provide a function to be called when the server has answered. Angular, in order to familiarize developers with asynchronous programming and make it easier to use, use Observables from RxJS to manage these delays, much like airlines manage planes’ departures and arrivals.
Observables work this way technically: when a function knows it needs to make an HTTP request, it will return a special object in TypeScript called an Observable
. This Observable
acts a bit like a cart in Amazon or Walmart: you’ll take note of what you want in your order, the location where you want to receive it, and once done, you hit the button “Buy” and your order is placed.
The Observable
will obviously not buy Cookies. Instead, it will take note of what request you want to make, what URL, what headers, what data to upload, where you expect to get the response, and what special filters you want to apply to this response (for example, decode JSON). Once you have everything set up, you call the .subscribe
method on the Observable
, your request is sent and everything starts.
So if you follow me so far, nothing happens before you call .subscribe.
You just edit a variable in the browser’s memory and nothing else. Keep that in mind and you won’t have any problems. Please note that the function returning the Observable will never call .subscribe.
Angular with its HttpClient
will always wait for you to subscribe before doing anything.
public getNumberViaMethod(): void { // This is just to show how to use .subscribe. // getNumber gives an Observable from HttpClient as we'll see in a bit // There's a better way to display an Observable in Angular this.numberService.getNumber().subscribe((httpNumber: number): void => { // Associate with a Component's property this.httpNumber = httpNumber; }); }
Do you notice the usage of a =>
(it’s called a “fat arrow”) in the code? It’s a special way to create a function. It’s because JavaScript and TypeScript destroy variables when you’re in a normal function that is itself inside a method. This will prevent you to call a method upon an HTTP response, which is clearly a problem.
So to help, you can use the =>
way to create a function. When you use =>
you instruct TypeScript to not destroy this
it because you’re really going to need it. If you have the habit to use .bind
, please don’t do that in TypeScript: the compiler will be unable to check for the types and the correctness of a function that uses .bind
.
Now, it’s better to connect to a real server and finally get this data from HTTP. I will talk about POST
in a bit.
Please note that Angular’s HttpClient
does support retrieving data in any kind of format. However, it only automatically decodes JSON so if you have to use another format, be prepared to handle the decoding yourself. For keeping this article simple, we will assume that your API is using JSON, which is commonly the case.
As well, the API server is on the same hostname and the same port as the Angular application. CORS (Cross-Origin Resource Sharing) is a complex topic with several security implications if it’s not correctly set up.
OK, so our first step is to create the Service file. Angular offers an easy way to generate a base Service file using the ng
command. It will avoid you to write the boilerplate code. Note it will only generate a wireframe so you will still have to code your features yourself. For our example, we will create a NumberService
. Let’s run the following command:
ng generate service number
So, after running this command – you can take a coffee – a new file is creating with the following content:
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class NumberService { constructor() { } }
Well, I’ve warned you: it only just taking charge of boilerplate code. In order to make it a useful Service, we need to import HttpClient
from Angular and Observable
from RxJS. Also, we need to inject in our constructor the HttpClient
provided from Angular.
Let’s focus on how Angular allows a Service to import other Services. After the importation of Angular modules that contains the Services you need, you only need to do two additional steps to get the imported Service into your own Service:
TypeScript has a shortcut to directly make a constructor argument as its object property. You just need to add the keyword public
, protected
or private
. Generally, Angular Components doesn’t want to access imported Services of Services they use, so making the property private
is the best choice to ensure the cleanest code.
Now you have all the needed knowledge to import HttpClient
and Observable
. Let’s do this:
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class NumberService { constructor(private http: HttpClient) { } }
OK, this part is now done – you can applause yourself, but honestly, it’s a little bit early to do that. The next step is to create a public
function that your Components will call to obtain the data. This function has to return an Observable
since the data is not available right now.
But if you were only telling TypeScript that you return an Observable
, the compiler wouldn’t have any idea of what kind of data the Observable
will receive when it got it. To help TypeScript in that matter, we need to use that syntax: Observable<ReturnedType>
. It’s called generics.
It’s basically the fact that a type can reference another type, which is exactly the case of Observable
: it’s a type by itself because it contains data about what to do when .subscribe
is called, but it will also give a value from an HTTP server and that value has a type. For the NumberService
, let’s do an Observable<number>
.
public getNumber(): Observable { return this.http.get("/api/number/").map((json: Object): number => { return (json["number"] as number); }); }
If all is going okay and your API is returning a JSON-like {"number": 28}
, you can safely subscribe to it and then use it in your Components. One way is to modify a property of your component when the data is given by the server, but generally, you would prefer to use Angular features and HTML templates that have support with the async template keyword.
Let’s say we want to display the number in a paragraph of a Component. It’s needed to create a function in the component to call the service and associate the Observable
to a Component’s property. Then in your HTML template, you can use the template pipe called async
. It will take in charge itself to subscribe and display the value. For example, in the HTML template:
And the number is: {{number | async}}
Now, with that new function and this change in the template, you can test your app! Doesn’t that look good?
You are also free to subscribe via your Component’s function (like we did earlier) and then change the property of your Component only when the data arrived. However, async
it takes a lot of work away from you, like subscription but also ending that subscription when the Component is removed. And it’s cool to write less code, right?
But you may also want in this case to use *ngIf
to hide the paragraph until the value is available. *ngIf
is far more likely to do what you need it to do and will avoid you writing too much code. For reference, *ngIf
explicitly supports Observable
.
What about POST
? Since you already know how to receive data I’ll assume here that we want to discard the POST
response and make sure to launch the request. Let’s do that:
public setNewNumber(newNumber: number): Observable<Object> { let json: Object = new Object; json["newNumber"] = newNumber; return this.http.post("/api/number/change/", json); }
And the eventual Component function you may want to write:
public setNumberViaService(): void { this.numberService.setNewNumber(this.input.value).subscribe(); }
As you can notice, by default the HttpClient
returns by default an Observable<Object>
, that’s why it’s the return type of setNewNumber
. As we don’t care at all about the value returned by POST
(in these cases generally, the HTTP response returns a 204 No Content
, that’s why extracting the content is useless), there’s no need to use a map
or anything.
However, we must not forget to call .subscribe.
In the GET
example, the async
template keyword (it’s officially called an Angular pipe) was taking in charge to subscribe to the Observable
. But here, as there’s no content, it will be hard to involve the Component template so it’s better to create a function in the Component that calls .subscribe
straight away.
Angular’s HttpClient
also have similar methods for PUT
, PATCH
or DELETE
. They look similar to POST
on Angular’s side except DELETE
which works like GET as deletion shouldn’t ask for data, it just needs to know the location of the data it needs to delete.
It’s time for us to take a break and to really applause this time, it’s deserved. You’re now making real requests!
There’s a lot of new concepts explained here and I recommend you familiarize yourself with them by writing your own Components and Services. I hope you enjoyed this Angular TypeScript tutorial.