Posts Contact Application Using ASP.NET Core Web API, Angular 6.0, And Visual Studio Code - Part Two
Post
Cancel

Contact Application Using ASP.NET Core Web API, Angular 6.0, And Visual Studio Code - Part Two

In this article, we are going to learn about how we can setup Angular 6.0 in ASP.Net Core Web API project and develop the contact list & form component using Angular Material UI.

In the Part 1 article, we set up ASP.NET Core Web API and developed the API for contact CRUD operation. Please go through Contact Application using ASP.NET Core Web API, Angular 6.0, and Visual Studio Code Part 1 once before proceeding with this article.

To setup Angular 6.0 in the project: first, we have to install NodeJS in the development environment. We can download NodeJS installer from NodeJS website. and then check the version on NodeJS as below:

contactapp

Now, let us globally install Angular CLI package by enter “npm install –g angular/cli@6.0.0”, as shown in below screenshot

contactapp

Scaffold Angular

To scaffold Angular, enter “ng new Contact-App –skip-install” command in VS code terminal as below.

contactapp

Here, –skip-install option is used to skip installation of the npm packages. And Contact-App is our Angular app name.

contactapp

After the project is created, we move all the files & folders from Contact-App to Root folder as below:

contactapp

Then we enter ‘npm install’ command in terminal to install all required packages for Angular.

Change Angular Configuration

Go to “angular.json” file, it is a configuration schema file. We changed “wwwroot” folder path in OutputPath.

contactapp

Then enter ng build command in terminal.

contactapp

We can see generated files under wwwroot folder.

contactapp

Configure startup to route to angular application

We have added the following code to configure method in “startup.cs”. Here we have setup mvc default rout to index.html that generates under wwwroot folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
//Redirect non api calls to angular app that will handle routing of the app.    
app.Use(async (context, next) => {  
    await next();  
    if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value) && !context.Request.Path.Value.StartsWith("/api/")) {  
        context.Request.Path = "/index.html";  
        await next();  
    }  
});  
// configure the app to serve index.html from /wwwroot folder    
app.UseDefaultFiles();  
app.UseStaticFiles();  
// configure the app for usage as api    
app.UseMvcWithDefaultRoute(); 

Now run project by enter “dotnet run” command in terminal and open “localhost:5000” URL in browser.

contactapp

Setup angular material UI

Install angular material packages and dependencies.

contactapp

Please go through this article to get more details about material components.

1
2
ng add @angular/material
npm install -d @angular/cdk hammerjs

To use angular material UI components in our Angular contact application, we have created a separate module named “app.material.module.ts” in app folder. In this module, we imported all dependant material components and we have included in our main app module in this module . Then, we have imported Angular material theme in the main style.css in src folder

1
@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';

Then, we added this link of material icons into index.html in src folder.

1
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">  

Generate contact list & form component

To generate new component, we used “ng generate component” command as below:

contactapp

This we have set up routing for that component in “app.routing.ts”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import {  
    ModuleWithProviders  
} from '@angular/core';  
import {  
    Routes,  
    RouterModule  
} from '@angular/router';  
import {  
    AppComponent  
} from './app.component';  
import {  
    ContactlistComponent  
} from './contactlist/contactlist.component';  
import {  
    ContactformComponent  
} from './contactform/contactform.component';  
const appRoutes: Routes = [{  
    path: '',  
    pathMatch: 'full',  
    component: ContactlistComponent  
}, {  
    path: 'contactform',  
    component: ContactformComponent  
}];  
export const Routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);  

Create angular services

To create a consumed contact API that we have created Part 1; we are generating Angular contact services class in app folder using this command:

contactapp

1
ng generate service contact
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import {  
    Injectable  
} from '@angular/core';  
import {  
    HttpClient,  
    HttpParams,  
    HttpErrorResponse  
} from '@angular/common/http';  
import {  
    HttpHeaders  
} from '@angular/common/http';  
import {  
    Observable,  
    throwError  
} from 'rxjs';  
import {  
    catchError  
} from 'rxjs/operators';  
import {  
    IContact  
} from '../model/contact';  
const httpOptions = {  
    headers: new HttpHeaders({  
        'Content-Type': 'application/json'  
    })  
};  
@Injectable()  
export class ContactService {  
    constructor(private http: HttpClient) {}  
    // get all contact data    
    getAllContact(url: string): Observable < IContact[] > {  
        return this.http.get < IContact[] > (url).pipe(catchError(this.handleError));  
    }  
    // insert new contact details    
    addContact(url: string, contact: IContact): Observable < any > {  
        return this.http.post(url, JSON.stringify(contact), httpOptions).pipe(catchError(this.handleError));  
    }  
    // update contact details    
    updateContact(url: string, id: number, contact: IContact): Observable < any > {  
        const newurl = `${url}?id=${id}`;  
        return this.http.put(newurl, contact, httpOptions).pipe(catchError(this.handleError));  
    }  
    // delete contact information    
    deleteContact(url: string, id: number): Observable < any > {  
        const newurl = `${url}?id=${id}`; // DELETE api/contact?id=42    
        return this.http.delete(newurl, httpOptions).pipe(catchError(this.handleError));  
    }  
    // custom handler    
    private handleError(error: HttpErrorResponse) {  
        if (error.error instanceof ErrorEvent) {  
            // A client-side or network error occurred. Handle it accordingly.    
            console.error('An error occurred:', error.error.message);  
        } else {  
            // the backend returned an unsuccessful response code.    
            // the response body may contain clues as to what went wrong,    
            console.error(`Backend returned code ${error.status}, ` + `body was: ${error.error}`);  
        }  
        // return an observable with a user-facing error message    
        return throwError('Something bad happened; please try again later.');  
    }  
} 

Update main app html template

We have included router outlet place holder to render component based on Angular route.

1
2
3
<!--The content below is only a placeholder and can be replaced.-->  
<mat-toolbar> <span>Contact Application</span> </mat-toolbar>  
<router-outlet></router-outlet>  

Update contact form component

In contact form component, we used form group named as ‘contactFrm’ to bind model with each control and injected dialog data to bind form based on request parameter.

contactform.component.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<form  (ngSubmit)="onSubmit(contactFrm)"  [formGroup]="contactFrm">  
  <h2></h2>  
    
  <div>  
      <mat-form-field appearance="outline">  
      <mat-label>Name</mat-label>  
      <input matInput placeholder="Name" formControlName="name">  
      <!-- <mat-icon matSuffix>sentiment_very_satisfied</mat-icon> -->  
      <!-- <mat-hint>Hint</mat-hint> -->  
      <mat-error *ngIf="formErrors.name">  
          
      </mat-error>  
    </mat-form-field>  
  </div>  
  <div>  
    <mat-form-field appearance="outline">  
      <mat-label>Email</mat-label>  
      <input type="email" matInput placeholder="email" formControlName="email">  
      <mat-error *ngIf="formErrors.email">  
          
      </mat-error>  
    </mat-form-field>  
    
  </div>  
  <p>  
      <mat-radio-group class="contact-radio-group" formControlName="gender" >  
        <mat-radio-button class="contact-radio-button" *ngFor="let gndr of genders" [value]="gndr.id">  
            
        </mat-radio-button>  
      </mat-radio-group>  
      <mat-error *ngIf="formErrors.gender">  
          
      </mat-error>  
  </p>  
  <div>  
    <mat-form-field appearance="outline">  
      <input matInput [matDatepicker]="picker" placeholder="Choose a birthday" formControlName="birth">  
      <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>  
      <mat-datepicker #picker></mat-datepicker>  
      
    <mat-error *ngIf="formErrors.birth ">  
        
    </mat-error>  
    </mat-form-field>  
  </div>  
  <div>  
    <mat-form-field appearance="outline">  
      <mat-select placeholder="Select a Technology" formControlName="techno">  
        <mat-option>-- None --</mat-option>  
        <mat-option *ngFor="let techno  of technologies" [value]="techno">  
            
        </mat-option>  
      </mat-select>  
      <mat-error *ngIf="formErrors.techno ">  
          
      </mat-error>  
    </mat-form-field>  
  </div>  
  <div>  
    <mat-form-field appearance="outline">  
      <textarea matInput placeholder="Message..." formControlName="message"></textarea>  
      <mat-error *ngIf="formErrors.message ">  
          
      </mat-error>  
    </mat-form-field>  
  </div>  
  <div>  
    
    <button type="button" mat-raised-button color="warn" (click)="dialogRef.close()">Cancel</button>   
    <button type="submit" mat-raised-button color="primary" [disabled]="contactFrm.invalid"></button>  
  </div>  
    
  </form> 

To see contactform.component.ts code please refer this GitHub link and final form looks as shown in the below screenshot.

contactapp

Update contact list component

In contact list component, we have used material table to bind contact record using ‘MatTableDataSource’ and called contact service to retrieve data from API. Then we used MatDialog to open contact form component. contactlist.component.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<div class="spinner" *ngIf="loadingState; else contactlist">  
<mat-spinner></mat-spinner>  
</div>  
<ng-template class="contactlist" #contactlist>  
  <h2 style="text-align: center;">Contact List</h2>  
  <div class="contactlist-container mat-elevation-z8">  
    <div><button title="Create" mat-raised-button color="accent" (click)="addContact()">Create</button></div>  
    <table mat-table #table [dataSource]="dataSource">  
  
      <!-- Id Column -->  
      <!-- <ng-container matColumnDef="id">  
      <th mat-header-cell *matHeaderCellDef> Id </th>  
      <td mat-cell *matCellDef="let element">  </td>  
    </ng-container> -->  
  
      <!-- Name Column -->  
      <ng-container matColumnDef="name">  
        <th mat-header-cell *matHeaderCellDef> Name </th>  
        <td mat-cell *matCellDef="let element">  </td>  
      </ng-container>  
  
      <!-- Email Column -->  
      <ng-container matColumnDef="email">  
        <th mat-header-cell *matHeaderCellDef> Email </th>  
        <td mat-cell *matCellDef="let element">  </td>  
      </ng-container>  
  
      <!-- Gender Column -->  
      <ng-container matColumnDef="gender">  
        <th mat-header-cell *matHeaderCellDef> Gender </th>  
        <td mat-cell *matCellDef="let element">  </td>  
      </ng-container>  
  
      <!-- Birth Column -->  
      <ng-container matColumnDef="birth">  
        <th mat-header-cell *matHeaderCellDef> Birthday </th>  
        <td mat-cell *matCellDef="let element">  </td>  
      </ng-container>  
  
      <!-- Technology Column -->  
      <ng-container matColumnDef="techno">  
        <th mat-header-cell *matHeaderCellDef> Technology </th>  
        <td mat-cell *matCellDef="let element">  </td>  
      </ng-container>  
  
      <!-- Message Column -->  
      <ng-container matColumnDef="message">  
        <th mat-header-cell *matHeaderCellDef> Message </th>  
        <td mat-cell *matCellDef="let element">  </td>  
      </ng-container>  
  
      <ng-container matColumnDef="action">  
        <th mat-header-cell *matHeaderCellDef> Action </th>  
        <td mat-cell *matCellDef="let element">  
          <button title="Edit" mat-raised-button color="primary" (click)="editContact(element.id)">Edit</button>  
          <button title="Delete" mat-raised-button color="warn" (click)="deleteContact(element.id)">Delete</button>  
        </td>  
      </ng-container>  
  
      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>  
      <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>  
    </table>  
  
  </div>  
</ng-template>  

To see contactlist.component.ts code please refer to this GitHub https://github.com/JayeshAgrawal/contact-app/blob/master/src/app/contactlist/contactlist.component.ts.

Build components and run the project

Our component is ready. Now build Angular project using ‘ng build’ command and then run the project by entering ‘dotnet run’ command in terminal.

contactapp

contactapp

Conclusion

This is how we created contact application using ASP.Net Core Web API, Angular 6.0, and Visual Studio code. You can see the entire code on GitHub https://github.com/JayeshAgrawal/contact-app/ and fork it for the starter project.

Please let me know your feedback.