Updated version of this post can be found here.
This post will be expanding on the basic Angular 2 application from last week’s post to download data from an ASP.NET Core web API. This is the same contacts API used in all of my Aurelia and Web API posts.
Contact Interface
First add a contacts.ts typescript file in the Angular2 folder to hold an interface that defines a contact. This will be used as a definition of what the application expects a contact to look like. A class could be used instead of an interface if some sort of functionality was need on a contact.
export interface Contact { Id: number; Name: string; }
Contact Service
This is the service that will be used to connect to the web API and retrieve a list of contacts.
import {Injectable} from 'angular2/core'; import {Http} from 'angular2/http'; import {Contact} from './contact'; import {Observable} from 'rxjs/Observable'; @Injectable() export class ContactService { private _url = 'http://localhost:38218/api/contacts/'; constructor(private http: Http) { } getContacts() { return this.http.get(this._url) .map(responce => <Contact[]>responce.json()) .catch(error => { console.log(error); return Observable.throw(error); }); } }
The @Injectable() is required for typescript to output the proper metadata for Angular to know what need to be injected into the service. For more information on Angular’s dependency injection look here.
Http from angular2/http is a library used for http interactions. Http interaction is not included in the core of Angular and will require a dependency on a new file.
Contact is just a reference to the contact interface from above.
Observable is a reference to a rxjs type. Rxjs is a set of libraries that help with async programming and is used heavily by Angular 2.
The service’s constructor get a reference to the http library and saves it to a http variable.
Here is just the getContacts() from above.
getContacts() { return this.http.get(this._url) .map(responce => <Contact[]>responce.json()) .catch(error => { console.log(error); return Observable.throw(error); }); }
http.get returns an rxjs observable which is then processed with the rxjs map function. Map takes the json data from the http response and maps it to a contact array.
The url used here will need to be moved to configuration at some point.
App.component Changes
Imports
import {Component, OnInit} from 'angular2/core'; import {HTTP_PROVIDERS} from 'angular2/http'; import {Contact} from './contact'; import {ContactService} from './contact.service'; import 'rxjs/Rx';
A couple of notes on the imports. OnInit is an Angular lifecycle hook that will be used to get a list of contacts. rxjs/Rx import the full set of rxjs libraries and is added here so they will be available to the full application level. rxjs is a fairly sizable and this should be replace with only the parts needed, but for this example the full set of libraries is easier.
AppComponent
The AppComponent class has been expanded and will server as the view model for the contact list. New properties were added for a title and an array of contacts.
A constructor was added to allow the injection of the ContactService.
Notice the class now implements OnInit and a ngOnInit function has been added. This is taking advantage of Angular’s lifecycle hooks. On initialization the class will call getContacts on the ContactService and subscribe to the returned observable and assign the values to the local contacts array.
export class AppComponent implements OnInit { public title = 'Contact List'; public contacts: Contact[]; constructor(private _contactService: ContactService) { } ngOnInit() { this._contactService.getContacts() .subscribe( contacts => { console.log(contacts); this.contacts = contacts; }, error => alert(error)); } }
@Component
The template has expanded to display the title and contact data.
{{title}} binds the title property of the AppComponent class.
The array of contacts is shown in an unordered list. The list items are created used *ngFor which loops each item in the contacts array. When inside the li element items can be bound using {{contact.PropertyName}} as one would expect.
@Component({ selector: 'my-app', template: ` <h1>{{title}}</h1> <ul class="list-unstyled"> <li *ngFor="#contact of contacts"> <span class="badge">{{contact.Id}}</span> {{contact.Name}} </li> </ul> `, providers: [ HTTP_PROVIDERS, ContactService ] })
The providers section is new and is a list of items that will be available for injection to this component and any of its child components.
app.component.ts
Here is the full app.companent.ts all together for reference.
import {Component, OnInit} from 'angular2/core'; import {HTTP_PROVIDERS} from 'angular2/http'; import {Contact} from './contact'; import {ContactService} from './contact.service'; import 'rxjs/Rx'; @Component({ selector: 'my-app', template: ` <h1>{{title}}</h1> <ul class="list-unstyled"> <li *ngFor="#contact of contacts"> <span class="badge">{{contact.Id}}</span> {{contact.Name}} </li> </ul> `, providers: [ HTTP_PROVIDERS, ContactService ] }) export class AppComponent implements OnInit { public title = 'Contact List'; public contacts: Contact[]; constructor(private _contactService: ContactService) { } ngOnInit() { this._contactService.getContacts() .subscribe( contacts => { console.log(contacts); this.contacts = contacts; }, error => alert(error)); } }
ASP.NET View
The Angualr2.cshtml view that contains the angular application needs the following new script tag for the new http library.
<script src="~/Angular/http.dev.js"></script>
Gulp
The gulpfile.js needs also needs to change to move the new http.dev.js file to wwwroot as part of the angualr2:moveLibs task.
gulp.task("angular2:moveLibs", function () { return gulp.src([ "node_modules/angular2/bundles/angular2-polyfills.js", "node_modules/systemjs/dist/system.src.js", "node_modules/systemjs/dist/system-polyfills.js", "node_modules/rxjs/bundles/Rx.js", "node_modules/angular2/bundles/angular2.dev.js", "node_modules/angular2/bundles/http.dev.js" ]) .pipe(gulp.dest(paths.webroot + "Angular")); });
Running the application should now display the list of contact returned by the web API.
How did you solve the cross-site scripting errors?
‘http://localhost:38218/api/contacts/’ differs from “http://localhost:4200”.
I implemented EnableCors and other solutions and have failed to get past the lack of an “Access-Control-Allow-Origin in the reponse headers” using the local server. I’d really like to know how to develop on a single machine.
I didn’t have to deal with any cross-site scripting error in this example since the Angular application and the web API are part of the same application. Unfortunately I can’t really help as I have never had to address this issue. If you find a solution please post your solution. I will add this to the list of things to be the subject of future blogs.
Thank you very much for this great example.
I have one query about the way that angular2 to access asp .net webapi.
is it possible to use _url =’\api\contacts’ directly, rathter than the full url?
Thanks.
Happy to hear that you found it helpful!
Since the call to the web API is going out via http the full url would be needed. Now instead of using hard coding the full url it would be better to store the base url in some sort of configuration, but for this simple example I skipped that step.