Angular 2: retorno de chamada quando ngFor terminou

Em Angular 1, escrevi uma diretiva personalizada (“pronto para repetidor”) para usar com ng-repeat para invocar um método de retorno de chamada quando a iteração foi concluída:

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

Uso na marcação:

 
  • Como posso obter uma funcionalidade semelhante com o ngFor no Angular 2?

    Você pode usar algo assim ( ngPara variables ​​locais ):

     
  • Então você pode Interceptar alterações de propriedade de input com um setter

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

    Você pode usar @ViewChildren para essa finalidade

     @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'); } }

    Resposta original está aqui https://stackoverflow.com/a/37088348/5700401

    Para mim funciona em Angular2 usando o Typescript.

     
  • ... {{ngForCallback()}}
  • Então você pode manipular usando esta function

     public ngForCallback() { ... } 

    Em vez de [ready], use [attr.ready] como abaixo

      
  • Eu encontrei no RC3 a resposta aceita não funciona. No entanto, encontrei uma maneira de lidar com isso. Para mim, preciso saber quando o ngFor acabou de executar o componente MDLHandler para atualizar os componentes.

    Primeiro você precisará de uma diretiva.

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

    Em seguida, importe isso para o seu componente e adicione-o às diretivas

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

    Agora, no HTML, defina o atributo “upgrade-components” como true.

      

    Quando esse atributo é definido como true, ele executará o método na declaração @Input (). No meu caso, ele executa o componentHandler.upgradeAllRegistered (). No entanto, ele poderia ser usado para qualquer coisa de sua escolha. Vinculando-se à propriedade ‘last’ da instrução ngFor, isso será executado quando terminar.

    Você não precisará usar [attr.upgrade-components] mesmo que este não seja um atributo nativo, já que agora é uma diretiva genuína.

    Eu escrevo uma demonstração para este problema. A teoria é baseada na resposta aceita, mas esta resposta não está completa porque o li deve ser um componente personalizado que pode aceitar uma input ready .

    Eu escrevo uma demonstração completa para este problema.

    Defina um novo componente:

    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!'); } } } 

    modelo

     {{item}} 

    uso no componente de aplicativo

      

    Você verá que o log no console imprimirá toda a sequência de itens e depois imprimirá o isReady.

    A solução é bastante trivial. Se você precisar saber quando o ngFor concluir a impressão de todos os elementos DOM na janela do navegador, faça o seguinte:

    1. Adicione um marcador de posição

    Adicione um espaço reservado para o conteúdo que está sendo impresso:

    Rendering content...

    2. Adicione um contêiner

    Crie um contêiner com display: none para o conteúdo. Quando todos os itens são impressos, display: block . contentPrinted é uma propriedade de sinalizador de componente, cujo padrão é false :

      ...items

    3. Crie um método de retorno de chamada

    Adicione onContentPrinted() ao componente, que se desativa depois que ngFor concluído:

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

    E não esqueça de usar ChangeDetectorRef para evitar ExpressionChangedAfterItHasBeenCheckedError .

    4. Use ngFor last valor

    Declare a last variável no ngFor . Use-o dentro de li para executar um método quando este item for o último :

  • ... {{ onContentPrinted() }}
    • Use a propriedade flag do componente contentPrinted para executar onContentPrinted() apenas uma vez .
    • Use ng-container para não causar impacto no layout.

    Como chamar a function “navegar” do retorno de chamada com params? Eu tenho algo assim:

      

    Eu ainda não olhei em profundidade como ngFor processa elementos sob o capô. Mas a partir da observação, notei que muitas vezes ele tende a avaliar expressões mais de uma vez para cada item que está interagindo.

    Isso faz com que qualquer chamada do método datilografado seja feita ao verificar a variável ‘last’ do ngFor, algumas vezes, acionada mais de uma vez.

    Para garantir uma única chamada ao seu método datilografado por ngPara quando terminar adequadamente a iteração através de itens, você precisará adicionar uma pequena proteção contra a reavaliação de várias expressões que o ngFor faz sob o capô.

    Aqui está uma maneira de fazer isso (através de uma diretiva), espero que ajude:

    O código da diretiva

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

    Depois de declarar a diretiva acima em seu módulo (supondo que você saiba como fazer isso, se não, pergunte e esperamos que atualize isso com um trecho de código), aqui está como usar a diretiva com ngFor:

     
  • {{item}}
  • ‘doSomething’ é o nome do método no seu arquivo TypeScript que você deseja chamar quando ngFor finaliza a iteração pelos itens.

    Nota: ‘doSomething’ não tem colchetes ‘()’ aqui, pois estamos apenas passando uma referência ao método typescript e não estamos chamando isso aqui.

    E finalmente aqui está como o método ‘doSomething’ se parece no seu arquivo datilografado:

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