AngularJS $watch 的 Angular 等价物是啥?
Posted
技术标签:
【中文标题】AngularJS $watch 的 Angular 等价物是啥?【英文标题】:What is the Angular equivalent to an AngularJS $watch?AngularJS $watch 的 Angular 等价物是什么? 【发布时间】:2016-04-06 18:19:47 【问题描述】:在 AngularJS 中,您可以使用 $scope
的 $watch
函数指定观察者来观察范围变量的变化。在 Angular 中观察变量变化(例如,组件变量)的作用是什么?
【问题讨论】:
在 typescript 中使用 get 访问器。 check this article 解释了差异 【参考方案1】:在 Angular 2 中,更改检测是自动的...$scope.$watch()
和 $scope.$digest()
R.I.P.
很遗憾,开发指南的变更检测部分尚未编写(Architecture Overview 页面底部附近的“其他内容”部分中有一个占位符)。
以下是我对变更检测工作原理的理解:
Zone.js “猴子修补世界”——它拦截浏览器中的所有异步 API(当 Angular 运行时)。这就是为什么我们可以在我们的组件中使用setTimeout()
而不是$timeout
...之类的东西,因为setTimeout()
是猴子补丁。
Angular 构建并维护了一个“变更检测器”树。每个组件/指令都有一个这样的变化检测器(类)。 (你可以通过注入 ChangeDetectorRef
来访问这个对象。)这些变化检测器是在 Angular 创建组件时创建的。他们跟踪所有绑定的状态,以进行脏检查。在某种意义上,这些类似于 Angular 1 为
模板绑定设置的自动 $watches()
。与 Angular 1 不同,更改检测图是有向树并且不能有循环(这使得 Angular 2 性能更高,我们将在下面看到)。
当事件触发时(在 Angular 区域内),我们编写的代码(事件处理程序回调)运行。它可以更新它想要的任何数据——共享的应用程序模型/状态和/或组件的视图状态。
之后,由于 Zone.js 添加了钩子,它会运行 Angular 的更改检测算法。默认情况下(即,如果您没有在任何组件上使用onPush
更改检测策略),树中的每个组件都会被检查一次(TTL=1)......从顶部开始,按深度优先顺序。 (好吧,如果您处于开发模式,更改检测会运行两次(TTL=2)。有关此内容的更多信息,请参阅ApplicationRef.tick()。)它使用那些更改检测器对象对您的所有绑定执行脏检查。
生命周期挂钩被称为变更检测的一部分。 如果您要查看的组件数据是原始输入属性(字符串、布尔值、数字),您可以实现ngOnChanges()
以收到更改通知。 如果输入属性是引用类型(对象、数组等),但引用没有改变(例如,您向现有数组添加了一个项目),则需要实现 ngDoCheck()
(请参阅this SO answer 了解更多信息)。 您应该只更改组件的属性和/或后代组件的属性(因为单树遍历实现——即单向数据流)。这是a plunker,违反了这一点。有状态的管道也可以在这里trip you up。
对于找到的任何绑定更改,将更新组件,然后更新 DOM。更改检测现已完成。
浏览器注意到 DOM 变化并更新屏幕。
了解更多信息的其他参考资料:
Angular’s $digest is reborn in the newer version of Angular - 解释 AngularJS 的想法如何映射到 Angular Everything you need to know about change detection in Angular - 非常详细地解释了变化检测是如何在幕后工作的 Change Detection Explained - Thoughtram 博客 2016 年 2 月 22 日 - 可能是最好的参考资料 Savkin 的 Change Detection Reinvented 视频 - 一定要看这个 How does Angular 2 Change Detection Really Work?- jhade 的博客 2016 年 2 月 24 日 Brian's video 和 Miško's video 关于 Zone.js。 Brian 是关于 Zone.js 的。 Miško 是关于 Angular 2 如何使用 Zone.js 来实现变更检测的。他还谈到了一般的变更检测,以及一些关于onPush
的内容。
Victor Savkins 博客文章:Change Detection in Angular 2、Two phases of Angular 2 applications、Angular, Immutability and Encapsulation。他很快涵盖了很多领域,但有时他可能很简洁,而您会摸不着头脑,想知道缺少的部分。
Ultra Fast Change Detection(Google 文档)- 非常技术性、非常简洁,但它描述/勾画了作为树的一部分构建的 ChangeDetection 类
【讨论】:
window.addEventListener() 不会在变量更改时触发检测......这让我发疯,任何地方都没有。 @AlbertJamesTeddy,请参阅host
,DirectiveMetadata API doc 中的“主机侦听器”文档。它解释了如何从 Angular 区域内监听全局事件(因此将根据需要触发更改检测)。 This answer 有一个工作人员。
this 链接会有所帮助..
@MarkRajcok,我冒昧地添加了对我关于变更检测的文章的引用。希望你不要介意。它详细解释了幕后发生的事情。
关于违反单向数据流规则的 plunkr,我想补充一点,如果您使用 enableProdMode() 运行 plunkr,您将不会在父视图中看到任何更新,因为更改检测器只运行一次。【参考方案2】:
此行为现在是组件生命周期的一部分。
组件可以在OnChanges 接口中实现ngOnChanges 方法以获取对输入更改的访问。
例子:
import Component, Input, OnChanges from 'angular2/core';
@Component(
selector: 'hero-comp',
templateUrl: 'app/components/hero-comp/hero-comp.html',
styleUrls: ['app/components/hero-comp/hero-comp.css'],
providers: [],
directives: [],
pipes: [],
inputs:['hero', 'real']
)
export class HeroComp implements OnChanges
@Input() hero:Hero;
@Input() real:string;
constructor()
ngOnChanges(changes)
console.log(changes);
【讨论】:
这仅适用于@Input()。如果您想跟踪组件自身数据的更改,这将不起作用 我无法更改简单变量(例如布尔值)。仅检测对象更改。 为什么需要在组件的装饰器中添加一个“inputs”数组?没有这个,变更检测也可以工作。【参考方案3】:如果除了自动双向绑定之外,您还想在值更改时调用函数,您可以将双向绑定快捷语法改为更详细的版本。
<input [(ngModel)]="yourVar"></input>
是简写
<input [ngModel]="yourVar" (ngModelChange)="yourVar=$event"></input>
(参见例如http://victorsavkin.com/post/119943127151/angular-2-template-syntax)
你可以这样做:
<input [(ngModel)]="yourVar" (ngModelChange)="changedExtraHandler($event)"></input>
【讨论】:
在上一个示例中,您的意思是删除 ngModel 周围的 []? 对我来说这是最好的答案,尤其是关于 (ngModelChange) 的最后一行。每当在 [(ngModel)] 中检测到更改时,就会触发该函数。杰出的!这就是我的大部分 $watch 是如何从旧的 AngularJS 转换为新的 Angular 应用程序的。【参考方案4】:您可以使用getter function
或get accessor
在角度2 上充当手表。
查看演示here。
import Component from 'angular2/core';
@Component(
// Declare the tag name in index.html to where the component attaches
selector: 'hello-world',
// Location of the template for this component
template: `
<button (click)="OnPushArray1()">Push 1</button>
<div>
I'm array 1 array1 | json
</div>
<button (click)="OnPushArray2()">Push 2</button>
<div>
I'm array 2 array2 | json
</div>
I'm concatenated concatenatedArray | json
<div>
I'm length of two arrays arrayLength | json
</div>`
)
export class HelloWorld
array1: any[] = [];
array2: any[] = [];
get concatenatedArray(): any[]
return this.array1.concat(this.array2);
get arrayLength(): number
return this.concatenatedArray.length;
OnPushArray1()
this.array1.push(this.array1.length);
OnPushArray2()
this.array2.push(this.array2.length);
【讨论】:
【参考方案5】:这是对模型使用 getter 和 setter 函数的另一种方法。
@Component(
selector: 'input-language',
template: `
…
<input
type="text"
placeholder="Language"
[(ngModel)]="query"
/>
`,
)
export class InputLanguageComponent
set query(value)
this._query = value;
console.log('query set to :', value)
get query()
return this._query;
【讨论】:
这个主题太疯狂了。我有一个对象,其 许多 属性与复杂表单相关联。我不想在每一个上都添加(change)
处理程序;我不想将get|sets
s 添加到模型中的每个属性;为this.object
添加get|set
无济于事; ngOnChanges()
仅检测对 @Input
s 的更改。圣吗哪!他们对我们做了什么???还给我们某种深度观察!【参考方案6】:
如果你想让它成为2路绑定,你可以使用[(yourVar)]
,但是你必须实现yourVarChange
事件并且每次你的变量改变时调用它。
这样的东西来跟踪英雄的变化
@Output() heroChange = new EventEmitter();
然后当你的英雄发生变化时,致电this.heroChange.emit(this.hero);
[(hero)]
绑定将为您完成剩下的工作
请参见此处的示例:
http://plnkr.co/edit/efOGIJ0POh1XQeRZctSx?p=preview
【讨论】:
【参考方案7】:这并没有直接回答这个问题,但是我在不同的场合遇到了这个 Stack Overflow 问题,以解决我在 angularJs 中使用 $watch 的问题。我最终使用了当前答案中描述的另一种方法,并希望分享它以防有人发现它有用。
我用来实现类似$watch
的技术是在Angular 服务中使用BehaviorSubject
(more on the topic here),并让我的组件订阅它以获取(观察)更改。这类似于 angularJs 中的$watch
,但需要更多的设置和理解。
在我的组件中:
export class HelloComponent
name: string;
// inject our service, which holds the object we want to watch.
constructor(private helloService: HelloService)
// Here I am "watching" for changes by subscribing
this.helloService.getGreeting().subscribe( greeting =>
this.name = greeting.value;
);
为我服务
export class HelloService
private helloSubject = new BehaviorSubject<value: string>(value: 'hello');
constructor()
// similar to using $watch, in order to get updates of our object
getGreeting(): Observable<value:string>
return this.helloSubject;
// Each time this method is called, each subscriber will receive the updated greeting.
setGreeting(greeting: string)
this.helloSubject.next(value: greeting);
这是Stackblitz上的演示
【讨论】:
【参考方案8】:当您的应用程序仍然需要 $parse
、$eval
、$watch
类似 Angular 中的行为时,请尝试此操作
https://github.com/vinayk406/angular-expression-parser
【讨论】:
以上是关于AngularJS $watch 的 Angular 等价物是啥?的主要内容,如果未能解决你的问题,请参考以下文章
AngularJS $watch 的 Angular 等价物是啥?