อะไรคือ Angular ที่เทียบเท่ากับ AngularJS $


213

ใน AngularJS คุณก็สามารถที่จะระบุดูจะสังเกตเห็นการเปลี่ยนแปลงในตัวแปรขอบเขตการใช้ฟังก์ชั่นของ$watch $scopeอะไรคือสิ่งที่เทียบเท่ากับการเฝ้าดูการเปลี่ยนแปลงของตัวแปร (ในตัวอย่างเช่นตัวแปรองค์ประกอบ) ในเชิงมุม?


ใช้รับ accessor ใน typescript
jmvtrinidad

ตรวจสอบบทความนี้ที่อธิบายถึงความแตกต่าง
Max Koretskyi

คำตอบ:


268

ใน Angular 2 การตรวจจับการเปลี่ยนแปลงนั้นเป็นไปโดยอัตโนมัติ ... $scope.$watch()และ$scope.$digest()RIP

น่าเสียดายที่ส่วนการตรวจจับการเปลี่ยนแปลงของคู่มือ dev ยังไม่ได้เขียน (มีตัวยึดใกล้ด้านล่างของหน้าภาพรวมสถาปัตยกรรมในส่วน "สิ่งอื่น ๆ ")

นี่คือความเข้าใจของฉันว่าการตรวจจับการเปลี่ยนแปลงทำงานอย่างไร

  • Zone.js "monkey patches the world" - มันจะสกัดกั้น API แบบอะซิงโครนัสทั้งหมดในเบราว์เซอร์ (เมื่อ Angular ทำงาน) นี่คือเหตุผลที่เราสามารถใช้setTimeout()ภายในส่วนประกอบของเรามากกว่าที่จะเป็น$timeout... เพราะsetTimeout()มีการปะติดลิง
  • เชิงมุมสร้างและดูแลต้นไม้ของ "ตัวตรวจจับการเปลี่ยนแปลง" มีตัวตรวจจับการเปลี่ยนแปลงหนึ่งตัว (คลาส) ต่อส่วนประกอบ / คำสั่ง (คุณสามารถเข้าถึงวัตถุนี้ได้โดยการฉีดChangeDetectorRef) อุปกรณ์ตรวจจับการเปลี่ยนแปลงเหล่านี้จะถูกสร้างขึ้นเมื่อ Angular สร้างส่วนประกอบ พวกเขาติดตามสถานะของการผูกทั้งหมดของคุณเพื่อตรวจสอบสกปรก ในแง่นี้คล้ายกับอัตโนมัติ$watches()ที่ Angular 1 จะตั้งค่าสำหรับ{{}}การผูกแม่แบบ
    ซึ่งแตกต่างจาก Angular 1 กราฟการตรวจจับการเปลี่ยนแปลงนั้นเป็นต้นไม้กำกับและไม่สามารถมีรอบได้
  • เมื่อเหตุการณ์เกิดขึ้น (ภายในโซนแองกูลาร์) โค้ดที่เราเขียน (การเรียกกลับตัวจัดการเหตุการณ์) จะทำงาน สามารถอัปเดตข้อมูลใดก็ได้ที่ต้องการ - โมเดล / สถานะและ / หรือสถานะมุมมองของส่วนประกอบที่ใช้ร่วมกัน
  • หลังจากนั้นเนื่องจากมีการเพิ่ม hooks Zone.js จึงเรียกใช้อัลกอริทึมตรวจจับการเปลี่ยนแปลงของ Angular ตามค่าเริ่มต้น (เช่นหากคุณไม่ได้ใช้onPushกลยุทธ์การตรวจจับการเปลี่ยนแปลงกับองค์ประกอบใด ๆ ของคุณ) ทุกองค์ประกอบในแผนผังจะถูกตรวจสอบหนึ่งครั้ง (TTL = 1) ... จากด้านบนตามลำดับความลึกแรก (ถ้าคุณอยู่ในโหมด dev การตรวจจับการเปลี่ยนแปลงจะทำงานสองครั้ง (TTL = 2) ดูApplicationRef.tick ()เพื่อดูข้อมูลเพิ่มเติม) ทำการตรวจสอบสิ่งสกปรกบนการเชื่อมโยงทั้งหมดโดยใช้วัตถุตัวตรวจจับการเปลี่ยนแปลงเหล่านั้น
    • Lifecycle hooks เรียกว่าเป็นส่วนหนึ่งของการตรวจจับการเปลี่ยนแปลง
      หากข้อมูลส่วนประกอบที่คุณต้องการดูเป็นคุณสมบัติอินพุตดั้งเดิม (String, บูลีน, ตัวเลข) คุณสามารถนำngOnChanges()ไปใช้เพื่อรับการแจ้งเตือนการเปลี่ยนแปลง
      หากคุณสมบัติการป้อนข้อมูลเป็นประเภทการอ้างอิง (วัตถุอาร์เรย์ ฯลฯ ) แต่การอ้างอิงไม่เปลี่ยนแปลง (เช่นคุณเพิ่มรายการในอาร์เรย์ที่มีอยู่) คุณจะต้องดำเนินการngDoCheck()(ดูคำตอบ SO นี้เพิ่มเติม บนนี้)
      คุณควรเปลี่ยนคุณสมบัติของส่วนประกอบและ / หรือคุณสมบัติของส่วนประกอบที่สืบทอด (เนื่องจากการใช้งานทรีทรีเดียว - นั่นคือการไหลของข้อมูลแบบทิศทางเดียว) นี่คือพลั่วเกอร์ที่ละเมิดสิ่งนั้น ท่อที่เป็นของรัฐสามารถเดินทางไปที่นี่ได้เช่นกัน
  • สำหรับการเปลี่ยนแปลงการเชื่อมโยงใด ๆ ที่พบว่ามีการปรับปรุงส่วนประกอบและ DOM นั้นจะได้รับการปรับปรุง การตรวจหาการเปลี่ยนแปลงเสร็จสิ้นแล้ว
  • เบราว์เซอร์สังเกตการเปลี่ยนแปลงของ DOM และอัปเดตหน้าจอ

การอ้างอิงอื่น ๆ เพื่อเรียนรู้เพิ่มเติม:


window.addEventListener () ไม่เริ่มต้นการตรวจจับเมื่อมีการเปลี่ยนแปลงตัวแปร ... มันทำให้ฉันเป็นบ้ามันไม่มีอะไรเลย
Albert James Teddy

@AlbertJamesTeddy ดูที่hostเอกสาร "ฟังพิธีกร" ในAPI doc มันอธิบายวิธีการฟังเหตุการณ์ระดับโลกจากภายในโซนเชิงมุม (ดังนั้นการตรวจจับการเปลี่ยนแปลงจะถูกเรียกตามที่ต้องการ) คำตอบนี้มีพลั่วเกอร์ทำงานอยู่
Mark Rajcok

ลิงค์นี้จะเป็นประโยชน์ ..
refactor

@ MarkRajcok ฉันมีอิสระในการเพิ่มการอ้างอิงถึงบทความของฉันในการตรวจจับการเปลี่ยนแปลง หวังว่าคุณจะไม่รังเกียจ มันอธิบายในรายละเอียดที่ดีว่าเกิดอะไรขึ้นภายใต้ประทุน
Max Koretskyi

เกี่ยวกับ plunkr ที่ละเมิดกฎการไหลของข้อมูลทิศทางเดียวฉันต้องการเพิ่มว่าถ้าคุณรัน plunkr ด้วย enableProdMode () คุณจะไม่เห็นการอัพเดตใด ๆ ในมุมมองพาเรนต์เนื่องจากตัวตรวจจับการเปลี่ยนแปลงทำงานเพียงครั้งเดียว
Mister_L

93

พฤติกรรมนี้ตอนนี้เป็นส่วนหนึ่งของวงจรชีวิตของส่วนประกอบ

ส่วนประกอบสามารถใช้วิธี ngOnChanges ในอินเทอร์เฟซOnChangesเพื่อเข้าถึงการเปลี่ยนแปลงอินพุต

ตัวอย่าง:

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
สิ่งนี้เป็นจริงสำหรับ @Input () เท่านั้น หากคุณต้องการติดตามการเปลี่ยนแปลงข้อมูลของส่วนประกอบของคุณสิ่งนี้จะไม่ทำงาน
LanderV

4
ฉันไม่สามารถรับการเปลี่ยนแปลงของตัวแปรอย่างง่าย (เช่นบูลีน) ตรวจพบเฉพาะการเปลี่ยนแปลงวัตถุ
mtoloo

ทำไมต้องเพิ่มอาร์เรย์ "อินพุต" ในมัณฑนากรของส่วนประกอบ? การตรวจจับการเปลี่ยนแปลงจะทำงานได้โดยปราศจากสิ่งนี้เช่นกัน
Gil Epshtain

68

หากนอกเหนือไปจากการผูกสองทางอัตโนมัติคุณต้องการเรียกใช้ฟังก์ชันเมื่อมีการเปลี่ยนแปลงค่าคุณสามารถทำลายไวยากรณ์ทางลัดการผูกมัดแบบสองทางเป็นรุ่น verbose เพิ่มเติม

<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

คุณสามารถใช้getter functionหรือget accessorเพื่อทำหน้าที่เฝ้าดูในเชิงมุม 2

ดูตัวอย่างที่นี่

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)ตัวจัดการ (s) ลงในทุก ๆ รายการ ฉันไม่ต้องการเพิ่มget|setsสถานที่ให้บริการในแบบจำลองของฉันทุกคน มันจะไม่ช่วยเพิ่มget|setสำหรับthis.object; ngOnChanges() ตรวจพบการเปลี่ยนแปลง@Inputของ sเท่านั้น มานาศักดิ์สิทธิ์! พวกเขาทำอะไรกับเรา ??? ให้เราดูลึก ๆ บางอย่าง!
โคดี

6

หากคุณต้องการทำให้มีผลผูกพัน 2 ทางคุณสามารถใช้[(yourVar)]แต่คุณต้องใช้yourVarChangeเหตุการณ์และเรียกมันว่าทุกครั้งที่มีการเปลี่ยนแปลงตัวแปร

บางอย่างเช่นนี้เพื่อติดตามการเปลี่ยนแปลงของฮีโร่

@Output() heroChange = new EventEmitter();

และเมื่อฮีโร่ของคุณได้รับการเปลี่ยนแปลงให้โทร this.heroChange.emit(this.hero);

[(hero)]ผูกพันจะไม่เหลือสำหรับคุณ

ดูตัวอย่างที่นี่:

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



2

นี่ไม่ใช่คำตอบของคำถามโดยตรง แต่ฉันมีโอกาสต่าง ๆ กันในคำถาม Stack Overflow นี้เพื่อแก้ปัญหาบางอย่างที่ฉันจะใช้ $ watch สำหรับใน angularJs ฉันใช้วิธีอื่นมากกว่าที่อธิบายไว้ในคำตอบปัจจุบันและต้องการแบ่งปันในกรณีที่มีคนพบว่ามีประโยชน์

เทคนิคที่ฉันใช้เพื่อให้ได้สิ่งที่คล้ายกัน$watchคือการใช้BehaviorSubject( เพิ่มเติมในหัวข้อที่นี่ ) ในบริการเชิงมุมและให้ส่วนประกอบของฉันสมัครสมาชิกเพื่อรับการเปลี่ยนแปลง (ดู) สิ่งนี้คล้ายกับ$watchangularJs แต่ต้องการการตั้งค่าและความเข้าใจเพิ่มเติม

ในองค์ประกอบของฉัน:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.