2017-03-07 70 views
18

我似乎無法找出原因,我需要比簡單的通知等ngDoCheck生命週期掛鉤,它裏面寫代碼特別是如何有差別的方面改變檢測。我發現的大多數示例都顯示了無用的示例,如this one,並帶有一些日誌記錄功能。爲什麼我們需要`ngDoCheck`

此外,在生成的類我沒有看到它被用於除簡單的通知其它別的東西:

conmponent/wrapper.ngfactory.js

Wrapper_AppComponent.prototype.ngDoCheck = function(view,el,throwOnChange) { 
    var self = this; 
    var changed = self._changed; 
    self._changed = false; 
    if (!throwOnChange) { 
    if (changed) { 
     jit_setBindingDebugInfoForChanges1(view.renderer,el,self._changes); 
     self._changes = {}; 
    } 
    self.context.ngDoCheck(); <----------- this calls ngDoCheck on the component 
              but the result is not used 
              anywhere and no params are passed 
     } 
     return changed; 
    }; 

回答

34

這篇大文章If you think ngDoCheck means your component is being checked — read this article解釋錯誤的深度。

這個答案的內容是基於角版本2.x.x.有關最新版本4.x.x,請參閱this post

沒有什麼變化檢測的內部運作在互聯網上的,所以我不得不花大約一個星期的調試來源,所以這個答案將在細節相當的技術。

角應用程序是viewsAppView類,由編譯器生成的組件特定類擴展的類)的樹。每個視圖都有一個更改檢測模式,它位於cdMode屬性中。 cdMode的默認值是ChangeDetectorStatus.CheckAlways,即cdMode = 2

當變更檢測週期中運行,每個父視圖檢查它是否應當在子視圖here執行變化檢測:

detectChanges(throwOnChange: boolean): void { 
    const s = _scope_check(this.clazz); 
    if (this.cdMode === ChangeDetectorStatus.Checked || 
     this.cdMode === ChangeDetectorStatus.Errored) 
     return; 
    if (this.cdMode === ChangeDetectorStatus.Destroyed) { 
     this.throwDestroyedError('detectChanges'); 
    } 
    this.detectChangesInternal(throwOnChange); <---- performs CD on child view 

其中this指向child視圖。因此,如果cdModeChangeDetectorStatus.Checked=1,則由於該行而導致直接子女及其所有後代的變更檢測被跳過。

if (this.cdMode === ChangeDetectorStatus.Checked || 
     this.cdMode === ChangeDetectorStatus.Errored) 
     return; 

changeDetection: ChangeDetectionStrategy.OnPush做什麼是簡單地設置cdModeChangeDetectorStatus.CheckOnce = 0,所以變化檢測後第一次運行子視圖將有其cdMode集,因爲this codeChangeDetectorStatus.Checked = 1

if (this.cdMode === ChangeDetectorStatus.CheckOnce) 
    this.cdMode = ChangeDetectorStatus.Checked; 

這意味着,下次更改檢測週期開始時,將不會爲子視圖執行更改檢測。

有幾個選項如何運行這種視圖的變化檢測。首先是子視圖的cdModeChangeDetectorStatus.CheckOnce,可以使用this._changeRef.markForCheck()ngDoCheck生命週期掛鉤進行改變:

constructor(private _changeRef: ChangeDetectorRef) { } 

    ngDoCheck() { 
    this._changeRef.markForCheck(); 
    } 

這只是改變當前視圖及其家長ChangeDetectorStatus.CheckOncecdMode,所以下一次進行變化檢測當前視圖被檢查。

檢查一個完整的例子here in the sources,但這裏是它的要點:

 constructor(ref: ChangeDetectorRef) { 
     setInterval(() => { 
      this.numberOfTicks ++ 
      // the following is required, otherwise the view will not be updated 
      this.ref.markForCheck(); 
      ^^^^^^^^^^^^^^^^^^^^^^^^ 
     }, 1000); 
     } 

第二個選項是在視圖本身呼叫detectChanges其中當前視圖將run change detection如果cdMode不是ChangeDetectorStatus.CheckedChangeDetectorStatus.Errored。由於onPush角度套件cdModeChangeDetectorStatus.CheckOnce,角度將運行變化檢測。

所以ngDoCheck不會覆蓋變化的檢測,它只是要求每個被改動過的檢測週期,它是唯一的工作就是設置當前視圖cdModecheckOnce,以便在接下來的變更檢測週期它檢查的變化。詳情請參閱this answer。如果當前視圖的更改檢測模式爲checkAlways(如果未使用onPush策略,則默認設置),ngDocCheck似乎無用。

6

DoCheck界面用於手動檢測角度變化檢測忽略的變化。使用可能是在更改組件的ChangeDetectionStrategy時,但您知道對象的一個​​屬性將會更改。

這是更有效的檢查這一變化,而不是讓changeDetector通過你的整個組件運行

let obj = { 
    iChange: 'hiii' 
} 

如果使用obj.iChange你的模板中,角不會,如果這個值變化來檢測它,因爲obj本身的參考不會改變。您需要實施ngDoCheck以檢查值是否已更改,並在組件的changeDetector上調用detectChanges

從角文檔中關於DoCheck

而當英雄的名字改變了ngDoCheck鉤可以檢測到,它有一個可怕的代價。這個鉤子以巨大的頻率被調用 - 在每個變化檢測週期之後,無論變化發生在何處。在用戶可以做任何事情之前,在這個例子中它被調用了二十次。

大多數這些初步檢查都是由角在網頁上的其他地方不相關的數據的第一渲染觸發。僅僅通過鼠標移動到另一個輸入框就會觸發一個呼叫。相對較少的調用顯示相關數據的實際變化。很顯然,我們的實施必須非常輕便,否則用戶體驗將受到影響。

測試例如

@Component({ 
    selector: 'test-do-check', 
    template: ` 
     <div [innerHtml]="obj.changer"></div> 
    `, 
    changeDetection: ChangeDetectionStrategy.OnPush 
}) 
export class TestDoCheckComponent implements DoCheck, OnInit { 

    public obj: any = { 
     changer: 1 
    }; 

    private _oldValue: number = 1; 

    constructor(private _changeRef: ChangeDetectorRef){} 

    ngOnInit() { 
     setInterval(() => { 
      this.obj.changer += 1; 
     }, 1000); 
    } 

    ngDoCheck() { 
     if(this._oldValue !== this.obj.changer) { 
      this._oldValue = this.obj.changer; 

      //disable this line to see the counter not moving 
      this._changeRef.detectChanges(); 
     } 
    } 

} 
+0

謝謝,但我還是不明白。你的答案似乎是關於'ngDoChanges'。它與'ngDoCheck'有什麼關係? –

+0

@Maximus什麼意思PierreDuc如果你**不要**使用'DoCheck',你需要手動觸發變化檢測,如果對象裁判並沒有改變。 – echonax

+0

@echonax,這更令人困惑。也許你可以展示一個精心設計的例子? –

0

//是必需的,否則該視圖將不被更新

this.ref.markForCheck()以下; ^^^^^^^^^^^^^^^^^^^^^^^^

嗨,馬克西姆@ AngularInDepth.com 的觀點是不調用this.ref.markForCheck更新()。我已經在consturctor和ngOnInit中測試過。 Check this

+1

您需要將視圖狀態更改爲'CheckOnce',我在我的答案中提到的狀態,例如,使用'ChangeDetectionStrategy.OnPush' –