Angular 2:ngFor 完成时回调

问题描述 投票:0回答:9

在 Angular 1 中,我编写了一个自定义指令(“repeater-ready”),与

ng-repeat
一起使用,以在迭代完成时调用回调方法:

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

标记中的用法:

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

如何在 Angular 2 中使用

ngFor
实现类似的功能?

javascript angularjs angular ngfor
9个回答
77
投票

您可以使用@ViewChildren来实现此目的

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

原答案在这里 https://stackoverflow.com/a/37088348/5700401


22
投票

你可以使用这样的东西(ngFor局部变量):

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

然后您可以使用setter拦截输入属性更改

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

8
投票

解决方案非常简单。如果您需要知道

ngFor
何时完成将所有 DOM 元素打印到浏览器窗口,请执行以下操作:

1.添加占位符

为正在打印的内容添加占位符:

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

2.添加容器

使用

display: none
为内容创建一个容器。打印完所有项目后,执行
display: block
contentPrinted
是组件标志属性,默认为
false
:

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

3.创建回调方法

onContentPrinted()
添加到组件,该组件会在
ngFor
完成后自行禁用:

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

并且不要忘记使用

ChangeDetectorRef
来避免
ExpressionChangedAfterItHasBeenCheckedError

4.使用 ngFor
last

last
上声明
ngFor
变量。当此项是
最后一项时,在 
li 中使用它来运行方法:

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



    使用
  • contentPrinted
     组件标志属性运行 
    onContentPrinted()
     
    仅运行一次
  • 使用
  • ng-container
    不影响布局。

8
投票
对我来说,使用 Typescript 在 Angular2 中工作。

<li *ngFor="let item in Items; let last = last"> ... <span *ngIf="last">{{ngForCallback()}}</span> </li>
然后就可以使用这个功能了

public ngForCallback() { ... }
    

4
投票
不要使用 [ready],而是使用 [attr.ready],如下所示


  • 3
    投票
    我发现在 RC3 中接受的答案不起作用。不过,我已经找到了解决这个问题的方法。对我来说,我需要知道 ngFor 何时完成运行 MDL componentHandler 来升级组件。

    首先您需要一个指令。

    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(); } }
    
    
    接下来将其导入到您的组件中并将其添加到指令中

    import {UpgradeComponentsDirective} from './upgradeComponents.directive'; @Component({ templateUrl: 'templates/mytemplate.html', directives: [UpgradeComponentsDirective] })
    
    
    现在在 HTML 中将“upgrade-components”属性设置为 true。

    <div *ngFor='let item of items;let last=last' [upgrade-components]="last ? true : false">
    
    
    当该属性设置为true时,它将运行@Input()声明下的方法。就我而言,它运行 componentHandler.upgradeAllRegistered()。但是,它可以用于您选择的任何内容。通过绑定到 ngFor 语句的“last”属性,它将在完成时运行。

    您不需要使用 [attr.upgrade-components],即使这不是本机属性,因为它现在是一个真正的指令。


    1
    投票
    我针对这个问题写了一个demo。该理论基于

    接受的答案,但这个答案并不完整,因为 li

     应该是一个可以接受 
    ready
     输入的自定义组件。

    我为此问题写了

    一个完整的演示。 定义一个新组件:

    从 '@angular/core' 导入 {Component, Input, OnInit};

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

    模板

    {{item}}

    在应用程序组件中的使用 

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

    你会看到控制台的日志会打印所有的item字符串,然后打印isReady。


    0
    投票

    这会导致在检查 ngFor 'last' 变量时进行的任何打字稿方法调用有时会多次触发。

    为了保证 ngFor 在正确完成对项目的迭代时对您的 typescript 方法进行一次调用,您需要添加一个小的保护措施,以防止 ngFor 在后台执行的多个表达式重新评估。

    这是一种方法(通过指令),希望它有帮助:

    指令代码

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

    在模块中声明上述指令后(假设您知道如何执行此操作,如果不知道,请询问,我希望用代码片段更新它),以下是如何在 ngFor 中使用该指令:

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

    'doSomething' 是 TypeScript 文件中您想要在 ngFor 完成项目迭代时调用的方法名称。

    注意:

    “doSomething”这里没有括号“()”,因为我们只是传递对打字稿方法的引用,而不是在这里实际调用它。 最后,这是“doSomething”方法在打字稿文件中的样子:

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

    
    

    0
    投票
    trackByFn

    失去了外部范围并且不应该产生副作用,所以我被卡住了。

    我的解决方案与此处介绍的其他解决方案略有不同,我使用数据集属性来调用组件方法。不需要设置器或指令;

    export class SomeComponent { items = []; public isLastItem() { // do something } } .... <ng-container *ngFor"let item of items; let last = last"> <li [attr.data-is-last-item]="last ? isLastItem() : null"></li> </ng-container>

    © www.soinside.com 2019 - 2024. All rights reserved.