Javascript 当 *ngFor 在 angular 2 中完成时执行一个函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37087864/
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 19:53:30  来源:igfitidea点击:

execute a function when *ngFor finished in angular 2

javascriptangular

提问by Ankit Singh

I trying to create a application with angular 2,i want when last element rendered in *ngFor,execute a function, somthing like this :

我试图创建一个带有 angular 2 的应用程序,我希望在 *ngFor 中呈现最后一个元素时,执行一个函数,如下所示:

<ul>
  <li *ngFor="#i of items">{{i.text}}</li> <==== i want when this completed, execute a functuon in my class
</ul>

Thanks.

谢谢。

回答by Ankit Singh

Update

更新

You can use @ViewChildrenfor that purpose

您可以@ViewChildren为此目的使用

There are three cases

有三种情况

1.on initialization ngForelement is not rendred due to ngIfon it or it's parent

1.on 初始化ngFor元素没有被渲染,因为ngIf它或者它的父元素

  • in which case, whenver ngIfbecomes truthy, you will be notified by the ViewChildrensubscription
  • 在这种情况下,当ngIf变得真实时,您将收到ViewChildren订阅通知

2.on initialization ngForelement is rendred regardless of ngIfon it or it's parent

2.初始化ngFor元素被渲染,不管ngIf它是在它还是它的父元素上

  • in which case ViewChildrensubscription will not notify you for the first time, but you can be sure it's rendered in the ngAfterViewInithook
  • 在这种情况下,ViewChildren订阅不会第一次通知您,但您可以确定它是在ngAfterViewInit钩子中呈现的

3.items are added/removed to/from the ngForArray

3.向/从ngFor数组中添加/删除项目

  • in which case also ViewChildrensubscription will notify you
  • 在这种情况下,ViewChildren订阅也会通知您

[Plunker Demo](see console log there)

[Plunker Demo](请参阅那里的控制台日志)

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


.

.



Original

原来的

You can do it like this ( but see the side Effects for yourself)

你可以这样做(但你自己看看副作用

<ul>
  <li *ngFor="let i of items; let last = last">{{i}} {{last ? callFunction(i) : ''}}</li>
</ul>


Which is Useless,unless used with changeDetectionStrategy.OnPush

这是无用的,除非与changeDetectionStrategy.OnPush 一起使用

Then you can have control over how many times change detection occurs, hence how many times your function is called.

然后您可以控制发生变化检测的次数,从而控制您的函数被调用的次数。

i.e:You can trigger next changeDetectionwhen the data of itemschanges, and your function will give proper indication that ngForis rendered for real change.

即:您可以changeDetection在数据items更改时触发 next,您的函数将给出正确的指示,ngFor真正的更改呈现。

回答by Methodician

I used a slight hack of the approach others have complained about and it worked like a charm:

我使用了一些其他人抱怨的方法的小技巧,它的作用就像一个魅力:

<ul>
  <li *ngFor="let i of items; let last = last">{{i}} {{last ? callFunction(i) : ''}}</li>
</ul>

Then in your component:

然后在您的组件中:

shouldDoIt = true; // initialize it to true for the first run

callFunction(stuff) {
    if (this.shouldDoIt) {
        // Do all the things with the stuff
        this.shouldDoIt = false; // set it to false until you need to trigger again
    }
}

This doesn't address the root of the problem with this approach but it's an extremely low-cost hack that made my app run smoothly. I do hope the Angular team will implement a more friendly way to trigger some code after the *ngFor loads its content.

这并没有解决这种方法的问题根源,但它是一种成本极低的黑客,使我的应用程序运行顺利。我确实希望 Angular 团队能够实现一种更友好的方式来在 *ngFor 加载其内容后触发一些代码。

回答by bdunn

I ran into this problem as well. In my case I was calling a web service to retrieve data and needed to execute javascript to initialize each template produced by *ngFor. Here's what I came up with:

我也遇到了这个问题。就我而言,我正在调用 Web 服务来检索数据,并且需要执行 javascript 来初始化由*ngFor. 这是我想出的:

updateData() {
  this.dataService.getData().subscribe(
    function(data) {
      this.data = data;
      setTimeout(javascriptInitializationFunction, 0);
    },
    function(err) { /* handle it */ }
  );
}

setTimoutwill wait for the DOM To be updated before executing. This isn't exactly what you asked, but hopefully it helps.

setTimout在执行之前会等待 DOM 更新。这不完全是你问的,但希望它有所帮助。

回答by Pardeep Jain

you can do the same by getting last index using #lastof *ngForand call function by getting last index value and do your stuff whatever you want. here is code for the same -

你可以通过使用#lastof获取最后一个索引来做同样的事情,*ngFor并通过获取最后一个索引值来调用函数,然后做任何你想做的事情。这是相同的代码 -

<ul>
   <li *ngFor="#item of items; #last = last">
    <span *ngIf='last'>{{hello(last)}}</span>
    {{item}}
   </li>
  </ul>


items: Array<number> = [1,2,3,4,5]
  constructor() { console.clear();}
  hello(a){
    if(a==true)
      this.callbackFunction();
  }
  callbackFunction(){
    console.log("last element");
  }

working example for the same Working Plunker

相同的工作示例 Working Plunker

回答by edmond wang

I am facing the same question and find one way to walk around this. Not sure whether it is suitable for you or not.
Pre-conditions is you are using ngFor to render inbound properties.

我面临同样的问题,并找到一种方法来解决这个问题。不确定它是否适合您。
先决条件是您使用 ngFor 来呈现入站属性。

context: I need call MDL's upgradeAllRegistered to make checkbox pretty after fetch grid data from back-end.

上下文:从后端获取网格数据后,我需要调用 MDL 的 upgradeAllRegistered 以使复选框变得漂亮。

I add one setTimeout inside 'ngOnChanges';

我在 'ngOnChanges' 中添加了一个 setTimeout;

grid.componet.ts:

grid.componet.ts:

export class GridComponent {
  @Input() public rows: Array<any>;
    ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
    for (let propName in changes) {
      if (propName == 'rows') {
        setTimeout(() => componentHandler.upgradeAllRegistered());
      }
    }
  }
}

grid.component.html:

grid.component.html:

<tr #rowDOM *ngFor="let row of rows;"></tr>

回答by Heru Phang Sebastian

I'd managed to perform some hacks to prevent event get triggred (I found that scrolling always trigger those events)

我设法执行了一些技巧以防止事件被触发(我发现滚动总是触发这些事件)

By adding : [Add in your component]

通过添加:[在您的组件中添加]

private globalFlag = -1;

and this is my event that will be triggered when looping is finished

这是我的事件,将在循环完成时触发

ev(flag){
    if(flag != this.globalFlag){
      console.log("TRIGGER!")
      this.globalFlag = flag;

    }
  }

and this the looping code

这是循环代码

 <div *ngFor="let var of list;let i=index;let last=last">
    {{last ? ev(i) : ''}}
</div>

The main idea is to prevent event be trigger if the flag was same. So it only be trigger when the globalFlag value differed with current flag that passed from ngFor. Sorry for my bad english, hope you can understand my idea.

主要思想是防止事件在标志相同时被触发。所以只有当 globalFlag 值与从 ngFor 传递的当前标志不同时才会触发。抱歉我的英语不好,希望你能理解我的想法。

回答by Manzer

Possible solution: the answer is displayed on https://stackblitz.com/edit/angular-phpauq?file=src%2Fapp%2Fapp.component.ts

可能的解决方案:答案显示在https://stackblitz.com/edit/angular-phpauq?file=src%2Fapp%2Fapp.component.ts

app.component.html

应用程序组件.html

<div [innerHTML]="html"></div>

app.component.ts

app.component.ts

 html = '';
ngAfterViewInit(){
       let  i;
       this.html += '<ul>';
       for(i = 0 ; i < this.abc.length ; i++){
           this.html += '<li>';
           this.html +=  this.abc[i];
           if(parseInt(i) === this.abc.length - 1){
           this.callMethod(i);
           }
           this.html += '</li>';
           }
           this.html += '</ul>';
    }


    callMethod(text){
    alert(text);
    }