diff --git a/projects/igniteui-angular/tree/src/tree/tree-selection.spec.ts b/projects/igniteui-angular/tree/src/tree/tree-selection.spec.ts
index a7438f0c994..7ce9114d3ea 100644
--- a/projects/igniteui-angular/tree/src/tree/tree-selection.spec.ts
+++ b/projects/igniteui-angular/tree/src/tree/tree-selection.spec.ts
@@ -1,6 +1,7 @@
import { TestBed, fakeAsync, waitForAsync } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
-import { ChangeDetectorRef, ElementRef, EventEmitter, QueryList } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, QueryList, provideZonelessChangeDetection, signal } from '@angular/core';
import { IgxTreeComponent } from './tree.component';
import { UIInteractions } from '../../../test-utils/ui-interactions.spec';
import { TreeTestFunctions, TREE_NODE_DIV_SELECTION_CHECKBOX_CSS_CLASS } from './tree-functions.spec';
@@ -11,6 +12,48 @@ import { IgxTreeNodeComponent } from './tree-node/tree-node.component';
import { IgxTreeNavigationService } from './tree-navigation.service';
import { IgxTreeSelectionSampleComponent, IgxTreeSimpleComponent } from './tree-samples.spec';
+@Component({
+ template: `
+
+
+ Parent
+ First child
+ @if (showSecondChild()) {
+ Second child
+ }
+
+
+ `,
+ changeDetection: ChangeDetectionStrategy.Eager,
+ imports: [IgxTreeComponent, IgxTreeNodeComponent]
+})
+class IgxTreeZonelessNodeDeletionComponent {
+ protected selection = IgxTreeSelectionType.Cascading;
+ protected firstChildSelected = signal(false);
+ protected showSecondChild = signal(true);
+
+ public selectFirstChild(): void {
+ this.firstChildSelected.set(true);
+ }
+
+ public removeSecondChild(): void {
+ this.showSecondChild.set(false);
+ }
+}
+
+@Component({
+ template: `
+
+ Selected node
+
+ `,
+ changeDetection: ChangeDetectionStrategy.Eager,
+ imports: [IgxTreeComponent, IgxTreeNodeComponent]
+})
+class IgxTreeZonelessInitialSelectionComponent {
+ protected selection = IgxTreeSelectionType.BiState;
+}
+
describe('IgxTree - Selection #treeView', () => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
@@ -688,3 +731,49 @@ describe('IgxTree - Selection #treeView', () => {
});
});
+describe('IgxTree selection in zoneless change detection #treeView', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ NoopAnimationsModule,
+ IgxTreeZonelessNodeDeletionComponent,
+ IgxTreeZonelessInitialSelectionComponent
+ ],
+ providers: [provideZonelessChangeDetection()]
+ });
+ });
+
+ it('should render an initially selected node', async () => {
+ const fixture = TestBed.createComponent(IgxTreeZonelessInitialSelectionComponent);
+ fixture.detectChanges();
+ await new Promise(resolve => requestAnimationFrame(() => resolve()));
+ await fixture.whenStable();
+
+ const tree = fixture.debugElement.query(By.directive(IgxTreeComponent)).componentInstance as IgxTreeComponent;
+ const node = tree.nodes.first as IgxTreeNodeComponent;
+
+ expect(node.selected).toBeTrue();
+ expect(TreeTestFunctions.getNodeCheckboxInput(node.nativeElement).checked).toBeTrue();
+ });
+
+ it('should render cascading selection changes after a child is removed', async () => {
+ const fixture = TestBed.createComponent(IgxTreeZonelessNodeDeletionComponent);
+ fixture.detectChanges();
+ await fixture.whenStable();
+
+ const tree = fixture.debugElement.query(By.directive(IgxTreeComponent)).componentInstance as IgxTreeComponent;
+ fixture.componentInstance.selectFirstChild();
+ await fixture.whenStable();
+
+ const parent = tree.nodes.first as IgxTreeNodeComponent;
+ expect(parent.indeterminate).toBeTrue();
+
+ fixture.componentInstance.removeSecondChild();
+ await fixture.whenStable();
+ await new Promise(resolve => requestAnimationFrame(() => resolve()));
+ await fixture.whenStable();
+
+ expect(parent.selected).toBeTrue();
+ expect(TreeTestFunctions.getNodeCheckboxInput(parent.nativeElement).checked).toBeTrue();
+ });
+});
diff --git a/projects/igniteui-angular/tree/src/tree/tree.component.ts b/projects/igniteui-angular/tree/src/tree/tree.component.ts
index df9d16a34e5..cde10620186 100644
--- a/projects/igniteui-angular/tree/src/tree/tree.component.ts
+++ b/projects/igniteui-angular/tree/src/tree/tree.component.ts
@@ -1,4 +1,4 @@
-import { Component, QueryList, Input, Output, EventEmitter, ContentChild, Directive, TemplateRef, OnInit, AfterViewInit, ContentChildren, OnDestroy, HostBinding, ElementRef, booleanAttribute, inject, ChangeDetectionStrategy } from '@angular/core';
+import { Component, QueryList, Input, Output, EventEmitter, ContentChild, Directive, TemplateRef, OnInit, AfterViewInit, ContentChildren, OnDestroy, HostBinding, ElementRef, booleanAttribute, inject, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, throttleTime } from 'rxjs/operators';
@@ -84,6 +84,7 @@ export class IgxTreeComponent implements IgxTree, OnInit, AfterViewInit, OnDestr
private treeService = inject(IgxTreeService);
private element = inject>(ElementRef);
private platform = inject(PlatformUtil);
+ private cdr = inject(ChangeDetectorRef, { optional: true });
@HostBinding('class.igx-tree')
@@ -487,9 +488,10 @@ export class IgxTreeComponent implements IgxTree, OnInit, AfterViewInit, OnDestr
private subToChanges() {
this.unsubChildren$.next();
const toBeSelected = [...this.forceSelect];
- if(this.platform.isBrowser) {
+ if (this.platform.isBrowser) {
requestAnimationFrame(() => {
this.selectionService.selectNodesWithNoEvent(toBeSelected);
+ this.cdr?.markForCheck();
});
}
this.forceSelect = [];