Angular实战记录
当ngModel双向绑定非基本数据类型值时
子组件中ngModel绑定的值改变时,通过onChangeCallback 传回父组件时,有两种情况:
- 基本数据类型:string/number 等变量,父组件中会跟着变化
- 非基本数据类型:{}/[]/Date/... 父组件中不会检测到变化
解决方法:
创建一个新对象传回:
this.onChangeCallback(new Date(this.date));
(顺便完善父子组件间双向数据绑定的实现)
子组件ts:
import { Component,forwardRef} from '@angular/core';
import { ControlValueAccessor,NG_VALUE_ACCESSOR, DefaultValueAccessor } from '@angular/forms';
@Component({
selector: 'iq-timepicker',
templateUrl: 'timepicker.component.html',
providers:[{
provide:NG_VALUE_ACCESSOR,
useExisting:forwardRef(()=>TimePickerComponent),
multi:true
}]
})
export class TimePickerComponent implements ControlValueAccessor{
private date:Date;
private onChangeCallback: Function = (_: any) => {};
private onTouchedCallback: any = {};
private triggerChange(){
//此时子组件中 this.date 已变化
let d = new Date(this.date);
this.onChangeCallback(d);//变化传给父组件
}
...
writeValue(value: Date) {
if(!value){
this.date=new Date();
}else{
this.date = new Date(value);
}
}
registerOnChange(fn) {
this.onChangeCallback = fn;
}
registerOnTouched(fn) {
this.onTouchedCallback = fn;
}
父组件html中使用:
<iq-timepicker [(ngModel)]="timeVariable"></iq-timepicker>
父组件向子组件传值
方式:
- 属性输入 @Input()
- 双向绑定 writevalue
注意:
-
传入顺序,先Input后writevalue
-
两者当传值为非基本数据类型时,都无法监测改变
Input方式:@Input() set 方法 或者 ngOnChanges,都无法监测改变。像这样:
//子组件 import { Component, OnInit, Input, OnChanges,SimpleChanges } from '@angular/core'; @Component({ selector: "NB-new-list", templateUrl: '...', styleUrls: ['...'] }) export class NBNewListComponent implements OnInit,OnChanges { @Input() currency;//币种(此时是基本数据类型) ngOnChanges(changes: SimpleChanges){ if(changes["currency"]){//币种 变化 //变化前的值:changes["currency"].previousValue //变化后的值:changes["currency"].currentValue ... } } }
解决方法:
可在生命周期ngDoCheck中监测变化:
//子组件 import { Component, OnInit, Input, DoCheck } from '@angular/core'; @Component({ selector: "NB-new-list", templateUrl: '...', styleUrls: ['...'] }) export class NBNewListComponent implements OnInit { beforeList;//暂存上一个清单数据 @Input() purchaseData=[];//清单数组(此时是非基本数据类型) ngDoCheck(){ if (JSON.stringify(this.purchaseData) != JSON.stringify(this.beforeList)) {//当清单变化 this.beforeList = JSON.parse(JSON.stringify(this.purchaseData)); ... } } }
注意:
在ngDoCheck中进行input传入值的前后比较时,如果是比较长的对象或数组,建议把上一个值保存为字符串,否则有些变化无法检测到
形如:
ngDoCheck() {
if (JSON.stringify(this.purchaseData) != this.beforeListStr){//采购清单 变化
this.beforeListStr = JSON.stringify(this.purchaseData);
}
}
某些有用的生命周期
- ngDoCheck:可监测每次input传参对象改变,例子如上
- ngAfterViewChecked:每次完成界面渲染
使页面强制刷新
如同AngularJS中的$apply()
使用如下:
import { ChangeDetectorRef} from '@angular/core';
@Component({
selector: "...",
templateUrl: '...',
styleUrls: ['...']
})
export class ..Component implements OnInit {
constructor(private changeDetectorRef:ChangeDetectorRef) {
}
Fun(){
this.changeDetectorRef.detectChanges();//需要强制刷新
}
}
当select下拉选项变化时
有种情况,在同一个页面,其他操作导致select的下拉选项发生变化,不刷新页面。
此时需要重置已选的项:
- 已选的项没有删除,保持选中状态
- 已选的项删除,则置为”请选择”
实现难点:
- 当获取新的下拉列表时,需强制刷新界面;
- 当判断已选的项目回来时,不仅需要设置双向绑定的值val,还要重置 select对象的已选index和显示text
html:
<tr *ngFor="let item of procurementList;index as i">
<td>
<select [(ngModel)]="item.MaterialSource" id="materialSource{{i}}" name="materialSource{{i}}" ngModel required>
<option value=''>请选择</option>
<option *ngFor="let em of contractList" [ngValue]="em.id">{{em.value}}</option>
</select>
</td>
</tr>
ts:
import { Component, OnInit, DoCheck,ChangeDetectorRef} from '@angular/core';
declare var $:any;
@Component({
selector: "...",
templateUrl: '...',
styleUrls: ['...']
})
export class ..Component implements OnInit {
procurementList;
contractList;//合同列表(下拉列表数据)结构为{"id":"","value":""}...
constructor(private changeDetectorRef:ChangeDetectorRef) {
}
ngDoCheck(){
if (JSON.stringify(this.contractList) != window.localStorage.getItem("contractList")) {//合同列表变化
this.contractList = JSON.parse(window.localStorage.getItem("contractList"));
this.changeDetectorRef.detectChanges();//需要强制刷新
for(let i=0,len=this.procurementList.length;i<len;i++){
//重新检查设置已选
let pro=this.procurementList[i];
let list=this.OnlyIdContract(pro["MaterialSource"]);
if(!list){
pro["MaterialSource"]="";//为请选择
}else{
pro["MaterialSource"]=list["em"]["id"];//val
$("#materialSource"+i)[0].selectedIndex = list["index"]+1; //index
$("#materialSource"+i)[0].text=list["em"]["value"]; //text
}
}
}
}
OnlyIdContract(id){//根据合同唯一(id)标识 匹配合同项
let list={
em:"",//匹配下拉的项
index:""//匹配已选在现有列表中的index
}
let item;
for(let i=0,len=this.contractList.length;i<len;i++){
item=this.contractList[i];
if(item.id==id){
list.em=item;
list.index=i;
return list;
}
}
return "";//已选的已经不在合同列表中 则置空
}
}
用ngModel绑定radio单选框
当从页面上input的value="true"/"false"获取的值是字符串,此时不能直接做布尔计算,需要函数进行下转化。
html:
<span>是否直接写入ERP</span>
<label>
<input type="radio" name="directly" [(ngModel)]="saveData.istoerp" #directly="ngModel" value="true" (onClick)="directlyChange($event)">
是
</label>
<label>
<input type="radio" name="directly" [(ngModel)]="saveData.istoerp" #directly="ngModel" value="false" (onClick)="directlyChange($event)">
否
</label>
ts:
directlyChange(e) {//是否写入ERP变化
if(String(e)=="true"){
this.saveData.istoerp=Boolean(1);
}else{
this.saveData.istoerp=Boolean(0);
}
}