Overview
Progressive Web Applications will be the game changer for Mobile application, many big companies like Flipkart, Twitter, Trivago, AliExpress are already using PWA features.
Here we are going to build Contact book using Angular 7. The application shows the list of contacts which is added by the user. The user can also edit the contact and some native feature like the splash screen, offline support and sharing data on any social media.
Getting Started
Let’s start a New Project named Contact Book. (My current Angular CLI version is 7.0.2, Node version is 10.8.0, NPM version is 6.2.0)
ng new ContactBook --routing cd ContactBook ng serve --open
The command will open a new tab in the default browser. The command will serve the project on http://localhost:4200/ with some default content.
Adding Angular Material
For designing purpose, Here we are using an Angular material framework.
npm install --save @angular/material @angular/cdk npm install --save @angular/animations hammerjs
Also, need to import in styles.css
/* src/styles.css */ @import ‘~@angular/material/prebuilt-themes/indigo-pink.css’
Now we add some material design modules to our application like hammerjs and BrowserAnimationsModule . For the use of any component from the material design we need to import that. Here first we import Toolbar component in app.module.ts file. Don’t forget to add in imports.
import 'hammerjs'; import { BrowserAnimationsModule} from "@angular/platform-browser/animations"; import { MatToolbarModule } from "@angular/material";
Now we can use the toolbar in any component of this application. Here we use the toolbar in app.component.html file.
<mat-toolbar color="warn"> <h1>My Contact Book</h1> </mat-toolbar> <router-outlet></router-outlet>
Create RESTful API
We need to create RESTful API for our project. Here we create simple API with NodeJs. For API, we create a new folder named as server in the current directory and then add a package.json file via npm init. Now the next step is to install dependencies that we are going to use. Install express-nedb-rest (npm install express-nedb-rest –save) that is an open source API using express & nedb that create restful API automatically. Then we install cors ( npm install cors –save) that are a technique which will serve API to sub party domain.
Now we create a new file called index.js for our server. This server we will be able to get an element from the database, to put the element to the database, to update and delete the element from the database.
index.js.
const express = require("express"); const nedb = require("nedb"); const rest = require("express-nedb-rest"); const cors = require("cors"); const app = express(); const datastore = new nedb({ filename: "mycontactapp.db", autoload: true }); const restAPI = rest(); restAPI.addDatastore('contacts', datastore); app.use(cors()); app.use('/', restAPI); app.listen(3000)
Now it’s time to run our server with node index. We can see the result something like below in the browser on http://localhost:3000/. Now our server is ready to use.
[{“name”:”contacts”,”link”:”http://localhost:3000/contacts”}]
Time to Put some logic
Let’s take contact dynamically from the server. For that purpose, we need to add
contact.ts.
export class Contact { _id: string phoneno: number email: string relation: string notes: string constructor(public name: string = "", public address: string = "") { } }
Now we need to generate service via ng generate service data. Update providers in app.module.ts file. Data service is used to get data from the database, update data into the database. For that first, we need to import httpmodule in the app.module.ts file. Then request to the server with GET request to get data from the database. Save or update data into the database. Here endpoint URL is server URL which will be changed according to the server.
Data.service.ts.
export class DataService { constructor(private http: Http) { } public endpoint = "http://localhost:3000" getList(callback) { this.http.get(`${this.endpoint}/contacts`).subscribe(res => { callback(res.json()) }) } get(contactId: string, callback) { this.http.get(`${this.endpoint}/contacts/${contactId}`).subscribe(res => { callback(res.json()); }) } save(contact, callback) { if (contact._id) { this.http.put(`${this.endpoint}/contacts/${contact._id}`, contact).subscribe(res => { callback(true); }) } else { this.http.post(`${this.endpoint}/contacts`, contact).subscribe(res => { callback(true); }) } } }
Adding Components & Routing
We need to add components to separate contact detail form and display contact list which is added by the user and put routing for navigating from one page to another page. Now let’s generate component.
ng generate component list ng generate component contact
As a result, angular generate two separate folders with necessary files and also update in the app.module.ts file.
In list.component.html file, we need to import angular material components to app.module.ts file. Add the following code
<mat-card *ngFor="let contact of list"> <mat-card-title >{{contact.name}}</mat-card-title> <mat-card-subtitle>Number: {{contact.phoneno}}</mat-card-subtitle> <mat-card-actions> <button (click)="goShare(contact)" name="share" mat-button> <mat-icon>share</mat-icon> </button> <button (click)="goDetails(contact)" name="list" mat-button> <mat-icon>list</mat-icon> </button> </mat-card-actions> </mat-card> <a id="btn-create" mat-fab routerLink="/contact" style="background-color: #f44336"> <mat-icon alt="create">create</mat-icon> </a>
In list.component.ts file add function to bind data dynamically and add routes. For sharing purpose here we are using WhatsApp URL. You can find URL from https://faq.whatsapp.com/en/iphone/23559013
export class ListComponent implements OnInit { list: [Contact]; constructor(private data: DataService, private router: Router) { } ngOnInit() { this.data.getList(list =>{ this.list=list; } ) } goShare(contact: Contact) { const shareText = ` ${contact.name} 's contact no is ${contact.phoneno}`; if ('share' in navigator) { (navigator as any).share({ title: contact.name, text: shareText, url: window.location.href }).then(() => { console.log("shared"); }).catch(() => { console.log("error shared"); }); } else { const shareURL = `whatsapp://send?text=${encodeURIComponent(shareText)}` location.href = shareURL; } } goDetails(contact: Contact) { this.router.navigate(["/contact", contact._id]) } }
Now, we will create forms in contact.component.html file. Here we are using ngModule for binding data to the input.
<form action="" method="post"> <mat-card> <mat-card-title>Basic Coffee Information</mat-card-title> <mat-form-field> <input matInput name="name" [(ngModel)]="contact.name"> <mat-placeholder class="placeholder">Person name</mat-placeholder> </mat-form-field> <mat-form-field> <input matInput name="cno" [(ngModel)]="contact.phoneno"> <mat-placeholder class="placeholder">Contact No</mat-placeholder> </mat-form-field> <mat-form-field> <input matInput name="email" [(ngModel)]="contact.email"> <mat-placeholder class="placeholder">Email Address</mat-placeholder> </mat-form-field> <mat-form-field> <input matInput name="address" [(ngModel)]="contact.address"> <mat-placeholder class="placeholder">Address</mat-placeholder> </mat-form-field> </mat-card> <mat-card> <mat-select placeholder="Relation" [(ngModel)]="contact.relation" name="relation"> <mat-option *ngFor="let relation of relations" [value]="relation"> {{relation}} </mat-option> </mat-select> </mat-card> <mat-card> <mat-form-field> <textarea matInput name="notes" [(ngModel)]="contact.notes"></textarea> <mat-placeholder class="placeholder">Notes</mat-placeholder> </mat-form-field> </mat-card> <mat-card> <button mat-raised-button (click)=cancel() name="cancel">Cancel</button> <button mat-raised-button color="warn" (click)=save() name="save">Save</button> </mat-card> </form>
In contact.component.ts file we put logic to bind data with input and get the data according to the id for editing purpose.
export class ContactComponent implements OnInit { contact: Contact; // from contact.ts file routingSub: any; relations = ["Family", "Friends",”Partner”]; constructor(private route: ActivatedRoute, private router: Router, private data: DataService) { } ngOnInit() { this.contact = new Contact() this.routingSub = this.route.params.subscribe(params => { console.log(params['id']); if (params["id"]) { this.data.get(params['id'], res => { this.contact = res; }) } }); } cancel() { this.router.navigate(['/']); } save() { this.data.save(this.contact, result => { if (result) { this.router.navigate(['/']); } }) } ngOnDestory() { this.routingSub.unsubscribe(); } }
Check Audit
We need to check out how our application is going and where we need to change for PWA feature. All we need to do is open our Chrome DevTools and go to the Audits tab, where we can find such a powerful tool as Lighthouse — the best diagnostics of web pages. Now press Perform an Audit. it will take some time, then it will get some result. The result is not so good for now. We need to increase PWA score for the better result.