0
votes

I'm having troubles getting reactivity in the child components when i update the list in the parent component.

The data i have is pretty nested and looks like this

{ //projectList
  "projects": [
        {
            "name": "project",
            "displayed":true,
            "layers": [
                {
                    "name": "child_1",
                    "displayed":true
                    "layers": [
                        {
                            "name": "child_1_1",
                            "displayed":true
                        },
                        {
                            "name": "child_1_2",
                            "displayed":true

                        }
                    ]
                }
            ]
        }
    ]
}

in the parent component I use *ngFor like this:

 <li *ngFor="let project of projectList">
                <child1
                    *ngIf="project.displayed"
                    [project]="project"
                ></child1>
  </li>

the child1 component is also using another child2 in order to go deeper inn the structure if it contains more layers. So the child2 component uses recursion to create more levels, but i think the problems occurs way before that.

What I'm trying to do here is to filter the list and set displayed:false in the nested structure from the parent component. I want this change to get detected by child1 and child2, why doesnt it happen?

(the shown examples are simplified alot)

One solution to trigger changedetection is to do a projectList = copy(projectList) after i change the displayed attributes, but this introduces some unwanted behaviour

the filter function (simplified alot):

  private setfilteredProjectList() { //STARTS HERE
        this.projectList?.forEach((projectArray) => {
            projectArray.layers.forEach((layer) => {
                    let filterSearch = this.filterSearchString(layer);
                    layer = filterSearch.layer;
                          
            });
        });
    }

 private filterSearchString(layer: Layer) {
        if (!layer.layers?.length) {
            if (this.search === '') {
                layer.displayed = true;
            } else {
                if (
                    layer.layername
                        .toLowerCase()
                        .startsWith(this.search.toLowerCase())
                ) {
                    layer.displayed = true;
                } else {
                    layer.displayed = false;
                }
            }
        } else {
            layer.layers.map((l) => {
                let childFiltered = this.filterSearchString(l);
                return childFiltered.layer;
            });
        }
        return { layer };
    }

UPDATE: i see a great mistake i made; used forEach instead of map in setfilteredProjectList()...

1
Have you tried using trackBy? dotnettutorials.net/lesson/angular-ngfor-trackbyPMO1948
Can you share the filter code?Owen Kelvin
@OwenKelvin yeah i've tried trackBy where i used a id attribute from the list objectsKim Vu
@PMO1948 I've updated the post with a simplified version of my filtermethodsKim Vu

1 Answers

2
votes

This is an issue with reference types (arrays and objects) where we have to change the location of RAM to ensure that change detection takes place and when you have nested objects and arrays, it becomes cumbersome and annoying but try this.

private setfilteredProjectList() { //STARTS HERE
       // assign projectList to a new array
        this.projectList = this.projectList?.map(project => { // the map in both cases 
            const layers = project.layers.map(layer => {     // creates a new location in
               let filterSearch = this.filterSearchString(layer); // memory and ensures
               return filterSearch.layer;                         // change detection 
            });                                                   // to take place
            return {
              ...project, // spread the old project object
              layers,     // update the layers value
            };
        });
    }