1. 程式人生 > >Angular實戰記錄

Angular實戰記錄

rms ted 通過 enc 對象 pic 創建 form ble

當ngModel雙向綁定非基本數據類型值時

子組件中ngModel綁定的值改變時,通過onChangeCallback 傳回父組件時,有兩種情況:

  1. 基本數據類型:string/number 等變量,父組件中會跟著變化
  2. 非基本數據類型:{}/[]/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: 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>

父組件向子組件傳值

方式:

  1. 屬性輸入 @Input()
  2. 雙向綁定 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 "";//已選的已經不在合同列表中 則置空
    }
}

Angular實戰記錄