0

I have a usecase as below: My objective is to update the array in if and else case.

The main array(cis) looks like below

enter image description here

.html

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

.ts This function runs a map over the array provided above and returns the same structure with all the objects execpt those with property checked:false (removed from the structure) by doing a recursive call on children everytime.

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

The issue I am facing here is, the filterCi function is calling infinitely. Initially, I thought it is because of the recursive call, but even after removing recursion it results in infinite loop.

Finally, I could conclude that it is because of map function used inside filterCi. Why is this behavior and how can I resolve it?

2
  • It's more due to the method retreiveChildren (call inside the map function) but which call itself on filter of data. Could we have an idea/ sample of the data manipulated ? Commented Mar 29, 2022 at 12:36
  • I observed it is not because of retreiveChildren function. Because when i replace return value of map function by this return { ...item, children : item.children && item.children.length > 0 ? item.children.filter(element => element.checked === true) : item.children } I have infinite loop even in this case @jeremy-denis Commented Mar 29, 2022 at 12:42

2 Answers 2

2

I tried your solution for retreiveChildren and it worked for me with the data you provided in the beginning of your question.

I agree @Jonathan with the statement that you shouldn't call the filter function from the HTML directly. You should use a Pipe as he mentioned.

But I found a bug in your implementation. Your function filters the parent nodes accordingly but it doesn't have any effect on the children. You should assign the return value of your recursion to the children to remove all children with are not checked.

That could look like following:

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

  return filteredCis;
}

I refactored your function a bit to be more readable in my opinion.

Sign up to request clarification or add additional context in comments.

1 Comment

Could you please show me how to use pipe in my case? I see that pipe is being being used to render values inside component. I am not sure how to use it for function call @Batajus
1

As you are calling a method from an input binding in a template it will be evaluating on every change detection cycle, as angular has no way of knowing that this is a pure function (see: https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496).

I think this may be the problem you are seeing. To resolve you can use an angular pipe: https://angular.io/guide/pipes

Edit: You should be able to treat the transform method of the pipe as the filterCi method creating a pipe like:

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

}

Then after registering the pipe in the app module. Use it in the template like:

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

3 Comments

I see pipes are used only in case of rendering values. How can i do that while calling the function ? Because in my case its the function .
Hey, I've edited the post to show how I would create the pipe, hope that helps! Reply to this if you have any trouble
Thanks a lot Jonathan . This really helped

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.