Gizzy Berry

Gizzy Berry

1581654606

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

A comprehensive step by step Angular 9 tutorial on learning to build a CRUD (create, read, update, delete) Angular app quickly. We will create an Angular app of Coronavirus cases that will use REST API that gets the required data, post the case data, put case changes, delete case data, and get statistic data that displayed as a bar-charts. So, we will learn how to use HTTPClient to access REST API, using Angular Form with Angular Material, using Routing and Navigation, and display bar charts using Ng2Charts and Chart.js.

This tutorial divided into several steps:

  • Step #1. Create a New Angular 9 App
  • Step #2. Add the Angular 9 Routing and Navigation
  • Step #3. Add the Angular 9 Service
  • Step #4. Display List of Data using Angular Material
  • Step #5. Show and Delete Data Details using Angular Material
  • Step #6. Show Statistic using Ng2Charts and Chart.js
  • Step #7. Add a Data using Angular Material Form
  • Step #8. Edit a Data using Angular Material Form
  • Step #9. Run and Test the Angular 9 Coronavirus Cases App

In the end, the final Angular 9 Coronavirus Cases app will look like this.

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly - List
Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly - Charts
Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly - Details
Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly - Add
Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly - Edit

The following tools, frameworks, and modules are required for this tutorial:

  1. Node.js (recommended version)
  2. Angular 9
  3. Coronavirus REST API
  4. Terminal (Mac/Linux) or Node Command Line (Windows)
  5. IDE or Text Editor (We are using Visual Studio Code)

We already provided the REST API for the Polyclinic reservation, you can just clone, install NPM, run MongoDB server, and Run the Express/MongoDB REST API server. We assume that you have installed Node.js. Now, we need to check the Node.js and NPM versions. Open the terminal or Node command line then type these commands.

node -v
v10.15.1
npm -v
6.13.6

Step #1. Create a New Angular 9 App

We will create a new Angular 9 App using Angular CLI. For that, we need to install or update the @angular/cli first to the latest version.

sudo npm install -g @angular/cli

Next, create a new Angular 9 app by running this command.

ng new coronavirus-cases

If you get the question like below, choose Yes and SCSS (or whatever you like to choose).

? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? SCSS   [ https://sass-lang.com/
documentation/syntax#scss                ]

Next, go to the newly created Angular 9 project folder.

cd coronavirus-cases

Open this Angular project with your IDE or Text editor. To use VSCode type this command.

code .

Type this command to run the Angular 9 app for the first time.

ng serve --open

Using the “–open” parameter will automatically open this Angular 9 app in the default browser. Now, the Angular initial app looks like this.

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly - Initial Page

Step #2. Add the Angular 9 Routing and Navigation

As you see in the first step of creating an Angular 9 app. We already add the Angular Routing for this Angular 9 app. Next, we just add the required Angular components for this Coronavirus cases app. Just type these commands to generate them.

ng g component cases
ng g component cases-details
ng g component add-cases
ng g component edit-cases 
ng g component cases-stat

Those components will automatically be registered to the app.module.ts. Next, open and edit src/app/app-routing.module.ts then add these imports.

import { CasesComponent } from './cases/cases.component';
import { CasesDetailsComponent } from './cases-details/cases-details.component';
import { CasesStatComponent } from './cases-stat/cases-stat.component';
import { AddCasesComponent } from './add-cases/add-cases.component';
import { EditCasesComponent } from './edit-cases/edit-cases.component';

Add these arrays to the existing routes constant that contain route for above-added components.

const routes: Routes = [
  {
    path: 'cases',
    component: CasesComponent,
    data: { title: 'List of Cases' }
  },
  {
    path: 'cases-details/:id',
    component: CasesDetailsComponent,
    data: { title: 'Cases Details' }
  },
  {
    path: 'cases-stat',
    component: CasesStatComponent,
    data: { title: 'Cases Statistic' }
  },
  {
    path: 'add-cases',
    component: AddCasesComponent,
    data: { title: 'Add Cases' }
  },
  {
    path: 'edit-cases/:id',
    component: EditCasesComponent,
    data: { title: 'Edit Cases' }
  },
  { path: '',
    redirectTo: '/cases',
    pathMatch: 'full'
  }
];

Open and edit src/app/app.component.html and you will see the existing router outlet. Next, modify this HTML page to fit the CRUD page.

<div class="container">
  <router-outlet></router-outlet>
</div>

Open and edit src/app/app.component.scss then replace all SASS codes with this.

.container {
  padding: 20px;
}

Step #3. Add the Angular 9 Service

All-access (POST, GET, PUT, DELETE) to the REST API will put in the Angular 9 Service. The response from the REST API emitted by Observable that can subscribe and read from the Components. Before creating a service for REST API access, first, we have to install or register HttpClientModule. Open and edit src/app/app.module.ts then add these imports of FormsModule, ReactiveFormsModule (@angular/forms) and HttpClientModule (@angular/common/http).

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';

Add it to @NgModule imports after BrowserModule.

imports: [
  BrowserModule,
  FormsModule,
  ReactiveFormsModule,
  HttpClientModule,
  AppRoutingModule
],

We will use the type specifier to get a typed result object. For that, create a new Typescript file src/app/cases.ts then add these lines of Typescript codes.

export class Cases {
  _id: string;
  name: string;
  gender: string;
  age: number;
  address: string;
  city: string;
  country: string;
  status: string;
  updated: Date;
}

And create a new Typescript file src/app/statistic.ts then add these lines of Typescript codes.

export class Statistic {
  _id: any;
  count: number;
}

Next, generate an Angular 9 service by typing this command.

ng g service api

Next, open and edit src/app/api.service.ts then add these imports.

import { Observable, of, throwError } from 'rxjs';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { catchError, tap, map } from 'rxjs/operators';
import { Cases } from './cases';
import { Statistic } from './statistic';

Add these constants before the @Injectable.

const httpOptions = {
  headers: new HttpHeaders({'Content-Type': 'application/json'})
};
const apiUrl = '/api/';

Inject the HttpClient module to the constructor.

constructor(private http: HttpClient) { }

Add the error handler function that returns as an Observable.

private handleError<T> (operation = 'operation', result?: T) {
  return (error: any): Observable<T> => {

    // TODO: send the error to remote logging infrastructure
    console.error(error); // log to console instead

    // Let the app keep running by returning an empty result.
    return of(result as T);
  };
}

Add the functions for all CRUD (create, read, update, delete) REST API call of cases and statistic data.

  getCases(): Observable<Cases[]> {
    return this.http.get<Cases[]>(`${apiUrl}`)
      .pipe(
        tap(cases => console.log('fetched cases')),
        catchError(this.handleError('getCases', []))
      );
  }

  getCasesById(id: string): Observable<Cases> {
    const url = `${apiUrl}/${id}`;
    return this.http.get<Cases>(url).pipe(
      tap(_ => console.log(`fetched cases id=${id}`)),
      catchError(this.handleError<Cases>(`getCasesById id=${id}`))
    );
  }

  addCases(cases: Cases): Observable<Cases> {
    return this.http.post<Cases>(apiUrl, cases, httpOptions).pipe(
      tap((c: Cases) => console.log(`added product w/ id=${c._id}`)),
      catchError(this.handleError<Cases>('addCases'))
    );
  }

  updateCases(id: string, cases: Cases): Observable<any> {
    const url = `${apiUrl}/${id}`;
    return this.http.put(url, cases, httpOptions).pipe(
      tap(_ => console.log(`updated cases id=${id}`)),
      catchError(this.handleError<any>('updateCases'))
    );
  }

  deleteCases(id: string): Observable<Cases> {
    const url = `${apiUrl}/${id}`;
    return this.http.delete<Cases>(url, httpOptions).pipe(
      tap(_ => console.log(`deleted cases id=${id}`)),
      catchError(this.handleError<Cases>('deleteCases'))
    );
  }

  getStatistic(status: string): Observable<Statistic> {
    const url = `${apiUrl}/daily/${status}`;
    return this.http.get<Statistic>(url).pipe(
      tap(_ => console.log(`fetched statistic status=${status}`)),
      catchError(this.handleError<Statistic>(`getStatistic status=${status}`))
    );
  }

Step #4. Display List of Data using Angular Material

We will display the list of data using the Angular Material Table. The data published from the API service read by subscribing as a Cases model in the Angular 9 component. For that, open and edit src/app/cases/cases.component.ts then add this import of the previously created API Service.

import { ApiService } from '../api.service';

Next, inject the API Service to the constructor.

constructor(private api: ApiService) { }

Next, for the user interface (UI) we will use Angular Material and CDK. There’s a CLI for generating a Material component like Table as a component, but we will create or add the Table component from scratch to the existing component. Type this command to install Angular Material (@angular/material).

ng add @angular/material

If there are questions like below, just use the default and “Yes” answer.

? Choose a prebuilt theme name, or "custom" for a custom theme: Indigo/Pink        [ Preview: http
s://material.angular.io?theme=indigo-pink ]
? Set up global Angular Material typography styles? Yes
? Set up browser animations for Angular Material? Yes

We will register all required Angular Material components or modules to src/app/app.module.ts. Open and edit that file then add these imports of required Angular Material Components.

import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSliderModule } from '@angular/material/slider';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatSelectModule } from '@angular/material/select';

Register the above modules to @NgModule imports.

  imports: [
    ...
    MatInputModule,
    MatPaginatorModule,
    MatProgressSpinnerModule,
    MatSortModule,
    MatTableModule,
    MatIconModule,
    MatButtonModule,
    MatCardModule,
    MatFormFieldModule,
    MatSliderModule,
    MatSlideToggleModule,
    MatButtonToggleModule,
    MatSelectModule,
  ],

Next, back to src/app/cases/cases.component.ts then add this import.

import { Cases } from '../cases';

Declare the variables of Angular Material Table Data Source before the constructor.

  displayedColumns: string[] = ['name', 'age', 'status'];
  data: Cases[] = [];
  isLoadingResults = true;

Modify the ngOnInit function to get a list of cases immediately.

  ngOnInit(): void {
    this.api.getCases()
    .subscribe((res: any) => {
      this.data = res;
      console.log(this.data);
      this.isLoadingResults = false;
    }, err => {
      console.log(err);
      this.isLoadingResults = false;
    });
  }

Next, open and edit src/app/cases/cases.component.html then replace all HTML tags with this Angular Material tags.

<div class="example-container mat-elevation-z8">
  <h2>Corona Virus Cases List</h2>
  <div class="example-loading-shade"
       *ngIf="isLoadingResults">
    <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
  </div>
  <div class="button-row">
    <a mat-flat-button color="primary" [routerLink]="['/add-cases']"><mat-icon>add</mat-icon> Cases</a>
    <a mat-flat-button color="accent" [routerLink]="['/cases-stat']"><mat-icon>bar_chart</mat-icon> Statistic</a>
  </div>
  <div class="mat-elevation-z8">
    <table mat-table [dataSource]="data" class="example-table"
           matSort matSortActive="name" matSortDisableClear matSortDirection="asc">

      <!-- Cases Name Column -->
      <ng-container matColumnDef="name">
        <th mat-header-cell *matHeaderCellDef>Cases Name</th>
        <td mat-cell *matCellDef="let row">{{row.name}}</td>
      </ng-container>

      <!-- Cases Age Column -->
      <ng-container matColumnDef="age">
        <th mat-header-cell *matHeaderCellDef>Age</th>
        <td mat-cell *matCellDef="let row">{{row.age}}</td>
      </ng-container>

      <!-- Cases Status Column -->
      <ng-container matColumnDef="status">
        <th mat-header-cell *matHeaderCellDef>Status</th>
        <td mat-cell *matCellDef="let row">{{row.status}}</td>
      </ng-container>

      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/cases-details/', row._id]"></tr>
    </table>
  </div>
</div>

Finally, to make a little UI adjustment, open and edit src/app/cases/cases.component.scss then add these CSS codes.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-table-contai

Step #5. Show and Delete Data Details using Angular Material

On the list page, there are 2 buttons to navigate to the Details and Statistic page. For, Details page the button action also sends an ID parameter. Next, open and edit src/app/cases-details/cases-details.component.ts then add these lines of imports.

import { ActivatedRoute, Router } from '@angular/router';
import { ApiService } from '../api.service';
import { Cases } from '../cases';

Inject the above modules to the constructor.

constructor(private route: ActivatedRoute, private api: ApiService, private router: Router) { }

Declare the variables before the constructor for hold cases data that get from the API.

  cases: Cases = { _id: '', name: '', gender: '', age: null, address: '', city: '', country: '', status: '', updated: null };
  isLoadingResults = true;

Add a function for getting Cases data from the API.

  getCasesDetails(id: string) {
    this.api.getCasesById(id)
      .subscribe((data: any) => {
        this.cases = data;
        console.log(this.cases);
        this.isLoadingResults = false;
      });
  }

Call that function when the component is initiated.

  ngOnInit(): void {
    this.getCasesDetails(this.route.snapshot.params.id);
  }

Add this function to delete a case.

  deleteCases(id: any) {
    this.isLoadingResults = true;
    this.api.deleteCases(id)
      .subscribe(res => {
          this.isLoadingResults = false;
          this.router.navigate(['/cases']);
        }, (err) => {
          console.log(err);
          this.isLoadingResults = false;
        }
      );
  }

For the view, open and edit src/app/cases-details/cases-details.component.html then replace all HTML tags with this.

<div class="example-container mat-elevation-z8">
  <h2>Corona Virus Cases Details</h2>
  <div class="example-loading-shade"
       *ngIf="isLoadingResults">
    <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
  </div>
  <div class="button-row">
    <a mat-flat-button color="primary" [routerLink]="['/cases']"><mat-icon>list</mat-icon></a>
  </div>
  <mat-card class="example-card">
    <mat-card-header>
      <mat-card-title><h2>{{cases.name}}</h2></mat-card-title>
      <mat-card-subtitle>{{cases.age}} year old</mat-card-subtitle>
    </mat-card-header>
    <mat-card-content>
      <dl>
        <dt>Gender:</dt>
        <dd>{{cases.gender}}</dd>
        <dt>Address:</dt>
        <dd>{{cases.address}}</dd>
        <dt>City:</dt>
        <dd>{{cases.city}}</dd>
        <dt>Country:</dt>
        <dd>{{cases.country}}</dd>
        <dt>Status:</dt>
        <dd><h2>{{cases.status}}</h2></dd>
      </dl>
    </mat-card-content>
    <mat-card-actions>
      <a mat-flat-button color="primary" [routerLink]="['/edit-cases', cases._id]"><mat-icon>edit</mat-icon> Cases</a>
      <a mat-flat-button color="warn" (click)="deleteCases(cases._id)"><mat-icon>delete</mat-icon> Cases</a>
    </mat-card-actions>
  </mat-card>
</div>

Finally, open and edit src/app/cases-details/cases-details.component.scss then add this lines of CSS codes.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-loading-shade {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 56px;
  right: 0;
  background: rgba(0, 0, 0, 0.15);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.mat-flat-button {
  margin: 5px;
}

Step #6. Show Statistic using Ng2Charts and Chart.js

We will use a bar chart to display the statistic of Coronavirus cases. So, we need to install Ng2Charts and Chart.js modules by type this command.

npm i --save ng2-charts chart.js

Next, open and edit src/app/app.module.ts then add this import of ng2-charts.

import { ChartsModule } from 'ng2-charts';

Add this module to the @NgModule imports.

  imports: [
    ...
    ChartsModule
  ],

Next, open and edit src/app/cases-stat/cases-stat.component.ts then add these imports of chart.js ChartOptions, ChartType, ChartDataSets, ng2-charts Label, ApiService, and Statistic data type.

import { ChartOptions, ChartType, ChartDataSets } from 'chart.js';
import { Label } from 'ng2-charts';
import { ApiService } from '../api.service';
import { Statistic } from '../statistic';

Declare these required variables before the constructor for building a bar chart.

  stats: Statistic[] = [];
  label = 'Positive';
  isLoadingResults = true;
  barChartOptions: ChartOptions = {
    responsive: true,
  };
  barChartLabels: Label[] = [];
  barChartType: ChartType = 'bar';
  barChartLegend = true;
  barChartPlugins = [];
  barChartData: ChartDataSets[] = [{ data: [], backgroundColor: [], label: this.label }];

Inject ApiService to the constructor.

constructor(private api: ApiService) { }

Add a function to load statistic data from REST API then implement it as a bar chart.

  getStatistic(status: string) {
    this.barChartData = [{ data: [], backgroundColor: [], label: this.label }];
    this.barChartLabels = [];
    this.api.getStatistic(status)
    .subscribe((res: any) => {
      this.stats = res;
      const chartdata: number[] = [];
      const chartcolor: string[] = [];
      this.stats.forEach((stat) => {
        this.barChartLabels.push(stat._id.date);
        chartdata.push(stat.count);
        if (this.label === 'Positive') {
          chartcolor.push('rgba(255, 165, 0, 0.5)');
        } else if (this.label === 'Dead') {
          chartcolor.push('rgba(255, 0, 0, 0.5)');
        } else {
          chartcolor.push('rgba(0, 255, 0, 0.5)');
        }
      });
      this.barChartData = [{ data: chartdata, backgroundColor: chartcolor, label: this.label }];
      this.isLoadingResults = false;
    }, err => {
      console.log(err);
      this.isLoadingResults = false;
    });
  }

Call that function to the NgOnInit function.

  ngOnInit(): void {
    this.getStatistic(this.label);
  }

Add a function to switch or reload statistic data by status value.

  changeStatus() {
    this.isLoadingResults = true;
    this.getStatistic(this.label);
  }

Next, open and edit src/app/cases-stat/cases-stat.component.html then replace all HTML tags with this implementation of an ng2-charts/Chart.js bar chart with statistic data.

<div class="example-container mat-elevation-z8">
  <h2>Corona Virus Cases Statistic</h2>
  <div class="example-loading-shade"
       *ngIf="isLoadingResults">
    <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
  </div>
  <div class="button-row">
    <a mat-flat-button color="primary" [routerLink]="['/cases']"><mat-icon>list</mat-icon></a>
  </div>
  <div class="button-row">
    <mat-button-toggle-group name="status" aria-label="Status" [(ngModel)]="label" (ngModelChange)="changeStatus()">
      <mat-button-toggle value="Positive">Positive</mat-button-toggle>
      <mat-button-toggle value="Dead">Dead</mat-button-toggle>
      <mat-button-toggle value="Recovered">Recovered</mat-button-toggle>
    </mat-button-toggle-group>
  </div>
  <div style="display: block;">
    <canvas baseChart
      [datasets]="barChartData"
      [labels]="barChartLabels"
      [options]="barChartOptions"
      [plugins]="barChartPlugins"
      [legend]="barChartLegend"
      [chartType]="barChartType">
    </canvas>
  </div>
</div>

Finally, give it a little style by modify src/app/cases-stat/cases-stat.component.scss with these.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-loading-shade {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 56px;
  right: 0;
  background: rgba(0, 0, 0, 0.15);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.mat-flat-button {
  margin: 5px;
}

Step #7. Add a Data using Angular Material Form

To create a form for adding a Coronavirus case, open and edit src/app/add-cases/add-cases.component.ts then add these imports.

import { Router } from '@angular/router';
import { ApiService } from '../api.service';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';

Inject the above modules to the constructor.

constructor(private router: Router, private api: ApiService, private formBuilder: FormBuilder) { }

Declare variables for the Form Group and all of the required fields inside the form before the constructor.

  casesForm: FormGroup;
  name = '';
  gender = '';
  age: number = null;
  address = '';
  city = '';
  country = '';
  status = '';
  statusList = ['Positive', 'Dead', 'Recovered'];
  genderList = ['Male', 'Female'];
  isLoadingResults = false;
  matcher = new MyErrorStateMatcher();

Add initial validation for each field.

  ngOnInit(): void {
    this.casesForm = this.formBuilder.group({
      name : [null, Validators.required],
      gender : [null, Validators.required],
      age : [null, Validators.required],
      address : [null, Validators.required],
      city : [null, Validators.required],
      country : [null, Validators.required],
      status : [null, Validators.required]
    });
  }

Create a function for submitting or POST product form.

  onFormSubmit() {
    this.isLoadingResults = true;
    this.api.addCases(this.casesForm.value)
      .subscribe((res: any) => {
          const id = res._id;
          this.isLoadingResults = false;
          this.router.navigate(['/cases-details', id]);
        }, (err: any) => {
          console.log(err);
          this.isLoadingResults = false;
        });
  }

Create a new class before the main class @Components.

/** Error when invalid control is dirty, touched, or submitted. */
export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const isSubmitted = form && form.submitted;
    return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
  }
}

Next, open and edit src/app/add-cases/add-cases.component.html then replace all HTML tags with this.

<div class="example-container mat-elevation-z8">
  <h2>Coronavirus Add Cases</h2>
  <div class="example-loading-shade"
        *ngIf="isLoadingResults">
    <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
  </div>
  <div class="button-row">
    <a mat-flat-button color="primary" [routerLink]="['/cases']"><mat-icon>list</mat-icon></a>
  </div>
  <mat-card class="example-card">
    <form [formGroup]="casesForm" (ngSubmit)="onFormSubmit()">
      <mat-form-field class="example-full-width">
        <mat-label>Name</mat-label>
        <input matInput placeholder="Name" formControlName="name"
                [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!casesForm.get('name').valid && casesForm.get('name').touched">Please enter Name</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <mat-label>Gender</mat-label>
        <mat-select formControlName="gender">
          <mat-option *ngFor="let gl of genderList" [value]="gl">
            {{gl}}
          </mat-option>
        </mat-select>
        <mat-error>
          <span *ngIf="!casesForm.get('gender').valid && casesForm.get('gender').touched">Please choose Gender</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <mat-label>Age</mat-label>
        <input matInput type="number" placeholder="Age" formControlName="age"
                [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!casesForm.get('age').valid && casesForm.get('age').touched">Please enter Age</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <mat-label>Address</mat-label>
        <input matInput placeholder="Address" formControlName="address"
                [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!casesForm.get('address').valid && casesForm.get('address').touched">Please enter Address</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <mat-label>City</mat-label>
        <input matInput placeholder="City" formControlName="city"
                [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!casesForm.get('city').valid && casesForm.get('city').touched">Please enter City</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <mat-label>Country</mat-label>
        <input matInput placeholder="Country" formControlName="country"
                [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!casesForm.get('country').valid && casesForm.get('country').touched">Please enter Country</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <mat-label>Status</mat-label>
        <mat-select formControlName="status">
          <mat-option *ngFor="let sl of statusList" [value]="sl">
            {{sl}}
          </mat-option>
        </mat-select>
        <mat-error>
          <span *ngIf="!casesForm.get('status').valid && casesForm.get('status').touched">Please select Status</span>
        </mat-error>
      </mat-form-field>
      <div class="button-row">
        <button type="submit" [disabled]="!casesForm.valid" mat-flat-button color="primary"><mat-icon>save</mat-icon></button>
      </div>
    </form>
  </mat-card>
</div>

Finally, open and edit src/app/add-cases/add-cases.component.scss then add this CSS codes.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-form {
  min-width: 150px;
  max-width: 500px;
  width: 100%;
}

.example-full-width {
  width: 100%;
}

.example-full-width:nth-last-child(0) {
  margin-bottom: 10px;
}

.button-row {
  margin: 10px 0;
}

.mat-flat-button {
  margin: 5px;
}

Step #8. Edit a Data using Angular Material Form

We already put an edit button inside the Cases Details component for the call Edit page. Now, open and edit src/app/edit-cases/edit-cases.component.ts then add these imports.

import { Router, ActivatedRoute } from '@angular/router';
import { ApiService } from '../api.service';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';

Inject the above modules to the constructor.

constructor(private router: Router, private route: ActivatedRoute, private api: ApiService, private formBuilder: FormBuilder) { }

Declare the Form Group variable and all of the required variables for the cases-form before the constructor.

  casesForm: FormGroup;
  _id = '';
  name = '';
  gender = '';
  age: number = null;
  address = '';
  city = '';
  country = '';
  status = '';
  statusList = ['Positive', 'Dead', 'Recovered'];
  genderList = ['Male', 'Female'];
  isLoadingResults = false;
  matcher = new MyErrorStateMatcher();

Next, add validation for all fields when the component is initiated.

  ngOnInit(): void {
    this.getCasesById(this.route.snapshot.params.id);
    this.casesForm = this.formBuilder.group({
      name : [null, Validators.required],
      gender : [null, Validators.required],
      age : [null, Validators.required],
      address : [null, Validators.required],
      city : [null, Validators.required],
      country : [null, Validators.required],
      status : [null, Validators.required]
    });
  }

Create a function for getting cases data that filled to each form field.

  getCasesById(id: any) {
    this.api.getCasesById(id).subscribe((data: any) => {
      this._id = data._id;
      this.casesForm.setValue({
        name: data.name,
        gender: data.gender,
        age: data.age,
        address: data.address,
        city: data.city,
        country: data.country,
        status: data.status
      });
    });
  }

Create a function to update the case changes.

  onFormSubmit() {
    this.isLoadingResults = true;
    this.api.updateCases(this._id, this.casesForm.value)
      .subscribe((res: any) => {
          const id = res._id;
          this.isLoadingResults = false;
          this.router.navigate(['/cases-details', id]);
        }, (err: any) => {
          console.log(err);
          this.isLoadingResults = false;
        }
      );
  }

Add a function for handling the show product details button.

  casesDetails() {
    this.router.navigate(['/cases-details', this._id]);
  }

Create a new class before the main class @Components.

/** Error when invalid control is dirty, touched, or submitted. */
export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const isSubmitted = form && form.submitted;
    return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
  }
}

Next, open and edit src/app/edit-cases/edit-cases.component.html then replace all HTML tags with this.

<div class="example-container mat-elevation-z8">
  <h2>Coronavirus Edit Cases</h2>
  <div class="example-loading-shade"
        *ngIf="isLoadingResults">
    <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
  </div>
  <div class="button-row">
      <a mat-flat-button color="primary" (click)="casesDetails()"><mat-icon>info</mat-icon></a>
  </div>
  <mat-card class="example-card">
    <form [formGroup]="casesForm" (ngSubmit)="onFormSubmit()">
      <mat-form-field class="example-full-width">
        <mat-label>Name</mat-label>
        <input matInput placeholder="Name" formControlName="name"
                [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!casesForm.get('name').valid && casesForm.get('name').touched">Please enter Name</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <mat-label>Gender</mat-label>
        <mat-select formControlName="gender">
          <mat-option *ngFor="let gl of genderList" [value]="gl">
            {{gl}}
          </mat-option>
        </mat-select>
        <mat-error>
          <span *ngIf="!casesForm.get('gender').valid && casesForm.get('gender').touched">Please choose Gender</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <mat-label>Age</mat-label>
        <input matInput type="number" placeholder="Age" formControlName="age"
                [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!casesForm.get('age').valid && casesForm.get('age').touched">Please enter Age</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <mat-label>Address</mat-label>
        <input matInput placeholder="Address" formControlName="address"
                [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!casesForm.get('address').valid && casesForm.get('address').touched">Please enter Address</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <mat-label>City</mat-label>
        <input matInput placeholder="City" formControlName="city"
                [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!casesForm.get('city').valid && casesForm.get('city').touched">Please enter City</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <mat-label>Country</mat-label>
        <input matInput placeholder="Country" formControlName="country"
                [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!casesForm.get('country').valid && casesForm.get('country').touched">Please enter Country</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <mat-label>Status</mat-label>
        <mat-select formControlName="status">
          <mat-option *ngFor="let sl of statusList" [value]="sl">
            {{sl}}
          </mat-option>
        </mat-select>
        <mat-error>
          <span *ngIf="!casesForm.get('status').valid && casesForm.get('status').touched">Please select Status</span>
        </mat-error>
      </mat-form-field>
      <div class="button-row">
        <button type="submit" [disabled]="!casesForm.valid" mat-flat-button color="primary"><mat-icon>save</mat-icon></button>
      </div>
    </form>
  </mat-card>
</div>

Finally, open and edit src/app/edit-cases/edit-cases.component.scss then add these lines of CSS codes.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-form {
  min-width: 150px;
  max-width: 500px;
  width: 100%;
}

.example-full-width {
  width: 100%;
}

.example-full-width:nth-last-child(0) {
  margin-bottom: 10px;
}

.button-row {
  margin: 10px 0;
}

.mat-flat-button {
  margin: 5px;
}

Step #9. Run and Test the Angular 9 Coronavirus Cases App

Let’s see the performance of the Angular 9 with the Ivy CRUD App. Now, we have to build the Angular 9 app using this command.

ng build --prod

Now, we have ES5 and ES2015 build of the Angular 9 app build for production. Next, we have to test the whole application, first, we have to run MongoDB server and Node/Express API in the different terminal.

mongod
nodemon

Then run the Angular 9 app build, simply type this command.

ng serve

Now, you will see the Coronavirus Cases app the same as you saw in the first paragraph of this tutorial. That it’s the Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly. You can find the full source code in our GitHub.

If you don’t want to waste your time design your own front-end or your budget to spend by hiring a web designer then Angular Templates is the best place to go. So, speed up your front-end web development with premium Angular templates. Choose your template for your front-end project here.

Originally published by Didin J at djamware.com

Thanks!

#angular #javascript #web-development

What is GEEK

Buddha Community

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

Ian C.

1585539685

Hi I got an error even I clone your repo. Could you please help me to fix this issue?

This is image title

Alicia L

1587171265

To solve ERROR http://localhost:3000/api

git clone https://github.com/didinj/coronavirus-restapi
cd bin
node www

Lili Bruk

1590398761

Hey! I am a beginner programmer, in Stage 3 when it is written
“Add the error handler function that returns as an Observable.”, where should I refer this code to? what file and within what exactly in my code? not sure what to do…

How to build an learning app like BYJU's| Cost to build app like BYJU's?

Due to schools & Colleges being closed due to Covid-19 Pandemic, the need for e-learning platforms like Byju’s has seen a rapid rise among school students. The rise is so fast that in just 3 months from the beginning of lockdowns its user base increased by twice.

Want to help school students learn with creative methods from an e-learning app?
WebClues Infotech is there for you to bring your vision to reality with its highly skilled e-learning App Development team. With past experience in developing different types of e-learning solutions like AshAce Papers, EduPlay Cloud, Squared, and many more, WebClues Infotech is fully equipped to develop an e-learning app development solution based on your need

Want more information on Byju’s like e-learning app Development?

Visit our detailed guide at https://www.webcluesinfotech.com/how-to-create-an-elearning-app-like-byjus/

#how to build an learning app like byju's? #how to develop a learning app like byju's features & cost #how to create an app like byju's #how much does it cost to develop an app like byjus #e-learning app development solution #e-learning apps development

Top Enterprise Angular Web Apps Development Company in USA

AppClues Infotech is one of the leading Enterprise Angular Web Apps Development Company in USA. Our dedicated & highly experienced Angular app developers build top-grade Angular apps for your business with immersive technology & superior functionalities.

For more info:
Website: https://www.appcluesinfotech.com/
Email: info@appcluesinfotech.com
Call: +1-978-309-9910

#top enterprise angular web apps development company in usa #enterprise angular web apps development #hire enterprise angular web apps developers #best enterprise angular web app services #custom enterprise angular web apps solution #professional enterprise angular web apps developers

Fredy  Larson

Fredy Larson

1595059664

How long does it take to develop/build an app?

With more of us using smartphones, the popularity of mobile applications has exploded. In the digital era, the number of people looking for products and services online is growing rapidly. Smartphone owners look for mobile applications that give them quick access to companies’ products and services. As a result, mobile apps provide customers with a lot of benefits in just one device.

Likewise, companies use mobile apps to increase customer loyalty and improve their services. Mobile Developers are in high demand as companies use apps not only to create brand awareness but also to gather information. For that reason, mobile apps are used as tools to collect valuable data from customers to help companies improve their offer.

There are many types of mobile applications, each with its own advantages. For example, native apps perform better, while web apps don’t need to be customized for the platform or operating system (OS). Likewise, hybrid apps provide users with comfortable user experience. However, you may be wondering how long it takes to develop an app.

To give you an idea of how long the app development process takes, here’s a short guide.

App Idea & Research

app-idea-research

_Average time spent: two to five weeks _

This is the initial stage and a crucial step in setting the project in the right direction. In this stage, you brainstorm ideas and select the best one. Apart from that, you’ll need to do some research to see if your idea is viable. Remember that coming up with an idea is easy; the hard part is to make it a reality.

All your ideas may seem viable, but you still have to run some tests to keep it as real as possible. For that reason, when Web Developers are building a web app, they analyze the available ideas to see which one is the best match for the targeted audience.

Targeting the right audience is crucial when you are developing an app. It saves time when shaping the app in the right direction as you have a clear set of objectives. Likewise, analyzing how the app affects the market is essential. During the research process, App Developers must gather information about potential competitors and threats. This helps the app owners develop strategies to tackle difficulties that come up after the launch.

The research process can take several weeks, but it determines how successful your app can be. For that reason, you must take your time to know all the weaknesses and strengths of the competitors, possible app strategies, and targeted audience.

The outcomes of this stage are app prototypes and the minimum feasible product.

#android app #frontend #ios app #minimum viable product (mvp) #mobile app development #web development #android app development #app development #app development for ios and android #app development process #ios and android app development #ios app development #stages in app development

Percy  Ebert

Percy Ebert

1591110180

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

A comprehensive step by step Angular 9 tutorial on learning to build a CRUD (create, read, update, delete) Angular app quickly. We will create an Angular app of Coronavirus cases that will use REST API that gets the required data, post the case data, put case changes, delete case data, and get statistic data that displayed as a bar-charts. So, we will learn how to use HTTPClient to access REST API, using Angular Form with Angular Material, using Routing and Navigation, and display bar charts using Ng2Charts and Chart.js.

#angular #angular9 #crud #tutorial

Carmen  Grimes

Carmen Grimes

1595494844

How to start an electric scooter facility/fleet in a university campus/IT park

Are you leading an organization that has a large campus, e.g., a large university? You are probably thinking of introducing an electric scooter/bicycle fleet on the campus, and why wouldn’t you?

Introducing micro-mobility in your campus with the help of such a fleet would help the people on the campus significantly. People would save money since they don’t need to use a car for a short distance. Your campus will see a drastic reduction in congestion, moreover, its carbon footprint will reduce.

Micro-mobility is relatively new though and you would need help. You would need to select an appropriate fleet of vehicles. The people on your campus would need to find electric scooters or electric bikes for commuting, and you need to provide a solution for this.

To be more specific, you need a short-term electric bike rental app. With such an app, you will be able to easily offer micro-mobility to the people on the campus. We at Devathon have built Autorent exactly for this.

What does Autorent do and how can it help you? How does it enable you to introduce micro-mobility on your campus? We explain these in this article, however, we will touch upon a few basics first.

Micro-mobility: What it is

micro-mobility

You are probably thinking about micro-mobility relatively recently, aren’t you? A few relevant insights about it could help you to better appreciate its importance.

Micro-mobility is a new trend in transportation, and it uses vehicles that are considerably smaller than cars. Electric scooters (e-scooters) and electric bikes (e-bikes) are the most popular forms of micro-mobility, however, there are also e-unicycles and e-skateboards.

You might have already seen e-scooters, which are kick scooters that come with a motor. Thanks to its motor, an e-scooter can achieve a speed of up to 20 km/h. On the other hand, e-bikes are popular in China and Japan, and they come with a motor, and you can reach a speed of 40 km/h.

You obviously can’t use these vehicles for very long commutes, however, what if you need to travel a short distance? Even if you have a reasonable public transport facility in the city, it might not cover the route you need to take. Take the example of a large university campus. Such a campus is often at a considerable distance from the central business district of the city where it’s located. While public transport facilities may serve the central business district, they wouldn’t serve this large campus. Currently, many people drive their cars even for short distances.

As you know, that brings its own set of challenges. Vehicular traffic adds significantly to pollution, moreover, finding a parking spot can be hard in crowded urban districts.

Well, you can reduce your carbon footprint if you use an electric car. However, electric cars are still new, and many countries are still building the necessary infrastructure for them. Your large campus might not have the necessary infrastructure for them either. Presently, electric cars don’t represent a viable option in most geographies.

As a result, you need to buy and maintain a car even if your commute is short. In addition to dealing with parking problems, you need to spend significantly on your car.

All of these factors have combined to make people sit up and think seriously about cars. Many people are now seriously considering whether a car is really the best option even if they have to commute only a short distance.

This is where micro-mobility enters the picture. When you commute a short distance regularly, e-scooters or e-bikes are viable options. You limit your carbon footprints and you cut costs!

Businesses have seen this shift in thinking, and e-scooter companies like Lime and Bird have entered this field in a big way. They let you rent e-scooters by the minute. On the other hand, start-ups like Jump and Lyft have entered the e-bike market.

Think of your campus now! The people there might need to travel short distances within the campus, and e-scooters can really help them.

How micro-mobility can benefit you

benefits-micromobility

What advantages can you get from micro-mobility? Let’s take a deeper look into this question.

Micro-mobility can offer several advantages to the people on your campus, e.g.:

  • Affordability: Shared e-scooters are cheaper than other mass transportation options. Remember that the people on your campus will use them on a shared basis, and they will pay for their short commutes only. Well, depending on your operating model, you might even let them use shared e-scooters or e-bikes for free!
  • Convenience: Users don’t need to worry about finding parking spots for shared e-scooters since these are small. They can easily travel from point A to point B on your campus with the help of these e-scooters.
  • Environmentally sustainable: Shared e-scooters reduce the carbon footprint, moreover, they decongest the roads. Statistics from the pilot programs in cities like Portland and Denver showimpressive gains around this key aspect.
  • Safety: This one’s obvious, isn’t it? When people on your campus use small e-scooters or e-bikes instead of cars, the problem of overspeeding will disappear. you will see fewer accidents.

#android app #autorent #ios app #mobile app development #app like bird #app like bounce #app like lime #autorent #bird scooter business model #bird scooter rental #bird scooter rental cost #bird scooter rental price #clone app like bird #clone app like bounce #clone app like lime #electric rental scooters #electric scooter company #electric scooter rental business #how do you start a moped #how to start a moped #how to start a scooter rental business #how to start an electric company #how to start electric scooterrental business #lime scooter business model #scooter franchise #scooter rental business #scooter rental business for sale #scooter rental business insurance #scooters franchise cost #white label app like bird #white label app like bounce #white label app like lime