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 functionget 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|setss 添加到模型中的每个属性;为this.object 添加get|set 无济于事; ngOnChanges() 仅检测对 @Inputs 的更改。圣吗哪!他们对我们做了什么???还给我们某种深度观察!【参考方案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

AngularJS $watch 的 Angular 等价物是啥?

初识AngularJS

AngularJS $watch 与 $watchCollection:性能哪个更好?

angularJS 里的$watch的用法

第一次更新后,AngularJS $watch 数组失败