„Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen.“ – Konfuzius, „Die Gespräche des Konfuzius. Lu Linggong“
Titelseite > Programmierung > Angular LAB: Lassen Sie uns eine Sichtbarkeitsanweisung erstellen

Angular LAB: Lassen Sie uns eine Sichtbarkeitsanweisung erstellen

Veröffentlicht am 03.11.2024
Durchsuche:596

Angular LAB: let

In diesem Artikel werde ich veranschaulichen, wie man eine sehr einfache Angular-Direktive erstellt, die den Sichtbarkeitsstatus eines Elements verfolgt, oder mit anderen Worten, wann es ein- und ausgeht das Ansichtsfenster. Ich hoffe, das wird eine schöne und vielleicht nützliche Übung!

Dazu verwenden wir die IntersectionObserver-JavaScript-API, die in modernen Browsern verfügbar ist.

Was wir erreichen wollen

Wir möchten die Richtlinie wie folgt verwenden:

I'm being observed! Can you see me yet?

  • Sichtbarkeit ist der Selektor unserer benutzerdefinierten Direktive
  • VisibilityMonitor ist eine optionale Eingabe, die angibt, ob das Element weiterhin beobachtet werden soll oder nicht (falls falsch, wird die Überwachung beendet, wenn es in das Ansichtsfenster eintritt)
  • VisibilityChange benachrichtigt uns

Die Ausgabe hat folgende Form:

type VisibilityChange =
  | {
      isVisible: true;
      target: HTMLElement;
    }
  | {
      isVisible: false;
      target: HTMLElement | undefined;
    };

Ein undefiniertes Ziel bedeutet, dass das Element aus dem DOM entfernt wurde (z. B. durch ein @if).

Erstellung der Richtlinie

Unsere Direktive überwacht lediglich ein Element, sie ändert nicht die DOM-Struktur: Es handelt sich um eine Attribut-Direktive.

@Directive({
  selector: "[visibility]",
  standalone: true
})
export class VisibilityDirective implements OnInit, OnChanges, AfterViewInit, OnDestroy {
  private element = inject(ElementRef);

  /**
   * Emits after the view is initialized.
   */
  private afterViewInit$ = new Subject();

  /**
   * The IntersectionObserver for this element.
   */
  private observer: IntersectionObserver | undefined;

  /**
   * Last known visibility for this element.
   * Initially, we don't know.
   */
  private isVisible: boolean = undefined;

  /**
   * If false, once the element becomes visible there will be one emission and then nothing.
   * If true, the directive continuously listens to the element and emits whenever it becomes visible or not visible.
   */
  visibilityMonitor = input(false);

  /**
   * Notifies the listener when the element has become visible.
   * If "visibilityMonitor" is true, it continuously notifies the listener when the element goes in/out of view.
   */
  visibilityChange = output();
}

Im Code oben sehen Sie:

  • die Eingabe und Ausgabe, über die wir zuvor gesprochen haben
  • eine Eigenschaft namens afterViewInit$ (ein Observable), die als reaktives Gegenstück zum ngAfterViewInit-Lebenszyklus-Hook fungiert
  • eine Eigenschaft namens Observer, die den IntersectionObserver speichert, der für die Überwachung unseres Elements verantwortlich ist
  • eine Eigenschaft namens isVisibile, die den letzten Sichtbarkeitsstatus speichert, um zu vermeiden, dass derselbe Status zweimal hintereinander erneut ausgegeben wird

Und natürlich fügen wir die ElementRef ein, um das DOM-Element zu erfassen, auf das wir unsere Direktive anwenden.

Bevor wir die Hauptmethode schreiben, kümmern wir uns um den Lebenszyklus der Direktive.

ngOnInit(): void {
  this.reconnectObserver();
}

ngOnChanges(): void {
  this.reconnectObserver();
}

ngAfterViewInit(): void {
  this.afterViewInit$.next();
}

ngOnDestroy(): void {
  // Disconnect and if visibilityMonitor is true, notify the listener
  this.disconnectObserver();
  if (this.visibilityMonitor) {
    this.visibilityChange.emit({
      isVisible: false,
      target: undefined
    });
  }
}

private reconnectObserver(): void {}
private disconnectObserver(): void {}

Jetzt passiert Folgendes:

  • Innerhalb von ngOnInit und ngOnChanges starten wir den Beobachter neu. Dies dient dazu, die Direktive reaktiv zu machen: Wenn sich die Eingabe ändert, verhält sich die Direktive anders. Beachten Sie, dass wir, selbst wenn ngOnChanges auch vor ngOnInit ausgeführt wird, immer noch ngOnInit benötigen, da ngOnChanges nicht ausgeführt wird, wenn in der Vorlage keine Eingaben vorhanden sind!
  • Wenn die Ansicht initialisiert wird, lösen wir den Betreff aus, wir werden in ein paar Sekunden dazu gelangen
  • Wir trennen unseren Beobachter, wenn die Direktive zerstört wird, um Speicherlecks zu vermeiden. Wenn der Entwickler schließlich darum bittet, benachrichtigen wir ihn, dass das Element aus dem DOM entfernt wurde, indem wir ein undefiniertes Element ausgeben.

IntersectionObserver

Das ist das Herzstück unserer Richtlinie. Unsere reconnectObserver-Methode wird diejenige sein, die mit der Beobachtung beginnt! Es wird ungefähr so ​​aussehen:

private reconnectObserver(): void {
    // Disconnect an existing observer
    this.disconnectObserver();
    // Sets up a new observer
    this.observer = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        const { isIntersecting: isVisible, target } = entry;
        const hasChangedVisibility = isVisible !== this.isVisible;
        const shouldEmit = isVisible || (!isVisible && this.visibilityMonitor);
        if (hasChangedVisibility && shouldEmit) {
          this.visibilityChange.emit({
            isVisible,
            target: target as HTMLElement
          });
          this.isVisible = isVisible;
        }
        // If visilibilyMonitor is false, once the element is visible we stop.
        if (isVisible && !this.visibilityMonitor) {
          observer.disconnect();
        }
      });
    });
    // Start observing once the view is initialized
    this.afterViewInit$.subscribe(() => {
        this.observer?.observe(this.element.nativeElement);
    });
  }

Vertrauen Sie mir, es ist nicht so kompliziert, wie es scheint! Hier ist der Mechanismus:

  • Zuerst trennen wir den Beobachter, wenn er bereits ausgeführt wurde
  • Wir erstellen einen IntersectionObserver und definieren sein Verhalten. Die Einträge enthalten die überwachten Elemente, also unser Element. Die Eigenschaft isIntersecting zeigt an, ob sich die Sichtbarkeit des Elements geändert hat: Wir vergleichen es mit dem vorherigen Status (unserer Eigenschaft) und geben es aus, wenn es fällig ist. Anschließend speichern wir den neuen Zustand in unserem Eigentum für später.
  • Wenn „visibilityMonitor“ den Wert „false“ hat, trennen wir den Beobachter, sobald das Element sichtbar wird: Seine Aufgabe ist erledigt!
  • Dann müssen wir den Beobachter starten, indem wir unser Element übergeben, also warten wir darauf, dass unsere Ansicht initialisiert wird, um das zu tun.

Zuletzt implementieren wir die Methode, die den Beobachter trennt, ganz einfach:

 private disconnectObserver(): void {
    if (this.observer) {
      this.observer.disconnect();
      this.observer = undefined;
    }
  }

Endgültiger Code

Hier ist die vollständige Anweisung. Das war nur eine Übung, Sie können sie also beliebig ändern!

type VisibilityChange =
  | {
      isVisible: true;
      target: HTMLElement;
    }
  | {
      isVisible: false;
      target: HTMLElement | undefined;
    };

@Directive({
  selector: "[visibility]",
  standalone: true
})
export class VisibilityDirective
  implements OnChanges, OnInit, AfterViewInit, OnDestroy {
  private element = inject(ElementRef);

  /**
   * Emits after the view is initialized.
   */
  private afterViewInit$ = new Subject();

  /**
   * The IntersectionObserver for this element.
   */
  private observer: IntersectionObserver | undefined;

  /**
   * Last known visibility for this element.
   * Initially, we don't know.
   */
  private isVisible: boolean = undefined;

  /**
   * If false, once the element becomes visible there will be one emission and then nothing.
   * If true, the directive continuously listens to the element and emits whenever it becomes visible or not visible.
   */
  visibilityMonitor = input(false);

  /**
   * Notifies the listener when the element has become visible.
   * If "visibilityMonitor" is true, it continuously notifies the listener when the element goes in/out of view.
   */
  visibilityChange = output();

  ngOnInit(): void {
    this.reconnectObserver();
  }

  ngOnChanges(): void {
    this.reconnectObserver();
  }

  ngAfterViewInit(): void {
    this.afterViewInit$.next(true);
  }

  ngOnDestroy(): void {
    // Disconnect and if visibilityMonitor is true, notify the listener
    this.disconnectObserver();
    if (this.visibilityMonitor) {
      this.visibilityChange.emit({
        isVisible: false,
        target: undefined
      });
    }
  }

  private reconnectObserver(): void {
    // Disconnect an existing observer
    this.disconnectObserver();
    // Sets up a new observer
    this.observer = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        const { isIntersecting: isVisible, target } = entry;
        const hasChangedVisibility = isVisible !== this.isVisible;
        const shouldEmit = isVisible || (!isVisible && this.visibilityMonitor);
        if (hasChangedVisibility && shouldEmit) {
          this.visibilityChange.emit({
            isVisible,
            target: target as HTMLElement
          });
          this.isVisible = isVisible;
        }
        // If visilibilyMonitor is false, once the element is visible we stop.
        if (isVisible && !this.visibilityMonitor) {
          observer.disconnect();
        }
      });
    });
    // Start observing once the view is initialized
    this.afterViewInit$.subscribe(() => {
        this.observer?.observe(this.element.nativeElement);
    });
  }

  private disconnectObserver(): void {
    if (this.observer) {
      this.observer.disconnect();
      this.observer = undefined;
    }
  }
}
Freigabeerklärung Dieser Artikel ist abgedruckt unter: https://dev.to/this-is-angular/angular-lab-lets-create-a-visibility-directive-5dpp?1 Bei Verstößen wenden Sie sich bitte an [email protected] um es zu löschen
Neuestes Tutorial Mehr>

Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.

Copyright© 2022 湘ICP备2022001581号-3