在本文中,我们将通过从零开始重新创建指令来了解为什么我们需要ngDoCheck
生命周期钩子和KeyValueDiffers
服务ngStyle
。
KeyValueDiffers服务:
该KeyValueDiffers
服务是一个不同的追踪对象随着时间的推移变化,也暴露了一个API来应对这些变化。(我们稍后会看到我们如何使用这个服务)
ngDoCheck生命周期:
我们都熟悉ngOnChange
s生命周期钩子。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
。我没有写这个代码,也不记得这个。我只是在解释这些概念,以及它如何在隐藏下工作。
你可以在这里看到完整的源代码。