In the past few days of articles, we have learned a lot about angular
. This time we will come up with a small product.
angualr
is combined with ng-zorro
to quickly and standardizedly develop a backend system. [Related tutorial recommendation: "Angular Tutorial"]
System functions include the following:
all services use simulated data.
Let's do it.
The more popular ui
angular
combined with ng-zorro
angular include:Ant Design
believe those who do front-end development are familiar with it. So here we combine it with the NG-ZORRO
framework. If you are familiar with Vue
or React
version of Ant Design
, I believe you can connect seamlessly ~
We reuse angular-cli
to generate a project ng-zorro
.
Adding ng-zorro
is very simple: enter ng-zorro
root directory and execute ng add ng-zorro-antd
.
Of course, you can also execute
npm install ng-zorro-antd
to add it, but it is not recommended.
After combining ng-zorro
, we run the project npm run start
, and you will see the following picture on the page http://localhost:4200
.
Not Bad, Bro.
We changedthe configuration routing
to hash
routing and added user routing. The scaffolding has done it for us. We only need to make a few minor modifications.
Idea:
First add the page user
list page, use table
component in ng-zorro
to add and change the user's page to share the same page, use form
ng-zorro
page deletion function in ng-zorro to directly use the pop-up prompt, use ng-zorro
modal
component in ng-zorro
introduces ng-zorro
component as needed to
adjust the routing file.
According to the idea, we have to introduce it in ng-zorro
:
// app.module.ts import { ReactiveFormsModule } from '@angular/forms'; import { NzTableModule } from 'ng-zorro-antd/table'; import { NzModalModule } from 'ng-zorro-antd/modal'; import { NzButtonModule } from 'ng-zorro-antd/button'; import { NzFormModule } from 'ng-zorro-antd/form'; import { NzInputModule } from 'ng-zorro-antd/input'; // ... imports: [ // Add it in imports instead of declaring NzTableModule in declarations, NzModalModule, NzButtonModule, NzFormModule, ReactiveFormsModule, NzInputModule ],
simple and easy to understand principle, we do not use children
to nest routing:
// app.routing.module.ts import { NgModule } from '@angular/core'; import { Routes, RouterModule, PreloadAllModules } from '@angular/router'; import { WelcomeComponent } from './pages/welcome/welcome.component'; import { UserComponent } from './pages/user/user.component'; import { UserInfoComponent } from './pages/user/user-info/user-info.component'; //Related routes const routes: Routes = [ { path: '', pathMatch: 'full', redirectTo: '/welcome' }, { path: 'welcome', component: WelcomeComponent }, { path: 'user', component: UserComponent }, { path: 'user/add', component: UserInfoComponent }, { path: 'user/edit/:uuid', component: UserInfoComponent } ]; @NgModule({ imports: [RouterModule.forRoot( routes, { useHash: true,//Use hash mode preloadingStrategy: PreloadAllModules } )], exports: [RouterModule] }) export class AppRoutingModule { }
Change menu
The menu generated using scaffolding does not match the functions we need to develop. Let's adjust it.
// app.component.html <nz-layout class="app-layout"> <nz-sider class="menu-sidebar" nzCollapsible nzWidth="256px" nzBreakpoint="md" [(nzCollapsed)]="isCollapsed" [nzTrigger]="null"> <div class="sidebar-logo"> <!-- By default, click on the logo to jump to the homepage --> <a routerLink="/welcome"> <img src="https://ng.ant.design/assets/img/logo.svg" alt="logo"> <h1>Ng-Zorro</h1> </a> </div> <ul nz-menu nzTheme="dark" nzMode="inline" [nzInlineCollapsed]="isCollapsed"> <li nz-submenu nzOpen nzTitle="User Management" nzIcon="dashboard"> <ul> <li nz-menu-item nzMatchRouter> <a routerLink="/user">User list</a> </li> </ul> </li> </ul> </nz-sider> <nz-layout> <nz-header> <div class="app-header"> <span class="header-trigger" (click)="isCollapsed = !isCollapsed"> <i class="trigger" nz-icon [nzType]="isCollapsed ? 'menu-unfold' : 'menu-fold'" ></i> </span> </div> </nz-header> <nz-content> <div class="inner-content"> <router-outlet></router-outlet> </div> </nz-content> </nz-layout> </nz-layout>
Menu display, if we need to do permission management, we need the backend to cooperate with the value transfer. Then we render the relevant permission menu to the page
and replace it with the above code. The basic skeleton obtained is as follows :
Complete the user list.
Next, complete the skeleton of the user list. Because we use the UI
framework, it is extremely convenient for us to write:
Get the user list
// user.component.html <nz-table #basicTable [nzData]="list"> <thead> <tr> <th>Name</th> <th>Position</th> <th>Action</th> </tr> </thead> <tbody> <!-- Traverse the obtained data --> <tr *ngFor="let data of basicTable.data"> <td>{{data.name}}</td> <td>{{data.position}}</td> <td> <a style="color: #f00;">Delete</a> </td> </tr> </tbody> </nz-table>
We simulated some data in the assets
folder user.json
:
{ "users": [ { "uuid": 1, "name": "Jimmy", "position": "Frontend" }, { "uuid": 2, "name": "Jim", "position": "Backend" } ], "environment": "development" }
After writing the service, we call to get the user's data:
// user.component.ts import { Component, OnInit } from '@angular/core'; import { UserService } from 'src/app/services/user.service'; @Component({ selector: 'app-user', templateUrl: './user.component.html', styleUrls: ['./user.component.scss'] }) export class UserComponent implements OnInit { public list: any = [] constructor( private readonly userService: UserService ) { } ngOnInit(): void { if(localStorage.getItem('users')) { let obj = localStorage.getItem('users') || '{}' this.list = JSON.parse(obj) } else { this.getList() } } // Get the user list getList() { this.userService.getUserList().subscribe({ next: (data: any) => { localStorage.setItem('users', JSON.stringify(data.users)) this.list = data.users }, error: (error: any) => { console.log(error) } }) } }
Because no back-end service is introduced, here we use localstorage
to record the status.
After completing the above, we get the list information as follows:
To add users and edit users,
we simply create a form, which contains only two fields, namely name
and position
. These two functions share a form ~
we add it in html
:
// user-info.component.html <form nz-form [formGroup]="validateForm" class="login-form" (ngSubmit)="submitForm()"> <nz-form-item> <nz-form-control nzErrorTip="Please enter username!"> <input type="text" nz-input formControlName="username" placeholder="Please enter username" style="width: 160px;" /> </nz-form-control> </nz-form-item> <nz-form-item> <nz-form-control nzErrorTip="Please enter a position!"> <input type="text" nz-input formControlName="position" placeholder="Please enter the position" style="width: 160px;"/> </nz-form-control> </nz-form-item> <button nz-button class="login-form-button login-form-margin" [nzType]="'primary'">Confirm</button> </form>
The page looks like this:
Then there is the logical judgment to add or modify. If the connection is marked with uuid
, it means editing, show you the codes
.
// user-info.component.ts import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, ParamMap } from '@angular/router'; @Component({ selector: 'app-user-info', templateUrl: './user-info.component.html', styleUrls: ['./user-info.component.scss'] }) export class UserInfoComponent implements OnInit { public isAdd: boolean = true; public userInfo: any = [] public uuid: number = 0; validateForm!: FormGroup; constructor( private fb: FormBuilder, private route: ActivatedRoute, ) { } ngOnInit(): void { this.userInfo = JSON.parse(localStorage.getItem('users') || '[]') this.route.paramMap.subscribe((params: ParamMap)=>{ this.uuid = parseInt(params.get('uuid') || '0') }) // It is the editing state, set the identifier if(this.uuid) { this.isAdd = false } if(this.isAdd) { this.validateForm = this.fb.group({ username: [null, [Validators.required]], position: [null, [Validators.required]] }); } else { let current = (this.userInfo.filter((item: any) => item.uuid === this.uuid))[0] || {} // Information backfill this.validateForm = this.fb.group({ username: [current.name, [Validators.required]], position: [current.position, [Validators.required]] }) } } submitForm() { // If it does not comply with the submission, an error will be reported if(!this.validateForm.valid) { Object.values(this.validateForm.controls).forEach((control: any) => { if(control?.invalid) { control?.markAsDirty(); control?.updateValueAndValidity({ onlySelf: true }); } }) return } // Get the form data const data = this.validateForm.value //Add new user if(this.isAdd) { let lastOne = (this.userInfo.length > 0 ? this.userInfo[this.userInfo.length-1] : {}); this.userInfo.push({ uuid: (lastOne.uuid ? (lastOne.uuid + 1) : 1), name: data.username, position: data.position }) localStorage.setItem('users', JSON.stringify(this.userInfo)) } else { //Edit user, update information let mapList = this.userInfo.map((item: any) => { if(item.uuid === this.uuid) { return { uuid: this.uuid, name: data.username, position: data.position } } return item }) localStorage.setItem('users', JSON.stringify(mapList)) } } }
We first set an identifier isAdd
, which defaults to a new user; when uuid
exists, set it to a false
value, indicating that it is in an editing state and backfills the form with the content. The operation of submitting a form is also judged according to this identifier. We directly change the localStorage
information to ensure synchronization of the list information.
For the deletion function,
we introduce a modal dialog box to ask whether to delete.
// user.component.ts // Delete delete(data: any) { this.modal.confirm({ nzTitle: '<i>Do you want to delete this user?</i>', nzOnOk: () => { let users = JSON.parse(localStorage.getItem('users') || '[]'); let filterList = users.filter((item: any) => item.uuid !== data.uuid); localStorage.setItem('users', JSON.stringify(filterList)); this.list = filterList } }); }
We find the deleted data, remove it, re-cache the new user data, and update table
's user list data.
So, so far, we have successfully completed a simple project. Let’s take a look at it as a whole using Gif
.
【over】