Angular学习
环境搭建
NodeJS 安装
C:\Users\hesai>node -v v12.22.3
cnpm 安装 淘宝镜像
npm install -g cnpm --registry=https://registry.npmmirror.com cnpm install
安装 Angular CLI
npm install -g @angular/cli #查看ng 版本 C:\Users\hesai>ng version _ _ ____ _ ___ / \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _| / △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | | / ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | | /_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___| |___/ Angular CLI: 11.2.14 Node: 12.22.3 OS: win32 x64 Angular: ... Ivy Workspace: Package Version ------------------------------------------------------ @angular-devkit/architect 0.1102.14 (cli-only) @angular-devkit/core 11.2.14 (cli-only) @angular-devkit/schematics 11.2.14 (cli-only) @schematics/angular 11.2.14 (cli-only) @schematics/update 0.1102.14 (cli-only)
运行条件
由于目前各种环境(浏览器或 Node)暂不支持ES6的代码,所以需要一些shim和polyfill(IE需要)让ES6写的代码能够转化为ES5形式并可以正常运行在浏览器中。

-
systemjs - 通用模块加载器,支持AMD、CommonJS、ES6等各种格式的JS模块加载。
-
es6-module-loader - ES6模块加载器,systemjs会自动加载这个模块。
-
traceur - ES6转码器,将ES6代码转换为当前浏览器支持的ES5代码,systemjs会自动加载 这个模块。
版本升级
12.2.13 #卸载当前版本 $ npm uninstall -g angular-cli $ npm uninstall -g @angular/cli #清除未卸载干净的angular-cli缓存 $ npm cache clean -f #删除安装文件夹 #安装最新版本 $ npm install -g @angular/cli@12 或者 $ yarn global add @angular/cli@12
遇到问题
ng serve --open 运行报错
ng : 无法加载文件 C:\Users\dell\AppData\Roaming\npm\ng.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Execution_ Policies。 所在位置 行:1 字符: 1 + ng serve --open + ~~ + CategoryInfo : SecurityError: (:) [],PSSecurityException + FullyQualifiedErrorId : UnauthorizedAccess #原因 powershell 权限问题 #1.在win10 系统中搜索框 输入 Windows PowerShell,选择 管理员身份运行 #2、使用,win+R打开了powershell命令行之后,输入set-ExecutionPolicy RemoteSigned,然后更改权限为A,最后通过 get-ExecutionPolicy 查看当前的状态
项目结构
项目目录说明
|-- 文件名 // 说明 |-- 首层目录 |-- e2e // 自动化集成测试目录 |-- node_modules // npm/cnpm第三方依赖包存放目录 |-- src // 应用源代码目录 |-- .editorconfig // 不同编译器统一代码风格 |-- .gitignore // git中的忽略文件列表 |-- angular.json // Angular的配置文件 |-- browserslist // 配置浏览器兼容性的文件 |-- karma.conf.js // 自动化测试框架Karma的配置文件 |-- package-lock.json // 依赖包版本锁定文件 |-- package.json // 标准的npm工具的配置文件 |-- README.md // 项目说明的MakeDown文件 |-- tsconfig.app.json // app项目的TypeScript的配置文件 |-- tsconfig.json // 整个工作区的TypeScript配置文件 |-- tsconfig.spec.json // 用于测试的TypeScript配置文件 |-- tslint.json // TypeScript的代码静态扫描配置 |-- src目录 |-- app // 工程源码目录 |-- assets // 资源目录 |-- environments // 环境配置目录 |-- favicon.ico // header里的icon |-- index.html // 单页应用的宿主HTML |-- main.ts // 入口ts文件 |-- polyfills.ts // 不同浏览器兼容脚本加载 |-- karma.conf.js // 自动化测试框架Karma的配置文件 |-- style.css // 整个项目的全局css |-- test.ts // 测试入口 |-- app目录 |-- app-routing.module.ts // app路由 |-- app.component.css // app的css |-- app.component.html // app的html |-- app.component.spec.ts // app的测试 |-- app.component.ts // app的组件 |-- app.module.ts // app的模块 |-- environments目录 |-- environments.prod.ts // 生产环境 |-- environments.ts // 开发环境
常用命令
$ ng g component #创建组件 $ ng g module #创建模块 $ ng g service #创建服务 $ ng g interface #创建接口
常用指令
| 指令 | 作用 |
|---|---|
| ngSubmit | 表单提交 |
基础知识
指令
[(ngModel)] : 双向绑定
构建
bundles - UMD 模块格式。
esm5 - 主要使用 es5 的模块格式,但也使用来自 es6 的导入/导出语法。
esm2015 - 使用 es2015/es6 的模块格式。
fesm5 - esm5 的扁平化版本。
fesm2015 -peerDependencies esm2015 的扁平化版本。
lib - 库的 TypeScript 定义。
名词定义
什么是UMD
所谓UMD (Universal Module Definition),就是一种javascript通用模块定义规范,让你的模块能在javascript所有运行环境中发挥作用。
模块
组件
接口
创建接口
$ ng g interface model/logBook/baseProject/BaseProjectDTO
使用接口
//imoprt 导入
服务
创建服务
$ ng g service services/storage.service
服务使用
// 1.app.module.ts 里面引入创建的服务
import { StorageService } from './services/storage.service';
// 2. NgModule 里面的 providers 里面依赖注入服务
@NgModule({
declarations: [
AppComponent,
HeaderComponent,
FooterComponent,
NewsComponent,
TodolistComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [StorageService],
bootstrap: [AppComponent]
})
export class AppModule { }
//3、使用的页面引入服务,注册服务
import { StorageService } from '../../services/storage.service';
constructor(private storage: StorageService) {
}
组件通信
@ViewChild
属性装饰器,用于配置一个视图查询。 变更检测器会在视图的 DOM 中查找能匹配上该选择器的第一个元素或指令。 如果视图的 DOM 发生了变化,出现了匹配该选择器的新的子节点,该属性就会被更新。
主要用来父组件获取子组件
@Input
一个装饰器,用来把某个类字段标记为输入属性,并提供配置元数据。 该输入属性会绑定到模板中的某个 DOM 属性。当变更检测时,Angular 会自动使用这个 DOM 属性的值来更新此数据属性。
主要用来父组件给子组件传值或者方法
@OutPut
一个装饰器,用于把一个类字段标记为输出属性,并提供配置元数据。 凡是绑定到输出属性上的 DOM 属性,Angular 在变更检测期间都会自动进行更新。
子组件触发父组件的方法
路由
NG ZORRO
项目搭建
创建项目
$ ng new ngproject $ ng new ngproject --skip-install #跳过安装
运行
$ cd ngproject $ ng serve --open #启动项目
安装ng zorro
$ ng add ng-zorro-antd
搭建项目结构
#1、创建登陆模块 $ ng g module module/login --routing #带路由的模块 #2、创建组件 $ ng g component module/login #3、创建其他模块及组件 $ ng g module module/default --routing $ ng g component module/default $ ng g component module/default/home $ ng g component module/default/report $ ng g component module/default/keywords $ ng g component module/default/alarm
配置路由
根路由(AppRoutingModule)路由懒加载
//根路由(AppRoutingModule)中配置子路由
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [{
path: 'login',
loadChildren: () => import('./module/login/login.module').then(m => m.LoginModule) //登陆模块
},{
path: 'default',
loadChildren: () => import('./module/default/default.module').then(m => m.DefaultModule) //首页
},
{ path: '**', pathMatch: 'full', redirectTo: 'login' }]; //配置没有路径时跳转到login
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
根模块中加载动态模块
<!--app.component.html中增加以下代码 --> <router-outlet></router-outlet>
登陆及首页路由配置
//登陆模块
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './login.component';
const routes: Routes = [
{ path: '', component: LoginComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LoginRoutingModule { }
//首页
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DefaultComponent } from './default.component';
import { HomeComponent } from './home/home.component';
import { ReportComponent } from './report/report.component';
import { KeywordsComponent } from './keywords/keywords.component';
import { AlarmComponent } from './alarm/alarm.component';
import { LoginGuard } from '../../service/login.guard';
const routes: Routes = [
{ path: '', component: DefaultComponent,
canActivate:[LoginGuard],
children:[ //配置子组件路由
{ path: 'home', component: HomeComponent },
{ path: 'report', component: ReportComponent },
{ path: 'keywords', component: KeywordsComponent },
{ path: 'alarm', component: AlarmComponent },
{ path: '**', component: HomeComponent }
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class DefaultRoutingModule { }
布局配置
//首页模块中引入nz 布局模块 DefaultModule
//antd对应的模块
import { NZ_I18N } from 'ng-zorro-antd/i18n';
import { zh_CN } from 'ng-zorro-antd/i18n';
//布局组件需要引入的模块
import { NzLayoutModule } from 'ng-zorro-antd/layout';
providers: [{ provide: NZ_I18N, useValue: zh_CN }] //引入服务
首页布局组件使用 src\app\module\default\default.component.html
<nz-layout> <nz-sider nzCollapsible [(nzCollapsed)]="isCollapsed" [nzTrigger]="null"> <div class="logo"></div> <ul nz-menu nzTheme="dark" nzMode="inline" > <li nz-submenu nzTitle="起始页码" nzIcon="user" nzOpen> <ul> <li nz-menu-item> <a [routerLink]="['/default/home']" routerLinkActive="router-link-active" >首页</a> </li> </ul> </li> <li nz-submenu nzTitle="舆情报告" nzIcon="team"> <ul> <li nz-menu-item> <a [routerLink]="['/default/report']" routerLinkActive="router-link-active" >全部舆情</a> </li> <li nz-menu-item> <a [routerLink]="['/default/report']" routerLinkActive="router-link-active" >正面舆情</a> </li> <li nz-menu-item> <a [routerLink]="['/default/report']" routerLinkActive="router-link-active" >负面舆情</a> </li> </ul> </li> <li nz-submenu nzTitle="舆情设置" nzIcon="team"> <ul> <li nz-menu-item> <a [routerLink]="['/default/keywords']" routerLinkActive="router-link-active" >舆情关键词设置</a> </li> <li nz-menu-item> <a [routerLink]="['/default/alarm']" routerLinkActive="router-link-active" >舆情报警设置</a> </li> </ul> </li> </ul> </nz-sider> <nz-layout> <!-- <nz-header> <i class="trigger" nz-icon [nzType]="isCollapsed ? 'menu-unfold' : 'menu-fold'" (click)="isCollapsed = !isCollapsed"></i> </nz-header> --> <nz-content> <!-- <nz-breadcrumb> <nz-breadcrumb-item>User</nz-breadcrumb-item> <nz-breadcrumb-item>Bill</nz-breadcrumb-item> </nz-breadcrumb> --> <div class="inner-content"> <router-outlet></router-outlet> </div> </nz-content> <nz-footer>Ant Design ©2020 Implement By Angular</nz-footer> </nz-layout> </nz-layout>
Button Icon
按钮的使用
//1、引入按钮模块
import { NzButtonModule } from 'ng-zorro-antd/button';
//nzType 按钮类型
<button nz-button nzType="primary">Primary Button</button>
//禁用
<i nz-icon nzType="stop" nzTheme="outline"></i>
//启用
<i nz-icon nzType="check" nzTheme="outline"></i>
问题
-
部分图标无法使用
ng-alain默认只导入了图标库的几十个图标,在 style-icons-auto.ts可进行查看
如果有需要可以在 style-icons-auto.ts中进入import and export
Message
全局展示操作反馈信息
//使用方式
import { NzMessageModule } from 'ng-zorro-antd/message';
constructor(private message: NzMessageService) {}
createBasicMessage(): void {
this.message.success('This is a normal message');
}
modal
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-modal-footer',
template: `
<button nz-button nzType="primary" (click)="showModal()">
<span>Show Modal</span>
</button>
<nz-modal
[(nzVisible)]="isVisible"
[nzTitle]="modalTitle"
[nzContent]="modalContent"
[nzFooter]="modalFooter"
(nzOnCancel)="handleCancel()"
>
<ng-template #modalTitle>Custom Modal Title</ng-template>
<ng-template #modalContent>
<p>Modal Content</p>
<p>Modal Content</p>
<p>Modal Content</p>
<p>Modal Content</p>
<p>Modal Content</p>
</ng-template>
<ng-template #modalFooter>
<span>Modal Footer:</span>
<button nz-button nzType="default" (click)="handleCancel()">Custom Callback</button>
<button nz-button nzType="primary" (click)="handleOk()" [nzLoading]="isConfirmLoading">Custom Submit</button>
</ng-template>
</nz-modal>
`
})
export class NzDemoModalFooterComponent {
isVisible = false;
isConfirmLoading = false;
constructor() {}
showModal(): void {
this.isVisible = true;
}
handleOk(): void {
this.isConfirmLoading = true;
setTimeout(() => {
this.isVisible = false;
this.isConfirmLoading = false;
}, 1000);
}
handleCancel(): void {
this.isVisible = false;
}
}
Select选择器
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-select-label-in-value',
template: `
<p>The selected option's age is {{ selectedValue?.age }}</p>
<br />
<nz-select
[(ngModel)]="selectedValue"
[compareWith]="compareFn"
(ngModelChange)="log($event)"
nzAllowClear
nzPlaceHolder="Choose"
>
<nz-option *ngFor="let option of optionList" [nzValue]="option" [nzLabel]="option.label"></nz-option>
</nz-select>
`,
styles: [
`
nz-select {
width: 120px;
}
`
]
})
export class NzDemoSelectLabelInValueComponent {
optionList = [
{ label: 'Lucy', value: 'lucy', age: 20 },
{ label: 'Jack', value: 'jack', age: 22 }
];
selectedValue = { label: 'Jack', value: 'jack', age: 22 };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
compareFn = (o1: any, o2: any): boolean => (o1 && o2 ? o1.value === o2.value : o1 === o2);
log(value: { label: string; value: string; age: number }): void {
console.log(value);
}
}
注: [(ngModel)] 与 (ngModelChange) 顺序不同,ngModel获取到的值不同
ngModel 在前 取到的是改变后的值
ngModel在后 取到的是改变前的值
Table
树形表格
import { Component, OnInit } from '@angular/core';
export interface TreeNodeInterface {
key: string;
name: string;
age?: number;
level?: number;
expand?: boolean;
address?: string;
children?: TreeNodeInterface[];
parent?: TreeNodeInterface;
}
@Component({
selector: 'nz-demo-table-expand-children',
template: `
<nz-table #expandTable [nzData]="listOfMapData" nzTableLayout="fixed">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let data of expandTable.data">
<ng-container *ngFor="let item of mapOfExpandedData[data.key]">
<tr *ngIf="(item.parent && item.parent.expand) || !item.parent">
<td
[nzIndentSize]="item.level! * 20"
[nzShowExpand]="!!item.children"
[(nzExpand)]="item.expand"
(nzExpandChange)="collapse(mapOfExpandedData[data.key], item, $event)"
>
{{ item.name }}
</td>
<td>{{ item.age }}</td>
<td>{{ item.address }}</td>
</tr>
</ng-container>
</ng-container>
</tbody>
</nz-table>
`
})
export class NzDemoTableExpandChildrenComponent implements OnInit {
listOfMapData: TreeNodeInterface[] = [
{
key: `1`,
name: 'John Brown sr.',
age: 60,
address: 'New York No. 1 Lake Park',
children: [
{
key: `1-1`,
name: 'John Brown',
age: 42,
address: 'New York No. 2 Lake Park'
},
{
key: `1-2`,
name: 'John Brown jr.',
age: 30,
address: 'New York No. 3 Lake Park',
children: [
{
key: `1-2-1`,
name: 'Jimmy Brown',
age: 16,
address: 'New York No. 3 Lake Park'
}
]
},
{
key: `1-3`,
name: 'Jim Green sr.',
age: 72,
address: 'London No. 1 Lake Park',
children: [
{
key: `1-3-1`,
name: 'Jim Green',
age: 42,
address: 'London No. 2 Lake Park',
children: [
{
key: `1-3-1-1`,
name: 'Jim Green jr.',
age: 25,
address: 'London No. 3 Lake Park'
},
{
key: `1-3-1-2`,
name: 'Jimmy Green sr.',
age: 18,
address: 'London No. 4 Lake Park'
}
]
}
]
}
]
},
{
key: `2`,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park'
}
];
mapOfExpandedData: { [key: string]: TreeNodeInterface[] } = {};
collapse(array: TreeNodeInterface[], data: TreeNodeInterface, $event: boolean): void {
if (!$event) {
if (data.children) {
data.children.forEach(d => {
const target = array.find(a => a.key === d.key)!;
target.expand = false;
this.collapse(array, target, false);
});
} else {
return;
}
}
}
convertTreeToList(root: TreeNodeInterface): TreeNodeInterface[] {
const stack: TreeNodeInterface[] = [];
const array: TreeNodeInterface[] = [];
const hashMap = {};
stack.push({ ...root, level: 0, expand: false });
while (stack.length !== 0) {
const node = stack.pop()!;
this.visitNode(node, hashMap, array);
if (node.children) {
for (let i = node.children.length - 1; i >= 0; i--) {
stack.push({ ...node.children[i], level: node.level! + 1, expand: false, parent: node });
}
}
}
return array;
}
visitNode(node: TreeNodeInterface, hashMap: { [key: string]: boolean }, array: TreeNodeInterface[]): void {
if (!hashMap[node.key]) {
hashMap[node.key] = true;
array.push(node);
}
}
ngOnInit(): void {
this.listOfMapData.forEach(item => {
this.mapOfExpandedData[item.key] = this.convertTreeToList(item);
});
}
}
树选择
import { Component } from '@angular/core';
import { NzFormatEmitEvent, NzTreeNodeOptions } from 'ng-zorro-antd/tree';
@Component({
selector: 'nz-demo-tree-select-async',
template: `
<nz-tree-select
style="width: 250px"
nzPlaceHolder="Please select"
[nzExpandedKeys]="expandKeys"
[(ngModel)]="value"
[nzDropdownMatchSelectWidth]="true"
[nzDropdownStyle]="{ 'max-height': '300px' }"
[nzNodes]="nodes"
[nzAsyncData]="true"
(nzExpandChange)="onExpandChange($event)"
></nz-tree-select>
`
})
export class NzDemoTreeSelectAsyncComponent {
expandKeys = ['0-0'];
value?: string;
nodes = [
{
title: 'Node1',
value: '0-0',
key: '0-0',
children: [
{
title: 'Child Node1',
value: '0-0-1',
key: '0-0-1'
},
{
title: 'Child Node2',
value: '0-0-2',
key: '0-0-2'
}
]
},
{
title: 'Node2',
value: '0-1',
key: '0-1'
}
];
onExpandChange(e: NzFormatEmitEvent): void {
const node = e.node;
if (node && node.getChildren().length === 0 && node.isExpanded) {
this.loadNode().then(data => {
node.addChildren(data);
});
}
}
loadNode(): Promise<NzTreeNodeOptions[]> {
return new Promise(resolve => {
setTimeout(
() =>
resolve([
{ title: 'Child Node', key: `${new Date().getTime()}-0` },
{ title: 'Child Node', key: `${new Date().getTime()}-1` }
]),
1000
);
});
}
}
Ng Alain
准备知识
-
文档类
-
TypeScript中文文档,虽然 TypeScript 跟 Java、C# 语法很像,这是语法基础需要认真阅读
-
Angular中文文档,建议一定要花时间阅读文档部分,透过它基本上就可以学会 Angular;同时,也是 Angular API 接口文档
-
NG-ZORRO中文文档,NG-ZORRO 作为 NG-ALAIN 的基础组件库,当你不懂某个组件时,它就是最好的文档,包含组件用法及API说明
-
NG-ALAIN中文文档,包含所有
@delon/*类型的用法及API说明 -
G2图表中文文档,如果需要图表开发,则这份文档是必备
-
-
辅助类
-
Ant Design 指引文章,学习 Ant Design 的设计理念,非常值得阅读的部分
-
NG-ZORRO 社区推荐,一份非常值得学习的清单
-
代码风格
NG-ALAIN 使用 ESLint 来保证代码质量 与 Prettier 来优化代码风格。
推荐安装几个插件在 vscode 中更友好的开发:
项目搭建
新建项目
$ ng new gzga-client --style less --routing #遇到问题,无法新建项目 D:\jsWorkspace>ng new gzga-client --style less --routing setTimeout is not defined #解决办法,重新安装脚手架 #卸载当前版本 $ npm uninstall -g angular-cli $ npm uninstall -g @angular/cli #清除未卸载干净的angular-cli缓存 $ npm cache clean -f #删除安装文件夹 #安装最新版本 $ npm install -g @angular/cli@12 #安装 ng-alain $ ng add ng-alain
禁用Eslint
暂时禁用ESlint插件
创建模块
#创建模块 $ ng g ng-alain:module logBook
创建组件
#创建基础项目组件 $ ng g component userProject/userProjectList --flat $ ng g component userProject/userProjectView --flat #创建项目人员关联组件 $ ng g component baseProject/baseProjectList --flat $ ng g component baseProject/baseProjectView --flat #创建过程组件 $ ng g component process/processView --flat $ ng g component process/processList --flat #活动分解组件 $ ng g component activity/activityList --flat $ ng g component activity/activityView --flat #日志填报组件 $ ng g component logFill/logFillList --flat $ ng g component logFill/logFillView --flat
配置模块
//1、声明组件
const COMPONENTS: Array<Type<void>> = [
BaseProjectViewComponent,
BaseProjectlistComponent,
UserProjectListComponent,
UserProjectViewComponent,
ProcessListComponent,
ProcessViewComponent,
ActivityListComponent,
ActivityViewComponent,
LogFillListComponent,
LogFillViewComponent
];
@NgModule({
imports: [SharedModule, LogBookRoutingModule],
declarations: COMPONENTS
})
export class LogBookModule {}
//2、导入路由
imports: [SharedModule, LogBookRoutingModule]
配置路由
//主路由配置,配置默认路径为进入日志模块 RouteRoutingModule
{ path: '', redirectTo: 'logBook', pathMatch: 'full' }
//日志子模块路由配置 LogBookRoutingModule
const routes: Routes = [
{ path: 'logFill', component: LogFillListComponent },
{
path: 'base',
children: [
{ path: 'project', component: BaseProjectlistComponent },
{ path: 'process', component: ProcessListComponent },
{ path: 'activity', component: ActivityViewComponent }
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LogBookRoutingModule {}
配置菜单
//app-data.json
{
"app": {
"name": "日志管理",
"description": "Ng-zorro admin panel front-end framework"
},
"user": {
"name": "Admin",
"avatar": "./assets/tmp/img/avatar.jpg",
"email": "cipchk@qq.com"
},
"menu": [
{
"text": "日志管理",
"i18n": "menu.log",
"group": true,
"hideInBreadcrumb": true,
"children": [
{
"text": "基础数据",
"i18n": "menu.log.base",
"icon": "anticon-dashboard",
"children": [
{
"text": "项目管理",
"link": "/logBook/base/project",
"i18n": "menu.log.project"
},
{
"text": "活动管理",
"link": "/logBook/base/activity",
"i18n": "menu.log.activity"
},
{
"text": "过程管理",
"link": "/logBook/base/process",
"i18n": "menu.log.process"
}
]},
{
"text": "日志填报",
"i18n": "menu.log.logFill",
"link": "/logBook/logFill",
"icon": "anticon-appstore"
}
]
}
]
}
//zh-CN.json
{
"menu.log":"日志管理",
"menu.log.base":"基础数据",
"menu.log.project":"项目管理",
"menu.log.process":"过程管理",
"menu.log.activity":"活动管理",
"menu.log.logFill":"日志填报"
}
创建服务
创建需要的服务
#项目信息服务 $ ng g service service/logBook/baseProject/baseProject #过程服务 $ ng g service service/logBook/process/process #活动服务 $ ng g service service/logBook/activity/activity #日志填写服务 $ ng g service service/logBook/logFill/logFill #人员管理服务 $ ng g service service/common/user/user #单位管理服务 $ ng g service service/common/unit/unit
创建接口
#项目基础信息DTO $ ng g interface model/logBook/baseProject/BaseProjectDTO #过程DTO $ ng g interface model/logBook/process/ProcessDTO #活动DTO $ ng g interface model/logBook/activity/ActivityDTO #日志填写DTO $ ng g interface model/logBook/logFill/LogFillDTO $ ng g interface model/common/base/AbstractBaseEntityDTO $ ng g interface model/common/base/ReturnJSON
创建表
--日志项目基础表
CREATE TABLE "GZGASYS"."NK_LOG_PROJECT"
(
"UUID" VARCHAR2(40) NOT NULL,
"PRJ_CODE" VARCHAR2(100),
"PRJ_NAME" VARCHAR2(200),
"START_DATE" DATE,
"END_DATE" DATE,
"STATE" CHAR(4) DEFAULT 1,
"CREATE_TIME" VARCHAR2(20),
"CREATE_USER_ID" VARCHAR2(20),
"UPDATE_USER_ID" VARCHAR2(20),
"UPDATE_TIME" VARCHAR2(20),
NOT CLUSTER PRIMARY KEY("UUID")) STORAGE(ON "GZGA_SYS", CLUSTERBTR) ;
COMMENT ON TABLE "GZGASYS"."NK_LOG_PROJECT" IS '日志管理-项目基础信息表';
COMMENT ON COLUMN "GZGASYS"."NK_LOG_PROJECT"."UUID" IS '主键';
COMMENT ON COLUMN "GZGASYS"."NK_LOG_PROJECT"."PRJ_CODE" IS '项目代码';
COMMENT ON COLUMN "GZGASYS"."NK_LOG_PROJECT"."PRJ_NAME" IS '项目名称';
COMMENT ON COLUMN "GZGASYS"."NK_LOG_PROJECT"."START_DATE" IS '开始日期';
COMMENT ON COLUMN "GZGASYS"."NK_LOG_PROJECT"."END_DATE" IS '结束日期';
COMMENT ON COLUMN "GZGASYS"."NK_LOG_PROJECT"."STATE" IS '状态 0 禁用 1 启用';
COMMENT ON COLUMN "GZGASYS"."NK_LOG_PROJECT"."CREATE_TIME" IS '创建时间';
COMMENT ON COLUMN "GZGASYS"."NK_LOG_PROJECT"."CREATE_USER_ID" IS '创建人';
COMMENT ON COLUMN "GZGASYS"."NK_LOG_PROJECT"."UPDATE_USER_ID" IS '更新人';
COMMENT ON COLUMN "GZGASYS"."NK_LOG_PROJECT"."UPDATE_TIME" IS '更新时间';
环境变量配置
export const environment = {
production: false,
useHash: true,
api: {
baseUrl: './',
serverUrl: 'http://localhost:8082/api', //配置后台调用地址
refreshTokenEnabled: true,
refreshTokenType: 'auth-refresh'
},
modules: [DelonMockModule.forRoot({ data: MOCKDATA })]
} as Environment;
拦截器配置
DefaultInterceptor 默认拦截器配置
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// 统一加上服务端前缀
let url = req.url;
if (!url.startsWith('https://') && !url.startsWith('http://')) {
const { baseUrl } = environment.api;
const { serverUrl } = environment.api;
//设置服务器地址
if (url.startsWith('/logBook')) {
url = serverUrl + url;
} else {
url = baseUrl + (baseUrl.endsWith('/') && url.startsWith('/') ? url.substring(1) : url);
}
}
const newReq = req.clone({ url, setHeaders: this.getAdditionalHeaders(req.headers) });
return next.handle(newReq).pipe(
mergeMap(ev => {
// 允许统一对请求错误处理
if (ev instanceof HttpResponseBase) {
return this.handleData(ev, newReq, next);
}
// 若一切都正常,则后续操作
return of(ev);
}),
catchError((err: HttpErrorResponse) => this.handleData(err, newReq, next))
);
}
Token设置
UserLoginComponent 登陆处理类中设置token
res.user.expired = +new Date() + 1000 * 60 * 5; //设置token res.user.token = 'xxxxx'; this.tokenService.set(res.user);
日期组件
由于项目默认没有日期组件,需手动引入
//SHARED_ZORRO_MODULES
import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
export const SHARED_ZORRO_MODULES = [
...,
NzSpinModule,
NzDatePickerModule //引入日期组件
];
项目结构
当使用 ng add ng-alain 生成后的 NG-ALAIN 脚手架,它的基本目录结构概略图如下:
├── _mock # Mock 数据规则目录 ├── angular.json # Angular 项目配置文件 ├── src │ ├── app │ │ ├── core # 核心模块 │ │ │ ├── i18n │ │ │ ├── net │ │ │ │ └── default.interceptor.ts # 默认HTTP拦截器 │ │ │ ├── services │ │ │ │ └── startup.service.ts # 初始化项目配置 │ │ │ └── core.module.ts # 核心模块文件 │ │ ├── layout # 通用布局 │ │ ├── routes │ │ │ ├── ** # 业务目录 │ │ │ ├── routes.module.ts # 业务路由模块 │ │ │ └── routes-routing.module.ts # 业务路由注册口 │ │ ├── shared # 共享模块 │ │ │ ├── shared-delon.module.ts # @Delon/* 次级共享模块导入 │ │ │ ├── shared-zorro.module.ts # NG-ZORRO 次级共享模块导入 │ │ │ └── shared.module.ts # 共享模块文件 │ │ ├── app.component.ts # 根组件 │ │ └── app.module.ts # 根模块 │ │ └── global-config.module.ts # @delon & ng-zorro 全局配置项 │ ├── assets # 本地静态资源 │ ├── environments # 环境变量配置 │ ├── styles # 样式目录 └── └── style.less # 样式引导入口
以下是针对各个目录及文件说明及使用目的:
| 名称 | 描述 |
|---|---|
| angular.json | Angular 工作区及项目的配置文件,参考Angular文档 |
| _mock | Mock 数据规则目录,若你通过 命令行工具 创建项目时可以指定 --mock 参数决定是否需要 Mock 功能 |
| src/app/core/core.module.ts | 核心模块,只会导入一次。因此,针对整个业务模块都需要使用的纯服务类(例如:消息、数据访问等) |
| src/app/core/i18n | 国际化数据加载及处理相关类,若你通过 命令行工具 创建项目时可以指定 -di 参数决定是否需要国际化支持 |
| src/app/core/net | 默认拦截器,你可以在这里统一处理请求参数、请求异常、业务异常等动作 |
| src/app/core/services/startup.service.ts | 当你需要在 Angular 启动前执行一些远程数据(例如:应用信息、用户信息等)时非常有用 |
| src/app/layout | 布局目录,包含基础布局、空白布局、用户登录布局 |
| src/app/routes | 业务模块,你的所有业务代码都将在这里 |
| src/app/shared/shared.module.ts | 共享模块,指当你需要针对整个业务模块都需要使用的一些第三方模块、自定义组件、自定义指令,都应该存在这里。除此之外,针对 @delon & NG-ZORRO 分别构建了 shared-delon.module.ts、shared-zorro.module.ts 两种次级共享模块的导入。 |
| src/app/global-config.module.ts | 针对 @delon & NG-ZORRO 的全局配置项 |
| src/environments | 应用环境变量,包含以下值:SERVER_URL 所有HTTP请求的前缀;production 是否生产环境;useHash 路由是否useHash模式 |
项目部署
项目编译
项目编译的几种方式如下
#项目直接打包 $ ng build # 项目打包时改变base-href $ ng build --base-href ./
常见问题
变更检测策略
默认变更检测器用来检测更改的策略。设置后,将在下次触发变更检测时生效
enum ChangeDetectionStrategy {
OnPush: 0
Default: 1
}
| 成员 | 说明 |
|---|---|
OnPush: 0 | 使用 CheckOnce 策略,这意味着把此策略设置为 Default( CheckAlways )将禁用自动变更检测,直到重新激活。变更检测仍然可以显式调用。此策略适用于所有子指令,并且不能被覆盖。 |
Default: 1 | 使用默认的 CheckAlways 策略,在该策略中,变更检测将自动执行,直到显式停用为止。 |
注意:
NG-ZORRO 及 @delon/* 组件默认在 OnPush 模式下工作,mutate 对象或者数组不会触发 Angular 的变更检测,请使用 immutable 方式
特殊选择器
| 名称 | 作用 |
|---|---|
| :host | 表示选择当前的组件 |
| ::ng-deep | 可以忽略中间className的嵌套层级关系。直接找到你要修改的className |
| :host-context | 如果需要满足某条件才能应用样式,这个正好。它在当前组件宿主元素的祖先节点中查找 CSS 类,直到文档的根节点为止。如果找到,才会应用后面的样式到内部元素 |
用法示例
//1、忽略层级,修改本组件下面某个特定类型的样式
:host ::ng-deep .className{
新的样式......
}
//2、把宿主元素作为目标的唯一方式。除此之外,你将没办法指定它,因为宿主不是组件自身模板的一部分,而是父组件模板的一部分
:host {
display: block;
border: 1px solid black;
}
//3、如果h1标签在设计的组件中有一个黑色标题,我希望能够在黑暗的主题背景上显示时将其更改为白色。如果无法访问源码,可以在父级的css中执行下面语句
.theme-dark widget-box ::ng-deep h1 {
color: white;
}
//或者
h1
{
color: black; // 默认颜色
:host-context(.theme-dark) &
{
color: white; // 当黑色主题时的颜色
}
// 或者设置属性 'outside' 为 [attr.theme]="'dark'"
:host-context([theme='dark']) &
{
color: white; // 当黑色主题时的颜色
}
}
//4、搜索某类型下面的特定类型
.card-container ::ng-deep .ant-tabs-card .ant-tabs-content {
height: 120px;
margin-top: -16px;
}
//5、:ng-deep如果放在顶头,这种强烈不推荐,会污染全局样式
::ng-deep .ant-tabs-card .ant-tabs-content {
height: 120px;
margin-top: -16px;
}
设置不生成spec文件
angular.json中设置
"@schematics/angular:component": {
"skipTests": true,
"flat": false,
"inlineStyle": true,
"inlineTemplate": false,
"style": "less"
}
图标问题
图标不显示问题
-
部分图标无法使用
ng-alain默认只导入了图标库的几十个图标,在 style-icons-auto.ts可进行查看
如果有需要可以在 style-icons-auto.ts中进入import and export
// 在style-icons-auto.ts进行配置图标组件
表格列渲染
columns: STColumn[] = [
{ title: '序号', type: 'no' },
{ title: '项目编号', index: 'prjCode' },
{ title: '项目名称', index: 'prjName' },
{ title: '项目开始日期', index: 'startDate' },
{ title: '项目结束日期', index: 'endDate' },
{ title: '项目状态', index: 'state', render: 'state' }, //状态列渲染
{
title: '操作',
buttons: [
{ text: '编辑', click: item => this.edit(item) },
{ text: '删除', click: item => this.remove(item) }
]
}
];
<st #st [columns]="columns" [data]="data" [loading]="loading">
<ng-template st-row="state" let-i let-index="index">
{{ i.state == '1' ? '启用' : '禁用' }}
</ng-template>
</st>
后台自动启动
echo 开始打包项目.... rem 项目目录 rem set "nkDir=D:\ideaWorkspace\nk-server" set nkDir=%cd% rem 项目发布的JAR目录 set nkJar=%nkDir%\nk-api\target rem d: cd %nkDir% git pull call mvn clean package -DskipTests cd %nkJar% java -jar nk-api-1.0.0.jar
nzValue与[nzValue]
模板中
[nzValue]="data"
nzValue="data"
nzValue="{{data}}" 有什么区别
nzValue="data" 组件收到的是字符串 data,
nzValue="{{data}}" 等价于 [nzValue]="data.toString()"。
如果你需要传入 number 或者 boolean 类型时,应当使用 [nzValue]="data" 的方式。
高度计算
height:calc(100% - 84);
常用接口
拦截器
HttpInterceptor
拦截 HttpRequest 并处理它们。
interface HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
}
使用说明
要想在整个应用中使用 HttpInterceptors 的同一个实例,就只能在 AppModule 模块中导入 HttpClientModule,并且把拦截器都添加到应用的根注入器中。 如果你在不同的模块中多次导入 HttpClientModule,则每次导入都会创建 HttpClientModule 的一个新复本,它将会覆盖根模块上提供的那些拦截器。
JSONP
Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。
TranslateService
angular国际化类库

1万+

被折叠的 条评论
为什么被折叠?



