Suggested Videos
Part 24 - Angular component lifecycle hooks | Text | Slides
Part 25 - Angular services tutorial | Text | Slides
Part 26 - Angular and ASP.NET Web API | Text | Slides
In this video we will discuss
How to call ASP.NET Web API service using Angular 2 http service. Though this example demonstrates calling ASP.NET Web API service, we can use this same approach to call any web service built using any server side technology. We will also briefly discuss Observable pattern
In our previous video we have created ASP.NET EmployeeWebAPIService. Here are the steps to call the Web API service using the Angular builtin http service.
Step 1 - Import the angular HTTP module : The first step is to import HttpModule which is present in a separate javascript file - @angular/http. After the HttpModule is imported, include it in the imports array of the NgModule() decorator of our root module "AppModule" which is in "app.module.ts" file. With this change we can now start using the angular built-in http service throughout our application to access web services over HTTP.
Step 2 - Modify angular EmployeeService to issue a GET request using the builtin http service : The angular EmployeeService is in employee.service.ts file.
Step 3 - Subscribe to the Observable returned by angular EmployeeService : EmployeeListComponent needs the employees data returned by the service. So in the ngOnInit() method of "employeeList.component.ts" use the subscribe method as shown below.
Notice to the subscribe() function we are passing an other arrow function as a parameter. This arrow function is called when the Observable emits an item. In our case the Observable emits an array of IEmployee objects. employeesData parameter receives the array of IEmployee objects, which we are then using to initialise employees property of the EmployeeListComponent class. We can specify upto 3 callback functions as parameters to the subscribe() method as shown below.
At the moment, in our example, we only have used onNext() callback method. In our upcoming videos we will discuss using onError() method to handle exceptions. Using Visual Studio intellisense you can see these 3 callback functions of the subscribe() method.
At this point, run the application. The data may not display and you see the following error in browser developer tools.
Cannot read property 'length' of undefined - at EmployeeListComponent.getTotalEmployeesCount
Reson for the above error : Consider this HTML in employeeList.component.html. Notice we are binding to the input properties (all, male and female) of <employee-count> component.
Here is the getTotalEmployeesCount() method which is binded to "all" property of <employee-count> component.
The Web API call to retrieve is in ngOnInit(). Before the service call can initialise "employees" property of the EmployeeListComponent class, getTotalEmployeesCount() method is trying to access "length" property of "employees" property. "employees" property is still undefined at that point, so we get the error - Cannot read property 'length' of undefined.
To fix this error use angular structural directive *ngIf as shown below. This will delay the initialization of employee-count component until "employees" property is initialised.
At this point if you run the application, you will see no employees on the web page and in the browser developer tools you will see the following error message
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:12345' is therefore not allowed access.
We get this error because our Angular application and Web API service are in different projects. Because they are present in different projects the port numbers are different. Since the port numbers are different, the request from the angular project to the web api project is a cross domain request which violates the same origin policy and as a result the browser blocks the request for security reasons.
To learn more about same origin policy and Cross-origin resource sharing please watch Parts 14 and 15 from ASP.NET Web API tutorial
To fix this error include the following setting in web.config file of the Web API project
Here is the complete employeeList.component.html
We can also use promises instead of Observables. We will discuss using promises in our upcoming videos.
Part 24 - Angular component lifecycle hooks | Text | Slides
Part 25 - Angular services tutorial | Text | Slides
Part 26 - Angular and ASP.NET Web API | Text | Slides
In this video we will discuss
How to call ASP.NET Web API service using Angular 2 http service. Though this example demonstrates calling ASP.NET Web API service, we can use this same approach to call any web service built using any server side technology. We will also briefly discuss Observable pattern
In our previous video we have created ASP.NET EmployeeWebAPIService. Here are the steps to call the Web API service using the Angular builtin http service.
Step 1 - Import the angular HTTP module : The first step is to import HttpModule which is present in a separate javascript file - @angular/http. After the HttpModule is imported, include it in the imports array of the NgModule() decorator of our root module "AppModule" which is in "app.module.ts" file. With this change we can now start using the angular built-in http service throughout our application to access web services over HTTP.
import { HttpModule } from '@angular/http';
@NgModule({
imports: [
BrowserModule,
FormsModule,
HttpModule],
declarations: [
AppComponent,
EmployeeComponent,
EmployeeListComponent,
EmployeeTitlePipe,
EmployeeCountComponent,
SimpleComponent
],
bootstrap: [AppComponent]
})
export class AppModule { }
Step 2 - Modify angular EmployeeService to issue a GET request using the builtin http service : The angular EmployeeService is in employee.service.ts file.
- Use the EmployeeService class constructor to inject Angular Http service. The injected http service can then be used anywhere in this class to call a web service over http.
- Since this Angular EmployeeService class has an injected dependency, @Injectable() decorator is required on this class. If there are no injectable dependencies then we may omit the @Injectable() decorator, but angular strongly recomends to use the @Injectable() decorator irrespective of there are injectible dependencies or not for consistency and future proof.
- Notice in the getEmployees() method, we are using the get() method of the angular http service to issue a get request over http. If you right click on get() method and go to it's definition you will notice that this method return Observable<Response>.
- Observable<Response> is not that useful to us, so we have set the return type of getEmployees() method to Observable<IEmployee[]>
- To convert Observable<Response> to Observable<IEmployee[]> we are using the map operator provided by rxjs.
- At the moment, we are not handling exceptions. We will discuss how to handle exceptions in our upcoming videos.
- Observable is an asynchronous pattern. In the Observable pattern we have an Observable and an Observer. Observer observes the Observable. In many implementations an Observer is also called as a Subscriber.
- An Observable can have many Observers (also called Subscribers).
- Observable emits items or notifications over time to which an Observer (also called Subscriber) can subscribe.
- When a subscriber subscribes to an Observable, the subscriber also specifies a callback function.
- This subscriber callback function is notified as and when the Observable emits items or notifications.
- Within this callback function we write code to handle data itmes or notifications received from the Observable.
import { Injectable } from '@angular/core';
import { IEmployee } from './employee';
// Import
Http & Response from angular HTTP module
import { Http, Response } from '@angular/http';
// Import
Observable from rxjs/Observable
import { Observable } from 'rxjs/Observable';
// Import
the map operator
import 'rxjs/add/operator/map';
@Injectable()
export class
EmployeeService {
// Inject
Angular http service
constructor(private _http:
Http) { }
// Notice the
method return type is Observable<IEmployee[]>
getEmployees(): Observable<IEmployee[]>
{
// To convert
Observable<Response> to Observable<IEmployee[]>
// we are using
the map operator
return this._http.get('http://localhost:24535/api/employees')
.map((response: Response) =>
<IEmployee[]>response.json());
}
}
Step 3 - Subscribe to the Observable returned by angular EmployeeService : EmployeeListComponent needs the employees data returned by the service. So in the ngOnInit() method of "employeeList.component.ts" use the subscribe method as shown below.
ngOnInit()
{
this._employeeService.getEmployees()
.subscribe(employeesData => this.employees = employeesData);
}
Notice to the subscribe() function we are passing an other arrow function as a parameter. This arrow function is called when the Observable emits an item. In our case the Observable emits an array of IEmployee objects. employeesData parameter receives the array of IEmployee objects, which we are then using to initialise employees property of the EmployeeListComponent class. We can specify upto 3 callback functions as parameters to the subscribe() method as shown below.
Callback Method | Purpose |
---|---|
onNext | The Observable calls this method whenever the Observable emits an item. The emitted item is passed as a parameter to this method |
onError | The Observable calls this method if there is an error |
onCompleted | The Observable calls this method after it has emitted all items, i.e after it has called onNext for the final time |
At the moment, in our example, we only have used onNext() callback method. In our upcoming videos we will discuss using onError() method to handle exceptions. Using Visual Studio intellisense you can see these 3 callback functions of the subscribe() method.
At this point, run the application. The data may not display and you see the following error in browser developer tools.
Cannot read property 'length' of undefined - at EmployeeListComponent.getTotalEmployeesCount
Reson for the above error : Consider this HTML in employeeList.component.html. Notice we are binding to the input properties (all, male and female) of <employee-count> component.
<employee-count [all]="getTotalEmployeesCount()"
[male]="getTotalMaleEmployeesCount()"
[female]="getTotalFemaleEmployeesCount()"
(countRadioButtonSelectionChanged)=
"onEmployeeCountRadioButtonChange($event)">
</employee-count>
Here is the getTotalEmployeesCount() method which is binded to "all" property of <employee-count> component.
getTotalEmployeesCount():
number {
return this.employees.length;
}
The Web API call to retrieve is in ngOnInit(). Before the service call can initialise "employees" property of the EmployeeListComponent class, getTotalEmployeesCount() method is trying to access "length" property of "employees" property. "employees" property is still undefined at that point, so we get the error - Cannot read property 'length' of undefined.
ngOnInit()
{
this._employeeService.getEmployees()
.subscribe(employeesData => this.employees = employeesData);
}
To fix this error use angular structural directive *ngIf as shown below. This will delay the initialization of employee-count component until "employees" property is initialised.
<employee-count *ngIf="employees" [all]="getTotalEmployeesCount()"
[male]="getTotalMaleEmployeesCount()"
[female]="getTotalFemaleEmployeesCount()"
(countRadioButtonSelectionChanged)=
"onEmployeeCountRadioButtonChange($event)">
</employee-count>
At this point if you run the application, you will see no employees on the web page and in the browser developer tools you will see the following error message
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:12345' is therefore not allowed access.
We get this error because our Angular application and Web API service are in different projects. Because they are present in different projects the port numbers are different. Since the port numbers are different, the request from the angular project to the web api project is a cross domain request which violates the same origin policy and as a result the browser blocks the request for security reasons.
To learn more about same origin policy and Cross-origin resource sharing please watch Parts 14 and 15 from ASP.NET Web API tutorial
To fix this error include the following setting in web.config file of the Web API project
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods"
value="GET, POST,
PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
</system.webServer>
Here is the complete employeeList.component.html
<employee-count *ngIf="employees" [all]="getTotalEmployeesCount()"
[male]="getTotalMaleEmployeesCount()"
[female]="getTotalFemaleEmployeesCount()"
(countRadioButtonSelectionChanged)=
"onEmployeeCountRadioButtonChange($event)">
</employee-count>
<br />
<br />
<table>
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>Gender</th>
<th>Annual
Salary</th>
<th>Date of
Birth</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let employee of employees;">
<tr *ngIf="selectedEmployeeCountRadioButton=='All' ||
selectedEmployeeCountRadioButton==employee.gender">
<td>{{employee.code | uppercase}}</td>
<td>{{employee.name | employeeTitle:employee.gender }}</td>
<td>{{employee.gender}}</td>
<td>{{employee.annualSalary | currency:'USD':true:'1.3-3'}}</td>
<td>{{employee.dateOfBirth | date:'dd/MM/y'}}</td>
</tr>
</ng-container>
<!--If
the web service takes time to return data, the message in this <tr>
is displayed. When the service call
returns this message disappears
and the employees data is
displayed-->
<tr *ngIf="!employees">
<td colspan="5">
Loading data. Please wait...
</td>
</tr>
<!--This
message is displayed if the web services does not return any data-->
<tr *ngIf="employees && employees.length==0">
<td colspan="5">
No employee records to display
</td>
</tr>
</tbody>
</table>
We can also use promises instead of Observables. We will discuss using promises in our upcoming videos.
Excellent explanation.
ReplyDeleteOne minor issue - I had applied *ngIf="employees" still I am getting length error. Please suggest why this is happening?
Hi Chandan,
DeleteYou are getting length error cause of below line.
*ngIf="employees && employees.length==0">
colspan="5">
No employee records to display
You can split this one as well using ng-container. It worked for me.
ng-container *ngIf="employees">
tr *ngIf="employees.length == 0">
colspan="8">No Employee Data
tr
ng-container
clear your browser cache
Deleteyour answer is corrent, great
DeleteERROR TypeError: _co.getMaleEmployeesCount is not a function
Deleteat Object.eval [as updateDirectives] (employeeList.component.html:4)
i am getting this error after including
*ngIf="employees" in
I am getting this error ...Property 'subscribe' does not exist on type 'IEmployee[]'. what could be wrong?
ReplyDeleteThats because you have to change the line from:
Deletethis.employees = this.employeeService.getEployees()
.subscribe((employeeData) => this.employees = employeeData);
to this one:
this.employeeService.getEployees()
.subscribe(employeesData => this.employees = employeesData);
Hello venkat sir, Thank you so much. I did follow all the steps and i'm wondering why my application always shows 'No data to display' rather than 'Loading data..' while server processing the request. And in your application while server is processing, Nested component didn't show up but my application shows that. can you help me figure out mistake.
ReplyDeleteHave you solved that issue?
DeleteWhy there is a red underline in './employee' in the line import { IEmployee } from './employee'; ?
ReplyDeleteI AM FACING BELOW ERROR
ReplyDeleteERROR SyntaxError: Unexpected token < in JSON at position 0
at JSON.parse ()
at Response.Body.json (body.ts:36)
at MapSubscriber.eval [as project] (employee.service.ts:15)
at MapSubscriber._next (map.ts:75)
at MapSubscriber.Subscriber.next (Subscriber.ts:95)
at XMLHttpRequest.onLoad (xhr_backend.ts:104)
at ZoneDelegate.invokeTask (zone.js:425)
at Object.onInvokeTask (ng_zone.ts:288)
at ZoneDelegate.invokeTask (zone.js:424)
at Zone.runTask (zone.js:192)
defaultErrorLogger @ errors.ts:42
KINDLY HELP ME SOLVE THIS
the most i know in angular2 and mvc is thanks to you.
ReplyDeletevery good explanation.
love your blog
Hi, Thank you for all efforts.
ReplyDeleteUsing web service in angular is an essential topic.
How can consume WCF service using angular if that supported?
Hi Venkat sir,
ReplyDeleteThank you so much for your videos.They are very helpful.
I facing an error as shown below, could you please help.
---------------------
Cannot read property 'subscribe' of undefined
at EmployeeListComponent.ngOnInit (employeeList.component.ts:21)
ERROR CONTEXT DebugContext_ {view: {…}, nodeIndex: 0, nodeDef: {…}, elDef: {…}, elView: {…}}
---------------------
My employeeList.component.ts ngOnInit has the following code already as per the video
---------------------
ngOnInit() {
this._employeeService.getEmployees()
.subscribe(employeesData => this.employees = employeesData);
}
---------------------
Thank you
Hi Venkat Sir,
ReplyDeleteTo add to my previous error, assuming to be a async bug I also tried modifying the ngOnInit() with a return statement but did not help, as below:
ngOnInit() {
this._employeeService.getEmployees()
.subscribe(employeesData => {
return this.employees = employeesData;
});
}
Ia m getting error still
ReplyDeleteerror: ProgressEvent {isTrusted: true, lengthComputable: false, loaded: 0, total: 0, type: "error", …}
headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, headers: Map(0)}
message: "Http failure response for http://localhost:58332/api/employees: 0 Unknown Error"
name: "HttpErrorResponse"
im using angular 6
ReplyDeleteand also getting error in the line " return this._http.get('http://localhost:24535/api/employees')
.map((response: Response) => response.json());"