From 560f0345359639f02505e85277754a8bf3bcc7fa Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 9 Jan 2025 18:11:39 +0200 Subject: [PATCH 1/4] feat(elements-grid): Add content children ready event. --- .../src/app/custom-strategy.ts | 26 +++++++++++++++++++ .../igniteui-angular-elements/src/index.html | 9 ++++--- .../src/lib/grids/grid-base.directive.ts | 10 ++++++- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/projects/igniteui-angular-elements/src/app/custom-strategy.ts b/projects/igniteui-angular-elements/src/app/custom-strategy.ts index a114035ff5b..8e94feaa11c 100644 --- a/projects/igniteui-angular-elements/src/app/custom-strategy.ts +++ b/projects/igniteui-angular-elements/src/app/custom-strategy.ts @@ -66,9 +66,22 @@ class IgxCustomNgElementStrategy extends ComponentNgElementStrategy { (this as any).componentRef = {}; const toBeOrphanedChildren = Array.from(element.children).filter(x => !this._componentFactory.ngContentSelectors.some(sel => x.matches(sel))); + const contentChildren = Array.from(element.children).filter(x => this._componentFactory.ngContentSelectors.some(sel => x.matches(sel))); for (const iterator of toBeOrphanedChildren) { // TODO: special registration OR config for custom } + + const promises = []; + for (const contentChild of contentChildren) { + // these resolve after the parent, both the components needs to be initialized and the parent query schedule should complete before it is really ready. + const child = contentChild as IgcNgElement; + const promiseComponentReady = this.waitForCondition(() => (child.ngElementStrategy as any)?.componentRef && this.schedule.size == 0); + promises.push(promiseComponentReady); + } + Promise.all(promises).then(() => { + (this as any).componentRef?.instance.contentChildrenReady.emit(); + }); + let parentInjector: Injector; let parentAnchor: ViewContainerRef; const parents: IgcNgElement[] = []; @@ -211,6 +224,19 @@ class IgxCustomNgElementStrategy extends ComponentNgElementStrategy { } } + public waitForCondition(conditionFn: any, interval = 10) { + return new Promise((resolve) => { + function checkCondition() { + if (conditionFn()) { + resolve(); + } else { + setTimeout(checkCondition, interval); + } + } + checkCondition(); + }); + } + public override setInputValue(property: string, value: any, transform?: (value: any) => any): void { if ((this as any).componentRef === null || !(this as any).componentRef.instance) { diff --git a/projects/igniteui-angular-elements/src/index.html b/projects/igniteui-angular-elements/src/index.html index c1db7ce6a44..00accdabf2c 100644 --- a/projects/igniteui-angular-elements/src/index.html +++ b/projects/igniteui-angular-elements/src/index.html @@ -131,6 +131,10 @@

Flat Grid (MRL column layout)

grid1.data = northwindProducts; grid2.data = northwindProducts; + grid1.addEventListener('contentChildrenReady', () => { + console.log("Ready!"); + restoreState(); + }); const categories = Array.from(new Set(northwindProducts.map(x => x.CategoryName))); let groupingExpression1 = []; @@ -206,10 +210,7 @@

Flat Grid (MRL column layout)

document.getElementById("saveState").addEventListener("click", saveState); document.getElementById("restoreState").addEventListener("click", restoreState); document.getElementById("toggleIcon").addEventListener("click", toggleIcon); - const stateComponent = document.getElementById('state'); - stateComponent.options = { - paging: false - }; + }); buttonGroup.addEventListener("igcSelect", (e) => { diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index f766a5d6b29..b58ad0fa349 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -1000,6 +1000,14 @@ export abstract class IgxGridBaseDirective implements GridType, @Output() public selectedRowsChange = new EventEmitter(); + /* blazorInclude */ + /** @hidden @internal */ + /** + * Emitted when content children are attached and collections in grid are updated. + */ + @Output() + public contentChildrenReady = new EventEmitter(); + /** * Emitted when the expanded state of a row gets changed. * @@ -4930,7 +4938,7 @@ export abstract class IgxGridBaseDirective implements GridType, * @param value * @param condition * @param ignoreCase - * @deprecated in version 19.0.0. + * @deprecated in version 19.0.0. */ public filterGlobal(value: any, condition, ignoreCase?) { this.filteringService.filterGlobal(value, condition, ignoreCase); From 13053beaa54c39225d944c55b13bc367f25e5798 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 14 Jan 2025 14:48:24 +0200 Subject: [PATCH 2/4] chore(*): Apply review comments. --- .../src/app/custom-strategy.ts | 29 +++++-------------- .../igniteui-angular-elements/src/index.html | 2 +- .../src/lib/grids/grid-base.directive.ts | 2 +- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/projects/igniteui-angular-elements/src/app/custom-strategy.ts b/projects/igniteui-angular-elements/src/app/custom-strategy.ts index 8e94feaa11c..f78e69cf08c 100644 --- a/projects/igniteui-angular-elements/src/app/custom-strategy.ts +++ b/projects/igniteui-angular-elements/src/app/custom-strategy.ts @@ -71,16 +71,10 @@ class IgxCustomNgElementStrategy extends ComponentNgElementStrategy { // TODO: special registration OR config for custom } - const promises = []; - for (const contentChild of contentChildren) { - // these resolve after the parent, both the components needs to be initialized and the parent query schedule should complete before it is really ready. - const child = contentChild as IgcNgElement; - const promiseComponentReady = this.waitForCondition(() => (child.ngElementStrategy as any)?.componentRef && this.schedule.size == 0); - promises.push(promiseComponentReady); + if (contentChildren.length === 0) { + // no content children, emit event immediately, since there's nothing to be attached. + (this as any).componentRef?.instance?.childrenAttached?.emit(); } - Promise.all(promises).then(() => { - (this as any).componentRef?.instance.contentChildrenReady.emit(); - }); let parentInjector: Injector; let parentAnchor: ViewContainerRef; @@ -224,19 +218,6 @@ class IgxCustomNgElementStrategy extends ComponentNgElementStrategy { } } - public waitForCondition(conditionFn: any, interval = 10) { - return new Promise((resolve) => { - function checkCondition() { - if (conditionFn()) { - resolve(); - } else { - setTimeout(checkCondition, interval); - } - } - checkCondition(); - }); - } - public override setInputValue(property: string, value: any, transform?: (value: any) => any): void { if ((this as any).componentRef === null || !(this as any).componentRef.instance) { @@ -333,6 +314,10 @@ class IgxCustomNgElementStrategy extends ComponentNgElementStrategy { list.reset(childRefs); list.notifyOnChanges(); } + if (this.schedule.size === 0) { + // children are attached and collections are updated, emit event. + (this as any).componentRef?.instance?.childrenAttached?.emit(); + } } private runQueryInDOM(element: HTMLElement, query: ContentQueryMeta): IgcNgElement[] { diff --git a/projects/igniteui-angular-elements/src/index.html b/projects/igniteui-angular-elements/src/index.html index 00accdabf2c..7694cc4b688 100644 --- a/projects/igniteui-angular-elements/src/index.html +++ b/projects/igniteui-angular-elements/src/index.html @@ -131,7 +131,7 @@

Flat Grid (MRL column layout)

grid1.data = northwindProducts; grid2.data = northwindProducts; - grid1.addEventListener('contentChildrenReady', () => { + grid1.addEventListener('childrenAttached', () => { console.log("Ready!"); restoreState(); }); diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index b58ad0fa349..d750d119048 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -1006,7 +1006,7 @@ export abstract class IgxGridBaseDirective implements GridType, * Emitted when content children are attached and collections in grid are updated. */ @Output() - public contentChildrenReady = new EventEmitter(); + public childrenAttached = new EventEmitter(); /** * Emitted when the expanded state of a row gets changed. From 6516dfe4eb075339f500038e991302e32bf9c61f Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 15 Jan 2025 14:30:43 +0200 Subject: [PATCH 3/4] chore(*): Add condition based on content children with rel. query collection. --- .../src/app/custom-strategy.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/projects/igniteui-angular-elements/src/app/custom-strategy.ts b/projects/igniteui-angular-elements/src/app/custom-strategy.ts index f78e69cf08c..2d2e2ee4d26 100644 --- a/projects/igniteui-angular-elements/src/app/custom-strategy.ts +++ b/projects/igniteui-angular-elements/src/app/custom-strategy.ts @@ -65,16 +65,12 @@ class IgxCustomNgElementStrategy extends ComponentNgElementStrategy { // TODO: Fail handling or cancellation needed? (this as any).componentRef = {}; + const contentChildrenTags = Array.from(element.children).filter(x => this._componentFactory.ngContentSelectors.some(sel => x.matches(sel))).map(x => x.tagName.toLocaleLowerCase()); const toBeOrphanedChildren = Array.from(element.children).filter(x => !this._componentFactory.ngContentSelectors.some(sel => x.matches(sel))); - const contentChildren = Array.from(element.children).filter(x => this._componentFactory.ngContentSelectors.some(sel => x.matches(sel))); for (const iterator of toBeOrphanedChildren) { // TODO: special registration OR config for custom } - if (contentChildren.length === 0) { - // no content children, emit event immediately, since there's nothing to be attached. - (this as any).componentRef?.instance?.childrenAttached?.emit(); - } let parentInjector: Injector; let parentAnchor: ViewContainerRef; @@ -157,6 +153,15 @@ class IgxCustomNgElementStrategy extends ComponentNgElementStrategy { this.detectChanges(); + // check if there are any content children associated with a content query collection. + // if no, then just emit the event, otherwise we wait for the collection to be updated in updateQuery. + const contentChildrenTypes = this.config.filter(x => contentChildrenTags.indexOf(x.selector) !== -1).map(x => x.provideAs ?? x.component); + const contentQueryChildrenCollection = componentConfig.contentQueries.filter(x => contentChildrenTypes.includes(x.childType)); + if (contentQueryChildrenCollection.length === 0) { + // no content children, emit event immediately, since there's nothing to be attached. + (this as any).componentRef?.instance?.childrenAttached?.emit(); + } + if (parentAnchor && parentInjector) { // attempt to attach the newly created ViewRef to the parents's instead of the App global const parentViewRef = parentInjector.get(ViewContainerRef); From 8b5559bf4260c9b2c3f1fcd8db5fa2bbbfc59c6b Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 15 Jan 2025 14:57:40 +0200 Subject: [PATCH 4/4] chore(*): Add scenario to test hgrid as well. --- .../igniteui-angular-elements/src/index.html | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular-elements/src/index.html b/projects/igniteui-angular-elements/src/index.html index 7694cc4b688..3ead52880be 100644 --- a/projects/igniteui-angular-elements/src/index.html +++ b/projects/igniteui-angular-elements/src/index.html @@ -287,7 +287,7 @@

Flat Grid (MRL column layout)

- + @@ -315,7 +315,12 @@

Flat Grid (MRL column layout)

+
+
+ + +
@@ -417,9 +422,27 @@

Flat Grid (MRL column layout)

import { html } from "/lit-html.js"; // jump around vite import analysis with dynamic import url const hgridData = (await import(`/assets/${'data'}/projects-hgrid.js`)).default; + function saveStateHierarchical() { + const stateComponent = document.getElementById('hgridState'); + const stringData = stateComponent.getStateAsString(); + window.localStorage.setItem(`hgrid1-state`, stringData); + } + + function restoreStateHierarchical() { + const stateComponent = document.getElementById('hgridState'); + const stateData = window.localStorage.getItem(`hgrid1-state`); + if (stateData) { + const obj = JSON.parse(stateData); + stateComponent.applyState(obj); + } + } let hgrid = document.getElementById('hgrid1'); + hgrid.addEventListener('childrenAttached', () => { + restoreStateHierarchical(); + }); hgrid.data = hgridData; - + document.getElementById("saveStateHierarchical").addEventListener("click", saveStateHierarchical); + document.getElementById("restoreStateHierarchical").addEventListener("click", restoreStateHierarchical); let developersRowIsland = document.getElementById('DevelopersRowIsland'); let virtualMachinesRowIsland = document.getElementById('VirtualMachinesRowIsland'); virtualMachinesRowIsland.paginatorTemplate = (ctx) => html`