Make writing a habit together! This is the 7th day of my participation in the “Gold Digging Day New Plan · April More text Challenge”. Click here for more details

We’ve learned a lot about Angular over the last few days, so let’s do a quick tutorial.

Angualr combines NG-Zorro to develop a backend system quickly and normatively.

System functions include the following:

  • The welcome page
  • List of users
  • The user of new
  • The user to change
  • The user to delete

All services use simulated data.

Let’s do it.

In combination with ng – zorro

Popular Angular UI frameworks include:

  • Angular Material officially specifies the UI framework
  • Ng-zorro, also known as Ant Design of Angular is a popular UI framework in China

Ant Design believes that people who do front-end development are familiar with it. So let’s do it with the ng-Zorro framework. If you’re familiar with the Vue or React versions of Ant Design, you’ll be able to seamlessly connect

We reuse angular-CLI to generate a project ng-Zorro. If you don’t already know the main angular content, go to the article to learn about Angular development.

Adding ng-zorro is simple: Go to the ng-zorro root directory and execute ng add ng-zorro-antd.

You can also install NPM ng-zorro-antd, not recommended.

After nG-Zorro is complete, we run the project NPM run Start. You will see the following image on the http://localhost:4200 page.

Not Bad, Bro.

Configure the routing

We changed to hash routing and added user routing, and the scaffolding did it for us, so we just had to make some minor changes.

Ideas:

  1. Add the page firstuserUser list page, usedng-zorrotablecomponent
  2. The user’s new and changed pages can share the same page, usingng-zorroformcomponent
  3. Page delete function directly using popup prompt, useng-zorromodalcomponent
  4. rightng-zorroComponents are imported on demand
  5. Adjusting routing Files

In line with the idea, we have to introduce 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: [ Imports are added instead of declared in declarations
  NzTableModule,
  NzModalModule,
  NzButtonModule,
  NzFormModule,
  ReactiveFormsModule,
  NzInputModule
],
Copy the code

Simple and easy to understand principle, we do not use children for routing nesting:

// 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';

// Associated 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 {}Copy the code

Change the menu

The menu generated by using scaffolding does not match the function 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">
      <! -- Default click logo to jump to home page -->
      <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">List of users</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>
Copy the code

Menu display, if we need to do permission management, is the need to back end with the transfer of value, and then we will render the relevant permission menu to the page

After replacing the above code, the resulting basic skeleton looks like this:

Complete the user list

Next, complete the skeleton of the user list. Since we use the UI framework, we are surprisingly easy to write:

Get user list

// user.component.html

<nz-table #basicTable [nzData] ="list">
  <thead>
    <tr>
      <th>Name</th>
      <th>Position</th>
      <th>Action</th>
    </tr>
  </thead>
  <tbody>
    <! Select * from * from *;
    <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>
Copy the code

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"
}
Copy the code

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)
      }
    })
  }

}
Copy the code

Since no back-end services are introduced, we use localStorage to record the status.

When the above is done, we have the following list information:

Add and edit users

We simply set up a form containing only two fields, name and position. These two functions share the same form

In HTML we add:

// user-info.component.html

<form nz-form [formGroup] ="validateForm" class="login-form" (ngSubmit) ="submitForm()">
  <nz-form-item>
    <nz-form-control nzErrorTip="Please enter user name!">
      <input type="text" nz-input formControlName="username" placeholder="Please enter user name" style="width: 160px;" />
    </nz-form-control>
  </nz-form-item>
  <nz-form-item>
    <nz-form-control nzErrorTip="Please enter position!">
      <input type="text" nz-input formControlName="position" placeholder="Please enter 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>
Copy the code

The page looks like this:

Then it is the logical decision to add or modify. If the link carries the UUID identifier, it means that you are the editor, 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')})// Yes edit state, set 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) | | {}// Backfill information
      this.validateForm = this.fb.group({
        username: [current.name, [Validators.required]],
        position: [current.position, [Validators.required]]
      })
    }
    
  }

  submitForm() {
    // If the submission does not match, an error is 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 a 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 the user to update the 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))
    }
  }

}
Copy the code

We’re going to start with isAdd, which is a new user by default; When the UUID exists, set it to false to indicate that it is in the edit state, backfilling the content from the form. Submission of forms is also judged by this identifier. We directly change the localStorage information to ensure that the list information is synchronized.

Delete function

We introduce a modal dialog to ask whether to delete.

// user.component.ts

/ / delete
delete(data: any) {
  this.modal.confirm({
    nzTitle: '< I > Do you want to delete this user? '.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
    }
  });
}
Copy the code

We find the deleted data, cull it, re-cache the new user data, and update the table’s user list data.

So far, we have successfully completed a simple project. Let’s look at the whole thing using a Gif.

【 the 】 ✅