Dans Angular 1, j'ai écrit une directive personnalisée ("prête pour le répéteur") à utiliser avec ng-repeat pour invoquer une méthode de rappel une fois l'itération terminée:

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

Utilisation dans le balisage:

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

Comment puis-je obtenir une fonctionnalité similaire avec ngFor dans Angular 2?

25
Tobias Punke 5 mars 2016 à 23:09

7 réponses

Meilleure réponse

Vous pouvez utiliser quelque chose comme ça (ngFor variables locales) :

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

Ensuite, vous pouvez Intercepter les modifications des propriétés d'entrée avec un setter

  @Input()
  set ready(isReady: boolean) {
    if (isReady) someCallbackMethod();
  }
17
Sasxa 6 mars 2016 à 12:04

Je n'ai pas encore examiné en profondeur comment ngFor rend les éléments sous le capot. Mais d'après l'observation, j'ai remarqué qu'il a souvent tendance à évaluer les expressions plus d'une fois pour chaque élément qu'il itère.

Cela provoque tout appel de méthode typographique effectué lors de la vérification de la dernière variable ngFor pour être parfois déclenché plus d'une fois.

Pour garantir un appel unique à votre méthode de dactylographie par ngFor lorsqu'elle termine correctement l'itération des éléments, vous devez ajouter une petite protection contre la réévaluation des expressions multiples que ngFor fait sous le capot.

Voici une façon de le faire (via une directive), j'espère que cela aide:

Le code de la directive

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

  }

}

Après avoir déclaré la directive ci-dessus dans votre module (en supposant que vous savez comment le faire, sinon, demandez et je l'espère mettre à jour cela avec un extrait de code), voici comment utiliser la directive avec ngFor:

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

'doSomething' est le nom de la méthode dans votre fichier TypeScript que vous souhaitez appeler lorsque ngFor a terminé l'itération des éléments.

Remarque: 'doSomething' n'a pas de crochets '()' ici car nous passons juste une référence à la méthode dactylographiée et ne l'appelons pas ici.

Et enfin, voici à quoi ressemble la méthode 'doSomething' dans votre fichier dactylographié:

public doSomething=()=> {
    console.log("triggered from the directive's parent component when ngFor finishes iterating");
}
-1
H7O 2 juil. 2017 à 00:02

Vous pouvez utiliser @ViewChildren à cette fin

@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 réponse d'origine est ici https://stackoverflow.com/a/37088348/5700401

36
Abhijit Jagtap 28 févr. 2018 à 08:28

J'écris une démo pour ce numéro. La théorie est basée sur la réponse acceptée mais cette réponse n'est pas complète car le li doit être un composant personnalisé qui peut accepter une entrée ready.

J'écris une démo complète pour ce problème.

Définissez un nouveau composant:

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

Modèle

{{item}}

Utilisation dans le composant d'application

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

Vous verrez que le journal dans la console imprimera toute la chaîne de l'élément, puis imprimera l'isReady.

1
Community 23 mai 2017 à 12:34

Pour moi, fonctionne dans Angular2 en utilisant Typescript.

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

Ensuite, vous pouvez vous pouvez gérer en utilisant cette fonction

public ngForCallback() {
  ...
}
8
FACode 7 janv. 2017 à 14:31

Au lieu de [prêt], utilisez [attr.ready] comme ci-dessous

 <li *ngFor="#item in Items; #last = last" [attr.ready]="last ? false : true">
4
Rajasekhar Kunati 10 juin 2016 à 13:06

J'ai trouvé dans RC3 que la réponse acceptée ne fonctionne pas. Cependant, j'ai trouvé un moyen de résoudre ce problème. Pour moi, j'ai besoin de savoir quand ngFor a terminé d'exécuter le gestionnaire de composants MDL pour mettre à niveau les composants.

Vous aurez d'abord besoin d'une directive.

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

Importez ensuite ceci dans votre composant et ajoutez-le aux directives

import {UpgradeComponentsDirective} from './upgradeComponents.directive';

@Component({
    templateUrl: 'templates/mytemplate.html',
    directives: [UpgradeComponentsDirective]
})

Maintenant, dans le HTML, définissez l'attribut "upgrade-components" sur true.

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

Lorsque cet attribut est défini sur true, il exécute la méthode sous la déclaration @Input (). Dans mon cas, il exécute componentHandler.upgradeAllRegistered (). Cependant, il pourrait être utilisé pour tout ce que vous choisissez. En se liant à la propriété «last» de l'instruction ngFor, celle-ci s'exécutera lorsqu'elle sera terminée.

Vous n'aurez pas besoin d'utiliser [attr.upgrade-components] même s'il ne s'agit pas d'un attribut natif car il s'agit désormais d'une directive authentique.

3
Warren Schilpzand 27 juin 2016 à 04:54