目前为止Angular版本是11,利用TypeScript的装饰器(Decorators)特性,可以写类似Java Spring Boots面向切面风格的代码。没写过这样代码的人可能会不适应,其实基础好的话稍微看看官方文档可以马上上手。
项目介绍
本项目使用脚手架 Angular CLI (version 11.2.12) 构建。UI使用 NG-ZORRO (官网),这是 Ant Design 的 Angular 版。
目前只实现纯静态登陆。用户名: admin 密码: 123456
截图:
1. 默认登陆界面
2. 点击登陆按钮验证表单非空
3. 验证用户名密码错误
4. 登陆成功
项目目录
ng-demo
├── README.md
├── angular.json
├── e2e
│ ├── protractor.conf.js
│ ├── src
│ │ ├── app.e2e-spec.ts
│ │ └── app.po.ts
│ └── tsconfig.json
├── karma.conf.js
├── package.json
├── src
│ ├── app
│ │ ├── app-routing.module.ts
│ │ ├── app.component.html
│ │ ├── app.component.scss
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ └── components
│ │ ├── home
│ │ │ ├── home.component.html
│ │ │ ├── home.component.scss
│ │ │ └── home.component.ts
│ │ ├── login
│ │ │ ├── login.component.html
│ │ │ ├── login.component.scss
│ │ │ ├── login.component.ts
│ │ │ └── login.module.ts
│ │ └── ng-zorro-antd.module.ts
│ ├── assets
│ │ └── images
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.scss
│ ├── test.ts
│ └── theme.less
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
└── tslint.json
项目说明
/src/app/app.modules.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginModule } from './components/login/login.module';
import { NZ_I18N } from 'ng-zorro-antd/i18n';
import { zh_CN } from 'ng-zorro-antd/i18n';
import { registerLocaleData } from '@angular/common';
import zh from '@angular/common/locales/zh';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
registerLocaleData(zh);
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
LoginModule,
FormsModule,
HttpClientModule,
BrowserAnimationsModule,
ReactiveFormsModule
],
providers: [{ provide: NZ_I18N, useValue: zh_CN }],
bootstrap: [AppComponent]
})
export class AppModule { }
- 引入路由模块 AppRoutingModule、登陆模块 LoginModule 、表单模块 FormsModule ,并通过 @NgModule 装饰器的 imports 属性导入。
- AppComponent 组件放在 @NgModule 装饰器的 declarations 属性 (可声明对象表) 和 bootstrap 属性 (主视图,只有根模块才设置这个属性) 中。
- 关于 @NgModule 的使用,参考官方文档模块部分 。
/src/app/app.component.html
<router-outlet></router-outlet>
只有一个路由标签。
/src/app/app.routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './components/login/login.component';
import { HomeComponent } from './components/home/home.component';
const routes: Routes = [
{path: 'login', component: LoginComponent},
{path: '', redirectTo: '/login', pathMatch: 'full'},
{path: 'home', component: HomeComponent},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
- 设置路由的文件,项目启动后页面默认重定向到 /login 路由。
- 添加了 /home 路由,本实例暂时没用到。
/src/app/components/ng-zorro-antd.module.ts
...
import { NzButtonModule } from 'ng-zorro-antd/button';
...
@NgModule({
exports: [
...
NzButtonModule,
...
]
})
export class NgZorroAntdModule {}
- 这个文件从 ng-zorro-antd 中取得所有组件 Module 并通过@NgModule 装饰器的 exports 属性导出。
/src/app/components/login/login.moudle.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { LoginComponent } from './login.component';
import { NgZorroAntdModule } from '../ng-zorro-antd.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [
LoginComponent
],
imports: [
BrowserModule,
NgZorroAntdModule,
FormsModule,
ReactiveFormsModule
],
providers: [],
bootstrap: [
LoginComponent
]
})
export class LoginModule { }
- 这里引入了刚才定义的 ng-zorro-antd 导出的模块 NgZorroAntdModule,可用在 html 部分,例如一个普通按钮 <button nz-button>按钮</button> 标签, 增加了 nz-button 属性,让它在编译之后称为 ng-zorro-antd 按钮组件。
- 引入 AppComponent 组件。
- 注意引入表单相关依赖,做表单校验等会用到。
/src/app/components/login/login.components.html
登陆的 html 部分只列出表单相关代码。
...
<form nz-form name="form" [formGroup]="validateForm" (ngSubmit)="onSubmit()">
<nz-form-item>
<nz-form-label [nzSpan]="4">用户名</nz-form-label>
<nz-form-control
nzHasFeedback
[nzSpan]="20"
nzErrorTip="请输入用户名"
>
<nz-input-group [nzSuffix]="suffixUsername">
<input
nz-input
placeholder="请输入用户名"
name="username"
formControlName="username"
required
/>
</nz-input-group>
<ng-template #suffixUsername>
<i
nz-icon
class="ant-input-clear-icon"
nzTheme="fill"
nzType="close-circle"
*ngIf="validateForm.controls.username.value.length>0"
(click)="validateForm.controls.username.setValue('')"
></i>
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="4">密 码</nz-form-label>
<nz-form-control
nzHasFeedback
[nzSpan]="20"
nzErrorTip="请输入密码"
>
<nz-input-group [nzSuffix]="suffixPassword">
<input
nz-input
name="password"
formControlName="password"
[type]="passwordVisible ? 'text' : 'password'"
placeholder="请输入密码"
required
/>
</nz-input-group>
<ng-template #suffixPassword>
<i
nz-icon
class="ant-input-clear-icon"
nzTheme="fill"
nzType="close-circle"
*ngIf="validateForm.controls.password.value.length>0"
(click)="validateForm.controls.password.setValue('')"
></i>
<i
nz-icon
[nzType]="passwordVisible ? 'eye-invisible' : 'eye'"
(click)="passwordVisible = !passwordVisible"
></i>
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control [nzSpan]="20" [nzOffset]="4">
<div nz-row class="login-form-margin">
<div nz-col [nzSpan]="12">
<label nz-checkbox formControlName="remember">
<span>记住我</span>
</label>
</div>
<div nz-col [nzSpan]="12" style="text-align: right;">
<a class="login-form-forgot">忘记密码?</a>
</div>
</div>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control [nzSpan]="24" [nzOffset]="0" style="text-align: center;">
<button
type="submit"
nz-button
style="width: 100px;"
nzType="primary"
ng-disabled="form.$invalid"
>登录</button>
</nz-form-control>
</nz-form-item>
</form>
...
- 表单的 [formGroup]=“validateForm” 属性绑定验证对象。
- 表单的 (ngSubmit)=“onSubmit()” 属性绑定表单提交事件。
- 输入控件的 formControlName 属性绑定对应的控件名。
/src/app/components/login/login.components.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { NzMessageService } from 'ng-zorro-antd/message';
@Component({
selector: 'app-component',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
constructor(
public login: FormBuilder,
private message: NzMessageService
) {}
public validateForm!: FormGroup;
public passwordVisible:Boolean = false;
// 提交表单
onSubmit():void{
// 更新表单未通过验证的控件高亮显示
for (const i in this.validateForm.controls) {
this.validateForm.controls[i].markAsDirty();
this.validateForm.controls[i].updateValueAndValidity();
}
// 如果验证通过
if(this.validateForm.status=='INVALID'){
this.message.create('warning', '请填写用户名和密码');
return;
}
// 如果验证通过
if(this.validateForm.status=='VALID'){
let { username, password } = this.validateForm.value;
if( username == 'admin' && password == '123456' ){
this.message.create('success', '登录成功');
}else{
this.message.create('error', '用户名密码错误');
}
}
}
// 初始化
ngOnInit(): void {
this.validateForm = this.login.group({
username: ['', [Validators.required]],
password: ['', [Validators.required]],
remember: [true]
});
}
}
- ngOnInit() 是组件生命周期的一种,Angular 会在首次检查完组件或指令的输入属性后,紧接着调用它。这里的业务逻辑是初始化验证表单的内容。
- onSubmit() 提交表单的逻辑。首先验证实现了 FormGroup 接口的表单。高亮化未通过验证的组件(用户名和密码只做了非空验证)。然后验证是否登陆成功,做对应处理。
项目地址: https://codechina.csdn.net/sonicwater1/ng-demo
结语:面向切面这种编码风格值得尝试,学习 Angular 的同时也能加强了对 TypeScript 的掌握。