【问题标题】:execute a function when *ngFor finished in angular 2当 *ngFor 在角度 2 中完成时执行一个函数
【发布时间】:2016-09-02 10:35:14
【问题描述】:

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

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

谢谢。

【问题讨论】:

  • 你真正想要的 *ngFor 的最后一个元素是什么?你想在最后一个元素上执行一些操作,比如 *ngIf 吗?
  • 我想调用滑块功能...我尝试了 ngAfterViewinit 或类似的东西但没有用...当我使用 setTimeout() 时它可以工作但不是一个好主意...你怎么办觉得我应该做吗?
  • 哦,不,setTimeOut 不是一个好主意,非常有趣的问题,请稍等片刻,然后在几分钟内给你答案。
  • 您是否只想在第一次渲染列表后才调用您的方法?还是应该再次调用它,例如,如果您在列表中添加/删除一个项目?

标签: javascript angular


【解决方案1】:

更新

您可以为此目的使用@ViewChildren

三种情况

1.初始化时ngFor 元素由于ngIf 或其父元素而未渲染

  • 在这种情况下,当ngIf 变为真时,ViewChildren 订阅会通知您

2. 在初始化时,ngFor 元素被渲染,无论 ngIf 在其上还是它的父级

  • 在这种情况下ViewChildren订阅不会第一次通知你,但你可以确定它是在ngAfterViewInit钩子中呈现的

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

  • 在这种情况下,ViewChildren 订阅也会通知您

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

  ngForRendered() {
    console.log('NgFor is Rendered');
  }
}

原创

你可以这样做(但请参阅the side Effects for yourself

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

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

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

即:items 的数据发生变化时,您可以触发下一个changeDetection,并且您的函数将正确指示ngFor 为真正的改变。

【讨论】:

  • 这个答案更好,因为它不会创建span
  • @smnbbrv haha​​ 不需要在我的回答中创建额外的span,我创建只是为了尝试新的东西:p 但没有成功。
  • 但是您必须处理函数调用,每次更改检测滴答时都会调用它
  • @PardeepJain 是的,我知道 :) 答案就是答案。而且这个函数还是会被调用这么多次......我真的不认为作者的意图是一个好方法
  • 因为递归可能,ngFor 渲染并最终调用该函数,这会勾选更改检测,从而触发 ngFor 再次渲染并再次调用该函数,依此类推。我不知道为什么它会在 8 点后停止
【解决方案2】:

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

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

然后在你的组件中:

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
    }
}

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

【讨论】:

    【解决方案3】:

    您可以通过使用#last*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 Plunker

    的工作示例

    【讨论】:

    • 这个函数总是执行!像一个循环!
    • @AlirezaValizade 是的,请阅读 @A_singh's 答案的 cmets 以了解更多信息
    【解决方案4】:

    我也遇到了这个问题。在我的例子中,我调用了一个 Web 服务来检索数据,并且需要执行 javascript 来初始化由*ngFor 生成的每个模板。这是我想出的:

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

    setTimout 将等待 DOM 更新后再执行。这不是您所要求的,但希望对您有所帮助。

    【讨论】:

    • 它可以工作,但在许多情况下它会产生一种 hacky 体验。例如,如果数据在 DOM 中显示产品,并且您需要使用 JavaScript 函数(而不是 javascriptInitializationFunction)进行设计更改。此解决方案将在运行 javascript 函数之前创建产品(数据)的一瞥。我有在 Angular 应用程序中实现 Gridify 的经验。
    • 您也可以在此解决方案中实施已接受的答案。
    【解决方案5】:

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

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

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

    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:

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

    【讨论】:

      【解决方案6】:

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

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

      private globalFlag = -1;
      

      这是我在循环结束时触发的事件

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

      这是循环代码

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

      主要思想是防止在标志相同的情况下触发事件。因此,只有当 globalFlag 值与从 ngFor 传递的当前标志不同时才会触发。对不起我的英语不好,希望你能理解我的想法。

      【讨论】:

        【解决方案7】:

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

        app.component.html

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

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

        【讨论】:

          猜你喜欢
          • 2020-02-01
          • 1970-01-01
          • 2017-01-31
          • 1970-01-01
          • 2020-12-09
          • 1970-01-01
          • 2021-03-17
          • 1970-01-01
          相关资源
          最近更新 更多