Angular 7 CRUD Example Tutorial


In this article, we will be building an Angular 7 CRUD Application step by step from scratch. We basically, developing Angular 7 CRUD Application using angular CLI and then modify it to have an Employee Management Project which performs CRUD operations such as create, read, update and delete employee with the sample Spring Boot REST API consumed using HttpClientModule. We will also be using RouterModule to have routing enabled. 

Angular is a platform and framework for building client applications in HTML and TypeScript. Angular is written in TypeScript. It implements core and optional functionality as a set of TypeScript libraries that you import into your apps.

In this Angular 7 web application, we will be consuming spring boot CRUD rest API exposed at Spring Boot 2 JPA MySQL CRUD Example article.

You can download the source code of this tutorial from my GitHub repository at the end of this tutorial.

Recommendation tutorials:

Before developing an Angular 7 client app, make sure that spring boot CRUD Rest API application is up and running, check out the complete tutorial at Spring Boot 2 JPA MySQL CRUD Example.

Angular 7 Client Application Development

Let's develop a step by step CRUD (Create, Read, Update, Delete) Web Application using Angular 7 which consume above CRUD rest APIs.

Good to know Angular 7 release notes and new features at Version 7 of Angular — CLI Prompts, Virtual Scroll, Drag and Drop and more.
I 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 this commands.
node -v
v8.12.0
npm -v
6.4.1
That's the Node.js and NPM version that we are using. Now, you can go to the main steps.

1. Install or Update Angular 7 CLI

To install or update Angular 7 CLI, type this command in the Terminal or Node Command Line.
npm install -g @angular/cli
Now, you have the latest version of Angular CLI.
ng --version

Angular CLI: 7.0.1
Node: 8.12.0
OS: darwin x64
Angular:
...

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.10.1
@angular-devkit/core         7.0.1
@angular-devkit/schematics   7.0.1
@schematics/angular          7.0.1
@schematics/update           0.10.1
rxjs                         6.3.3
typescript                   3.1.3
Next, create a new Angular 7 Web Application using this Angular CLI command.
The Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold, and maintain Angular applications. 
If you are new to Angular CLI then check out official documentation at https://cli.angular.io.

2. Create Angular 7 web application using Angular CLI

Let's use below command to generate an Angular 7 Client application. We name this project as "angular7-springboot-client".
ng new angular7-springboot-client

3. Identify Components, Services, and Modules

Let's list out what are components, service, and modules we are going to create in this application. We will use Angular CLI to generate components, services because Angular CLI follows best practices and saves much of time.
  1. Components
  • create-employee
  • employee-list
  • employee-details
  1. Services
  • employee.service.ts - Service for Http Client methods
  1. Modules
  • FormsModule
  • HttpClientModule
  • AppRoutingModule.
  1. Employee Class (Typescript class)
  • employee.ts: class Employee (id, firstName, lastName, emailId)
In this next step, we will generate these components, classes, and services using Angular CLI.

4. Create Service & Components

Let's auto-generate service and components using Angular CLI. Change your project directory to angular7-springboot-client\src\app and run the following commands:
- ng g s employee
– ng g c create-employee
– ng g c employee-details
– ng g c employee-list
Here is complete command and output for your reference:
C:\angular7\angular7-springboot-client\src\app>ng g s employee
CREATE src/app/employee.service.spec.ts (343 bytes)
CREATE src/app/employee.service.ts (137 bytes)

C:\angular7\angular7-springboot-client\src\app>ng g c create-employee
CREATE src/app/create-employee/create-employee.component.html (34 bytes)
CREATE src/app/create-employee/create-employee.component.spec.ts (685 bytes)
CREATE src/app/create-employee/create-employee.component.ts (304 bytes)
CREATE src/app/create-employee/create-employee.component.css (0 bytes)
UPDATE src/app/app.module.ts (509 bytes)

C:\angular7\angular7-springboot-client\src\app>ng g c employee-details
CREATE src/app/employee-details/employee-details.component.html (35 bytes)
CREATE src/app/employee-details/employee-details.component.spec.ts (692 bytes)
CREATE src/app/employee-details/employee-details.component.ts (308 bytes)
CREATE src/app/employee-details/employee-details.component.css (0 bytes)
UPDATE src/app/app.module.ts (629 bytes)

C:\angular7\angular7-springboot-client\src\app>ng g c employee-list
CREATE src/app/employee-list/employee-list.component.html (32 bytes)
CREATE src/app/employee-list/employee-list.component.spec.ts (671 bytes)
CREATE src/app/employee-list/employee-list.component.ts (296 bytes)
CREATE src/app/employee-list/employee-list.component.css (0 bytes)
UPDATE src/app/app.module.ts (737 bytes)
We will use Bootstrap 4 for styling our application so let's integrate bootstrap 4 with Angular 7.

5. Integrate Bootstrap with Angular

Use NPM to download Bootstrap & JQuery. Bootstrap and jQuery will be installed into the node_modules folder.
npm install bootstrap jquery --save
Configure installed Bootstrap & JQuery in an angular.json file:
...
 
"styles": [
  "src/styles.css",
  "node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
  "node_modules/jquery/dist/jquery.min.js",
  "node_modules/bootstrap/dist/js/bootstrap.min.js"
]
 
...
Let's discuss each of the above generate components and service files and we will customize it as per our requirement.

6. package.json

This file Configures npm package dependencies that are available to all projects in the workspace.
Note that angular version 7.2.0 in dependencies section in below file.
{
  "name": "angular7-springboot-client",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve --proxy-config proxy.conf.json",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "~7.2.0",
    "@angular/common": "~7.2.0",
    "@angular/compiler": "~7.2.0",
    "@angular/core": "~7.2.0",
    "@angular/forms": "~7.2.0",
    "@angular/platform-browser": "~7.2.0",
    "@angular/platform-browser-dynamic": "~7.2.0",
    "@angular/router": "~7.2.0",
    "bootstrap": "^4.2.1",
    "core-js": "^2.5.4",
    "jquery": "^3.3.1",
    "rxjs": "~6.3.3",
    "tslib": "^1.9.0",
    "zone.js": "~0.8.26"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.12.0",
    "@angular/cli": "~7.2.1",
    "@angular/compiler-cli": "~7.2.0",
    "@angular/language-service": "~7.2.0",
    "@types/node": "~8.9.4",
    "@types/jasmine": "~2.8.8",
    "@types/jasminewd2": "~2.0.3",
    "codelyzer": "~4.5.0",
    "jasmine-core": "~2.99.1",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~3.1.1",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "~2.0.1",
    "karma-jasmine": "~1.1.2",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.4.0",
    "ts-node": "~7.0.0",
    "tslint": "~5.11.0",
    "typescript": "~3.2.2"
  }
}

7. Create Employee class - employee.ts

Before defining the EmployeeListComponent, let’s define an Employee class for working with employees. create a new file employee.ts inside src/app folder and add the following code to it -
export class Employee {
    id: number;
    firstName: string;
    lastName: string;
    emailId: string;
    active: boolean;
}

8. EmployeeService - employee-service.ts

The EmployeeService will be used to get the data from backend by calling spring boot APIs. Update the employee.service.ts file inside src/app directory with the following code to it -
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class EmployeeService {

  private baseUrl = '/api/v1/employees';

  constructor(private http: HttpClient) { }

  getEmployee(id: number): Observable<Object> {
    return this.http.get(`${this.baseUrl}/${id}`);
  }

  createEmployee(employee: Object): Observable<Object> {
    return this.http.post(`${this.baseUrl}`, employee);
  }

  updateEmployee(id: number, value: any): Observable<Object> {
    return this.http.put(`${this.baseUrl}/${id}`, value);
  }

  deleteEmployee(id: number): Observable<any> {
    return this.http.delete(`${this.baseUrl}/${id}`, { responseType: 'text' });
  }

  getEmployeesList(): Observable<any> {
    return this.http.get(`${this.baseUrl}`);
  }
}

9. EmployeeListComponent - employee-list.component.ts

Let's update the EmployeeListComponent component which will be used to display a list of employee, create a new employee, and delete an employee.
Update/remove the content of employee-list.component.ts inside src/app directory and add the following code to it -
import { Observable } from "rxjs";
import { EmployeeService } from "./../employee.service";
import { Employee } from "./../employee";
import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-employee-list",
  templateUrl: "./employee-list.component.html",
  styleUrls: ["./employee-list.component.css"]
})
export class EmployeeListComponent implements OnInit {
  employees: Observable<Employee[]>;

  constructor(private employeeService: EmployeeService) {}

  ngOnInit() {
    this.reloadData();
  }

  reloadData() {
    this.employees = this.employeeService.getEmployeesList();
  }

  deleteEmployee(id: number) {
    this.employeeService.deleteEmployee(id)
      .subscribe(
        data => {
          console.log(data);
          this.reloadData();
        },
        error => console.log(error));
  }
}

10. Create a template for EmployeeListComponent -> employee-list.component.html

Update employee-list.component.html file with the following code to it -
<div class="panel panel-default">
    <div class="panel-heading">
        <h1>Employees</h1>
    </div>
    <div class="panel-body">
        <table class="table table-striped table-bordered">
            <thead>
                <tr>
                    <th>Firstname</th>
                    <th>Lastname</th>
                    <th>Email</th>
                    <th>Actions</th>
                </tr>
            </thead>
            <tbody>
                <tr *ngFor="let employee of employees | async">
                    <td>{{employee.firstName}}</td>
                    <td>{{employee.lastName}}</td>
                    <td>{{employee.emailId}}</td>
                    <td><button (click)="deleteEmployee(employee.id)">Delete</button></td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

11. CreateEmployeeComponent -> create-employee.component.ts

CreateEmployeeComponent is used to create and handle a new employee form data. Add the following code to it -
import { EmployeeService } from './../employee.service';
import { Employee } from './../employee';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-create-employee',
  templateUrl: './create-employee.component.html',
  styleUrls: ['./create-employee.component.css']
})
export class CreateEmployeeComponent implements OnInit {

  employee: Employee = new Employee();
  submitted = false;

  constructor(private employeeService: EmployeeService) { }

  ngOnInit() {
  }

  newEmployee(): void {
    this.submitted = false;
    this.employee = new Employee();
  }

  save() {
    this.employeeService.createEmployee(this.employee)
      .subscribe(data => console.log(data), error => console.log(error));
    this.employee = new Employee();
  }

  onSubmit() {
    this.submitted = true;
    this.save();
  }
}

12. Create a template for EmployeeCreateComponent -> create-employee.component.html

<h3>Create Employee</h3>
<div [hidden]="submitted" style="width: 400px;">
    <form (ngSubmit)="onSubmit()">
        <div class="form-group">
            <label for="name">First Name</label>
            <input type="text" class="form-control" id="firstName" required [(ngModel)]="employee.firstName" name="firstName">
        </div>

        <div class="form-group">
            <label for="name">Last Name</label>
            <input type="text" class="form-control" id="lastName" required [(ngModel)]="employee.lastName" name="lastName">
        </div>

        <div class="form-group">
            <label for="name">Email ID</label>
            <input type="text" class="form-control" id="emailId" required [(ngModel)]="employee.emailId" name="emailId">
        </div>

        <button type="submit" class="btn btn-success">Submit</button>
    </form>
</div>

<div [hidden]="!submitted">
    <h4>You submitted successfully!</h4>
</div>

13. EmployeeDetailsComponent -> employee-details.component.ts

This component shows details of employee -
import { Employee } from './../employee';
import { Component, OnInit, Input } from '@angular/core';
import { EmployeeService } from '../employee.service';
import { EmployeeListComponent } from '../employee-list/employee-list.component';

@Component({
  selector: 'app-employee-details',
  templateUrl: './employee-details.component.html',
  styleUrls: ['./employee-details.component.css']
})
export class EmployeeDetailsComponent implements OnInit {

  @Input() employee: Employee;

  constructor(private employeeService: EmployeeService, private listComponent: EmployeeListComponent) { }

  ngOnInit() {
  }
}

14. Create a template for EmployeeDetailsComponent -> employee-details.component.html

<div *ngIf="employee">
    <div>
        <label>Name: </label> {{employee.firstName}}
    </div>
    <div>
        <label>Age: </label> {{employee.lastName}}
    </div>
    <div>
        <label>Active: </label> {{employee.emailId}}
    </div>
    <div>
        <label>Active: </label> {{employee.active}}
    </div>

    <span class="button is-small btn-primary" *ngIf='employee.active' (click)='updateActive(false)'>Inactive</span>

    <span class="button is-small btn-primary" *ngIf='!employee.active' (click)='updateActive(true)'>Active</span>

    <span class="button is-small btn-danger" (click)='deleteEmployee()'>Delete</span>

    <hr/>
</div>

15. AppRoutingModule -> app-routing.module.ts

import { EmployeeDetailsComponent } from './employee-details/employee-details.component';
import { CreateEmployeeComponent } from './create-employee/create-employee.component';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { EmployeeListComponent } from './employee-list/employee-list.component';

const routes: Routes = [
  { path: '', redirectTo: 'employee', pathMatch: 'full' },
  { path: 'employees', component: EmployeeListComponent },
  { path: 'add', component: CreateEmployeeComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

16. AppComponent -> app/app.component.ts

Defines the logic for the app's root component, named AppComponent. The view associated with this root component becomes the root of the view hierarchy as you add components and services to your app.
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Angular 7 + Spring Boot 2 + Spring Data JPA + MySQL + CRUD Tutorial';
}

17. App Component -> app/app.component.html

Defines the HTML template associated with the root AppComponent.
<div class="container">
    <h2>{{title}}</h2>
    <hr>

    <nav class="navbar navbar-expand-sm bg-dark navbar-dark">
        <!-- Links -->
        <ul class="navbar-nav">
            <li class="nav-item">
                <a routerLink="employees" class="btn btn-primary active" role="button" routerLinkActive="active">Employees</a>
            </li>
            <li class="nav-item" style="margin-left: 10px;">
                <a routerLink="add" class="btn btn-primary active" role="button" routerLinkActive="active">Add</a>
            </li>
        </ul>

    </nav>
    <router-outlet></router-outlet>
</div>

18. App Module -> app/app.module.ts

Defines the root module, named AppModule, that tells Angular how to assemble the application. Initially declares only the AppComponent. As you add more components to the app, they must be declared here.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CreateEmployeeComponent } from './create-employee/create-employee.component';
import { EmployeeDetailsComponent } from './employee-details/employee-details.component';
import { EmployeeListComponent } from './employee-list/employee-list.component';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
  declarations: [
    AppComponent,
    CreateEmployeeComponent,
    EmployeeDetailsComponent,
    EmployeeListComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

19. Configure a proxy for your API calls with Angular CLI

Here I will show you how to set up your Angular development server and how to configure it to properly communicate with your backend (REST) API.

In a real-world setup where you have your Angular CLI development server running on http://localhost:4200 and your backend API (in whichever technology you like) running on some other port or even host, let’s say http://localhost:8080 (this could also be http://dev-123.mycompany.com or something else ofc).
When you then want to execute an HTTP call within the app, you’d have to write something like this, indicating the full path of your backend API.
this.http.get('http://locahost:8080/api/v1/employees')
    .map(res => res.json());
Obviously, the base URL (i.e. the host) can be configured in a central place somewhere, such as via the environment.ts file (that gets generated by the Angular CLI). But there’s another issue as well. Unless you’re creating some publicly consumable API and you inject the required CORS headers, you’ll most probably get some CORS exceptions.

What is the solution? Here is a solution with different options.

Configuring your Angular CLI dev-server proxy

There are different options:
  • add the proper CORS headers - This is definitely a must in a case when you design a generic API where you don’t even know the consumer.
  • use a proxy - A proxy is a piece of software which is in between your JavaScript/Angular app doing the Ajax request and your backend API. This is the choice to go in a classic app.
In this example, I have used proxy to configure our Angular CLI dev-server proxy.
To set it up, we need to create a file proxy.conf.json at the root of our Angular CLI project. The content should look as follows:

proxy.conf.json 

{
  "/api/v1/employees": {
    "target": "http://localhost:8080",
    "secure": false
  }
}
All requests made to /api/v1/employees from within our application will be forwarded to http://localhost:8080//api/v1/employees

As we already started Spring boot application. Now let's run this Angular 7 application.

20. Running Angular 7 Application

Let's run the above developed Angular App with a command: npm start

Note that when you execute npm start, the ng serve will be invoked which is a command to the CLI to fire up its internal development server.

21. Demo

Open browser for URL - http://URLalhost:4200/:

Employee List Page


Add Employee Page

Delete Employee

Note that update employee functionality is not implemented so you can take as exercise and try to implement yourself (Tip: Add an update button to the employee list page and based on employee id you can implement update functionality. Note that Rest API is already created for update employee functionality).
The source code of this article available on my GitHub repository at https://github.com/RameshMF/angular7-springboot-crud-tutorial

Comments