Angular等效于AngularJS $ watch?


Answers:


267

在Angular 2中,更改检测是自动的... $scope.$watch()$scope.$digest()RIP

不幸的是,开发指南的“更改检测”部分尚未编写(“ 体系结构概述”页面底部“其他内容” 附近有一个占位符)。

这是我对变更检测的工作原理的理解:

  • Zone.js是“猴子修补世界”-它拦截了浏览器中的所有异步API(当Angular运行时)。这就是为什么我们可以setTimeout()在组件内部使用而不是$timeout...之类的原因,因为它setTimeout()是猴子修补的。
  • Angular构建并维护“变化检测器”树。每个组件/指令只有一个这样的变化检测器(类)。(您可以通过注入来访问该对象ChangeDetectorRef。)这些变化检测器是在Angular创建组件时创建的。他们跟踪所有绑定的状态,以进行脏检查。从某种意义上讲,它们类似于$watches()Angular 1为{{}}模板绑定设置的自动功能。
    与Angular 1不同,更改检测图是有向树,不能有循环(这使Angular 2的性能更高,如下所示)。
  • 当事件触发时(在Angular区域内),我们编写的代码(事件处理程序回调)将运行。它可以更新任何想要的数据-共享应用程序模型/状态和/或组件的视图状态。
  • 之后,由于添加了挂接Zone.js,因此它将运行Angular的更改检测算法。默认情况下(即,如果您未onPush在任何组件上使用更改检测策略),则从树顶开始按深度优先顺序对树中的每个组件进行一次检查(TTL = 1)...。(好吧,如果您处于开发人员模式,则更改检测运行两次(TTL = 2)。有关更多信息,请参见ApplicationRef.tick()。)它将使用那些更改检测器对象对所有绑定执行脏检查。
    • 生命周期挂钩被称为变更检测的一部分。
      如果要监视的组件数据是原始输入属性(字符串,布尔值,数字),则可以实施ngOnChanges()以通知更改。
      如果输入属性是引用类型(对象,数组等),但是引用没有改变(例如,您将项目添加到现有数组中),则需要实现ngDoCheck()(有关更多信息,请参见此SO答案)在此)。
      您应该仅更改组件的属性和/或后代组件的属性(由于单树遍历实现-即单向数据流)。这是违反了这一点的小伙子。有状态的管道也可以将您绊倒在这里。
  • 对于找到的任何绑定更改,将更新组件,然后更新DOM。更改检测现已完成。
  • 浏览器会注意到DOM更改并更新了屏幕。

其他参考以了解更多信息:


window.addEventListener()不会在更改变量时触发检测……这使我发疯,任何地方都没有。
艾伯特·詹姆斯·泰迪

@AlbertJamesTeddy,请参阅DirectiveMetadata APIhost文档中的“主机侦听器”文档。它说明了如何从Angular区域内侦听全局事件(因此将根据需要触发更改检测)。 这个答案有一个可行的工具。
Mark Rajcok's

链接将很有帮助..
重构

@MarkRajcok,我可以自由添加对更改检测文章的引用。希望你不要介意。它详细解释了引擎盖下发生的情况。
Max Koretskyi

关于违反单向数据流规则的plunkr,我想补充一点,如果使用enableProdMode()运行plunkr,则在父视图中将看不到任何更新,因为更改检测器仅运行一次。
Mister_L

92

现在,此行为是组件生命周期的一部分。

组件可以在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);
  }
}

76
这仅适用于@Input()。如果您想跟踪组件自身数据的更改,则将无法使用
LanderV '16

4
我无法更改简单变量(例如布尔值)。仅检测到对象更改。
mtoloo

为什么需要在组件的装饰器中添加“输入”数组?没有这种情况,更改检测也将起作用。
吉尔·艾普史丹

68

如果除了自动双向绑定之外,您还想在值更改时调用函数,则可以将双向绑定快捷方式语法更改为更详细的版本。

<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周围的[]?
Eugene Kulabuhov

16

您可以在角2上使用getter functionget 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);
    }
}

12

这是对模型使用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;
  }
}

4
这个话题太疯狂了。我有一个对象,具有绑定到复杂表格的许多属性。我不想(change)在其中的每一个上添加处理程序;我不想将get|setss 添加到模型中的每个属性。添加get|setfor 会无济于事this.object; ngOnChanges() 仅检测到@Inputs的更改。天哪!他们对我们做了什么???给我们带来某种深度的监视!
科迪

6

如果要使其成为2种方式的绑定,可以使用[(yourVar)],但是必须实现yourVarChangeevent并在每次变量更改时调用它。

像这样跟踪英雄的变化

@Output() heroChange = new EventEmitter();

然后当你的英雄被改变时,打电话给 this.heroChange.emit(this.hero);

[(hero)]结合将完成剩下的你

在这里查看示例:

http://plnkr.co/edit/efOGIJ0POh1XQeRZctSx?p=preview



2

这不能直接回答问题,但是我在不同情况下都会遇到这个Stack Overflow问题,以解决在angularJs中使用$ watch的问题。我最终使用了不同于当前答案中所述的另一种方法,并希望共享它,以防有人发现它有用。

我用来实现类似$watch功能的技术是在Angular服务中使用BehaviorSubject更多关于此主题的信息),然后让我的组件订阅它以获取(观看)更改。这类似于$watchangularJs中的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的演示

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.