在 AngularJS 中,您可以$watch
使用$scope
. 在 Angular 中观察变量变化(例如,在组件变量中)的等价物是什么?
8 回答
在 Angular 2 中,更改检测是自动的……$scope.$watch()
和$scope.$digest()
RIP
不幸的是,开发指南的变更检测部分尚未编写(在架构概述页面底部附近的“其他东西”部分中有一个占位符)。
以下是我对变更检测如何工作的理解:
- 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()。)它使用这些更改检测器对象对您的所有绑定执行脏检查。 - 对于找到的任何绑定更改,将更新组件,然后更新 DOM。更改检测现已完成。
- 浏览器会注意到 DOM 的变化并更新屏幕。
了解更多信息的其他参考资料:
- Angular 的 $digest 在新版本的 Angular 中重生——解释了来自 AngularJS 的想法如何映射到 Angular
- 关于 Angular 中的变更检测你需要知道的一切——详细解释了变更检测是如何在幕后工作的
- 变更检测解释- Thoughtram 博客 2016 年 2 月 22 日 - 可能是最好的参考资料
- Savkin 的Change Detection Reinvented视频 - 一定要看这个
- Angular 2 变更检测如何真正起作用?- jhade 的博客 2016 年 2 月 24 日
- Brian 的视频和Miško 的关于 Zone.js的视频。Brian 是关于 Zone.js 的。Miško 是关于 Angular 2 如何使用 Zone.js 来实现变更检测的。他还谈到了一般的变更检测,以及一点点关于
onPush
. - Victor Savkins 博客文章: Angular 2 中的变更检测、 Angular 2 应用程序的两个阶段、Angular、不变性和封装。他很快就覆盖了很多领域,但有时他可能很简洁,你会摸不着头脑,想知道丢失的部分。
- Ultra Fast Change Detection (Google doc) - 非常技术性,非常简洁,但它描述/勾画了作为树的一部分构建的 ChangeDetection 类
此行为现在是组件生命周期的一部分。
组件可以在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 [(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>
您可以在 angular 2 上使用getter function
或get accessor
充当手表。
在此处查看演示。
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);
}
}
这是对模型使用 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;
}
}
如果你想让它成为 2 路绑定,你可以使用[(yourVar)]
,但你必须实现yourVarChange
event 并在每次变量更改时调用它。
像这样跟踪英雄变化的东西
@Output() heroChange = new EventEmitter();
然后当你的英雄改变时,打电话this.heroChange.emit(this.hero);
[(hero)]
绑定将为您完成剩下的工作
看这里的例子:
这并不能直接回答这个问题,但是我在不同的场合遇到了这个 Stack Overflow 问题,以解决我在 angularJs 中使用 $watch 的问题。我最终使用了不同于当前答案中描述的另一种方法,并希望分享它以防有人发现它有用。
我用来实现类似功能的技术$watch
是在 Angular 服务中使用BehaviorSubject
(更多关于这里的主题),并让我的组件订阅它以获取(观察)更改。这类似于$watch
angularJs 中的 a,但需要更多的设置和理解。
在我的组件中:
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的演示
当您的应用程序仍然需要$parse
,时尝试此操作$eval
,$watch
例如 Angular 中的行为