diff --git a/projects/igniteui-angular-elements/src/app/custom-strategy.spec.ts b/projects/igniteui-angular-elements/src/app/custom-strategy.spec.ts
index e577e37b21c..6473aa6bcaa 100644
--- a/projects/igniteui-angular-elements/src/app/custom-strategy.spec.ts
+++ b/projects/igniteui-angular-elements/src/app/custom-strategy.spec.ts
@@ -1,4 +1,5 @@
import { IgxColumnComponent, IgxGridComponent, IgxHierarchicalGridComponent } from 'igniteui-angular';
+import { html } from 'lit-html';
import { firstValueFrom, fromEvent, skip, timer } from 'rxjs';
import { ComponentRefKey, IgcNgElement } from './custom-strategy';
import hgridData from '../assets/data/projects-hgrid.js';
@@ -52,6 +53,43 @@ describe('Elements: ', () => {
const columnComponent = (await columnEl.ngElementStrategy[ComponentRefKey]).instance as IgxColumnComponent;
expect(gridComponent.columnList.toArray()).toContain(columnComponent);
});
+
+ it(`should keep IgcNgElement instance in template of another IgcNgElement #15678`, async () => {
+ const gridEl = document.createElement("igc-grid");
+ testContainer.appendChild(gridEl);
+ const columnEl = document.createElement("igc-column") as IgcNgElement;
+ gridEl.appendChild(columnEl);
+ gridEl.primaryKey = 'id';
+ gridEl.data = [{ id: '1' }];
+ (gridEl as any).detailTemplate = (ctx) => {
+ return html`
+
+
`;
+ }
+
+ // TODO: Better way to wait - potentially expose the queue or observable for update on the strategy
+ await firstValueFrom(timer(10 /* SCHEDULE_DELAY */ * 2));
+
+ // sigh (ãīšã*)
+ (gridEl as any).toggleRow('1');
+ await firstValueFrom(timer(10 /* SCHEDULE_DELAY */ * 2));
+
+ let detailGrid = document.querySelector('#child1');
+ expect(detailGrid).toBeDefined();
+ let detailGridComponent = (await detailGrid?.ngElementStrategy[ComponentRefKey])?.instance as IgxGridComponent;
+ expect(detailGridComponent).toBeDefined();
+
+ // close and re-expand row detail:
+ (gridEl as any).toggleRow('1');
+ await firstValueFrom(timer(10 /* SCHEDULE_DELAY */ * 2));
+ (gridEl as any).toggleRow('1');
+ await firstValueFrom(timer(10 /* SCHEDULE_DELAY */ * 2));
+
+ detailGrid = document.querySelector('#child1');
+ expect(detailGrid).toBeDefined();
+ detailGridComponent = (await detailGrid?.ngElementStrategy[ComponentRefKey])?.instance as IgxGridComponent;
+ expect(detailGridComponent).toBeDefined("Detail child grid was destroyed on re-expand");
+ });
});
describe('Grid integration scenarios.', () => {
diff --git a/projects/igniteui-angular-elements/src/app/custom-strategy.ts b/projects/igniteui-angular-elements/src/app/custom-strategy.ts
index a114035ff5b..d8cfcb7ae2b 100644
--- a/projects/igniteui-angular-elements/src/app/custom-strategy.ts
+++ b/projects/igniteui-angular-elements/src/app/custom-strategy.ts
@@ -1,4 +1,5 @@
-import { ApplicationRef, ChangeDetectorRef, ComponentFactory, ComponentRef, Injector, OnChanges, QueryList, Type, ViewContainerRef, reflectComponentType } from '@angular/core';
+import { ApplicationRef, ChangeDetectorRef, ComponentFactory, ComponentRef, DestroyRef, Injector, OnChanges, QueryList, Type, ViewContainerRef, reflectComponentType } from '@angular/core';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { NgElement } from '@angular/elements';
import { fromEvent } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@@ -48,6 +49,14 @@ class IgxCustomNgElementStrategy extends ComponentNgElementStrategy {
return this._templateWrapper;
}
+ private _configSelectors: string;
+ public get configSelectors(): string {
+ if (!this._configSelectors) {
+ this._configSelectors = this.config.map(x => x.selector).join(',');
+ }
+ return this._configSelectors;
+ }
+
constructor(private _componentFactory: ComponentFactory, private _injector: Injector, private config: ComponentConfig[]) {
super(_componentFactory, _injector);
}
@@ -233,6 +242,14 @@ class IgxCustomNgElementStrategy extends ComponentNgElementStrategy {
}
value = this.templateWrapper.addTemplate(value);
// TODO: discard oldValue
+
+ // check template for any angular-element components
+ this.templateWrapper.templateRendered.pipe(takeUntilDestroyed(componentRef.injector.get(DestroyRef))).subscribe((element) => {
+ element.querySelectorAll(this.configSelectors)?.forEach((c) => {
+ // tie to angularParent lifecycle for cached scenarios like detailTemplate:
+ c.ngElementStrategy.angularParent = componentRef;
+ });
+ });
}
if (componentRef && componentConfig?.boolProps?.includes(property)) {
// bool coerce:
diff --git a/projects/igniteui-angular-elements/src/app/wrapper/wrapper.component.ts b/projects/igniteui-angular-elements/src/app/wrapper/wrapper.component.ts
index 35ba746cd94..5e782714f5f 100644
--- a/projects/igniteui-angular-elements/src/app/wrapper/wrapper.component.ts
+++ b/projects/igniteui-angular-elements/src/app/wrapper/wrapper.component.ts
@@ -1,4 +1,5 @@
import { ChangeDetectorRef, Component, QueryList, TemplateRef, ViewChildren } from '@angular/core';
+import { Subject } from 'rxjs';
import { TemplateRefWrapper } from './template-ref-wrapper';
import { render, TemplateResult } from 'lit-html';
@@ -14,6 +15,7 @@ type TemplateFunction = (arg: any) => TemplateResult;
export class TemplateWrapperComponent {
public templateFunctions: TemplateFunction[] = [];
+ public templateRendered = new Subject();
/**
* All template refs
@@ -27,6 +29,7 @@ export class TemplateWrapperComponent {
public litRender(container: HTMLElement, templateFunc: (arg: any) => TemplateResult, arg: any) {
render(templateFunc(arg), container);
+ this.templateRendered.next(container);
}
public addTemplate(templateFunc: TemplateFunction): TemplateRef {
diff --git a/projects/igniteui-angular-elements/src/index.html b/projects/igniteui-angular-elements/src/index.html
index c1db7ce6a44..783773b1434 100644
--- a/projects/igniteui-angular-elements/src/index.html
+++ b/projects/igniteui-angular-elements/src/index.html
@@ -195,7 +195,11 @@ Flat Grid (MRL column layout)
ctx.cell.editValue = e.detail.newValue} single-select>
`;
- grid1.detailTemplate = (ctx) => html`Stock: ${ctx.implicit.InStock}
`;
+ grid1.detailTemplate = (ctx) => {
+ return html`
+
+
`;
+ }
grid2.querySelector('igc-column[field="ProductName"]').inlineEditorTemplate = (ctx) =>
html`