Building an application Company with Spring boot and Angular Part 2

Getting Started | Building an Application with Angular


Angular 10 Front-end

In this tutorial We will build an application Company To display departments with code, location and description, employees with name, hiring date, salary etc... and projects with name, description and process. Each employee can have one project or many projects to work on and every user can management the functionality Add, update or delete.


Technology

Angular 10

Angular HTTPClient

Angular Router


Angular CLI version (10.1.1)

ng new

The Angular CLI makes it easy to create an application that already works, right out of the box. It already follows our best practices!


ng generate

Generate components, routes, services and pipes with a simple command. The CLI will also create simple test shells for all of these.


ng serve

Easily test your app locally while developing.



$  npm install -g @angular/cli
$ ng new company
$ cd company
$ ng serve

Project Structure


Font Awesome

Font Awesome is a font and icon toolkit based on CSS and Less. It was made by Dave Gandy for use with Bootstrap, and later was incorporated into the BootstrapCDN. Font Awesome has a 38% market share among those websites that use third-party font scripts on their platform, ranking it second place after Google Fonts.


Add this link in index.html to use Font Awesome


<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">

Angular Material Tutorial

The idea of this is article is to teach you how to use Angular Material through a hands-on exercise. First, you will check the dependencies that you need on your computer to use and develop with Angular Material, then you will learn how to configure and use different components.


Install Angular Material

Angular Material In addition to the install schematic, Angular Material comes with several schematics (like nav, table, address-form, etc.) that can be used to easily generate pre-built components in your application.


$  ng add @angular/material

MaterialModule

In earlier versions of Angular Material, we could import MaterialModule which imports all components from Angular Material and makes them available in our app. Most of the time we will need to import the most of material.


src/app/material-module.ts


import { NgModule } from '@angular/core'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatBadgeModule } from '@angular/material/badge'; import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; import { MatButtonModule } from '@angular/material/button'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatChipsModule } from '@angular/material/chips'; import { MatStepperModule } from '@angular/material/stepper'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDialogModule } from '@angular/material/dialog'; import { MatDividerModule } from '@angular/material/divider'; import { MatExpansionModule } from '@angular/material/expansion'; import { MatGridListModule } from '@angular/material/grid-list'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatListModule } from '@angular/material/list'; import { MatMenuModule } from '@angular/material/menu'; import { MatNativeDateModule, MatRippleModule } from '@angular/material/core'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatRadioModule } from '@angular/material/radio'; import { MatSelectModule } from '@angular/material/select'; import { MatSidenavModule } from '@angular/material/sidenav'; import { MatSliderModule } from '@angular/material/slider'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { MatTabsModule } from '@angular/material/tabs'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTreeModule } from '@angular/material/tree'; @NgModule({ exports: [ MatAutocompleteModule, MatBadgeModule, MatBottomSheetModule, MatButtonModule, MatButtonToggleModule, MatCardModule, MatCheckboxModule, MatChipsModule, MatStepperModule, MatDatepickerModule, MatDialogModule, MatDividerModule, MatExpansionModule, MatGridListModule, MatIconModule, MatInputModule, MatListModule, MatMenuModule, MatNativeDateModule, MatPaginatorModule, MatProgressBarModule, MatProgressSpinnerModule, MatRadioModule, MatRippleModule, MatSelectModule, MatSidenavModule, MatSliderModule, MatSlideToggleModule, MatSnackBarModule, MatSortModule, MatTableModule, MatTabsModule, MatToolbarModule, MatTooltipModule, MatTreeModule, ], imports: [ MatAutocompleteModule, MatBadgeModule, MatBottomSheetModule, MatButtonModule, MatButtonToggleModule, MatCardModule, MatCheckboxModule, MatChipsModule, MatStepperModule, MatDatepickerModule, MatDialogModule, MatDividerModule, MatExpansionModule, MatGridListModule, MatIconModule, MatInputModule, MatListModule, MatMenuModule, MatNativeDateModule, MatPaginatorModule, MatProgressBarModule, MatProgressSpinnerModule, MatRadioModule, MatRippleModule, MatSelectModule, MatSidenavModule, MatSliderModule, MatSlideToggleModule, MatSnackBarModule, MatSortModule, MatTableModule, MatTabsModule, MatToolbarModule, MatTooltipModule, MatTreeModule, ] }) export class MaterialModule { }

Modals interfaces

The purpose of this class is to map the specified fields with the fields of Spring entity class.


Angular NgModules differ from and complement JavaScript (ES2015) modules. An NgModule declares a compilation context for a set of components that is dedicated to an application domain, a workflow, or a closely related set of capabilities. An NgModule can associate its components with related code, such as services, to form functional units.


Every Angular app has a root module, conventionally named AppModule, which provides the bootstrap mechanism that launches the application. An app typically contains many functional modules.


src/app/modal/Modal.ts


export interface Company { id: number; name: string; location: string; description: string; createAt: any; departments: Department[]; } export interface Department { id: number; code: string; location: string; description: string; createAt: any; employees: Employee[]; projects: Project[]; } export interface Employee { id: number; name: string; address: string; salary: number; hiringDate: string; birthDate: string; employeePhones: EmployeePhone[]; projects: Project[]; } export interface EmployeePhone { id: number; phone: string; } export interface Project { id: number; name: string; description: string; createAt: any; process: Process; workOns: WorkOn[]; } export interface WorkOn { id: number; done: boolean; createAt: any; } export interface User { id: number; username: string; password: string; admin: boolean; companies: Company[]; } export enum Process { TODO, WORKON, DONE }

Angular Services

Angular services are singleton objects that get instantiated only once during the lifetime of an application. They contain methods that maintain data throughout the life of an application, i.e. data does not get refreshed and is available all the time. The main objective of a service is to organize and share business logic, models, or data and functions with different components of an Angular application.


An example of when to use services would be to transfer data from one controller to another custom service.


Why Should We Use Services in Angular? The separation of concerns is the main reason why Angular services came into existence. An Angular service is a stateless object and provides some very useful functions. These functions can be invoked from any component of Angular, like Controllers, Directives, etc. This helps in dividing the web application into small, different logical units which can be reused.


Create Data services

This service will use Angular HTTPClient to send HTTP requests. You can see that its functions includes CRUD operations and finder method


Create these services in the service package

-> CompanyService

-> DepartmentService

-> EmployeeService

-> EmployeePhoneService

-> ProjectService

-> UserService


src/app/service/company.service.ts


import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CompanyService { constructor(private http: HttpClient) { } addCompany(data: any, id: number): Observable<any> { return this.http.post<any>(`http://localhost:8080/api/addCompany/${id}`, data); } editCompany(data: any, id: number): Observable<any> { return this.http.put<any>(`http://localhost:8080/api/editCompany/${id}`, data); } findCompany(id: number): Observable<any> { return this.http.get<any>(`http://localhost:8080/api/findCompany/${id}`); } deleteCompany(id: number): Observable<any> { return this.http.delete<any>(`http://localhost:8080/api/deleteCompany/${id}`); } findCompanies(): Observable<any> { return this.http.get<any>(`http://localhost:8080/api/findCompanies`); } }

src/app/service/department.service.ts


import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class DepartmentService { constructor(private http: HttpClient) { } addDepartment(data: any, id: number): Observable<any> { return this.http.post<any>(`http://localhost:8080/api/addDepartment/${id}`, data); } editDepartment(data: any, id: number): Observable<any> { return this.http.put<any>(`http://localhost:8080/api/editDepartment/${id}`, data); } findDepartment(id: number): Observable<any> { return this.http.get<any>(`http://localhost:8080/api/findDepartment/${id}`); } deleteDepartment(id: number): Observable<any> { return this.http.delete<any>(`http://localhost:8080/api/deleteDepartment/${id}`); } }

src/app/service/employee-phone.service.ts


import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class EmployeePhoneService { constructor(private http: HttpClient) { } addEmployeePhone(data: any, id: number): Observable<any> { return this.http.post<any>(`http://localhost:8080/api/addEmployeePhone/${id}`, data); } editEmployeePhone(data: any, id: number): Observable<any> { return this.http.put<any>(`http://localhost:8080/api/editEmployeePhone/${id}`, data); } findEmployeePhone(id: number): Observable<any> { return this.http.get<any>(`http://localhost:8080/api/findEmployeePhone/${id}`); } deleteEmployeePhone(id: number): Observable<any> { return this.http.delete<any>(`http://localhost:8080/api/deleteEmployeePhone/${id}`); } findEmployeePhones(id: number): Observable<any> { return this.http.get<any>(`http://localhost:8080/api/findEmployeePhones/${id}`); } }

src/app/service/employee.service.ts


import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class EmployeeService { constructor(private http: HttpClient) { } addEmployee(data: any, id: number): Observable<any> { return this.http.post<any>(`http://localhost:8080/api/addEmployee/${id}`, data); } editEmployee(data: any, id: number): Observable<any> { return this.http.put<any>(`http://localhost:8080/api/editEmployee/${id}`, data); } findEmployee(id: number): Observable<any> { return this.http.get<any>(`http://localhost:8080/api/findEmployee/${id}`); } deleteEmployee(id: number): Observable<any> { return this.http.delete<any>(`http://localhost:8080/api/deleteEmployee/${id}`); } }

src/app/service/project.service.ts


import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class ProjectService { constructor(private http: HttpClient) { } addProjectToEmployee(idEmployee: number, idProject: number): Observable<any> { return this.http.post<any>(`http://localhost:8080/api/addProjectToEmployee/${idEmployee}/${idProject}`, null); } addProjectToDeprtment(data: any, id: number): Observable<any> { return this.http.post<any>(`http://localhost:8080/api/addProjectToDeprtment/${id}`, data); } editProject(data: any, id: number): Observable<any> { return this.http.put<any>(`http://localhost:8080/api/editProject/${id}`, data); } findProject(id: number): Observable<any> { return this.http.get<any>(`http://localhost:8080/api/findProject/${id}`); } deleteProejct(id: number): Observable<any> { return this.http.delete<any>(`http://localhost:8080/api/deleteProejct/${id}`); } findProjectsForEmplopyee(id: number): Observable<any> { return this.http.get<any>(`http://localhost:8080/api/findProjectsForEmplopyee/${id}`); } findProjects(): Observable<any> { return this.http.get<any>(`http://localhost:8080/api/findProjects`); } deleteProjectFromEmployee(idEmployee: number, idProject: number): Observable<any> { return this.http.delete<any>(`http://localhost:8080/api/deleteProjectFromEmployee/${idEmployee}/${idProject}`); } }

src/app/service/user.service.ts


import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { User } from '../model/Model'; const USERNAME_KEY = 'USERNAME'; @Injectable({ providedIn: 'root' }) export class UserService { constructor(private http: HttpClient) { } addUser(user: User): Observable<User> { return this.http.post<User>(`http://localhost:8080/api/addUser`, user); } editUser(user: User, idUser: number): Observable<User> { return this.http.post<User>(`http://localhost:8080/api/editUser/${idUser}`, user); } findUserById(id: number): Observable<User> { return this.http.get<User>(`http://localhost:8080/api/findUserById/${id}`); } deleteUser(id: number): Observable<User> { return this.http.delete<User>(`http://localhost:8080/api/deleteUser/${id}`); } findAllUsers(): Observable<User[]> { return this.http.get<User[]>(`http://localhost:8080/api/findAllUsers/`); } findByUsername(name: string): Observable<User> { return this.http.get<User>(`http://localhost:8080/api/findByUsername/${name}`); } saveUsername(username: string) { window.sessionStorage.removeItem( USERNAME_KEY); window.sessionStorage.setItem( USERNAME_KEY, username); } getUsername(): string { return sessionStorage.getItem( USERNAME_KEY); } }

Flat tree

The mat-tree provides a Material Design styled tree that can be used to display hierarchy data.
This tree builds on the foundation of the CDK tree and uses a similar interface for its data source input and template, except that its element and attribute selectors will be prefixed with mat- instead of cdk-.
There are two types of trees: Flat tree and nested tree. The DOM structures are different for these two types of trees.


In a flat tree, the hierarchy is flattened; nodes are not rendered inside of each other, but instead are rendered as siblings in sequence. An instance of TreeFlattener is used to generate the flat list of items from hierarchical data. The "level" of each tree node is read through the getLevel method of the TreeControl; this level can be used to style the node such that it is indented to the appropriate level.


TreeCompanyService

We are going fill this service with date


src/app/service/tree-company-service.ts


import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { Company} from '../modal/Modal'; import { CompanyService} from './company.service'; export class NodeAction { linkActive: string; url: string; title: string; } export class ApplicationItemNode { children: ApplicationItemNode[] = []; name: string; id: number; clazz: string; actions: NodeAction[] = []; title: string; routerLink: any; } export class ApplicationItemFlatNode { id: number; item: string; clazz: string; level: number; name: string; expandable: boolean; actions: NodeAction[] = []; title: string; routerLink: any; } @Injectable({ providedIn: 'root' }) export class TreeCompanyService { companies: Company[]; applicationData = new BehaviorSubject<ApplicationItemNode[]>([]); applicationState = new BehaviorSubject<Company>({} as Company); constructor(private companyService: CompanyService) { this.initialize(); } initialize() { this.applicationState.subscribe(() => { this.buildFileTree(0).subscribe(value => { this.applicationData.next(value); }); }); this.buildFileTree(0).subscribe(value => { this.applicationData.next(value); }); } get data(): ApplicationItemNode[] { return this.applicationData.value; } buildFileTree(level: number): Observable<ApplicationItemNode[]> { const nodes: ApplicationItemNode[] = []; return new Observable((observer) => { return this.companyService.findCompanies().subscribe(companies => { this.companies = companies.forEach(company => { const nodeCompany = new ApplicationItemNode(); nodeCompany.name = company.name; nodeCompany.id = company.id; nodeCompany.routerLink = 'display-company/' + company.id; nodeCompany.clazz = 'fab fa-accusoft'; nodeCompany.title = 'Display ' + company.name; nodes.push(nodeCompany); if(company.departments.length > 0) { company.departments.forEach(department => { const nodeDepartment = new ApplicationItemNode(); nodeDepartment.name = department.code; nodeDepartment.id = department.id; nodeDepartment.routerLink = 'display-department/' + department.id; nodeDepartment.clazz = 'fas fa-building'; nodeDepartment.title = 'Display department ' + department.code; nodeCompany.children.push(nodeDepartment); if(department.employees.length > 0) { department.employees.forEach(employee => { const nodeEmployee = new ApplicationItemNode(); nodeEmployee.name = employee.name nodeEmployee.id = employee.id; nodeEmployee.routerLink = 'display-employee/' + employee.id; nodeEmployee.clazz = 'fas fa-user-secret'; nodeEmployee.title = 'Display employee ' + employee.name; nodeDepartment.children.push(nodeEmployee); }) } }); } }); observer.next(nodes); }); }); } }

Components

The @Component decorator identifies the class immediately below it as a component class, and specifies its metadata. In the example code below, you can see that Component is just a class, with no special Angular notation or syntax at all. It's not a component until you mark it as one with the @Component decorator.


The metadata for a component tells Angular where to get the major building blocks that it needs to create and present the component and its view. In particular, it associates a template with the component, either directly with inline code, or by reference. Together, the component and its template describe a view.


In addition to containing or pointing to the template, the @Component metadata configures, for example, how the component can be referenced in HTML and what services it requires.


- app.component.ts
- app.component.html
- app.component.scss

The above files were created by default when we created new project using the angular-cli command.

src/app/app.component.ts


import { Component, OnInit } from '@angular/core'; import { User } from '../modal/Modal'; import { StorageService } from '../service/storage.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit { user: User = {} as User; constructor(private userService: UserService) {} ngOnInit(): void { if(this.user!=null) { this.userService.findByUsername(this.userService.getUsername()).subscribe(user => { this.user = user; }) } } logout() { this.userService.logout(); window.location.replace(`/dashbaord`); } }

src/app/app.component.html


<nav> <a [routerLink]="['/dashbaord']"> <i class="material-icons">dashboard</i> <span>Dashboard</span> </a> <a [routerLink]="['/login']" class=active *ngIf="!user"> <i class="material-icons">psychology</i> <span>Login</span> </a> <a [routerLink]="['/login']" class=active *ngIf="user"> <i class="material-icons">person</i> <span>Users</span> </a> <a [routerLink]="['/dashbaord']" (click)="logout()"> <i class="material-icons">logout</i> <span>Logout</span> </a> <a [routerLink]="['/login']"> <i class="material-icons">error</i> <span>Something</span> </a> </nav> <div id=body> <div id=content> <router-outlet></router-outlet> </div> </div>

src/app/app.component.scss


body { font-size: 16px; margin: 0; padding: 0; display: flex; flex-direction: row; background: #f8f8f8; #body { padding-left: 4.5675em; width: 100%; height: 8em; background-color: white; } } nav { z-index: 999; background: #f8f8f8; border-right: 1px solid #ccc; position: fixed; left: 0; top: 0; bottom: 0; display: flex; flex-direction: column; a { position: relative; display: flex; flex-direction: row; text-decoration: none; color: black; font-size: 1em; align-items: center; &:hover { background: #ddd; } &.active { i { padding: .75em .8125em .75em 1.1875em; } color: white; background: #3f51b5; box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.5); &::after { box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.5); clip-path: inset(-5px -5px -5px 0); content: ""; z-index: 2; background: #3f51b5; position: absolute; right: -0.5em; width: 0.5em; height: 100%; border-radius: 0 0.375em 0.375em 0; } } i { padding: 0.75em 1em; } span { transition: opacity 0.5s 1s, padding 0.5s 1.25s, font-size 0.5s 1.25s; padding: 0; font-size: 0; opacity: 0; } } &:hover a { &.active span { padding: .75em .8125em .75em .1875em; } span { transition: padding 0.5s 1s, font-size 0.5s 1s, opacity 0.25s 1.5s; opacity: 1; font-size: 1em; padding: 0.75em 1em 0.75em 0; } } } #content { margin-left: 100px; }

- dashboard.component.ts
- dashboard.component.html
- dashboard.component.css

src/app/dashboard.component.ts


import { Component, OnInit } from '@angular/core'; import { FlatTreeControl } from '@angular/cdk/tree'; import { User, Company } from '../modal/Modal'; import { MatTreeFlattener, MatTreeFlatDataSource } from '@angular/material/tree'; import { ApplicationItemFlatNode, ApplicationItemNode, TreeCompanyService } from '../service/tree-company.service'; import { Router,ActivatedRoute } from '@angular/router'; import { UserService } from '../service/user.service'; @Component({ selector: 'app-dashboard', templateUrl: './dashboard.component.html', styleUrls: ['./dashboard.component.css'] }) export class DashboardComponent implements OnInit { company: Company = {} as Company; user: User = {} as User; flatNodeMap = new Map<ApplicationItemFlatNode, ApplicationItemNode>(); nestedNodeMap = new Map<ApplicationItemNode, ApplicationItemFlatNode>(); treeControl: FlatTreeControl<ApplicationItemFlatNode>; treeFlattener: MatTreeFlattener<ApplicationItemNode, ApplicationItemFlatNode>; dataSource: MatTreeFlatDataSource<ApplicationItemNode, ApplicationItemFlatNode>; private expandedNodes: ApplicationItemFlatNode[]; constructor(private userService: UserService, private route: ActivatedRoute, private treeCompanyService: TreeCompanyService) { this.treeFlattener = new MatTreeFlattener(this.transform, this.getLevel, this.isExpandable, this.getChildren); this.treeControl = new FlatTreeControl<ApplicationItemFlatNode>(this.getLevel, this.isExpandable); this.dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); this.route.params.subscribe( params => { this.saveExpandedNodes(); this.restoreExpandedNodes(); treeCompanyService.applicationData.subscribe(company => { this.dataSource.data = company; }); if(this.user!=null) { this.userService.findByUsername(this.userService.getUsername()).subscribe(user => { this.user = user; }) } this.treeCompanyService.applicationState.next(this.company); }); } ngOnInit(): void { } getLevel = (node: ApplicationItemFlatNode) => node.level; isExpandable = (node: ApplicationItemFlatNode) => node.expandable; getChildren = (node: ApplicationItemNode): ApplicationItemNode [] => node.children; hasChild = (_: number, nodeDate: ApplicationItemFlatNode) => nodeDate.expandable; hasNoContent = (_: number, nodeData: ApplicationItemFlatNode) => nodeData.item === ''; transform = (node: ApplicationItemNode, level: number) => { const existingNode = this.nestedNodeMap.get(node); const flatNode = existingNode && existingNode.name === node.name ? existingNode : new ApplicationItemFlatNode(); flatNode.name = node.name; flatNode.id = node.id; flatNode.clazz = node.clazz; flatNode.level = level; flatNode.title = node.title; flatNode.routerLink = node.routerLink; flatNode.actions = node.actions; flatNode.expandable = (node.children && node.children.length > 0); this.flatNodeMap.set(flatNode, node); this.nestedNodeMap.set(node, flatNode); return flatNode; } private saveExpandedNodes() { if (this.treeControl && this.treeControl.dataNodes) { this.expandedNodes = new Array<ApplicationItemFlatNode>(); this.treeControl.dataNodes.forEach(node => { if (node.expandable && this.treeControl.isExpanded(node)) { this.expandedNodes.push(node); } }); } } private restoreExpandedNodes() { if (this.expandedNodes && this.expandedNodes.length > 0) { this.expandedNodes.forEach(node => { this.treeControl.expand(this.treeControl.dataNodes.find(n => n.id === node.id)); }); } } }

src/app/dashboard.component.html


<br <div style="width: 30%; float:left;"> <mat-card class="example-card"> <mat-card-header <mat-card-title{{user.username}}</mat-card-title <mat-card-subtitleFind all companies</mat-card-subtitle </mat-card-header<hr <mat-card-content <mat-tree [dataSource]="dataSource" [treeControl]="treeControl"> <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle matTreeNodePadding> <mat-basic-chip class="checklist-leaf-node root"> <span style="margin-top: 4px;"> <i [ngClass]="node.clazz" style="margin-left: 22px; font-size: 18px; color: #666a6c; margin-right: 4px;"></i <a title="{{node.title}}" [routerLink]="[node.routerLink]" routerLinkActive="linkActive"> {{node.name }} </a </span </mat-basic-chip </mat-tree-node <mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding> <span id="chevronIcon" matTreeNodeToggle [attr.aria-label]="'toggle ' + node.filename" style="background: transparent;"> <mat-icon id="mat-icon-plus" class="mat-icon-rtl-mirror"> {{treeControl.isExpanded(node) ? 'arrow_drop_down' : 'arrow_right'}} </mat-icon> </span> <mat-basic-chip class="root"> <a title="{{node.title}}" [routerLink]="[node.routerLink]" routerLinkActive="linkActive"> <i [ngClass]="node.clazz" style="margin-right: 5px; color: #666a6c;"></i {{node.name}} </a> </mat-basic-chip> </mat-tree-node> </mat-tree><br><br </mat-card-content> </mat-card> </div><br> <div style="width: 68%; float:right; margin-left: 1%; margin-right: 1%;" id="border-left"> <router-outlet></router-outlet </div>

src/app/dashboard.component.css


#applicationName { font-size: 22px; font-weight: bold; } .root { width: 100%; font-size: 14px; margin-bottom: 0; margin-top: 4px; display: block; } a { font-family: initial; color: #2f3542; text-decoration: none; } #mat-icon-plus { color: #666a6c; font-size: 24px; margin-top: 4px; } .root:hover { -webkit-transition: all .4s ease-in-out; -moz-transition: all .4s ease-in-out; transition: all .4s ease-in-out; cursor: pointer; } #chevronIcon { cursor: pointer; } .panel-danger { border-radius: 0 !important; border-bottom: none; } .panel-body { padding: 0 10px; padding-bottom: 20px; border-bottom: none; } @media screen and (max-width: 767px) { .panel-body, .panel-danger { padding-bottom: 0 !important; margin-bottom: 0 !important; } } #btnApplicationInfo { float: right; padding: 3px 6px 1px; } .linkActive { background-color: #f2dede !important; padding: 2px 10px 0 3px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; color: #a94442 !important; -webkit-transition: all .4s ease-in-out; -moz-transition: all .4s ease-in-out; transition: all .4s ease-in-out; } .mat-tree-node { margin-bottom: -20px; } .vl { border-left: 1px solid #f2dede; height: 589px; float: right; margin-top: -20px; } @media screen and (max-width: 767px) { #border-left { border-left: 1px solid #f2dede; } } @media screen and (max-width: 967px) { #border-left { border-left: 1px solid #f2dede; } }

- login.component.ts
- login.component.html

src/app/login.component.ts


import { Component, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog''; import { User } from '../modal/Modal'; import { UserService } from '../service/user.service'; import { AddCompanyComponent } from '../add-company/add-company.component'; @Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) export class LoginComponent implements OnInit { users: User[]; formUser: User = {} as User; existUser: User = {} as User; progressBar = false; admin: boolean; constructor(private userService: UserService, private dialog: MatDialog) {} ngOnInit(): void { if(this.existUser!=null) { this.userService.findAllUsers().subscribe(users => { this.users = users; }); this.userService.findByUsername(this.userService.getUsername()).subscribe(existUser => { this.existUser = existUser; this.admin = this.existUser.admin; }) } } addUser() { this.showProgressBar = true; this.userService.addUser(this.formUser).subscribe((formUser) => { this.formUser = formUser; this.userService.saveUsername(formUser.username); window.location.replace(`/dashbaord`); }); } deleteUser(id) { if(confirm('Are you sure')) { this.userService.deleteUser(id).subscribe(() => { window.location.replace(`/dashbaord`); }) } } addCompany(idUser) { this.dialog.open(AddCompanyComponent, { data: {idUser}, height: '400px', width: '400px', }) } }

src/app/login.component.html


<div style="margin: auto; width: 30%; border: 3px solid #5d89ff; margin-top: 40px;" *ngIf="existUser==null"> <mat-card class="example-card"> <mat-card-header> <div mat-card-avatar> <img src="https://zupimages.net/up/20/49/hnp3.png" style="width: 50px;"> </div> <mat-card-title>Authentification</mat-card-title> </mat-card-header> <mat-card-content><br><br> <mat-progress-bar mode="buffer" *ngIf="progressBar"></mat-progress-bar> <form> <mat-form-field appearance="fill" style="width: 100%;"> <mat-label>Enter your username</mat-label> <input matInput type="text" placeholder="Username" name="username" [(ngModel)]="formUser.username" #username="ngModel" required> </mat-form-field><br> <mat-form-field appearance="fill" style="width: 100%;"> <mat-label>Enter your password</mat-label> <input matInput type="password" placeholder="Password" name="password" [(ngModel)]="formUser.password" #password="ngModel" required> </mat-form-field><br> <button mat-raised-button color="primary" [disabled]="username.invalid || password.invalid" (click)="addUser()" style="width : 10em !important">Login</button> </form> </mat-card-content> </mat-card> </div> <div style="margin: auto; width: 40%; border: 3px solid #6F1E51; margin-top: 40px;" *ngIf="admin==true"> <h1 style="margin: 10px">Welcome {{existUser.username}}</h1> <mat-card class="example-card"> <mat-card-header> <mat-card-title>Users</mat-card-title> </mat-card-header> <mat-card-content> <mat-list> <mat-list-item *ngFor="let user of users"> <mat-icon mat-list-icon>account_circle</mat-icon> <div mat-line>{{user.username}} <button mat-button color="primary" (click)="addCompany(user.id)">Add company</button> <button mat-button color="warn" (click)="deleteUser(user.id)">Delete</button></div> </mat-list-item> <mat-divider></mat-divider> </mat-list> </mat-card-content> </mat-card> </div> <div style="margin: auto; width: 40%; border: 3px solid #6F1E51; margin-top: 40px;" *ngIf="admin==false"> <h1 style="margin: 10px">Welcome {{existUser.username}}</h1> </div>

Conclusion

Now we have an overview of Angular 10 + Spring Boot CRUD example when building a CRUD App.

We also take a look at client-server architecture for REST API using Spring Web MVC & Spring Data JPA, as well as Angular 10 project structure for building a front-end app to make HTTP requests and consume responses. We will continue to complete the project in part 3






Post a Comment

Previous Post Next Post


Follow us on facebook