This is part 4 of Learn Angular tutorial. In this article, we will understand how to make HTTP calls and how to create custom Angular components using Input and Outputs.
Contents
- In the first part, we look into Node, TypeScript, Module loaders /Bundlers and VS Code
- In the second article, we create a simple basic Angular application with a screen and we ran through important concepts like components and modules
- In the third article, we look into implementing SPA and validations
- In the fourth article, we understand how to make HTTP calls and how to create custom Angular components using Input and Outputs
- In the fifth part, we cover two labs - one is how to use Jquery with Angular and Lazy routing
- In the sixth part, we again cover two labs - Pipes and DI using providers
HTML user interfaces are dead if there is no server side interaction. Normally in a web application, we would like to send the data entered by end user to the server. On server side, we would have some kind of service which can be created in technologies like Java, C# and so on. The server side technology can save, edit or delete this data in a database.
In simple words, we need to understand how to make HTTP calls from Angular code to a server side technology.
Yes, this is a pure Angular article.
I intend to keep this article as a pure Angular article. So teaching server side technology like ASP.NET MVC, Java services, PHP is out of scope.
So the server side service would be FAKED (Mocked) by using “Angular inmemory Webapi”. Please note this is not a service which you will use for production. It's only for test and development purposes.
In case you want to learn ASP.NET MVC, you can start from this video.
|
|
Even though we have decided that we will not be creating a professional server side service using ASP.NET, Java, etc. but we will still need one. So we will be using a fake service called as “Angular in-memory web api”.
Already, the “Angular inmemory web api” has been installed when we did npm. You can check your “package.json” file for the entry of “Angular inmemory web api”.
Already the “Angular inmemory web api” has been installed when we did npm. You can check your “package.json” file for the entry of “angular inmemory web api”.
So let’s create a folder called as “Api” and in that, let's add “CustomerApi.ts” file and in this file, we will write code which will create a fake customer service.
On this fake service, we will make HTTP calls.
| |
Below is a simple service created using “angular-in-memory” open source. In this service, we have loaded a simple “customers
” collection with some sample customer records.
import { InMemoryDbService } from 'angular-in-memory-web-api'
import {Customer} from "../Model/Customer"
export class CustomerApiService implements InMemoryDbService {
createDb() {
let customers =[
{ CustomerCode: '1001', CustomerName: 'Shiv' , CustomerAmount :100.23 },
{ CustomerCode: '1002', CustomerName: 'Shiv1' , CustomerAmount :1.23 },
{ CustomerCode: '1003', CustomerName: 'Shiv2' , CustomerAmount :10.23 },
{ CustomerCode: '1004', CustomerName: 'Shiv3' , CustomerAmount :700.23 }
]
return {customers};
}
}
So now when Angular makes HTTP call, it will hit this in-memory API.
So when you make a HTTP GET , it will return the above four records. When you make a HTTP POST , it will add the data to the in-memory collection.
In other words, we do not need to create a professional server side service using ASP.NET or Java service.
|
|
The next step is to import “HttpModule
” from “angular/http
” and in-memory API into main module. Remember module is collection of components. So the “MainModule
” has “CustomerComponent
” , “SupplierComponent
”, “MasterpageComponent
” and “WelcomeComponent
”.
import { HttpModule} from '@angular/http';
import {CustomerApiService} from "../Api/CustomerApi"
Also in the “NgModule
” attribute in imports, we need to specify “HttpModule
” and “InMemoryWebApiModule
”.
@NgModule({
imports: [….
HttpModule,
InMemoryWebApiModule.forRoot(CustomerApiService),
…..],
declarations: […],
bootstrap: [….]
})
export class MainModuleLibrary { }
Shiv: Talk about the sequence of the Httpmodule
and Angular Webapi.
The next question comes which is the right place to put HTTP calls?
So if you see the normal architecture of Angular, it is as follows:
- User interface is binded with the Angular component using bindings ”
[()] ”. - So once end user starts putting data in the UI, the model object (in our case, it’s the customer object) will be loaded and sent to the
Customer component. - Now customer component has the filled
customer object. So the right place to make HTTP call is in the component.
| |
So let’s go ahead and import the Angular HTTP inside the Customer
component.
import { Http, Response, Headers, RequestOptions } from '@angular/http';
We do not need to create object of HTTP using the new
keyword, it will be dependency injected via the constructor. So on the constructor
component, we have defined the object injection of HTTP.
constructor(public http:Http){
}
When we send HTTP request from client to server or we get response, header information is passed with the request and response. In header information, we have things like content types, status, user agent and so on.
|
|
So to create a request, we need to first create a header and using that header, create a request. One of the header information we need to provide is type of content we are sending to server - is it XML, JSON, etc.
Below is how to create a simple request using basic header information.
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({ headers: headers });
Angular HTTP uses something called as observable
s. So Angular is an observer and it subscribes to observable
like HTTP response. In other words, it's listening to data coming from the server.
So the below code says that we will be making a GET
call to “api/customers” URL and when the data comes, we will send the successful data to the “Success
” function and when error occurs, we will get it in “Error
” function.
var observable = this.http.get("api/customers", options);
observable.subscribe(res => this.Success(res),
res => this.Error(res));
Below is the code of Error
and Success
function. In “Success
” function, we are converting the response to JSON and assigning it to “Customers
” collection which is in the component. If we have error, we are displaying it in the browser console.
Error(err) {
console.debug(err.json());
}
Success(res) {
this.Customers = res.json().data;
}
With all that wisdom we have gathered from Step 4 and Step 5, let's write down two functions, one which will display data and the other which will post data.
Below is a simple “Display
” function which makes a HTTP GET
call.
Display(){
let headers = new Headers({
'Content-Type': 'application/json'
});
let options = new RequestOptions({ headers: headers });
var observable = this.http.get("api/customers", options);
observable.subscribe(res => this.Success(res),
res => this.Error(res));
}
As soon as the customer UI is loaded, the customercomponent
object will be created. So in the constructor
, we have called the “Display
” function to load the customers collection.
export class CustomerComponent {
constructor(public http:Http){
this.Display();
}
}
Below is simple “Add
” function which makes a POST
call to the server. In http POST
call code below, you can see customer
object sent as the third parameter. After the “Add
” call, we have made call to “Display
” so that we can see the new data added on the server.
Add(){
let headers = new Headers({
'Content-Type': 'application/json'
});
var cust:any = {};
cust.CustomerCode = this.CurrentCustomer.CustomerCode;
cust.CustomerName = this.CurrentCustomer.CustomerName;
cust.CustomerAmount = this.CurrentCustomer.CustomerAmount;
let options = new RequestOptions({ headers: headers });
var observable = this.http.post("api/customers",cust, options);
observable.subscribe(res => this.Success1(res),
res => this.Error(res));
this.Display();
}
In the above “Add
” function, you can see the below code which creates a fresh lightweight customer
object. So why do we need to create this fresh new object?
var cust:any = {};
cust.CustomerCode = this.CurrentCustomer.CustomerCode;
cust.CustomerName = this.CurrentCustomer.CustomerName;
cust.CustomerAmount = this.CurrentCustomer.CustomerAmount;
The current customer object has lot of other things like validations, prototype object, etc. So posting this whole object to the server does not make sense. We just want to send three properties “CustomerCode ”, “CustomerAmount ” and “CustomerName ”.
In other words, we need to create a light weight DTO (Data Transfer Object) which just has those properties.
| |
Now that our component is completed, we need to attach the “Add
” function to the button using the “click
” event of Angular. You can see that the (click) is inside a round bracket, in other words, we are sending something (event click) from UI to the Object.
<input type="button"
value="Click" (click)="Add()" [disabled]="!(CurrentCustomer.formGroup.valid)"/>
Also, we need to create a table in which we will use “ngFor
” loop and display the customers collection on the HTML UI. In the below code, we have created a temporary object “cust
” which loops through the “Customers
” collection and inside tag, we are using the expressions ({{cust.CustomerName}}
) to display data.
<table>
<tr>
<td>Name</td>
<td>code</td>
<td>amount</td>
</tr>
<tr *ngFor="let cust of Customers">
<td>{{cust.CustomerName}}</td>
<td>{{cust.CustomerCode}}</td>
<td>{{cust.CustomerAmount}}</td>
</tr>
</table>
Go ahead and run the application. If you go and add “customer
” object you should see the HTTP calls happening and the list getting loaded in the HTML table.
Reusability is one of the most important aspects of development. As Angular is a UI technology, we would like to create UI controls and reuse them in different UI.
For example, in the Customer UI, we have the table grid display. If we want to create grid in other UI, we need to again repeat the “<tr><td> ” loop. It would be great if we can create a generic reusable grid control which can be plugged into any UI.
| |
If we want to create a GENERIC reusable grid control, you need to think GENERICALLY, you need to think in terms of INPUTS and OUTPUTS.
So if you are using this grid control with a Customer UI, you get a Customer collection, if you are using a Supplier UI, you will get a supplier collection.
| |
In order to achieve this generic thought process, Angular has provided three things, Input , Output and Event emitters.
Input helps us define the input data to the user control. Output uses event emitters to send data to the UI in which the user control is located.
| |
First, let us plan how our grid component will look like. Grid component will be called in main HTML using “<grid-ui></grid-ui>
” HTML element. The grid component will have three attributes:
grid-columns
: This will be an input in which we will specify the columns names of the grid. grid-data
: This will again be an input in which we will provide the data to be displayed on the grid. grid-selected
: This will be an output from which the selected object will be sent via emitter events to the contained UI.
<grid-ui [grid-columns]="In this we will give column names"
[grid-data]="In this we will give data for grid"
(grid-selected)="The selected object will be sent in event">
</grid-ui>
So first, let us go ahead and add a separate file for grid component code and name it “GridComponent.ts”.
In this file, we will write all the code that we need for Grid
component.
The first step is to add necessary components which will bring in Input
and Output
capabilities. For that, we need to import component, Input
, Output
and event
emitter component from “angular/core”.
import {Component,
Input,
Output,
EventEmitter} from "@angular/core"
As this is a component
, we need to decorate the class with “@Component
” decorator. The UI for this component
will coded in “Grid.html”. You can also see in the below code we defined the selector has “grid-ui
”, can you guess why?
If you remember in the planning phase, we had said that the grid can be called by using “<grid-ui>
” tag.
@Component({
selector: "grid-ui",
templateUrl : "../UI/Grid.html"
})
export class GridComponent {
}
As this is a grid component, we need data for the grid and column names for the grid. So we have created two array collection one, “gridColumns
” (which will have column names) and “gridData
” ( to data for the table).
export class GridComponent {
gridColumns: Array<Object> = new Array<Object>();
gridData: Array<Object> = new Array<Object>();
}
There are two methods “setGridColumns
” and “setGridDataSet
” which will help us to set column names and table data to the above defined two variables.
These methods will be decorated by using “@Input
” decorators and in this, we will put the names by which these inputs will be invoked while invoking this component.
export class GridComponent {
@Input("grid-columns")
set setgridColumns(_gridColumns: Array<Object>) {
this.gridColumns = _gridColumns;
}
@Input("grid-data")
set setgridDataSet(_gridData: Array<Object>) {
this.gridData = _gridData;
}
}
The names defined in the input decorator will be used as shown below while making call to the component in the main UI.
<grid-ui [grid-columns]="In this we will give column names"
[grid-data]="In this we will give data for grid" >
</grid-ui>
As discussed in this Labs theory section, we will have inputs and outputs. Outputs are again defined by using “@Output
” decorator and data is sent via event emitter object.
To define output, we need to use “@Output
” decorator as shown in the below code. This decorator is defined over “EventEmitter
” object type. You can see that the type is “Object
” and not “Customer
” or “Supplier
” because we want it to be of a generic type. So that we can attach this output with any component type.
@Output("grid-selected")
selected: EventEmitter<Object> = new EventEmitter<Object>();
Now when any end user selects a object from the grid, we need to raise event by using the “EventEmitter
” object by calling the “emit
” method as shown below:
// Other codes have been removed for clarity purpose.
export class GridComponent {
@Output("grid-selected")
selected: EventEmitter<Object> = new EventEmitter<Object>();
Select(_selected: Object) {
this.selected.emit(_selected);
}
}
Below goes the full code of “GridComponent.ts” which we have discussed till now.
import {Component,
Input,
Output,
EventEmitter} from "@angular/core"
@Component({
selector: "grid-ui",
templateUrl : "../UI/Grid.html"
})
export class GridComponent {
gridColumns: Array<Object> = new Array<Object>();
// inputs
gridData: Array<Object> = new Array<Object>();
@Output("grid-selected")
selected: EventEmitter<Object> = new EventEmitter<Object>();
@Input("grid-columns")
set setgridColumns(_gridColumns: Array<Object>) {
this.gridColumns = _gridColumns;
}
@Input("grid-data")
set setgridDataSet(_gridData: Array<Object>) {
this.gridData = _gridData;
}
Select(_selected: Object) {
this.selected.emit(_selected);
}
}
Also, we need to create UI for the “GridComponent.ts”. Remember if we have an Angular component, we NEED A HTML UI for it.
So in the UI folder, we will add “Grid.html” in which we will write the code of table display.
In the “GridComponent.ts” (refer to Step 4 of this Lab), we have defined input “gridColumns
” variable in which we will provide the columns for the grid. So for that, we had made a loop using “*ngFor
” which will create the columns “<td>
” dynamically.
<table>
<tr>
<td *ngFor="let col of gridColumns">
{{col.colName}}
</td>
</tr>
</table>
And to display data in the grid, we need to loop through “gridData
” variable.
{{colObj[col.colName]}}
<a>Select</a>
So the complete code of “Grid.html” looks as shown below:
<table>
<tr>
<td *ngFor="let col of gridColumns">
{{col.colName}}
</td>
</tr>
<tr *ngFor="let colObj of gridData">
<td *ngFor="let col of gridColumns">
{{colObj[col.colName]}}
</td>
<td><a [routerLink]="['Customer/Add']"
(click)="Select(colObj)">Select</a></td>
</tr>
</table>
So now that our reusable component and its UI is completed, let's call the component in the “Customer.html” UI.
Below is the full proper code which has column names defined in “grid-columns
” and in “grid-data
”, we have set “Customers
” collection. This “Customers
” collection object is exposed from the “CustomerComponent
” class. This “Customers
” collection is that collection which we had created during the “HTTP” call lab. This variable has collection of “Customer
” data.
<grid-ui
[grid-columns]="[{'colName':'CustomerCode'},
{'colName':'CustomerName'},{'colName':'CustomerAmount'}]"
[grid-data]="Customers"
(grid-selected)="Select($event)"></grid-ui>
Also, we need to ensure that the old “<table>
” code is deleted and is replaced with the above “<grid-ui>
” input /output tag.
If you remember in our "GridComponent ", we are emitting event by calling “emit ” method. Below is the code snippet for it. Now this event which is emitted needs to caught in the “CustomerComponent ” and processed.
| |
export class GridComponent {
Select(_selected: Object) {
this.selected.emit(_selected);
}
}
So in order to catch that event in the main component, we need to create a method in “CustomerComponent” file. So in the customer component typescript file, we will create a “Select
” function in which the selected customer
will come from the GridComponent
and that selected object will be set to “CurrentCustomer
” object.
export class CustomerComponent {
Select(_selected:Customer) {
this.CurrentCustomer.CustomerAmount =_selected.CustomerAmount;
this.CurrentCustomer.CustomerCode = _selected.CustomerCode;
this.CurrentCustomer.CustomerName = _selected.CustomerName;
}
}
Also, we need to ensure that the “GridComponent
” is loaded in the main module. So in the main module, import the “GridComponent
” and include the same in the declaration of “NgModule
” decorator as shown in the below code:
import { GridComponent } from '../Component/GridComponent';
@NgModule({
imports: [RouterModule.forRoot(ApplicationRoutes),
InMemoryWebApiModule.forRoot(CustomerApiService),
BrowserModule,ReactiveFormsModule,
FormsModule,HttpModule],
declarations: [CustomerComponent,
MasterPageComponent,
SupplierComponent,
WelcomeComponent,
GridComponent],
bootstrap: [MasterPageComponent]
})
export class MainModuleLibrary { }
And that’s it! Hit Control + B, run the server and see your reusable grid working.
In Part 5, we will look into how to do dynamic routing and how to use Jquery in Angular.
For further reading, do watch the below interview preparation videos and step by step video series.
- 22nd September, 2017: Initial version