Angular 中的函数调用使用打字稿创建无限循环

function call in Angular using typescript creates infinite loop

我有一个用例如下:我的 objective 是更新 ifelse 案例中的数组。

主数组(cis)如下所示

.html

<sample-container  [cis]="filterCi(currentBaseline.cis, currentBaseline)"> </sample-container>

.ts 此函数在上面提供的数组和 returns 上运行一个映射,所有对象都具有相同的结构,除了那些具有 属性 checked:false 的对象(从结构中删除)每次都对子对象进行递归调用.

//retrieveChildren

retreiveChildren(children: Structure[]) : Structure[] {
  let filteredCis =  children && children.filter((ci) =>{
      if(ci.children && ci.children.length > 0){
         retreiveChildren(ci.children);
      }
      return ci.checked === true;
    }) ;
  return filteredCis;
}

//filterCi function

filterCi(cis: Structure[], baseline: Baseline): Structure[] {
    if(baseline.isHideElementsActivated){
      let newArray = [...cis]
      let result =  newArray.filter(ci => ci.checked === true).map((item,index) => {
        return {
          ...item,
          children : retreiveChildren(item.children)
        }
      })
      console.log("Result array...." , result)
      return result;
    }else{
      return cis;
    }
}

我在这里面临的问题是,filterCi 函数正在无限调用。起初,我以为是递归调用的缘故,但即使去掉递归,它也会导致无限循环。

最后,我可以得出结论,这是因为 filterCi 内部使用了 map 函数。 为什么会出现这种行为,我该如何解决?

当您从模板中的输入绑定调用方法时,它将在每个更改检测周期进行评估,因为 angular 无法知道这是一个纯函数(请参阅:https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496).

我认为这可能是您遇到的问题。要解决这个问题,您可以使用 angular 管道:https://angular.io/guide/pipes

编辑:您应该能够将管道的转换方法视为创建管道的 filterCi 方法,例如:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'filterCis'
})
export class FilterCisPipe implements PipeTransform {

  public transform(cis: Structure[], baseline: Baseline): Structure[] {
    if(baseline.isHideElementsActivated){
      let newArray = [...cis]
      let result =  newArray.filter(ci => ci.checked === true).map((item,index) => {
        return {
          ...item,
          children : retreiveChildren(item.children)
        }
      })
      console.log("Result array...." , result)
      return result;
    }else{
      return cis;
    }
  }

  private retreiveChildren(children: Structure[]) : Structure[] {
    let filteredCis =  children && children.filter((ci) =>{
        if(ci.children && ci.children.length > 0){
           retreiveChildren(ci.children);
        }
        return ci.checked === true;
      }) ;
    return filteredCis;
  }

}

然后在应用模块中注册管道之后。在模板中使用它,例如:

<sample-container [cis]="currentBaseline.cis | filterCi:currentBaseline"> </sample-container>

我尝试了您的 retreiveChildren 解决方案,它适用于您在问题开头提供的数据。

我同意@Jonathan 的说法,即您不应直接从HTML 调用过滤器函数。你应该像他提到的那样使用管道。

但我在您的实施中发现了一个错误。您的函数会相应地过滤父节点,但对子节点没有任何影响。您应该将递归的 return 值分配给子级以删除所有未选中的子级。

可能如下所示:

retrieveChildren(children: Structure[]): Structure[]{
  const filteredCis = children && children.filter((ci) => {
    if (!ci.checked) {
      return false;
    }
    
    ci.children = retrievedChildren(ci.children);
    return true;
  });

  return filteredCis;
}

我认为我对您的函数进行了一些重构,使其更具可读性。