Angular 2: callback quando ngFor ha finito

In Angular 1 ho scritto una direttiva personalizzata (“ripetitore pronto”) da utilizzare con ng-repeat per richiamare un metodo di callback quando l’iterazione è stata completata:

 if ($scope.$last === true) { $timeout(() => { $scope.$parent.$parent.$eval(someCallbackMethod); }); } 

Utilizzo nel markup:

 
  • Come posso ottenere una funzionalità simile con ngFor in Angular 2?

    Puoi usare qualcosa come questo ( ngPer le variabili locali ):

     
  • Quindi puoi intercettare le modifiche alle proprietà di input con un setter

      @Input() set ready(isReady: boolean) { if (isReady) someCallbackMethod(); } 

    Puoi usare @ViewChildren a tale scopo

     @Component({ selector: 'my-app', template: ` 
    • {{i}}

    `, }) export class App { items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; @ViewChildren('allTheseThings') things: QueryList; ngAfterViewInit() { this.things.changes.subscribe(t => { this.ngForRendred(); }) } ngForRendred() { console.log('NgFor is Rendered'); } }

    Risposta originale è qui https://stackoverflow.com/a/37088348/5700401

    Per me lavora in Angular2 usando Typescript.

     
  • ... {{ngForCallback()}}
  • Quindi puoi gestire questa funzione

     public ngForCallback() { ... } 

    Invece di [pronto], usa [attr.ready] come sotto

      
  • Ho trovato in RC3 la risposta accettata non funziona. Tuttavia, ho trovato un modo per affrontare questo. Per quanto mi riguarda, ho bisogno di sapere quando ngFor ha finito di eseguire il componente MDLHandler per aggiornare i componenti.

    Innanzitutto avrai bisogno di una direttiva.

    upgradeComponents.directive.ts

     import { Directive, ElementRef, Input } from '@angular/core'; declare var componentHandler : any; @Directive({ selector: '[upgrade-components]' }) export class UpgradeComponentsDirective{ @Input('upgrade-components') set upgradeComponents(upgrade : boolean){ if(upgrade) componentHandler.upgradeAllRegistered(); } } 

    Successivamente importa questo nel tuo componente e aggiungilo alle direttive

     import {UpgradeComponentsDirective} from './upgradeComponents.directive'; @Component({ templateUrl: 'templates/mytemplate.html', directives: [UpgradeComponentsDirective] }) 

    Ora nel codice HTML imposta l’attributo “upgrade-components” su true.

      

    Quando questo attributo è impostato su true, eseguirà il metodo sotto la dichiarazione @Input (). Nel mio caso esegue componentHandler.upgradeAllRegistered (). Tuttavia, potrebbe essere usato per qualsiasi cosa tu scelga. Legandosi alla proprietà ‘last’ dell’istruzione ngFor, questa verrà eseguita al termine.

    Non avrai bisogno di usare [attr.upgrade-components] anche se questo non è un attributo nativo a causa del fatto che ora è una direttiva in buona fede.

    Scrivo una demo per questo problema. La teoria è basata sulla risposta accettata ma questa risposta non è completa perché il li dovrebbe essere un componente personalizzato che può accettare un input ready .

    Scrivo una demo completa per questo problema.

    Definisci un nuovo componente:

    import {Component, Input, OnInit} da ‘@ angular / core’;

     @Component({ selector: 'app-li-ready', templateUrl: './li-ready.component.html', styleUrls: ['./li-ready.component.css'] }) export class LiReadyComponent implements OnInit { items: string[] = []; @Input() item; constructor() { } ngOnInit(): void { console.log('LiReadyComponent'); } @Input() set ready(isReady: boolean) { if (isReady) { console.log('===isReady!'); } } } 

    modello

     {{item}} 

    utilizzo nel componente dell’app

      

    Vedrai che il log nella console stamperà tutta la stringa di elementi e quindi stamperà isReady.

    La soluzione è abbastanza banale. Se è necessario sapere quando ngFor completa la stampa di tutti gli elementi DOM nella finestra del browser, effettuare le seguenti operazioni:

    1. Aggiungi un segnaposto

    Aggiungi un segnaposto per il contenuto da stampare:

    Rendering content...

    2. Aggiungi un contenitore

    Crea un contenitore con display: none per il contenuto. Quando tutti gli articoli sono stampati, fare display: block . contentPrinted è una proprietà flag componente, che di default è false :

      ...items

    3. Creare un metodo di callback

    Aggiungi onContentPrinted() al componente, che si disabilita automaticamente dopo il completamento di ngFor :

    onContentPrinted() { this.contentPrinted = true; this.changeDetector.detectChanges(); }

    E non dimenticare di usare ChangeDetectorRef per evitare ExpressionChangedAfterItHasBeenCheckedError .

    4. Usa ngPer last valore

    Dichiara l’ last variabile su ngFor . Usalo dentro li per eseguire un metodo quando questo elemento è l’ ultimo :

  • ... {{ onContentPrinted() }}
    • Utilizzare la proprietà flag componente contentPrinted per eseguire onContentPrinted() solo una volta .
    • Usa ng-container per non avere alcun impatto sul layout.

    Come chiamare la funzione “navigate” del callback con i parametri? Ho qualcosa di simile a questo:

      

    Non ho ancora visto in profondità come ngFor rende gli elementi sotto il cofano. Ma dall’osservazione, ho notato che spesso tende a valutare le espressioni più di una volta per ogni articolo che sta iterando.

    Questo fa sì che qualsiasi chiamata al metodo typescript venga eseguita quando si controlla l’ultima variabile ‘ngFor’ per ottenere, a volte, l’triggerszione più di una volta.

    Per garantire una chiamata al tuo metodo typescript da parte di ngFor quando termina correttamente iterando attraverso gli elementi, devi aggiungere una piccola protezione contro la rivalutazione di più espressioni che ngFor fa sotto il cofano.

    Ecco un modo per farlo (tramite una direttiva), spero che aiuti:

    Il codice della direttiva

     import { Directive, OnDestroy, Input, AfterViewInit } from '@angular/core'; @Directive({ selector: '[callback]' }) export class CallbackDirective implements AfterViewInit, OnDestroy { is_init:boolean = false; called:boolean = false; @Input('callback') callback:()=>any; constructor() { } ngAfterViewInit():void{ this.is_init = true; } ngOnDestroy():void { this.is_init = false; this.called = false; } @Input('callback-condition') set condition(value: any) { if (value==false || this.called) return; // in case callback-condition is set prior ngAfterViewInit is called if (!this.is_init) { setTimeout(()=>this.condition = value, 50); return; } if (this.callback) { this.callback(); this.called = true; } else console.error("callback is null"); } } 

    Dopo aver dichiarato la direttiva sopra nel tuo modulo (supponendo che tu sappia come farlo, in caso contrario, chiedi e spero di aggiornarlo con uno snippet di codice), ecco come usare la direttiva con ngFor:

     
  • {{item}}
  • “doSomething” è il nome del metodo nel file TypeScript che si desidera chiamare quando ngFor termina l’iterazione degli elementi.

    Nota: ‘doSomething’ non ha parentesi ‘()’ qui mentre stiamo passando un riferimento al metodo typescript e non lo chiamiamo in realtà qui.

    E finalmente ecco come appare il metodo “doSomething” nel tuo file typescript:

     public doSomething=()=> { console.log("triggered from the directive's parent component when ngFor finishes iterating"); }