How do I create custom Lifecycle Hooks for Angular Components

Discussão em 'Angular' iniciado por Shane Loveland, Novembro 15, 2024.

  1. I want to create custom lifecycle hooks for Angular components.

    Two examples are that I want to add hooks based on blur / focus of the browser as well as visibility changes on a browser. I'd prefer to not add a @HostListener to every component and to use a decorator, but I'm not sure I'll be able to access the parts I need.

    One example of an issue I'm running into is trying to open / close an angular material menu and based on the new "lifecycle" hooks.

    I'm not sure I'm taking the right approach. Any other recommended approach that means I don't have to add @HostListener and repeat code everywhere would be appreciated.

    Here is a stackblitz of my attempt to use a class decorator. The component reference is always "undefined" in the lifecycle methods: https://stackblitz.com/edit/angular-7oyay8?file=src/app/menu/menu.component.ts

    Here is the component code:

    import { Component, OnInit, ViewChild } from '@angular/core';
    import { MatMenuTrigger } from '@angular/material/menu';
    import { ExtraHooks } from '../extra-hooks';

    @Component({
    selector: 'app-menu',
    template: `
    <button mat-button [matMenuTriggerFor]="menu">Menu</button>
    <mat-menu #menu="matMenu">
    <button mat-menu-item>Item 1</button>
    <button mat-menu-item>Item 2</button>
    </mat-menu>
    `,
    styles: [],
    })
    @ExtraHooks()
    export class AppMenu implements ExtraHooks {
    @ViewChild(MatMenuTrigger, { static: false }) matMenuTrigger: MatMenuTrigger;

    constructor() {}

    onFocusIn() {
    console.log('focus in');
    if (this.matMenuTrigger) {
    this.matMenuTrigger.openMenu();
    }
    }

    onFocusOut() {
    console.log('focus out');
    if (this.matMenuTrigger) {
    this.matMenuTrigger.closeMenu();
    }
    }

    onViewHidden() {
    console.log('view hidden');
    if (this.matMenuTrigger) {
    this.matMenuTrigger.closeMenu();
    }
    }

    onViewVisible() {
    console.log('view visible');
    if (this.matMenuTrigger) {
    this.matMenuTrigger.openMenu();
    }
    }
    }


    Class Decorator:

    export function ExtraHooks() {
    let set = false;
    const watchVisibility = (target: ExtraHooks) => {
    // DOCUMENT VISIBILITY
    document.addEventListener('visibilitychange', function (event) {
    if (document.hidden) {
    target.onViewHidden();
    } else {
    target.onViewVisible();
    }
    });
    };

    const watchFocus = (target: ExtraHooks) => {
    window.addEventListener('focus', function (event) {
    target.onFocusIn();
    });

    window.addEventListener('blur', function (event) {
    target.onFocusOut();
    });
    };

    return function (target) {
    // MAKE SURE IT'S NOT SET MORE THAN ONCE
    if (!set) {
    watchFocus(target.prototype);
    watchVisibility(target.prototype);
    set = true;
    }
    };
    }

    export interface ExtraHooks {
    onFocusIn();
    onFocusOut();
    onViewVisible();
    onViewHidden();
    }

    Continue reading...

Compartilhe esta Página