Angular tương đương với đồng hồ AngularJS $ là gì?


213

Trong AngularJS, bạn có thể chỉ định người theo dõi để quan sát các thay đổi trong các biến phạm vi bằng cách sử dụng $watchchức năng của $scope. Tương đương với việc theo dõi các thay đổi của biến (ví dụ: các biến thành phần) trong Angular là gì?


Sử dụng get accessor trong bản thảo.
jmvtrinidad

kiểm tra bài viết này giải thích sự khác biệt
Max Koretskyi

Câu trả lời:


268

Trong Angular 2, phát hiện thay đổi là tự động ... $scope.$watch()$scope.$digest()RIP

Thật không may, phần Phát hiện thay đổi của hướng dẫn dành cho nhà phát triển chưa được viết (có một trình giữ chỗ ở gần cuối trang Tổng quan kiến ​​trúc , trong phần "Các nội dung khác").

Dưới đây là sự hiểu biết của tôi về cách phát hiện thay đổi hoạt động:

  • Zone.js "khỉ vá thế giới" - nó chặn tất cả các API không đồng bộ trong trình duyệt (khi Angular chạy). Đây là lý do tại sao chúng ta có thể sử dụng setTimeout()bên trong các thành phần của mình chứ không phải là một thứ gì đó như $timeout... bởi vì setTimeout()khỉ đã được vá.
  • Angular xây dựng và duy trì một cây "phát hiện thay đổi". Có một bộ phát hiện thay đổi (lớp) như vậy cho mỗi thành phần / chỉ thị. (Bạn có thể có quyền truy cập vào đối tượng này bằng cách tiêm ChangeDetectorRef.) Những trình phát hiện thay đổi này được tạo khi Angular tạo các thành phần. Họ theo dõi trạng thái của tất cả các ràng buộc của bạn, để kiểm tra bẩn. Theo một nghĩa nào đó, chúng tương tự như tự động $watches()mà Angular 1 sẽ thiết lập cho {{}}các ràng buộc mẫu.
    Không giống như Angular 1, biểu đồ phát hiện thay đổi là một cây có hướng và không thể có chu kỳ (điều này làm cho Angular 2 hoạt động hiệu quả hơn nhiều, như chúng ta sẽ thấy bên dưới).
  • Khi một sự kiện kích hoạt (bên trong vùng Angular), mã chúng tôi đã viết (hàm gọi lại xử lý sự kiện) chạy. Nó có thể cập nhật bất kỳ dữ liệu nào nó muốn - mô hình / trạng thái ứng dụng được chia sẻ và / hoặc trạng thái xem của thành phần.
  • Sau đó, do hook.j.j được thêm vào, nên nó chạy thuật toán phát hiện thay đổi của Angular. Theo mặc định (nghĩa là, nếu bạn không sử dụng onPushchiến lược phát hiện thay đổi trên bất kỳ thành phần nào của mình), mọi thành phần trong cây đều được kiểm tra một lần (TTL = 1) ... từ đầu, theo thứ tự chiều sâu. (Chà, nếu bạn đang ở chế độ dev, phát hiện thay đổi sẽ chạy hai lần (TTL = 2). Xem ApplicationRef.tick () để biết thêm về điều này.) Nó thực hiện kiểm tra bẩn trên tất cả các ràng buộc của bạn, bằng cách sử dụng các đối tượng phát hiện thay đổi đó.
    • Móc vòng đời được gọi là một phần của phát hiện thay đổi.
      Nếu dữ liệu thành phần bạn muốn xem là thuộc tính đầu vào nguyên thủy (Chuỗi, boolean, số), bạn có thể thực hiện ngOnChanges()để được thông báo về các thay đổi.
      Nếu thuộc tính đầu vào là loại tham chiếu (đối tượng, mảng, v.v.), nhưng tham chiếu không thay đổi (ví dụ: bạn đã thêm một mục vào mảng hiện có), bạn sẽ cần triển khai ngDoCheck()(xem câu trả lời SO này để biết thêm về điều này).
      Bạn chỉ nên thay đổi các thuộc tính và / hoặc thuộc tính của các thành phần con cháu (do việc thực hiện đi bộ trên một cây - tức là luồng dữ liệu một chiều). Đây là một plunker vi phạm điều đó. Ống nhà nước cũng có thể đi bạn lên đây.
  • Đối với mọi thay đổi ràng buộc được tìm thấy, các Thành phần được cập nhật và sau đó DOM được cập nhật. Phát hiện thay đổi đã hoàn tất.
  • Trình duyệt thông báo các thay đổi DOM và cập nhật màn hình.

Các tài liệu tham khảo khác để tìm hiểu thêm:


window.addEventListener () không kích hoạt phát hiện khi biến được thay đổi ... nó làm tôi phát điên, không có gì ở đó cả.
Albert James Teddy

@AlbertJamesTeddy, xem hosttài liệu "Trình lắng nghe máy chủ" trong tài liệu API DirectiveMetadata . Nó giải thích cách lắng nghe các sự kiện toàn cầu từ bên trong Vùng Angular (vì vậy phát hiện thay đổi sẽ được kích hoạt như mong muốn). Câu trả lời này có một plunker làm việc.
Mark Rajcok

liên kết này sẽ hữu ích ..
refactor

@MarkRajcok, tôi đã tự do thêm tài liệu tham khảo vào bài viết của mình về phát hiện thay đổi. Hy vọng bạn không phiền. Nó giải thích rất chi tiết những gì xảy ra dưới mui xe.
Max Koretskyi

Về plunkr vi phạm quy tắc luồng dữ liệu đơn hướng, tôi muốn nói thêm rằng nếu bạn chạy plunkr với enableProdMode (), bạn sẽ không thấy bất kỳ cập nhật nào trong chế độ xem chính, bởi vì trình phát hiện thay đổi chỉ chạy một lần.
Mister_L

93

Hành vi này bây giờ là một phần của vòng đời thành phần.

Một thành phần có thể thực hiện phương thức ngOnChanges trong giao diện OnChanges để có quyền truy cập vào các thay đổi đầu vào.

Thí dụ:

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);
  }
}

77
điều này chỉ đúng với @Input (). nếu bạn muốn theo dõi các thay đổi của dữ liệu của chính thành phần của mình, điều này sẽ không hoạt động
LanderV

4
Tôi không thể nhận được các thay đổi của các biến đơn giản (ví dụ boolean). Chỉ những thay đổi đối tượng được phát hiện.
mtoloo

Tại sao cần phải thêm một mảng "đầu vào" trong trang trí của thành phần? phát hiện thay đổi sẽ làm việc mà không có điều này là tốt.
Gil Epshtain

68

Nếu, ngoài ràng buộc hai chiều tự động, bạn muốn gọi một hàm khi giá trị thay đổi, bạn có thể ngắt cú pháp phím tắt ràng buộc hai chiều thành phiên bản dài hơn.

<input [(ngModel)]="yourVar"></input>

là tốc ký cho

<input [ngModel]="yourVar" (ngModelChange)="yourVar=$event"></input>

(xem ví dụ: http://victorsavkin.com/post/119943127151/angular-2-template-syntax )

Bạn có thể làm một cái gì đó như thế này:

<input [(ngModel)]="yourVar" (ngModelChange)="changedExtraHandler($event)"></input>


Trong ví dụ cuối bạn muốn xóa [] xung quanh ngModel?
Eugene Kulabuhov

16

Bạn có thể sử dụng getter functionhoặc get accessorhoạt động như đồng hồ trên góc 2.

Xem demo tại đây .

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

Đây là một cách tiếp cận khác bằng cách sử dụng các hàm getter và setter cho mô hình.

@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
Chủ đề này là điên rồ. Tôi có một đối tượng với nhiều thuộc tính gắn liền với một hình thức phức tạp. Tôi không muốn thêm (change)(các) trình xử lý trên mỗi một trong số chúng; Tôi không muốn thêm get|setss vào mọi thuộc tính trong mô hình của mình; Nó sẽ không giúp để thêm một get|setcho this.object; ngOnChanges() chỉ phát hiện các thay đổi cho @Inputs . Thánh manna! Họ đã làm gì với chúng ta ??? Hãy cho chúng tôi trở lại một cái nhìn sâu sắc của một số loại!
Cody

6

Nếu bạn muốn làm cho nó ràng buộc 2 chiều, bạn có thể sử dụng [(yourVar)], nhưng bạn phải triển khai yourVarChangesự kiện và gọi nó mỗi khi thay đổi biến của bạn.

Một cái gì đó như thế này để theo dõi sự thay đổi anh hùng

@Output() heroChange = new EventEmitter();

và sau đó khi anh hùng của bạn được thay đổi, hãy gọi this.heroChange.emit(this.hero);

các [(hero)]ràng buộc sẽ làm phần còn lại cho bạn

xem ví dụ ở đây:

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



2

Điều này không trả lời trực tiếp câu hỏi, nhưng trong những dịp khác nhau, tôi đã trả lời câu hỏi Stack Overflow này để giải quyết vấn đề tôi sẽ sử dụng đồng hồ $ cho angularJs. Tôi đã kết thúc bằng cách sử dụng một cách tiếp cận khác so với mô tả trong các câu trả lời hiện tại và muốn chia sẻ nó trong trường hợp ai đó thấy nó hữu ích.

Kỹ thuật tôi sử dụng để đạt được điều gì đó tương tự $watchlà sử dụng BehaviorSubject( thêm về chủ đề ở đây ) trong dịch vụ Angular và để các thành phần của tôi đăng ký với nó để có được (xem) các thay đổi. Điều này tương tự như $watchtrong angularJs, nhưng đòi hỏi một số thiết lập và hiểu biết nhiều hơn.

Trong thành phần của tôi:

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;
    });
  }
}

Trong dịch vụ của tôi

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});
  }
}

Đây là một bản demo trên Stackblitz

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.