angular入门级教程

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

准备知识

代码风格

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.jsonAngular 工作区及项目的配置文件,参考Angular文档
_mockMock 数据规则目录,若你通过 命令行工具 创建项目时可以指定 --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.tsshared-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 策略,这意味着把此策略设置为 DefaultCheckAlways )将禁用自动变更检测,直到重新激活。变更检测仍然可以显式调用。此策略适用于所有子指令,并且不能被覆盖。
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国际化类库

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果你是初学者,以下是一个简单的 Angular 入门教程,帮助你开始学习 Angular: 1. 安装 Angular CLI:首先,你需要安装 Node.js 和 npm(Node 包管理器)。然后,使用 npm 安装 Angular CLI。在命令行中运行以下命令: ``` npm install -g @angular/cli ``` 2. 创建新的 Angular 项目:使用 Angular CLI 创建一个新的 Angular 项目。在命令行中运行以下命令: ``` ng new my-app ``` 这将创建一个名为 "my-app" 的新项目,并自动安装所需的依赖项。 3. 运行应用程序:进入项目目录,然后使用以下命令启动开发服务器: ``` cd my-app ng serve ``` 这将启动一个开发服务器,并在默认端口上运行你的应用程序(通常是 http://localhost:4200)。 4. 编辑应用程序:打开你喜欢的代码编辑器,并导航到 "src/app" 目录。在这里,你将找到应用程序的根组件 "app.component.ts"。你可以编辑这个文件并开始构建你的应用程序。 5. 添加新组件:使用 Angular CLI 生成一个新的组件。在命令行中运行以下命令: ``` ng generate component my-component ``` 这将生成一个名为 "my-component" 的新组件,并自动更新应用程序中的文件和配置。 6. 在应用程序中使用组件:在根组件的模板中使用新生成的组件,或者在其他组件中使用它。 以上是一个简单的 Angular 入门教程。当你熟悉了这些基础知识后,你可以继续学习 Angular 的更多高级主题,如路由、表单处理、服务等。记得查阅官方文档和其他教程资源以获得更多深入的学习。祝你学习愉快!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值