javascript angularjs angular ngfor

javascript - Angular 2: Devolución de llamada cuando ngFor ha finalizado



angularjs (8)

En Angular 1, he escrito una directiva personalizada ("lista para el repetidor") para usar con ng-repeat para invocar un método de devolución de llamada cuando la iteración se ha completado:

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

Uso en marcado:

<li ng-repeat="item in vm.Items track by item.Identifier" repeater-ready="vm.CallThisWhenNgRepeatHasFinished()">

¿Cómo puedo lograr una funcionalidad similar con ngFor en Angular 2?


En lugar de [listo], use [attr.ready] como a continuación

<li *ngFor="#item in Items; #last = last" [attr.ready]="last ? false : true">


Encontré en RC3 que la respuesta aceptada no funciona. Sin embargo, he encontrado una manera de lidiar con esto. Para mí, necesito saber cuándo ngFor ha terminado de ejecutar el MDL componentHandler para actualizar los componentes.

Primero necesitará una directiva.

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(); } }

Luego importe esto en su componente y agréguelo a las directivas

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

Ahora, en HTML, establezca el atributo "upgrade-components" en verdadero.

<div *ngFor=''let item of items;let last=last'' [upgrade-components]="last ? true : false">

Cuando este atributo se establece en verdadero, ejecutará el método bajo la declaración @Input (). En mi caso, ejecuta componentHandler.upgradeAllRegistered (). Sin embargo, podría usarse para cualquier cosa que elija. Al vincularse a la propiedad ''last'' de la instrucción ngFor, esto se ejecutará cuando haya terminado.

No necesitará usar [attr.upgrade-components] aunque este no sea un atributo nativo debido a que ahora es una directiva de buena fe.


Escribo una demostración para este problema. La teoría se basa en la respuesta aceptada, pero esta respuesta no está completa porque el li debería ser un componente personalizado que pueda aceptar una entrada ready .

Escribo github.com/xmeng1/ngfor-finish-callback de este problema.

Definir un nuevo componente:

import {Component, Input, OnInit} desde ''@ 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!''); } } }

modelo

{{item}}

uso en el componente de la aplicación

<app-li-ready *ngFor="let item of items; let last1 = last;" [ready]="last1" [item]="item"></app-li-ready>

Verá que el registro en la consola imprimirá toda la cadena de elementos y luego imprimirá isReady.


La solución es bastante trivial. Si necesita saber cuándo ngFor completa la impresión de todos los elementos DOM en la ventana del navegador, haga lo siguiente:

1. Agregar un marcador de posición

Agregue un marcador de posición para el contenido que se imprime:

<div *ngIf="!contentPrinted">Rendering content...</div>

2. Agregar un contenedor

Cree un contenedor con display: none para el contenido. Cuando se imprimen todos los elementos, display: block . contentPrinted es una propiedad de indicador de componente, cuyo valor predeterminado es false :

<ul [class.visible]="contentPrinted"> ...items </ul>

3. Crear un método de devolución de llamada

Agregue onContentPrinted() al componente, que se desactiva después de que ngFor complete:

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

Y no olvide usar ChangeDetectorRef para evitar ExpressionChangedAfterItHasBeenCheckedError .

4. Use ng para el last valor

Declare la last variable en ngFor . Úselo dentro de li para ejecutar un método cuando este elemento sea el último :

<li *ngFor="let item of items; let last = last"> ... <ng-container *ngIf="last && !contentPrinted"> {{ onContentPrinted() }} </ng-container> <li>

  • Use la propiedad de indicador de componente onContentPrinted() para ejecutar onContentPrinted() solo una vez .
  • Use ng-container para no afectar el diseño.

Para mí trabaja en Angular2 usando el mecanografiado.

<li *ngFor="let item in Items; let last = last"> ... <span *ngIf="last">{{ngForCallback()}}</span> </li>

Entonces puedes manejar esta función

public ngForCallback() { ... }



Todavía no he analizado en profundidad cómo ngFor renderiza elementos bajo el capó. Pero por observación, he notado que a menudo tiende a evaluar expresiones más de una vez por cada elemento que está iterando.

Esto hace que cualquier llamada al método mecanografiado realizada al verificar ngFor ''last'' variable se active, a veces, más de una vez.

Para garantizar una llamada única a su método de mecanografiado por ngFor cuando finalice correctamente la iteración a través de los elementos, debe agregar una pequeña protección contra la reevaluación de múltiples expresiones que ngFor hace bajo el capó.

Aquí hay una forma de hacerlo (a través de una directiva), espero que ayude:

El codigo directivo

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"); } }

Después de declarar la directiva anterior en su módulo (suponiendo que sepa cómo hacerlo, si no, pregunte y espero actualizar esto con un fragmento de código), aquí le mostramos cómo usar la directiva con ngFor:

<li *ngFor="let item of some_list;let last = last;" [callback]="doSomething" [callback-condition]="last">{{item}}</li>

''doSomething'' es el nombre del método en su archivo TypeScript al que desea llamar cuando ngFor termina de iterar por los elementos.

Nota: ''doSomething'' no tiene corchetes ''()'' aquí, ya que solo estamos pasando una referencia al método de mecanografiado y en realidad no lo estamos llamando aquí.

Y finalmente, así es como se ve el método ''doSomething'' en su archivo de mecanografía:

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


Puede usar @ViewChildren para ese propósito

@Component({ selector: ''my-app'', template: ` <ul *ngIf="!isHidden"> <li #allTheseThings *ngFor="let i of items; let last = last">{{i}}</li> </ul> <br> <button (click)="items.push(''another'')">Add Another</button> <button (click)="isHidden = !isHidden">{{isHidden ? ''Show'' : ''Hide''}}</button> `, }) export class App { items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; @ViewChildren(''allTheseThings'') things: QueryList<any>; ngAfterViewInit() { this.things.changes.subscribe(t => { this.ngForRendred(); }) } ngForRendred() { console.log(''NgFor is Rendered''); } }

La respuesta original está aquí https://.com/a/37088348/5700401