16 votes

Agir lorsque le module chargé paresseux est détruit

J'ai une application Angular 6 et je charge paresseusement un module et je passe des données via le routeur. Une fois dans le module, j'appelle une méthode au sein d'un service partagé auquel je passe ces données (des trucs de configuration).

J'ai besoin d'appeler une méthode lorsque ce module est détruit (essentiellement, lorsque l'utilisateur quitte ce chemin), mais je ne veux l'appeler qu'une seule fois, afin d'éviter d'avoir à regarder l'itinéraire changer constamment car cela serait coûteux. La raison est que j'ai besoin de réinitialiser la configuration du service partagé. En gros, j'ai certaines données de configuration pour l'application, et je dois les remplacer pour le module chargé paresseusement, mais les remettre en place une fois que l'utilisateur n'est plus dans le module.

J'essaie d'appeler la fonction sur le crochet OnDestroy du module mais elle n'est pas déclenchée.

Module de routage :

const appRoutes: Routes = [
    {
        path: 'lazy',
        loadChildren: 'lazy.module#LazyModule',
        data: {
            test: 'data'
        }
    },
    {
        path: 'home',
        loadChildren: 'home.module#HomeModule',
    },
{
        path: '**',
        redirectTo: '/home'
    }
]

Module chargé paresseusement :

export class LazyModule implements OnDestroy {

    constructor() {
        console.warn('LazyModule launched!'); // I see this
    }

    ngOnDestroy() {
        console.warn('destroyed'); // This is not triggered
    }

}

26voto

Mathew Foscarini Points 6076

À partir des modules Angular 6 jamais Déchargez.

Le routeur ne vérifie pas actuellement si un module a été détruit après son chargement paresseux. Ainsi, même si vous obtenez le NgModuleRef et que vous appelez destroy manuellement, le routeur pense toujours qu'il a été chargé. Il ne le chargera donc pas paresseusement une seconde fois.

Le routeur se contente de charger le module mais ne gère pas son cycle de vie. Même si vous pouviez détruire un module, cela ne libérerait pas beaucoup de mémoire. Les modules chargés paresseusement sont gérés par des bundles WebPack qui sont chargés avec SystemJS. Une fois qu'ils sont chargés, ils restent en mémoire. Même si Angular détruit l'instance du module, le code source du bundle se trouve toujours dans la mémoire cache des bundles chargés de SystemJS.

Ce problème s'étend aux bibliothèques des fournisseurs. Si vous avez un module à chargement paresseux qui utilise une bibliothèque graphique tierce comme D3, la bibliothèque du fournisseur sera chargée et vous ne pourrez pas la décharger.

Si vous avez besoin d'avoir des fournisseurs qui n'existent que pour des routes spécifiques, alors vous devriez utiliser des fournisseurs de vues.

@Component({
     ...
     viewProviders: [
         LazyFeatureService
     ]
})
export class MyLazyComponent {}

Lorsque vous utilisez le composant ci-dessus comme composant de routeur, alors le service LazyFeatureService est créé lorsque le module est chargé paresseusement, mais lorsque le composant est détruit, le service est également détruit. La prochaine fois que l'utilisateur visitera la route paresseuse, le service sera à nouveau créé.

Mise à jour :

La raison en est que je dois réinitialiser la configuration du service partagé. En gros, j'ai certaines données de configuration pour l'application, et je dois les remplacer pour le module chargé paresseusement, mais les remettre en place une fois que l'utilisateur n'est plus dans le module.

Vous pouvez y parvenir en utilisant l'option canActivate y canDeactivate dans l'itinéraire.

Dans votre configuration de route pour le module paresseux, créez une route de premier niveau pour gérer les activations.

const routes: Routes = [
    {
        path: '',
        canActivate: [ConfigActivator],
        canDeactivate: [ConfigActivator],
        children: [
            // move the routes here
        ]
    };

Vous pouvez alors définir l'activateur comme suit.

 @Injectable()
 export class ConfigActivator implement CanActivate, CanDeactivate<any> {
       public constructor(private config: MyConfigService) {
       }

       public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
           this.config.lazyModuleLoaded();
           return true;
       }

       public canDeactivate(component: any, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): boolean {
           this.config.lazyModuleUnloaded();
           return true;
       }
 }

Ce qui précède appelle des méthodes sur le service pour lui dire quand il doit mettre à jour la configuration en fonction du changement d'état du routeur. Comme il s'agit d'une route de haut niveau pour le module paresseux, elle ne sera déclenchée que lorsque la route est activée, et lorsque la route quitte ce chemin.

2voto

Adrian Brand Points 2081

Les modules ne sont pas détruits après leur chargement paresseux mais vous pouvez implémenter OnDestroy sur le composant que le module charge.

Le composant de votre module qui possède la sortie du routeur est celui qui est créé et détruit lorsque vous naviguez.

Quel composant héberge le

<router-outlet></router-outlet>

Et dans le TypeScript de ce composant, implémentez OnDestroy et il appellera la méthode ngOnDestroy.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X