Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug(MatTree): expandAll() only expand the first level #29865

Closed
1 task done
robmv opened this issue Oct 11, 2024 · 4 comments · Fixed by #30226
Closed
1 task done

bug(MatTree): expandAll() only expand the first level #29865

robmv opened this issue Oct 11, 2024 · 4 comments · Fixed by #30226
Assignees
Labels
area: material/tree P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Comments

@robmv
Copy link

robmv commented Oct 11, 2024

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

17.x

Description

The method expandAll() of MatTree expands only the first level of a deeply nested tree. On deprecated MatTreeControl, the same method was able to expand the entire tree without problems.

Note: the deprecated control had an advantage that the expansion state could be manipulated before even the view was initialized, but that is another topic.

WORKAROUND:

this.dataSource.forEach(node => this.tree.expandDescendants(node));

Reproduction

StackBlitz link:

Steps to reproduce:

  1. Open the demos.
  2. The tree is partially expanded
  3. Edit the demo and enable the workaround on the ngAfterViewInit callback.
  4. The tree is now fully expanded.

Expected Behavior

MatTree's expandAll() should expand all nodes.

Actual Behavior

Only the first level nodes are expanded.

Environment

  • Angular: 18.2.8
  • CDK/Material: 18.2.8
  • Browser(s): Firefox, Chromium
  • Operating System (e.g. Windows, macOS, Ubuntu): Linux (Fedora)
@robmv robmv added the needs triage This issue needs to be triaged by the team label Oct 11, 2024
@wagnermaciel wagnermaciel added P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent area: material/tree and removed needs triage This issue needs to be triaged by the team labels Oct 18, 2024
@ranitg
Copy link

ranitg commented Nov 3, 2024

Can this be fixed please?

@tanis2000
Copy link

This is still relevant as of today

@AnnaKozlova
Copy link

AnnaKozlova commented Dec 19, 2024

Hello!

I ran into the same problem when switching to the new api cdk tree;

I used to use treeControl.expandAll() / collapseAll() without problems;

My version of angular cdk: 18.2.11, but I see the same logic I'll describe below in 19.0.4 as well:

  1. update dataSource -> inner _flattenedNodes will be calculated:

   /**
   * Given a set of root nodes and the current node level, flattens any nested
   * nodes into a single array.
   *
   * If any nodes are not expanded, then their children will not be added into the array.
   * This will still traverse all nested children in order to build up our internal data
   * models, but will not include them in the returned array.
   */
  private _flattenNestedNodesWithExpansion(nodes: readonly T[], level = 0): Observable<T[]> {
    const childrenAccessor = this._getChildrenAccessor();
    // If we're using a level accessor, we don't need to flatten anything.
    if (!childrenAccessor) {
      return observableOf([...nodes]);
    }

    return observableOf(...nodes).pipe(
      concatMap(node => {
        const parentKey = this._getExpansionKey(node);
        if (!this._parents.has(parentKey)) {
          this._parents.set(parentKey, null);
        }
        this._levels.set(parentKey, level);

        const children = coerceObservable(childrenAccessor(node));
        return concat(
          observableOf([node]),
          children.pipe(
            take(1),
            tap(childNodes => {
              this._ariaSets.set(parentKey, [...(childNodes ?? [])]);
              for (const child of childNodes ?? []) {
                const childKey = this._getExpansionKey(child);
                this._parents.set(childKey, node);
                this._levels.set(childKey, level + 1);
              }
            }),
            switchMap(childNodes => {
              if (!childNodes) {
                return observableOf([]);
              }
              return this._flattenNestedNodesWithExpansion(childNodes, level + 1).pipe(
                map(nestedNodes => (this.isExpanded(node) ? nestedNodes : [])),
              );
            }),
          ),
        );
      }),
      reduce((results, children) => {
        results.push(...children);
        return results;
      }, [] as T[]),
    );
  }

line map(nestedNodes => (this.isExpanded(node) ? nestedNodes : [])), is key

and we can see from the description that it's intentional:

  • If any nodes are not expanded, then their children will not be added into the array.
  • This will still traverse all nested children in order to build up our internal data
  • models, but will not include them in the returned array.
  1. expandAll uses the same _flattenedNodes and from description below and algorithm -> _flattenedNodes has only first level, because nested children weren't expanded yet
 /** Expands all data nodes in the tree. */
  expandAll(): void {
    if (this.treeControl) {
      this.treeControl.expandAll();
    } else if (this._expansionModel) {
      const expansionModel = this._expansionModel;
      expansionModel.select(
        ...this._flattenedNodes.value.map(child => this._getExpansionKey(child)),
      );
    }
  }

and unfortunately, we can't use expandAll/collapseAll as it was before - they work only for first level.


in contrast to the old logic:

  /**
   * Expands all data nodes in the tree.
   *
   * To make this working, the dataNodes variable of the TreeControl must be set to all flattened
   * data nodes of the tree.
   */
  expandAll(): void {
    this.expansionModel.select(...this.dataNodes.map(node => this._trackByValue(node)));
  }

dataNodes were filled and have nested children.

Please, could you please look at this and make changes? Because the expandAll function doesn't do what it promises to do.

@crisbeto (I see from the git history that you often work with tree, if you don't deal with such issues, I apologize for bothering you, but maybe you know someone who does)

@crisbeto crisbeto self-assigned this Dec 21, 2024
crisbeto added a commit to crisbeto/material2 that referenced this issue Dec 21, 2024
Fixes that the tree wasn't recursing down and expanding all child nodes when calling `expandAll`.

Fixes angular#29865.
crisbeto added a commit that referenced this issue Dec 27, 2024
Fixes that the tree wasn't recursing down and expanding all child nodes when calling `expandAll`.

Fixes #29865.
crisbeto added a commit that referenced this issue Dec 27, 2024
Fixes that the tree wasn't recursing down and expanding all child nodes when calling `expandAll`.

Fixes #29865.

(cherry picked from commit 0f053ff)
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Jan 27, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: material/tree P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants