This week’s post is going to take theĀ Angular 2 applicationĀ from a couple of weeks ago and add the same functionality currently present in the Aurelia application found the ASP.NET Core Basic repo. This release is the starting point for the solution used in this post.
Starting point overview
When you download a copy of the repo you will find an ASP.NET Core solution that contains three projects. The AngularĀ project is where this post will be focused.
The Contacts project has a set of razor views and a controller to go with them that support standard CRUD operations, which at the moment is the best way to get contact information in the database. It also contains the ContactsApiControllerĀ which will be the controller used to feed contacts to the Angular 2 and Aurelia applications.
Multiple startup projects in Visual Studio
In order to properly test the functionality that will be covered here both the Contacts project and the Angular project will need to be running at the same time. Visual Studio provides a way to handle this. TheĀ Multiple startup projects in Visual Studio section of this post walks through the steps of setting up multiple startup project. The walk through is for the Aurelia project, but the same steps can be applied to the Angular project.
Model
Create a contactsĀ directory inside ofĀ ClientApp/app/components/ of the Angular project. Next createĀ aĀ contact.tsĀ file to theĀ contactsĀ directory. This file will be the model of a contact in the system. If you read the Aurelia version of this post you will noticed that this model is more fully defined since this project is using TypeScript the more fully defiled model provides more type safety. The following is the contests file.
export class Contact { id: number; name: string; address: string; city: string; state: string; postalCode: string; phone: string; email: string; constructor(data) { Object.assign(this, data); } getAddress() { return `${this.address} ${this.city}, ${this.state} ${this.postalCode}`; } }
Service
To isolate HTTP access the application will use a service to encapsulate access to the ASP.NET Core API. For the service create aĀ contact.service.tsĀ file in theĀ contactsĀ directory of the Angular project. The following is the code for the service.
import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; import 'rxjs/add/operator/toPromise'; import { Contact } from './contact'; @Injectable() export class ContactService { constructor(private http: Http) { } getAll(): Promise<Contact[]> { return this.http.get('http://localhost:13322/api/contactsApi/') .toPromise() .then(response => response.json()) .then(contacts => Array.from(contacts, c => new Contact(c))) .catch(error => console.log(error)); } }
This class uses Angular’s HTTP client to access the API and download a list of contacts. Angular’s HTTP client uses reactive extensionsĀ and returns an observable. In this case we don’t need an observable so for this service the observable is being converted to a promise. Then from there the response from the API is being converted an array of typeĀ contact.
Also make note of theĀ InjectableĀ decorator which tells Angular 2 the class should be available for dependency injection.
View Model
The next step is to create a view model to support the view that will be used to display the contacts download from the API. Add a file named contactlist.component.tsĀ to the contactsĀ directory of the Angular project. The following is the full contents of the view model file. This will be followed by a breakdown of the file in order to highlight some parts of the file.
import { Component, OnInit } from '@angular/core'; import { Contact } from './contact'; import { ContactService } from './contact.service'; @Component({ selector: 'contactlist', template: require('./contactlist.component.html'), providers: [ContactService] }) export class ContactListComponent implements OnInit { contacts: Contact[]; constructor(private contactService: ContactService) { } ngOnInit(): void { this.contactService.getAll() .then(contacts => this.contacts = contacts); } }
The import statementsĀ are pulling in a couple parts of the Angular 2 framework in addition to the contact model and contact service created above.
Next is a component decorator which marks the class as an Angular component and provides a method to set metadata about the class.
@Component({ selector: 'contactlist', template: require('./contactlist.component.html'), providers: [ContactService] })
The selectorĀ property sets the identifier for the class to be used in templates. TheĀ templateĀ property sets the view that should be used with the view model. In this case it is requiring in another file, but it could also contain the actual template that should be used to render the component. An alternate is to use templateUrlĀ to point to an external file containing a template. The final property used in this example is theĀ providersĀ property which is a list of providers that the framework needs to be made available to the component, in this case theĀ ContractService. For more information on the component decorator check out the Angular docs.
The next thing of note on this class is that it implementsĀ OnInit.
export class ContactListComponent implements OnInit
OnInitĀ is one of Angular’s lifecycle hooks, see the docs for the rest of the available hooks.Ā OnInitĀ is called once after component creation and runs theĀ ngOnInitĀ function which in the case of this class is being used to get a list of contacts from theĀ ContactService.
View
For the view create aĀ contactlist.component.htmlĀ in theĀ contactsĀ directory of the Angular project. This is the file that the veiw model created above is bound with to display the contact data retrieved from the API. The following is the complete contents of the view file.
<ul> <li *ngFor="let contact of contacts"> <h4>{{contact.name}}</h4> <p>{{contact.getAddress()}}</p> </li> </ul>
The second line repeats the liĀ tag for each contact in the contacts array of the view model class.Ā {{expression}}Ā is Angular’s syntax for one way data binding.Ā {{contact.name}}Ā does a one way binding to theĀ nameĀ property of the current contact in theĀ *ngForĀ loop. For more details on the different options available for data binding see the docs.
Add menu
The final piece is to add an item to the menu from with the contact list can be accessed. OpenĀ app.module.tsĀ in theĀ ClientApp/app/components/Ā directory. Add an imports for theĀ ContactListComponent.
import { ContactListComponent } from './components/contacts/contactlist.component';
Next add a new path to theĀ RouterModule. The third from the bottom is the line that was added for the contact list.
RouterModule.forRoot([ { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: 'home', component: HomeComponent }, { path: 'counter', component: CounterComponent }, { path: 'fetch-data', component: FetchDataComponent }, { path: 'contact-list', component: ContactListComponent }, { path: '**', redirectTo: 'home' } ])
Finally open the navmenu.component.htmlĀ file in the ClientApp/app/components/navmenu/Ā directory. Add a newĀ liĀ for the contact list matching the following.
<li [routerLinkActive]="['link-active']"> <a [routerLink]="['/contact-list']"> <span class='glyphicon glyphicon-list-alt'></span> Contact List </a> </li>
Wrapping up
That is all it takes to consumeĀ some data from an ASP.NET Core API and use it in an Angular 2 application. I can’t stress enough how easy working with in the structure provided by JavaScriptServices helped in getting this project up and going quickly.
The completed code that goes along with this post can be found here. Also note that the Aurelia project has be redone as well also based on JavaScriptServices and TypeScript so that the applications will be easier to compare.