在本文中,我们将通过从零开始重新创建指令来了解为什么我们需要ngDoCheck生命周期钩子和KeyValueDiffers服务ngStyle。
KeyValueDiffers服务:
该KeyValueDiffers服务是一个不同的追踪对象随着时间的推移变化,也暴露了一个API来应对这些变化。(我们稍后会看到我们如何使用这个服务)
ngDoCheck生命周期:
我们都熟悉ngOnChanges生命周期钩子。ngOnChanges如果你Input是一个原始类型或你的Input引用的变化(使用一些不变性策略),你可以实现通知变化。
如果模型引用不改变,但Input模型的某些属性发生变化,则可以实现ngDoCheck生命周期挂钩,以手动构建更改检测逻辑。
好吧,我们谈判就让我们开始编码。
import { Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer } from '@angular/core';@Directive({selector: '[ngStyle]'})export class NgStyle implements DoCheck {private _ngStyle: {[key: string]: string};private _differ: KeyValueDiffer;constructor(private _differs: KeyValueDiffers,private _ngEl: ElementRef,private _renderer: Renderer) {}}
我们正在创建ngStyle指令并实现DoCheck接口。我们还注入我们的服务,KeyValueDiffers,Renderer,并参照主机元素(ElementRef)。
@Input()
set ngStyle(value : {[key: string]: string}) {
this._ngStyle = value;
if (!this._differ && value) {
this._differ = this._differs.find(value).create(null);
}
}
我们正在创建ngStyle输入作为setter。如果我们没有什么不同,我们正在创造一个新的不同:
this._differ = this._differs.find(value).create(null);
该find()方法只是寻找一个不同的价值。
如果您好奇,这是Angular如何检查我们的价值是否有适当的差异:
return obj instanceof Map || isJsObject(obj);
create()只是创建不同的方法,并返回一个实例·DefaultKeyValueDiffer·。(null为ChangeDetectorRef)
ngDoCheck() {
if (this._differ) {
const changes = this._differ.diff(this._ngStyle);
if (changes) {
this._applyChanges(changes);
}
}
}
我们需要实现ngDoCheck生命周期钩子,因为正如我们之前所说的,我们不改变对象引用,所以我们不能使用ngOnChanges。
例如:
<div [ngStyle] =“style”> </ div>
//在我们的组件构造函数中
this.style = {color:'red'};
//点击事件后
this.style.color ='blue'
接下来,我们通过调用diff()具有新值的方法来检查我们的对象是否被更改。如果没有任何更改,则返回值将为空。如果有任何更改,返回值将是一个对象,公开三个方法,我们可以使用这些方法来对这些更改作出反应。
private _applyChanges(changes: any): void {
changes.forEachRemovedItem((record: KeyValueChangeRecord) =>
this._setStyle(record.key, null));
changes.forEachAddedItem(
(record: KeyValueChangeRecord) =>
this._setStyle(record.key, record.currentValue));
changes.forEachChangedItem(
(record: KeyValueChangeRecord) =>
this._setStyle(record.key, record.currentValue));
}
方法是不言自明的,我们可以将回调传递给我们需要知道的每一个变化。每个回调给你一个类型的记录KeyValueChangeRecord.这是一个对象有三个有用的键key, currentValue和previousValue.
现在,每当我们有一个变化,我们正在调用从我们的渲染器服务_setStyle()调用setElementStyle()来设置新的元素样式的方法。
你可以在这里阅读更多关于Renderer服务的信息。
private _setStyle(nameAndUnit: string, value: string): void {
this._renderer.setElementStyle(this._ngEl.nativeElement, name, value);
}
事实是,这是Angular的源代码ngStyle。我没有写这个代码,也不记得这个。我只是在解释这些概念,以及它如何在隐藏下工作。
你可以在这里看到完整的源代码。
