Γωνιακοί γάντζοι κύκλου ζωής: ngOnChanges, ngOnInit και άλλα

Γιατί χρειαζόμαστε άγκιστρα κύκλου ζωής;

Τα σύγχρονα πλαίσια front-end μεταφέρουν την εφαρμογή από πολιτεία σε πολιτεία. Τα δεδομένα τροφοδοτούν αυτές τις ενημερώσεις. Αυτές οι τεχνολογίες αλληλεπιδρούν με τα δεδομένα που με τη σειρά τους μεταβαίνουν στην κατάσταση. Με κάθε αλλαγή κατάστασης, υπάρχουν πολλές συγκεκριμένες στιγμές όπου ορισμένα στοιχεία γίνονται διαθέσιμα.

Σε μία περίπτωση το πρότυπο μπορεί να είναι έτοιμο, σε άλλα δεδομένα θα έχει ολοκληρωθεί η μεταφόρτωση. Η κωδικοποίηση για κάθε παρουσία απαιτεί ένα μέσο ανίχνευσης. Τα άγκιστρα κύκλου ζωής ανταποκρίνονται σε αυτήν την ανάγκη. Τα μοντέρνα μπροστινά πλαίσια συσκευάζονται με μια ποικιλία από άγκιστρα κύκλου ζωής. Η γωνιακή δεν αποτελεί εξαίρεση

Εξήγησαν άγκιστρα κύκλου ζωής

Τα άγκιστρα κύκλου ζωής είναι χρονικές μέθοδοι. Διαφέρουν ως προς το πότε και γιατί εκτελούν. Η ανίχνευση αλλαγών ενεργοποιεί αυτές τις μεθόδους. Εκτελούνται ανάλογα με τις συνθήκες του τρέχοντος κύκλου. Οι γωνιακές διαδρομές αλλάζουν συνεχώς την ανίχνευση στα δεδομένα της. Τα άγκιστρα κύκλου ζωής βοηθούν στη διαχείριση των αποτελεσμάτων του.

Μια σημαντική πτυχή αυτών των αγκιστριών είναι η σειρά εκτέλεσης τους. Δεν παρεκκλίνει ποτέ. Εκτελούνται με βάση μια προβλέψιμη σειρά συμβάντων φόρτωσης που παράγονται από έναν κύκλο ανίχνευσης. Αυτό τους καθιστά προβλέψιμους.

Ορισμένα περιουσιακά στοιχεία είναι διαθέσιμα μόνο μετά την εκτέλεση ενός συγκεκριμένου αγκίστρου. Φυσικά, ένα άγκιστρο εκτελείται μόνο υπό ορισμένες συνθήκες που ορίζονται στον τρέχοντα κύκλο ανίχνευσης αλλαγών.

Αυτό το άρθρο παρουσιάζει τα άγκιστρα του κύκλου ζωής κατά τη σειρά εκτέλεσης τους (εάν εκτελούνται όλοι). Ορισμένες προϋποθέσεις αξίζουν την ενεργοποίηση ενός γάντζου. Υπάρχουν μερικοί που εκτελούν μόνο μία φορά μετά την αρχικοποίηση του στοιχείου.

Όλες οι μέθοδοι κύκλου ζωής είναι διαθέσιμες από @angular/core. Αν και δεν απαιτείται, η Angular συνιστά την εφαρμογή κάθε γάντζου. Αυτή η πρακτική οδηγεί σε καλύτερα μηνύματα σφάλματος σχετικά με το στοιχείο.

Σειρά εκτέλεσης αγκιστριών κύκλου ζωής

ngOnΑλλαγές

ngOnChangesενεργοποιεί μετά την τροποποίηση των @Inputδεσμευμένων μελών τάξης. Τα δεδομένα που δεσμεύονται από τον @Input()διακοσμητή προέρχονται από εξωτερική πηγή. Όταν η εξωτερική πηγή αλλάζει αυτά τα δεδομένα με ανιχνεύσιμο τρόπο, περνάει @Inputξανά από την ιδιότητα.

Με αυτήν την ενημέρωση, ngOnChangesενεργοποιείται αμέσως. Ενεργοποιείται επίσης κατά την αρχικοποίηση των δεδομένων εισόδου. Το άγκιστρο λαμβάνει μια προαιρετική παράμετρο τύπου SimpleChanges. Αυτή η τιμή περιέχει πληροφορίες σχετικά με τις ιδιότητες που έχουν αλλάξει.

import { Component, Input, OnChanges } from '@angular/core'; @Component({ selector: 'app-child', template: ` 

Child Component

TICKS: {{ lifecycleTicks }}

DATA: {{ data }}

` }) export class ChildComponent implements OnChanges { @Input() data: string; lifecycleTicks: number = 0; ngOnChanges() { this.lifecycleTicks++; } } @Component({ selector: 'app-parent', template: `

ngOnChanges Example

` }) export class ParentComponent { arbitraryData: string = 'initial'; constructor() { setTimeout(() => { this.arbitraryData = 'final'; }, 5000); } }

Περίληψη: Το ParentComponent δεσμεύει δεδομένα εισόδου στο ChildComponent. Το στοιχείο λαμβάνει αυτά τα δεδομένα μέσω της@Inputιδιότητάς του. ngOnChangesπυρκαγιές. Μετά από πέντε δευτερόλεπτα,setTimeoutενεργοποιείταιηεπιστροφή κλήσης. Το ParentComponent μεταλλάσσει την πηγή δεδομένων της ιδιότητας σύνδεσης του ChildComponent. Τα νέα δεδομένα ρέουν μέσω της ιδιότητας εισαγωγής. ngOnChangesπυρκαγιά ξανά.

ngOnInit

ngOnInitενεργοποιείται μία φορά κατά την αρχικοποίηση των @Inputιδιοτήτων ενός στοιχείου-δεσμευμένου ( ). Το επόμενο παράδειγμα θα μοιάζει με το τελευταίο. Το άγκιστρο δεν ενεργοποιείται καθώς το ChildComponent λαμβάνει τα δεδομένα εισόδου. Αντίθετα, ενεργοποιείται αμέσως μετά την απόδοση των δεδομένων στο πρότυπο ChildComponent.

import { Component, Input, OnInit } from '@angular/core'; @Component({ selector: 'app-child', template: ` 

Child Component

TICKS: {{ lifecycleTicks }}

DATA: {{ data }}

` }) export class ChildComponent implements OnInit { @Input() data: string; lifecycleTicks: number = 0; ngOnInit() { this.lifecycleTicks++; } } @Component({ selector: 'app-parent', template: `

ngOnInit Example

` }) export class ParentComponent { arbitraryData: string = 'initial'; constructor() { setTimeout(() => { this.arbitraryData = 'final'; }, 5000); } }

Περίληψη: Το ParentComponent δεσμεύει δεδομένα εισόδου στο ChildComponent. Το ChildComponent λαμβάνει αυτά τα δεδομένα μέσω της@Inputιδιότητάς του. Τα δεδομένα αποδίδονται στο πρότυπο. ngOnInitπυρκαγιές. Μετά από πέντε δευτερόλεπτα,setTimeoutενεργοποιείταιηεπιστροφή κλήσης. Το ParentComponent μεταλλάσσει την πηγή δεδομένων της ιδιότητας σύνδεσης του ChildComponent. Το ngOnInit ΔΕΝ ΠΥΡΚΑΓΕΙ.

ngOnInitείναι ένα άγκιστρο ενός και τελειωμένου. Η προετοιμασία είναι η μόνη ανησυχία της.

ngDoCheck

ngDoCheckανάβει με κάθε κύκλο ανίχνευσης αλλαγών. Οι γωνιακές διαδρομές αλλάζουν συχνά την ανίχνευση. Η εκτέλεση οποιασδήποτε ενέργειας θα την προκαλέσει κύκλο. ngDoCheckπυρκαγιές με αυτούς τους κύκλους. Χρησιμοποιήστε το με προσοχή. Μπορεί να δημιουργήσει προβλήματα απόδοσης όταν εφαρμοστεί εσφαλμένα.

ngDoCheckεπιτρέπει στους προγραμματιστές να ελέγχουν τα δεδομένα τους με μη αυτόματο τρόπο. Μπορούν να ενεργοποιήσουν μια νέα ημερομηνία αίτησης υπό όρους. Σε συνδυασμό με ChangeDetectorRef, οι προγραμματιστές μπορούν να δημιουργήσουν τους δικούς τους ελέγχους για εντοπισμό αλλαγών.

import { Component, DoCheck, ChangeDetectorRef } from '@angular/core'; @Component({ selector: 'app-example', template: ` 

ngDoCheck Example

DATA: {{ data[data.length - 1] }}

` }) export class ExampleComponent implements DoCheck { lifecycleTicks: number = 0; oldTheData: string; data: string[] = ['initial']; constructor(private changeDetector: ChangeDetectorRef) { this.changeDetector.detach(); // lets the class perform its own change detection setTimeout(() => { this.oldTheData = 'final'; // intentional error this.data.push('intermediate'); }, 3000); setTimeout(() => { this.data.push('final'); this.changeDetector.markForCheck(); }, 6000); } ngDoCheck() { console.log(++this.lifecycleTicks); if (this.data[this.data.length - 1] !== this.oldTheData) { this.changeDetector.detectChanges(); } } }

Δώστε προσοχή στην κονσόλα έναντι της οθόνης. Τα δεδομένα εξελίσσονται μέχρι το «ενδιάμεσο» πριν το πάγωμα. Τρεις κύκλοι εντοπισμού αλλαγών συμβαίνουν κατά τη διάρκεια αυτής της περιόδου, όπως υποδεικνύεται στην κονσόλα. Ένας ακόμη γύρος ανίχνευσης αλλαγών συμβαίνει καθώς το «τελικό» ωθείται στο τέλος του this.data. Εμφανίζεται ένας τελευταίος γύρος ανίχνευσης αλλαγών. Η αξιολόγηση της δήλωσης if καθορίζει ότι δεν απαιτούνται ενημερώσεις στην προβολή.

Περίληψη: Η τάξη δημιουργείται μετά από δύο γύρους εντοπισμού αλλαγών. Ο κατασκευαστής τάξης ξεκινάsetTimeoutδύο φορές. Μετά από τρία δευτερόλεπτα, οι πρώτοιsetTimeoutενεργοποιούν την ανίχνευση αλλαγών. ngDoCheckεπισημαίνει την οθόνη για ενημέρωση. Τρία δευτερόλεπτα αργότερα, η δεύτερηsetTimeoutενεργοποίηση εντοπισμού αλλαγών. Δεν απαιτείται ενημέρωση προβολής σύμφωνα με την αξιολόγηση τουngDoCheck.

Προειδοποίηση

Πριν προχωρήσετε, μάθετε τη διαφορά μεταξύ του περιεχομένου DOM και προβολής DOM (το DOM σημαίνει Document Object Model).

Το περιεχόμενο DOM καθορίζει το εσωτερικόHTML των στοιχείων οδηγιών. Αντίθετα, η προβολή DOM είναι πρότυπο ενός στοιχείου εξαιρουμένου οποιουδήποτε προτύπου HTML που βρίσκεται μέσα σε μια οδηγία. Για καλύτερη κατανόηση, ανατρέξτε σε αυτήν την ανάρτηση ιστολογίου.

ngAfterContentInit

ngAfterContentInitενεργοποιείται μετά την προετοιμασία του περιεχομένου του DOM (φορτώνεται για πρώτη φορά). Η αναμονή σε @ContentChild(ren)ερωτήματα είναι η κύρια θήκη χρήσης του γάντζου.

@ContentChild(ren)τα ερωτήματα αποδίδουν αναφορές στοιχείων για το περιεχόμενο DOM. Ως εκ τούτου, δεν είναι διαθέσιμες μόνο μετά τη φόρτωση του περιεχομένου DOM. Ως εκ τούτου, γιατί ngAfterContentInitκαι το αντίστοιχό ngAfterContentCheckedτου χρησιμοποιούνται.

import { Component, ContentChild, AfterContentInit, ElementRef, Renderer2 } from '@angular/core'; @Component({ selector: 'app-c', template: ` 

I am C.

Hello World!

` }) export class CComponent { } @Component({ selector: 'app-b', template: `

I am B.

` }) export class BComponent implements AfterContentInit { @ContentChild("BHeader", { read: ElementRef }) hRef: ElementRef; @ContentChild(CComponent, { read: ElementRef }) cRef: ElementRef; constructor(private renderer: Renderer2) { } ngAfterContentInit() { this.renderer.setStyle(this.hRef.nativeElement, 'background-color', 'yellow') this.renderer.setStyle(this.cRef.nativeElement.children.item(0), 'background-color', 'pink'); this.renderer.setStyle(this.cRef.nativeElement.children.item(1), 'background-color', 'red'); } } @Component({ selector: 'app-a', template: `

ngAfterContentInit Example

I am A.

BComponent Content DOM

` }) export class AComponent { }

Τα @ContentChildαποτελέσματα του ερωτήματος είναι διαθέσιμα από ngAfterContentInit. Renderer2ενημερώνει το περιεχόμενο DOM του BComponent που περιέχει μια h3ετικέτα και το CComponent. Αυτό είναι ένα κοινό παράδειγμα προβολής περιεχομένου.

Περίληψη: Η απόδοση ξεκινά με AComponent. Για να ολοκληρωθεί, το AComponent πρέπει να αποδώσει το BComponent. Το BComponent προβάλλει περιεχόμενο που είναι ένθετο στο στοιχείο του μέσω τουστοιχείου. Το CComponent είναι μέρος του προβαλλόμενου περιεχομένου. Το προβαλλόμενο περιεχόμενο ολοκληρώνει την απόδοση. ngAfterContentInitπυρκαγιές. Το BComponent ολοκληρώνει την απόδοση. Το AComponent ολοκληρώνει την απόδοση. ngAfterContentInitδεν θα πυροβολήσει ξανά.

ngAfterContentChecked

ngAfterContentCheckedενεργοποιείται μετά από κάθε κύκλο ανίχνευσης αλλαγών που στοχεύει το περιεχόμενο DOM. Αυτό επιτρέπει στους προγραμματιστές να διευκολύνουν τον τρόπο με τον οποίο το περιεχόμενο DOM αντιδρά στην αλλαγή εντοπισμού. ngAfterContentCheckedμπορεί να πυροδοτήσει συχνά και να προκαλέσει προβλήματα απόδοσης, εάν δεν εφαρμοστεί καλά.

ngAfterContentCheckedπυρκαγιά κατά τη διάρκεια των σταδίων αρχικοποίησης ενός στοιχείου. Έρχεται αμέσως μετά ngAfterContentInit.

import { Component, ContentChild, AfterContentChecked, ElementRef, Renderer2 } from '@angular/core'; @Component({ selector: 'app-c', template: ` 

I am C.

Hello World!

` }) export class CComponent { } @Component({ selector: 'app-b', template: `

I am B.

CLICK ` }) export class BComponent implements AfterContentChecked { @ContentChild("BHeader", { read: ElementRef }) hRef: ElementRef; @ContentChild(CComponent, { read: ElementRef }) cRef: ElementRef; constructor(private renderer: Renderer2) { } randomRGB(): string { return `rgb(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)})`; } ngAfterContentChecked() { this.renderer.setStyle(this.hRef.nativeElement, 'background-color', this.randomRGB()); this.renderer.setStyle(this.cRef.nativeElement.children.item(0), 'background-color', this.randomRGB()); this.renderer.setStyle(this.cRef.nativeElement.children.item(1), 'background-color', this.randomRGB()); } } @Component({ selector: 'app-a', template: `

ngAfterContentChecked Example

I am A.

BComponent Content DOM

` }) export class AComponent { }

Αυτό σχεδόν δεν διαφέρει από ngAfterContentInit. Ένα απλό προστέθηκε στο BComponent. Κάνοντας κλικ προκαλεί βρόχο ανίχνευσης αλλαγών. Αυτό ενεργοποιεί το άγκιστρο όπως υποδεικνύεται από την τυχαιοποίηση του background-color.

Περίληψη: Η απόδοση ξεκινά με AComponent. Για να ολοκληρωθεί, το AComponent πρέπει να αποδώσει το BComponent. Το BComponent προβάλλει περιεχόμενο που είναι ένθετο στο στοιχείο του μέσω τουστοιχείου. Το CComponent είναι μέρος του προβαλλόμενου περιεχομένου. Το προβαλλόμενο περιεχόμενο ολοκληρώνει την απόδοση. ngAfterContentCheckedπυρκαγιές. Το BComponent ολοκληρώνει την απόδοση. Το AComponent ολοκληρώνει την απόδοση. ngAfterContentCheckedμπορεί να πυροδοτήσει ξανά μέσω ανίχνευσης αλλαγών.

ngAfterViewInit

ngAfterViewInitενεργοποιείται μία φορά μετά την ολοκλήρωση της προετοιμασίας της προβολής DOM. Η προβολή φορτώνεται πάντα αμέσως μετά το περιεχόμενο. ngAfterViewInitπεριμένει την @ViewChild(ren)επίλυση των ερωτημάτων. Αυτά τα στοιχεία εξετάζονται από την ίδια προβολή του στοιχείου.

Στο παρακάτω παράδειγμα, ερωτάται η h3κεφαλίδα του BComponent . ngAfterViewInitεκτελείται μόλις τα αποτελέσματα του ερωτήματος είναι διαθέσιμα.

import { Component, ViewChild, AfterViewInit, ElementRef, Renderer2 } from '@angular/core'; @Component({ selector: 'app-c', template: ` 

I am C.

Hello World!

` }) export class CComponent { } @Component({ selector: 'app-b', template: `

I am B.

` }) export class BComponent implements AfterViewInit { @ViewChild("BStatement", { read: ElementRef }) pStmt: ElementRef; constructor(private renderer: Renderer2) { } ngAfterViewInit() { this.renderer.setStyle(this.pStmt.nativeElement, 'background-color', 'yellow'); } } @Component({ selector: 'app-a', template: `

ngAfterViewInit Example

I am A.

BComponent Content DOM

` }) export class AComponent { }

Renderer2αλλάζει το χρώμα φόντου της κεφαλίδας του BComponent. Αυτό δείχνει ότι το στοιχείο προβολής υποβλήθηκε σε ερώτηση με επιτυχία χάρη στο ngAfterViewInit.

Summary: Rendering starts with AComponent. For it to finish, AComponent must render BComponent. BComponent projects content nested in its element through the element. CComponent is part of the projected content. The projected content finishes rendering. BComponent finishes rendering. ngAfterViewInit fires. AComponent finishes rendering. ngAfterViewInit will not fire again.

ngAfterViewChecked

ngAfterViewChecked fires after any change detection cycle targeting the component’s view. The ngAfterViewChecked hook lets developers facilitate how change detection affects the view DOM.

import { Component, ViewChild, AfterViewChecked, ElementRef, Renderer2 } from '@angular/core'; @Component({ selector: 'app-c', template: ` 

I am C.

Hello World!

` }) export class CComponent { } @Component({ selector: 'app-b', template: `

I am B.

CLICK ` }) export class BComponent implements AfterViewChecked { @ViewChild("BStatement", { read: ElementRef }) pStmt: ElementRef; constructor(private renderer: Renderer2) { } randomRGB(): string { return `rgb(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)})`; } ngAfterViewChecked() { this.renderer.setStyle(this.pStmt.nativeElement, 'background-color', this.randomRGB()); } } @Component({ selector: 'app-a', template: `

ngAfterViewChecked Example

I am A.

BComponent Content DOM

` }) export class AComponent { }

Summary: Rendering starts with AComponent. For it to finish, AComponent must render BComponent. BComponent projects content nested in its element through the element. CComponent is part of the projected content. The projected content finishes rendering. BComponent finishes rendering. ngAfterViewChecked fires. AComponent finishes rendering. ngAfterViewChecked may fire again through change detection.

Clicking the element initiates a round of change detection. ngAfterContentChecked fires and randomizes the background-color of the queried elements each button click.

ngOnDestroy

ngOnDestroy fires upon a component’s removal from the view and subsequent DOM. This hook provides a chance to clean up any loose ends before a component’s deletion.

import { Directive, Component, OnDestroy } from '@angular/core'; @Directive({ selector: '[appDestroyListener]' }) export class DestroyListenerDirective implements OnDestroy { ngOnDestroy() { console.log("Goodbye World!"); } } @Component({ selector: 'app-example', template: ` 

ngOnDestroy Example

TOGGLE DESTROY

I can be destroyed!

` }) export class ExampleComponent { destroy: boolean = true; toggleDestroy() { this.destroy = !this.destroy; } }

Summary: The button is clicked. ExampleComponent’s destroy member toggles false. The structural directive *ngIf evaluates to false. ngOnDestroy fires. *ngIf removes its host . This process repeats any number of times clicking the button to toggle destroy to false.

Conclusion

Remember that certain conditions must be met for each hook. They will always execute in order of each other regardless. This makes hooks predictable enough to work with even if some do not execute.

With lifecycle hooks, timing the execution of a class is easy. They let developers track where change detection is occurring and how the application should react. They stall for code that requires load-based dependencies available only after sometime.

Ο κύκλος ζωής των συστατικών χαρακτηρίζει μοντέρνα πλαίσια εμπρόσθιου άκρου. Η γωνιακή σχεδιάζει τον κύκλο ζωής της παρέχοντας τα προαναφερθέντα άγκιστρα.

Πηγές

  • Γωνιακή ομάδα. «Άγκιστρα κύκλου ζωής». Google . Πρόσβαση στις 2 Ιουνίου 2018
  • Gechev, Μίνκο. "ViewChildren and ContentChildren in Angular". Πρόσβαση στις 2 Ιουνίου 2018

Πόροι

  • Γωνιακή τεκμηρίωση
  • Γωνιακό αποθετήριο GitHub
  • Άγκιστρα κύκλου ζωής σε βάθος