> Angular 6 Samples
Environment: MS VS Code, bootstrap 3 and Angular 6
Download Project (zip): SampleHttp
The new HTTP client in Angular 6.
Important: Open servers.service.ts and change the backEndUrl per your Firebase URL as described under Firebase Setup below.
We'll use Google Firebase for the database and testing client server functionality with http. Click here to look at the Firebase setup.
Keep a note of your Firebase url
https://testhttp-XXXX.firebaseio.com/
Note that for this test we are not using the new HttpClient.
Create a new service as servers.service.ts with the CLI. Make sure to add ServersService to app.module.ts provides array and to import it properly as well.
>ng g s servers
Importing FormsModule and HttpModule into AppModule
We'll also need to import FormsModule and HttpModule and then add to the imports sections.
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [ServersService],
bootstrap: [AppComponent]
})
Inject a Http Service into this newly created service. Make sure to put @Injectable() directive and to import it from the @angular/core. Then inject http service via contrictor. Make sure to import Http from "@angular/http".
Also make sure to change "backEndUrl" variable with your own URL which you'll need to pick up from Firebase.
Here is the basic setup for the service. We'll add functionality to it later. This service will have the methods to connect to the Firebase and perform actions.
import { Injectable } from "@angular/core";
import { Http } from "@angular/http";
@Injectable()
export class ServersService {
backEndUrl = "https://testhttp-XXXX.firebaseio.com/";
constructor(private http: Http) { }
}
Now go to app.components.ts and inject the service. Make sure to import it as well.
constructor(private serversService : ServersService){}
In the app component currently we have servers array which we display on the page.
servers = [
{
name: 'Testserver',
capacity: 10,
id: this.generateId()
},
{
name: 'Liveserver',
capacity: 100,
id: this.generateId()
}
];
On the template, we display the above array data. We also have a textbox in which user can type in a new server and then click Add Server button to add the server to the array.
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
<input type="text" #serverName>
<button class="btn btn-primary" (click)="onAddServer(serverName.value)">Add Server</button>
<hr>
<ul class="list-group" *ngFor="let server of servers">
<li class="list-group-item">{{ server.name }} (ID: {{ server.id }})</li>
</ul>
</div>
</div>
</div>
and the onAddServer event in the component looks like
onAddServer(name: string) {
this.servers.push({
name: name,
capacity: 50,
id: this.generateId()
});
}
private generateId() {
return Math.round(Math.random() * 10000);
}
Run this basic setup to see how things are. We are not saving any thing to the Firebase yet.
Now the fun part begins...
Add a new button to the template
<button class="btn btn-primary" (click)="onSave()">Save Servers</button>
The onSave event will get to the service and will execute the storeServersMethods. The service is returning an observable so we need to subscribe to it. In the call back we'll get the response and errors.
No need to unsubscribe since there will be only one response and Angular will tke care of it for us.
Service method
storeServers(servers: any[]){
//we are sending back the observable which we'll then use in the component.
return this.http.post(this.backEndUrl+'data.json', servers);
}
onSave Event
onSave(){
this.serversService.storeServers(this.servers)
.subscribe(
(response) => console.log(response),
(error) => console.log(error)
);
}
Run the app and and click Save Servers. Take a look at the console and see the response. We should get some thing like
Response {_body: "{"name":"-LG-16xsYwb2C9z2phA6"}", status: 200, ok: true, statusText: "OK", headers: Headers, …}
Take a look at the Firebase, we should see our servers there as well.
In the above example we didn't configure any header. We might be needing these. Inorder to send these, we'll need to generate these with Headers object. Make sure to import it as well.
We'll pass key value pairs to to the Header.
We can then add a third argument as javascript object to the post request
import { Http, Headers } from "@angular/http";
storeServers(servers: any[]){
//we are sending back the observable which we'll then use in the component.
const headers = new Headers({'Content-Type':'application/json'})
return this.http.post(this.backEndUrl+'data.json', servers, {headers: headers});
}
We are getting the servers back from the server. So in servers service we'll put a new method which will do a GET action and will return an observable.
getServers(){
//returns observable
return this.http.get(this.backEndUrl+'data.json');
}
In the template, create a new button GetServers
<button class="btn btn-primary" (click)="onGet()">Get Servers</button>
Then in the component, we'll write the onGet() method which will call the getServers method of the service. Since the service is returning an observable, we'll need to subscribe to it. After clicking the button, take a look at the console and there should be some data in there.
onGet(){
this.serversService.getServers()
.subscribe(
(response) => console.log(response),
(error) => console.log(error)
);
}
We can unwrap the text returned into real javascript object. For this first import Response from http.
import { Response } from '@angular/http';
We'll extract our data from the Response object using response.json. It will extract the data from the body property and turn it into json. Here is updated method. Do take note of response is of type Response being added here.
onGet(){
this.serversService.getServers()
.subscribe(
(response: Response) => {
const data = response.json();
console.log(data);
},
(error) => console.log(error)
);
}
After the above change, we should see something like following
{-LG-16xsYwb2C9z2phA6: Array(2)}
-LG-16xsYwb2C9z2phA6:Array(2)
0:{capacity: 10, id: 2712, name: "Testserver"}
1:{capacity: 100, id: 4584, name: "Liveserver"}
length:2
With the put request, Firebase will update existing data.
storeServers(servers: any[]){
//we are sending back the observable which we'll then use in the component.
const headers = new Headers({'Content-Type':'application/json'})
// return this.http.post(this.backEndUrl+'data.json', servers, {headers: headers});
return this.http.put(this.backEndUrl+'data.json', servers, {headers: headers});
}
Click save, console should look like
Response {_body: "[{"capacity":10,"id":6665,"name":"Testserver"},{"capacity":100,"id":8004,"name":"Liveserver"}]", status: 200, ok: true, statusText: "OK", headers: Headers, …}
and then take a look at the firebase
We'll use map for this. Since we are not using rxjs-compat, the map operator needs to be used as pipe(map(...)). Make sure to import map
import { map } from 'rxjs/operators';
If you are using rxjs-compat, make sure to import map as following and also do not wrap map inside pipe.
import 'rxjs/Rx';
We'll be re-writting the onGet component method and then getServers service method. We'll be still getting back observable, but in this case we'll transform the data.
We'll wrap the map inside pipe. Make to import map as detailed above in the service. In map we get back response which is of type Response. So make sure to import it.
In the function, we are returning the data but map operator will take the data and will wrap it another observable. Here is the new function
getServers(){
//map returns observable
return this.http.get(this.backEndUrl+'data.json')
.pipe(
map(
(response: Response) => {
const data = response.json();
//here we are returning the data but map operator will take the data and will wrap in another observable
return data;
}
)
);
}
Now in the component onGet method, we are not any more getting back a response instead it will be an array of servers of type any. The new code now looks like
onGet(){
this.serversService.getServers()
.subscribe(
(servers: any[]) => console.log(servers),
(error) => console.log(error)
);
}
Run the app after this change and take a look at the console.
(2) [{…}, {…}]
0:{capacity: 10, id: 6665, name: "Testserver"}
1:{capacity: 100, id: 8004, name: "Liveserver"}
length:2
__proto__:Array(0)
We are getting the data but haven't been using it so far.
All we have to do is instead of console.log(servers), change to this.servers = servers.
onGet(){
this.serversService.getServers()
.subscribe(
(servers: any[]) => this.servers = servers,
(error) => console.log(error)
);
}
Depending upon whether we are using rxjs-compat or not, the operator use will be little different. In this example, we are not using rxjs-compat so
Instead of
....catch(error => {
return Observable.throw(...)
})
use
....pipe(catchError(error => {
return throwError(...)
}))
And make sure to import it:
Instead of
import 'rxjs/Rx';
and
import { Observable } from 'rxjs/Observable';
use
import { catchError } from 'rxjs/operators';
and
import { throwError } from 'rxjs';
For this example, go to the service and update getServers from
return this.http.get(this.backEndUrl+'data.json')
to
return this.http.get(this.backEndUrl+'data')
For Firebase this will be an invalid request. Run the app and take a look at the console
Failed to load https://console.firebase.google.com/project/testhttp-XXXX/database/data/data: Redirect from 'https://console.firebase.google.com/project/testhttp-XXXX/database/data/data' to 'https://accounts.google.com/ServiceLogin?passive=XXXXX&osid=1&continue=https://console.firebase.google.com/project/testhttp-XXXX/database/data/data&followup=https://console.firebase.google.com/project/testhttp-XXXX/database/data/data' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
app.component.ts:51
Response {_body: ProgressEvent, status: 0, ok: false, statusText: "", headers: Headers, …}
headers:Headers
_headers:Map(0) {}
_normalizedNames:Map(0) {}
__proto__:Object
ok:false
status:0
statusText:""
type:3
url:null
_body:ProgressEvent {isTrusted: true, lengthComputable: false, loaded: 0, total: 0, type: "error", …}
__proto__:Body
We'll now implement the catchError and throw error operators in getServers method of the service. For this do proper imports as detailed above. Since in this example we are not using rxjs-compat, we'll work with catchError and throwError. Also, make sure to wrap the operator inside pipe. Here is the updated code.
getServers(){
//map and catchError returns observable
return this.http.get(this.backEndUrl+'data')
.pipe(
map(
(response: Response) => {
const data = response.json();
//here we are returning the data but map operator will take the data and will wrap in another observable
return data;
}
)
)
.pipe(
catchError(
(error: Response) => {
//we need to return an observable here as well so use throwError
console.log(error);
return throwError(error);
}
)
);
}
Run the app and take a look at the console. You'll see a full message since the service method is doing console.log; and will also see the new string message since the component is doing console.log on the received error.
We'll be using the async pipe which we learned in the pipe sample.
Follow the following 4 steps in Firebase and create a new value and pick its url.
In the service create a new method called getMyNewTest and get the new MyNewTest
getMyNewTest(){
//this will return an observable
return this.http.get(this.backEndUrl+'MyNewTest.json')
.pipe(
map(
(response: Response) => {
return response.json();
}
)
);
}
Go to the component and create a new property. Usually we need to subscribe to an observable but not in this case.
myNewTest = this.serversService.getMyNewTest();
We want to display MyNewTest in the template. For this go to the template and output the myNewTest.
<h1>{{ myNewTest }}</h1>
Now run the app and you'll see Object Object.
Since myNewTest is an observable and to unpack we need to subscribe. However, with the async pipe, just go to the template and add pipe async.
<h1>{{ myNewTest | async }}</h1>
With that in place, run the app and you'll see the value now.
Content Covered
- Firebase Setup
- Creating a Service with CLI
- Service Basic Setup
- Component and Template Basic Setup
- Adding Functionality to Perform Post/Put/Get Actions
- Adjusting Request Headers
- Sending Get Request to Read Data From Server
- Changing Service storeServers Method to PUT Request
- Transform Response with Observable MAP Operator
- Using the Returned Data
- Catching Errors
- Using the "async" Pipe with Http Requests
Environment: MS VS Code, bootstrap 3 and Angular 6
Download Project (zip): SampleHttp
The new HTTP client in Angular 6.
Important: Open servers.service.ts and change the backEndUrl per your Firebase URL as described under Firebase Setup below.
Firebase Setup
We'll use Google Firebase for the database and testing client server functionality with http. Click here to look at the Firebase setup.
Keep a note of your Firebase url
https://testhttp-XXXX.firebaseio.com/
Note that for this test we are not using the new HttpClient.
Creating a Service with CLI
Create a new service as servers.service.ts with the CLI. Make sure to add ServersService to app.module.ts provides array and to import it properly as well.
>ng g s servers
Importing FormsModule and HttpModule into AppModule
We'll also need to import FormsModule and HttpModule and then add to the imports sections.
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [ServersService],
bootstrap: [AppComponent]
})
Service Basic Setup
Inject a Http Service into this newly created service. Make sure to put @Injectable() directive and to import it from the @angular/core. Then inject http service via contrictor. Make sure to import Http from "@angular/http".
Also make sure to change "backEndUrl" variable with your own URL which you'll need to pick up from Firebase.
Here is the basic setup for the service. We'll add functionality to it later. This service will have the methods to connect to the Firebase and perform actions.
import { Injectable } from "@angular/core";
import { Http } from "@angular/http";
@Injectable()
export class ServersService {
backEndUrl = "https://testhttp-XXXX.firebaseio.com/";
constructor(private http: Http) { }
}
Component and Template Basic Setup
Now go to app.components.ts and inject the service. Make sure to import it as well.
constructor(private serversService : ServersService){}
In the app component currently we have servers array which we display on the page.
servers = [
{
name: 'Testserver',
capacity: 10,
id: this.generateId()
},
{
name: 'Liveserver',
capacity: 100,
id: this.generateId()
}
];
On the template, we display the above array data. We also have a textbox in which user can type in a new server and then click Add Server button to add the server to the array.
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
<input type="text" #serverName>
<button class="btn btn-primary" (click)="onAddServer(serverName.value)">Add Server</button>
<hr>
<ul class="list-group" *ngFor="let server of servers">
<li class="list-group-item">{{ server.name }} (ID: {{ server.id }})</li>
</ul>
</div>
</div>
</div>
and the onAddServer event in the component looks like
onAddServer(name: string) {
this.servers.push({
name: name,
capacity: 50,
id: this.generateId()
});
}
private generateId() {
return Math.round(Math.random() * 10000);
}
Run this basic setup to see how things are. We are not saving any thing to the Firebase yet.
Adding Functionality to Perform Post/Put/Get/Delete Actions
Now the fun part begins...
Storing Servers
Add a new button to the template
<button class="btn btn-primary" (click)="onSave()">Save Servers</button>
The onSave event will get to the service and will execute the storeServersMethods. The service is returning an observable so we need to subscribe to it. In the call back we'll get the response and errors.
No need to unsubscribe since there will be only one response and Angular will tke care of it for us.
Service method
storeServers(servers: any[]){
//we are sending back the observable which we'll then use in the component.
return this.http.post(this.backEndUrl+'data.json', servers);
}
onSave Event
onSave(){
this.serversService.storeServers(this.servers)
.subscribe(
(response) => console.log(response),
(error) => console.log(error)
);
}
Run the app and and click Save Servers. Take a look at the console and see the response. We should get some thing like
Response {_body: "{"name":"-LG-16xsYwb2C9z2phA6"}", status: 200, ok: true, statusText: "OK", headers: Headers, …}
Take a look at the Firebase, we should see our servers there as well.
Adjusting Request Headers
In the above example we didn't configure any header. We might be needing these. Inorder to send these, we'll need to generate these with Headers object. Make sure to import it as well.
We'll pass key value pairs to to the Header.
We can then add a third argument as javascript object to the post request
import { Http, Headers } from "@angular/http";
storeServers(servers: any[]){
//we are sending back the observable which we'll then use in the component.
const headers = new Headers({'Content-Type':'application/json'})
return this.http.post(this.backEndUrl+'data.json', servers, {headers: headers});
}
Sending Get Request to Read Data From Server
We are getting the servers back from the server. So in servers service we'll put a new method which will do a GET action and will return an observable.
getServers(){
//returns observable
return this.http.get(this.backEndUrl+'data.json');
}
In the template, create a new button GetServers
<button class="btn btn-primary" (click)="onGet()">Get Servers</button>
Then in the component, we'll write the onGet() method which will call the getServers method of the service. Since the service is returning an observable, we'll need to subscribe to it. After clicking the button, take a look at the console and there should be some data in there.
onGet(){
this.serversService.getServers()
.subscribe(
(response) => console.log(response),
(error) => console.log(error)
);
}
We can unwrap the text returned into real javascript object. For this first import Response from http.
import { Response } from '@angular/http';
We'll extract our data from the Response object using response.json. It will extract the data from the body property and turn it into json. Here is updated method. Do take note of response is of type Response being added here.
onGet(){
this.serversService.getServers()
.subscribe(
(response: Response) => {
const data = response.json();
console.log(data);
},
(error) => console.log(error)
);
}
After the above change, we should see something like following
{-LG-16xsYwb2C9z2phA6: Array(2)}
-LG-16xsYwb2C9z2phA6:Array(2)
0:{capacity: 10, id: 2712, name: "Testserver"}
1:{capacity: 100, id: 4584, name: "Liveserver"}
length:2
Changing Service storeServers Method to PUT Request
With the put request, Firebase will update existing data.
storeServers(servers: any[]){
//we are sending back the observable which we'll then use in the component.
const headers = new Headers({'Content-Type':'application/json'})
// return this.http.post(this.backEndUrl+'data.json', servers, {headers: headers});
return this.http.put(this.backEndUrl+'data.json', servers, {headers: headers});
}
Click save, console should look like
Response {_body: "[{"capacity":10,"id":6665,"name":"Testserver"},{"capacity":100,"id":8004,"name":"Liveserver"}]", status: 200, ok: true, statusText: "OK", headers: Headers, …}
and then take a look at the firebase
Transform Response with Observable MAP Operator
We'll use map for this. Since we are not using rxjs-compat, the map operator needs to be used as pipe(map(...)). Make sure to import map
import { map } from 'rxjs/operators';
If you are using rxjs-compat, make sure to import map as following and also do not wrap map inside pipe.
import 'rxjs/Rx';
We'll be re-writting the onGet component method and then getServers service method. We'll be still getting back observable, but in this case we'll transform the data.
We'll wrap the map inside pipe. Make to import map as detailed above in the service. In map we get back response which is of type Response. So make sure to import it.
In the function, we are returning the data but map operator will take the data and will wrap it another observable. Here is the new function
getServers(){
//map returns observable
return this.http.get(this.backEndUrl+'data.json')
.pipe(
map(
(response: Response) => {
const data = response.json();
//here we are returning the data but map operator will take the data and will wrap in another observable
return data;
}
)
);
}
Now in the component onGet method, we are not any more getting back a response instead it will be an array of servers of type any. The new code now looks like
onGet(){
this.serversService.getServers()
.subscribe(
(servers: any[]) => console.log(servers),
(error) => console.log(error)
);
}
Run the app after this change and take a look at the console.
(2) [{…}, {…}]
0:{capacity: 10, id: 6665, name: "Testserver"}
1:{capacity: 100, id: 8004, name: "Liveserver"}
length:2
__proto__:Array(0)
Using the Returned Data
We are getting the data but haven't been using it so far.
All we have to do is instead of console.log(servers), change to this.servers = servers.
onGet(){
this.serversService.getServers()
.subscribe(
(servers: any[]) => this.servers = servers,
(error) => console.log(error)
);
}
Catching Errors
Depending upon whether we are using rxjs-compat or not, the operator use will be little different. In this example, we are not using rxjs-compat so
Instead of
....catch(error => {
return Observable.throw(...)
})
use
....pipe(catchError(error => {
return throwError(...)
}))
And make sure to import it:
Instead of
import 'rxjs/Rx';
and
import { Observable } from 'rxjs/Observable';
use
import { catchError } from 'rxjs/operators';
and
import { throwError } from 'rxjs';
For this example, go to the service and update getServers from
return this.http.get(this.backEndUrl+'data.json')
to
return this.http.get(this.backEndUrl+'data')
For Firebase this will be an invalid request. Run the app and take a look at the console
Failed to load https://console.firebase.google.com/project/testhttp-XXXX/database/data/data: Redirect from 'https://console.firebase.google.com/project/testhttp-XXXX/database/data/data' to 'https://accounts.google.com/ServiceLogin?passive=XXXXX&osid=1&continue=https://console.firebase.google.com/project/testhttp-XXXX/database/data/data&followup=https://console.firebase.google.com/project/testhttp-XXXX/database/data/data' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
app.component.ts:51
Response {_body: ProgressEvent, status: 0, ok: false, statusText: "", headers: Headers, …}
headers:Headers
_headers:Map(0) {}
_normalizedNames:Map(0) {}
__proto__:Object
ok:false
status:0
statusText:""
type:3
url:null
_body:ProgressEvent {isTrusted: true, lengthComputable: false, loaded: 0, total: 0, type: "error", …}
__proto__:Body
We'll now implement the catchError and throw error operators in getServers method of the service. For this do proper imports as detailed above. Since in this example we are not using rxjs-compat, we'll work with catchError and throwError. Also, make sure to wrap the operator inside pipe. Here is the updated code.
getServers(){
//map and catchError returns observable
return this.http.get(this.backEndUrl+'data')
.pipe(
map(
(response: Response) => {
const data = response.json();
//here we are returning the data but map operator will take the data and will wrap in another observable
return data;
}
)
)
.pipe(
catchError(
(error: Response) => {
//we need to return an observable here as well so use throwError
console.log(error);
return throwError(error);
}
)
);
}
Run the app and take a look at the console. You'll see a full message since the service method is doing console.log; and will also see the new string message since the component is doing console.log on the received error.
Using the "async" Pipe with Http Requests
We'll be using the async pipe which we learned in the pipe sample.
Follow the following 4 steps in Firebase and create a new value and pick its url.
In the service create a new method called getMyNewTest and get the new MyNewTest
getMyNewTest(){
//this will return an observable
return this.http.get(this.backEndUrl+'MyNewTest.json')
.pipe(
map(
(response: Response) => {
return response.json();
}
)
);
}
Go to the component and create a new property. Usually we need to subscribe to an observable but not in this case.
myNewTest = this.serversService.getMyNewTest();
We want to display MyNewTest in the template. For this go to the template and output the myNewTest.
<h1>{{ myNewTest }}</h1>
Now run the app and you'll see Object Object.
Since myNewTest is an observable and to unpack we need to subscribe. However, with the async pipe, just go to the template and add pipe async.
<h1>{{ myNewTest | async }}</h1>
With that in place, run the app and you'll see the value now.