angular实战踩坑记录
本文记录了一下本人学习了angular框架以后,首次生产开发踩的坑,因为是学得很急,基本上是边学边开发。开发的是一个轧辊管理的系统,写了不少界面,感觉还是实战提升比较大哈哈哈。
因为是纯新手,所以文章可能存在问题,请友善交流。
大概会涉及以下几个知识点:
service interface 时间戳 路由 三元表达式 接口数组嵌套以及html实现 区分id路由 代码规范 string与number转化 嵌套显示数组子元素 rowSpan行合并 Router和ActivatedRoute input输入限制为数字以及正数 自定义表单验证 生命周期(数据undefined) 枚举、管道
正文开始
1. 接口发送是post 获取是get get也会有参数
// 入炉作业清单-装炉计划查询 get
inquirePlans(params: PlansInquiry): Observable<Pagination<FurnaceList>> {
return this.http.get<Pagination<FurnaceList>>(API.PLANS, {params});
}
freight是参数类型,调用post方法 返回调用后的值 Observable是请求数据的异步请求 其中<>是他的调用get的参数类型 接口<泛型> ,return一个调用完get方法后得到的值
接口中类型有对象(object),有数组(array),注意写的格式也不同,比如
类型 | 形式 |
---|---|
对象 | area: Area |
数组 | link: Link [ ] |
同时在mock模拟数据的时候也是如此 是数组的比如pagination就加个[{ }] ,是对象的就直接写 { }
2. interface问题
① 有分页的时候注意在参数中加入pagination,没有的时候直接写接口。mock模拟数据没有分页的话,就直接写个方括号,里面是数据,比如:
② 若get接口带参数,不明原因报错可以在定义接口的时候加个extends HttpParams,不带参数就直接写一个params,比如
// 入炉作业操作-显示 get
inquireLinks(params): Observable<Pagination<LinkInquiry>> {
return this.http.get<Pagination<LinkInquiry>>(API.PUT_LINK, {params});
}
③ 如果请求中需要含有id之类的额外参数,比如修改或者删除,参数是id(或id数组)并在api后面加 /${id}
。
// 获取修改日志列表详情
modifyListGet(id: number): Observable<ModifyDetails<ModifyList>> {
return this.http.get<ModifyDetails<ModifyList>>(API.MODIFICATION_LIST + `/${id}`);
}
③ 删除的时候直接用delete
/* 轧辊货号管理-删除货号 */
deleteFreight(id: number): Observable<ResponseResult> { // 删除货号
return this.http.delete<ResponseResult>(API.FREIGHT + `/${id}`);
}
④ 没有响应内容时,observable里面的类型可以用any
// 入炉作业操作-保存轧辊入炉 post
savePutFreight(putWork: SavePutWork): Observable<any> {
return this.http.post<any>(API.FREIGHT_SAVE, putWork);
}
⑤ http.request 类型是可选方法的,包括delete、get、post 感觉不咋用
// 入炉作业操作-显示 get
inquireLinks(id: number): Observable<Pagination<FurnaceList>> {
return this.http.request<Pagination<FurnaceList>>('delete', API.PUT_LINK + `/${id}`);
}
⑥订阅接口数据的时候可以用这中方式接收
tableData: DetailInquiry;
this.enterFurnaceService.inquireDetail({detailId: this.detailId}).subscribe((data) => {
this.tableData = data;
});
1. 时间转换 时间戳
时间戳转换为时间
时间戳 | date:'yyyy-MM-dd HH:mm:ss’
时间转换应该用时间戳传递 再格式转换显示
时间转化为时间戳
传时间的时候如果未选中,默认会变成NaN这个时候应该加个判断变成
nullcreatedDt: (new Date(this.createdDate).getTime()) ? (new Date(this.createdDate).getTime()) : null,
路由
一般路由传值
① routerLink = ‘url’
② [routerLink] = “[url, key]” key不一定必须有
③ 子路由获取id
通过订阅的方式获取路由id
this.route.params.subscribe(data => {
this.linkId = data.id;
console.log(this.linkId);});
通过snapshot的方式获取id
if (data && data.length){
this.detailId = this.route.snapshot.params.id;
}
方法一: 适用于有可观察对象的 需要订阅的
constructor里写 private route: ActivatedRoute
,
ngInit中写
this.route.params.subscribe(data => {
this.linkId = data.id;
console.log(this.linkId);
});
方法二:
constructor里写 private route: ActivatedRoute
,
ngInit中写
console.log(this.route.snapshot.params.id);
想要给路由加id
① 在path的末尾加上 /:id
② 给 路由传过值去
子路由
默认路由是redirectTo
百度一下pathMatch的作用
breadcrumb是面包屑 一般用于左上角的导航
npm
前段的npm要在ui界面下执行 如果报错 记得先看cd目录文件!!!
常用表达式
① 三元表达式
判断式 ? a : b
代码规范
① 没用的代码删掉
② if这种代码注意 不存在的时候
③ 前端也会用递归的数据结构 要优雅
④ 加类型 加注释
前端问题
① 当接口中数组套数组的时候 用ng-container 来 ng-for,把数组都显示出来 一直for循环到最底层子元素
② html中?的作用
/* TypeScript
当 product没有值的时候,不访问其 price属性,
当 product有值的时候再去访问其 price属性
*/
product?.price
<td [colSpan]="10">{{ delongTable.tableData[0]?.remark }}</td>
③ 合并行列
用 [rowSpan]="link.details.length"
如果是数组嵌套多个数组 不知道具体的长度 需要累加
(1)在接口中定义一个长度的属性 这个属性写在最顶层(底下有很多嵌套数组)
technician: Technician; // 技术员
links: Links[]; // 计划内环节
totalTr?: number; // 计算当前子元素的行数
(2)在ts文件中循环forEach获得最底层的子元素的个数 赋值给最顶层的totalTr属性
this.delongTable.tableData.forEach(
item => {
let total = 0;
item.links.forEach(
link => {
link.details.forEach(
detail => {
total++;
}
);
}
);
item.totalTr = total;
}
);
(3) 在html中为rowSpan赋值
<td *ngIf="detailIndex == 0" [rowSpan]="link.details.length">{{ link.name }}</td>
④ 栅格布局
在行级元素加 nz-row 在列加nz-col 以及 nzSpan=“8”
共分为24份,8代表三分之一
<div class="furnace-item" nz-row *ngFor="let furItem of furNo; let furItemIndex = index">
<ul nz-col nzSpan="8">
<li (click)="selectFurnace(1)">a</li>
</ul>
⑤ 有时候代码报错显示没有这个数据或未定义,是由于接口传递过来的数据是异步的,不一定有值,需要加一个ngIf 和 ?
<tbody *ngIf="tableData?.freights">
<tr *ngFor="let freight of tableData.freights; let freightIndex = index">
⑥ input限制为数字以及正数,以及相应的格式
方法一: 自定义表单验证 + 正则表达式
⑦ 空格
一 直接使用
materialCode: [this.freight.materialCode, [ Validators.required, Validators.pattern(/^\d+(\.\d+)?$/) ]],
二 定义自定义表单
1)js中调用方法
① constructor中加
private fb: FormBuilder,
size: [this.freight.size, [ Validators.required, this.confirmNetWeight]],
② 定义 GroupForm
checkReading: FormGroup;
③ ngInit中写表单验证
this.checkReading = this.fb.group(
{
furnaceReading: [null, [ Validators.required , this.checkNum ]]
}
);
2)定义自定义表单
输入 数字 * 数字 * 数字
private confirmNetWeight(control: FormControl): { [ key: string ]: boolean} {
if (!/^\d+\*\d+\*\d+$/.test(control.value)) {
return { weightError: true};
}
return null;
}
输入 数字 ()? 表示可能有,可能没有
private confirmSize(control: FormControl): { sizeError: boolean } {
if (!control.value) { // 如果输入为空则返回空,相当于去空格
return null;
}
if (!/^\d+(\.\d+)?$/.test(control.value)) {
return { sizeError: true};
}
return null;
}
3)html中使用
<nz-form-explain *ngIf="freightForm.get('size').hasError('weightError')">请输入直径*辊身长度*总长度</nz-form-explain>
或者: 注意要在最外侧加一个[formGroup]="checkReading"
里层包裹的是 nz-form-control
<div class="reading-group" nz-form [formGroup]="checkReading">
<nz-form-control style="width: 100%">
<input type="text"
[(ngModel)]="readingText"
[disabled]="readOnly"
nz-input
placeholder="请输入电表读数"
formControlName="furnaceReading"
id="furnaceReading"
>
<nz-form-explain *ngIf="checkReading.get('furnaceReading').hasError('isNum')">请输入数字</nz-form-explain>
</nz-form-control>
<button nz-button nzType="primary" (click)="saveReading()" [disabled]="readOnly">保存</button>
</div>
方法二:
加一个 type="number ",设置最小数值 min=“0”, 加入下面这句话来取消字母的输入
onKeypress="return (/[\d.]/.test(String.fromCharCode(event.keyCode)))"
缺点: 科学计数法e也将无法输入,右边多了一个上下的箭头
改进: 在正则表达式里更改允许输入的字符 d是纯数字,加个点能输入浮点数
/[\d.e]/
正则表达式
验证数字:1$
验证n位的数字:^\d{n}$
验证至少n位数字:^\d{n,}$
验证m-n位的数字:^\d{m,n}$
验证零和非零开头的数字:^(0|[1-9][0-9])$
验证有两位小数的正实数:2+(.[0-9]{2})?$
验证有1-3位小数的正实数:3+(.[0-9]{1,3})?$
验证非零的正整数:^+?[1-9][0-9]$
验证非零的负整数:^-[1-9][0-9]$
验证非负整数(正整数 + 0) ^\d+$
验证非正整数(负整数 + 0) ^((-\d+)|(0+))$
验证长度为3的字符:^.{3}$
验证由26个英文字母组成的字符串:4+$
验证由26个大写英文字母组成的字符串:5+$
验证由26个小写英文字母组成的字符串:6+$
验证由数字和26个英文字母组成的字符串:7+$
验证由数字、26个英文字母或者下划线组成的字符串:^\w+$
验证用户密码:8\w{5,17}$ 正确格式为:以字母开头,长度在6-18之间,只能包含字符、数字和下划线。
验证是否含有 ^%&’,;=?KaTeX parse error: Can't use function '\"' in math mode at position 1: \̲"̲ 等字符:[^%&',;=?\x22]+
验证汉字:9,{0,}$
验证Email地址:^\w+[-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)$
验证InternetURL:^http://([\w-]+.)+[\w-]+(/[\w-./?%&=])?$ ;10+://(w+(-w+))(.(w+(-w+)))(?S)?$
验证电话号码:^((\d{3,4})|\d{3,4}-)?\d{7,8}KaTeX parse error: Undefined control sequence: \d at position 99: …证号(15位或18位数字):^\̲d̲{15}|\d{}18
验证一年的12个月:^(0?[1-9]|1[0-2])$ 正确格式为:“01”-“09”和“1”“12”
验证一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$ 正确格式为:01、09和1、31。
整数:^-?\d+$
非负浮点数(正浮点数 + 0):^\d+(.\d+)?$
正浮点数 ^(([0-9]+.[0-9][1-9][0-9])|([0-9][1-9][0-9].[0-9]+)|([0-9][1-9][0-9]))$
非正浮点数(负浮点数 + 0) ^((-\d+(.\d+)?)|(0+(.0+)?))$
负浮点数 ^(-(([0-9]+.[0-9][1-9][0-9])|([0-9][1-9][0-9].[0-9]+)|([0-9][1-9][0-9])))$
浮点数 ^(-?\d+)(.\d+)?$
number与string互相转换
① string转number
方法一: parseInt(value) 或者 parseFloat(value)
方法二: Number(value) 这个在html和ts中都可用
② number转string
value.toString()
1.
对数据在服务中进行处理
使用管道 以及 tap
// 入炉作业清单-装炉计划查询 get
inquirePlans(params: PlansInquiry): Observable<Pagination<Plans>> {
return this.http.get<Pagination<Plans>>(API.PLANS, {params}).pipe(
tap(
pagination => {
pagination.content.forEach(
item => {
let total = 0;
item.links.forEach(
link => {
link.details.forEach(
detail => {
total++;
}
);
}
);
item.totalTr = total;
}
);
}
));
}
Router和ActivatedRoute
① Router
路由中的信息存放在Router中 比如路径 url
可以通过切分来获取URL中的数据,此处应用是:获取了路由路径来判断是出炉还是入炉,从而决定putOrTake
这样就可以获取 具体的路径 split切分完以后的数组从1开始,0是空
url: "/enterFurnace/operation/1"
const path = this.router.url.split('/')[1];
② ActivatedRoute
动态路由参数 路由传参时使用 可以从中获得传递给子路由的id等参数,详情见上文
this.detailId = this.route.snapshot.params.id;
接口枚举类型
接口中使用
status: RollStatus; // 入厂0 在厂1 报废 2 转辊 3
枚举定义
export enum RollStatus {
ENTER = '0', // 入厂
IN = '1' , // 在厂
SCRAP = '2', // 报废
CHANGE = '3' // 转辊
}
具体使用
status.ENTRY == ‘0’ 之类的或者pipe管道
@Pipe({name: 'rollStatus'})
export class RollStatusChange implements PipeTransform {
transform(value: RollStatus): string {
switch (value) {
case RollStatus.ENTER: return '入厂';
case RollStatus.IN: return '在厂';
case RollStatus.SCRAP: return '报废';
case RollStatus.CHANGE: return '转辊';
}
}
}