Javascript Angular 2:ngFor 完成时的回调

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35819264/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:15:21  来源:igfitidea点击:

Angular 2: Callback when ngFor has finished

javascriptangularjsangularngfor

提问by Tobias Punke

In Angular 1 I have written a custom directive ("repeater-ready") to use with ng-repeatto invoke a callback method when the iteration has been completed:

在 Angular 1 中,我编写了一个自定义指令(“repeater-ready”),用于ng-repeat在迭代完成时调用回调方法:

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

Usage in markup:

标记中的用法:

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

How can I achieve a similar functionality with ngForin Angular 2?

如何ngFor在 Angular 2 中实现类似的功能?

采纳答案by Sasxa

You can use something like this (ngFor local variables):

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

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

Then you can Intercept input property changes with a setter

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

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

回答by Abhijit Jagtap

You can use @ViewChildren for that purpose

您可以为此使用@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');
  }
}

origional Answer is herehttps://stackoverflow.com/a/37088348/5700401

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

回答by FACode

For me works in Angular2 using Typescript.

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

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

Then you can you can handle using this function

那么你可以使用这个函数来处理

public ngForCallback() {
  ...
}

回答by Rajasekhar Kunati

Instead of [ready], use [attr.ready] like below

使用 [attr.ready] 代替 [ready],如下所示

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

回答by Warren Schilpzand

I found in RC3 the accepted answer doesn't work. However, I have found a way to deal with this. For me, I need to know when ngFor has finished to run the MDL componentHandler to upgrade the components.

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

First you will need a directive.

首先你需要一个指令。

upgradeComponents.directive.ts

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

Next import this into your component and add it to the directives

接下来将其导入您的组件并将其添加到指令中

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

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

Now in the HTML set the "upgrade-components" attribute to true.

现在在 HTML 中将“upgrade-components”属性设置为 true。

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

When this attribute is set to true, it will run the method under the @Input() declaration. In my case it runs componentHandler.upgradeAllRegistered(). However, it could be used for anything of your choosing. By binding to the 'last' property of the ngFor statement, this will run when it is finished.

当此属性设置为 true 时,它​​将运行@Input() 声明下的方法。就我而言,它运行 componentHandler.upgradeAllRegistered()。但是,它可以用于您选择的任何内容。通过绑定到 ngFor 语句的“last”属性,它会在完成时运行。

You will not need to use [attr.upgrade-components] even though this is not a native attribute due to it now being a bonafide directive.

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

回答by vulp

The solution is quite trivial. If you need to know when ngForcompletes printing all the DOM elements to the browser window, do the following:

解决方案非常简单。如果您需要知道何时ngFor完成将所有 DOM 元素打印到浏览器窗口,请执行以下操作:

1. Add a placeholder

1. 添加占位符

Add a placeholder for the content being printed:

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

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

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

2. Add a container

2. 添加容器

Create a container with display: nonefor the content. When all items are printed, do display: block. contentPrintedis a component flag property, which defaults to false:

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

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

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

3. Create a callback method

3.创建回调方法

Add onContentPrinted()to the component, which disables itself after ngForcompletes:

添加onContentPrinted()到组件中,ngFor完成后自动禁用:

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

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

And don't forget to use ChangeDetectorRefto avoid ExpressionChangedAfterItHasBeenCheckedError.

并且不要忘记使用ChangeDetectorRef来避免ExpressionChangedAfterItHasBeenCheckedError

4. Use ngFor lastvalue

4. 使用 ngForlast

Declare lastvariable on ngFor. Use it inside lito run a method when this item is the last one:

last在 上声明变量ngForli当此项是最后一项时,在内部使用它来运行一个方法:

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

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

  • Use contentPrintedcomponent flag property to run onContentPrinted()only once.
  • Use ng-containerto make no impact on the layout.
  • 使用contentPrinted组件标志属性onContentPrinted()运行一次
  • 用于ng-container不影响布局。

回答by Xin Meng

I write a demo for this issue. The theory is based on the accepted answerbut this answer is not complete because the lishould be a custom component which can accept a readyinput.

我为这个问题写了一个演示。该理论基于已接受的答案,但该答案并不完整,因为它li应该是一个可以接受ready输入的自定义组件。

I write a complete demofor this issue.

我为这个问题写了一个完整的演示

Define a new component:

定义一个新组件:

import {Component, Input, OnInit} from '@angular/core';

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

template

模板

{{item}}

usage in the app component

在应用组件中的使用

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

You will see the log in the console will print all the item string and then print the isReady.

您将看到控制台中的日志将打印所有项目字符串,然后打印 isReady。

回答by H7O

I haven't yet looked in depth of how ngFor renders elements under the hood. But from observation, I've noticed it often tends to evaluate expressions more than once per each item it's iterating.

我还没有深入研究 ngFor 如何在幕后渲染元素。但是从观察中,我注意到它经常倾向于对它迭代的每个项目多次评估表达式。

This causes any typescript method call made when checking ngFor 'last' variable to get, sometimes, triggered more than once.

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

To guarantee a one call to your typescript method by ngFor when it properly finishes iterating through items, you need to add a small protection against the multiple expression re-evaluation that ngFor does under the hood.

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

Here is one way to do it (via a directive), hope it helps:

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

The directive code

指令代码

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

  }

}

After declaring the above directive in your module (assuming you know how to do so, if not, ask and I'll hopefully update this with a code snippet), here is how to use the directive with ngFor:

在您的模块中声明上述指令后(假设您知道如何这样做,如果不知道,请询问,我希望用代码片段更新它),以下是如何将指令与 ngFor 一起使用:

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

'doSomething' is the method name in your TypeScript file that you want to call when ngFor finishes iterating through items.

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

Note:'doSomething' doesn't have brackets '()' here as we're just passing a reference to the typescript method and not actually calling it here.

注意:“doSomething”在这里没有方括号“()”,因为我们只是传递对 typescript 方法的引用,而不是在此处实际调用它。

And finally here is how 'doSomething' method looks like in your typescript file:

最后这里是“doSomething”方法在你的打字稿文件中的样子:

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