Angular 2: ngFor执行完毕回调
Angular 2: Callback when ngFor has finished
在 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
类似的功能?
你可以使用这样的东西 (ngFor local variables):
<li *ngFor="#item in Items; #last = last" [ready]="last ? false : true">
那你可以Intercept input property changes with a setter
@Input()
set ready(isReady: boolean) {
if (isReady) someCallbackMethod();
}
而不是[就绪],使用[attr.ready],如下所示
<li *ngFor="#item in Items; #last = last" [attr.ready]="last ? false : true">
我发现在 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">
当此属性设置为真时,它将运行 @Input() 声明下的方法。在我的例子中是 运行s componentHandler.upgradeAllRegistered()。但是,它可以用于您选择的任何东西。通过绑定到 ngFor 语句的 'last' 属性,这将在完成时 运行。
您将不需要使用 [attr.upgrade-components],即使这不是本机属性,因为它现在是真正的指令。
对我来说,使用 Typescript 在 Angular2 中工作。
<li *ngFor="let item in Items; let last = last">
...
<span *ngIf="last">{{ngForCallback()}}</span>
</li>
那你就可以用这个函数来处理了
public ngForCallback() {
...
}
我为这个问题写了一个demo。该理论基于 但这个答案并不完整,因为 li
应该是一个可以接受 ready
输入的自定义组件。
我为这个问题写a complete demo。
定义一个新组件:
从“@angular/core”导入{组件、输入、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。
我还没有深入了解 ngFor 如何在底层渲染元素。但是通过观察,我注意到它经常倾向于对每个迭代的每个项目多次计算表达式。
这会导致在检查 ngFor 'last' 变量时进行的任何打字稿方法调用,有时会触发不止一次。
为了保证 ngFor 在正确完成对项目的迭代时对您的打字稿方法进行一次调用,您需要添加一个小的保护措施,以防止 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' 这里没有括号 '()' 因为我们只是传递对 typescript 方法的引用而不是在这里实际调用它.
最后是 'doSomething' 方法在您的打字稿文件中的样子:
public doSomething=()=> {
console.log("triggered from the directive's parent component when ngFor finishes iterating");
}
您可以为此目的使用@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');
}
}
原答案在这里
解决方案非常简单。如果您需要知道 ngFor
何时将所有 DOM 元素打印到浏览器 window,请执行以下操作:
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
值
在 ngFor
上声明 last
变量。在li
到运行一个方法里使用它当这个项目是最后一个:
<li *ngFor="let item of items; let last = last">
...
<ng-container *ngIf="last && !contentPrinted">
{{ onContentPrinted() }}
</ng-container>
<li>
- 使用
contentPrinted
组件标志属性到运行onContentPrinted()
仅一次.
- 使用
ng-container
不影响布局
在 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
类似的功能?
你可以使用这样的东西 (ngFor local variables):
<li *ngFor="#item in Items; #last = last" [ready]="last ? false : true">
那你可以Intercept input property changes with a setter
@Input()
set ready(isReady: boolean) {
if (isReady) someCallbackMethod();
}
而不是[就绪],使用[attr.ready],如下所示
<li *ngFor="#item in Items; #last = last" [attr.ready]="last ? false : true">
我发现在 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">
当此属性设置为真时,它将运行 @Input() 声明下的方法。在我的例子中是 运行s componentHandler.upgradeAllRegistered()。但是,它可以用于您选择的任何东西。通过绑定到 ngFor 语句的 'last' 属性,这将在完成时 运行。
您将不需要使用 [attr.upgrade-components],即使这不是本机属性,因为它现在是真正的指令。
对我来说,使用 Typescript 在 Angular2 中工作。
<li *ngFor="let item in Items; let last = last">
...
<span *ngIf="last">{{ngForCallback()}}</span>
</li>
那你就可以用这个函数来处理了
public ngForCallback() {
...
}
我为这个问题写了一个demo。该理论基于 li
应该是一个可以接受 ready
输入的自定义组件。
我为这个问题写a complete demo。
定义一个新组件:
从“@angular/core”导入{组件、输入、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。
我还没有深入了解 ngFor 如何在底层渲染元素。但是通过观察,我注意到它经常倾向于对每个迭代的每个项目多次计算表达式。
这会导致在检查 ngFor 'last' 变量时进行的任何打字稿方法调用,有时会触发不止一次。
为了保证 ngFor 在正确完成对项目的迭代时对您的打字稿方法进行一次调用,您需要添加一个小的保护措施,以防止 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' 这里没有括号 '()' 因为我们只是传递对 typescript 方法的引用而不是在这里实际调用它.
最后是 'doSomething' 方法在您的打字稿文件中的样子:
public doSomething=()=> {
console.log("triggered from the directive's parent component when ngFor finishes iterating");
}
您可以为此目的使用@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');
}
}
原答案在这里
解决方案非常简单。如果您需要知道 ngFor
何时将所有 DOM 元素打印到浏览器 window,请执行以下操作:
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
值
在 ngFor
上声明 last
变量。在li
到运行一个方法里使用它当这个项目是最后一个:
<li *ngFor="let item of items; let last = last">
...
<ng-container *ngIf="last && !contentPrinted">
{{ onContentPrinted() }}
</ng-container>
<li>
- 使用
contentPrinted
组件标志属性到运行onContentPrinted()
仅一次. - 使用
ng-container
不影响布局