From 20e9aa6304fdf20ae17cca34d6f74f7c0a4d83b7 Mon Sep 17 00:00:00 2001 From: skrustev Date: Fri, 20 Jun 2025 19:02:20 +0300 Subject: [PATCH 1/2] fix(elements): Add check for elements templates in case container is detached. --- .../src/app/wrapper/wrapper.component.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 dc4fde75841..5b4e3f98580 100644 --- a/projects/igniteui-angular-elements/src/app/wrapper/wrapper.component.ts +++ b/projects/igniteui-angular-elements/src/app/wrapper/wrapper.component.ts @@ -5,6 +5,7 @@ import { TemplateRefWrapper } from './template-ref-wrapper'; import { render, type RootPart, type TemplateResult } from 'lit-html'; type TemplateFunction = (arg: any) => TemplateResult; +const SCHEDULE_DELAY = 10; @Component({ selector: 'igx-template-wrapper', @@ -18,6 +19,7 @@ export class TemplateWrapperComponent { public templateRendered = new Subject(); private childParts: WeakMap = new WeakMap(); + private timeoutId: NodeJS.Timeout | string | number | undefined; /** * All template refs @@ -30,6 +32,15 @@ export class TemplateWrapperComponent { constructor(private cdr: ChangeDetectorRef) { } protected litRender(container: HTMLElement, templateFunc: (arg: any) => TemplateResult, arg: any) { + if (!container.isConnected) { + // Wait a bit if it gets attached back, otherwise do nothing + this.timeoutId = setTimeout(() =>{ + if (container.isConnected) { + this.litRender(container, templateFunc, arg); + } + }, SCHEDULE_DELAY); + return; + } const part = render(templateFunc(arg), container); let existingPart = this.childParts.get(container); @@ -71,5 +82,6 @@ export class TemplateWrapperComponent { this.childParts.get(container).setConnected(false); this.childParts.delete(container); } + clearTimeout(this.timeoutId); } } From 91ede5c6f48f41243428971ce0f0358d74970c1d Mon Sep 17 00:00:00 2001 From: skrustev Date: Fri, 20 Jun 2025 19:55:29 +0300 Subject: [PATCH 2/2] fix(elements): Add test component and make sure templates are rendered for tests. --- .../src/app/wrapper/wrapper.component.spec.ts | 30 ++++++++++++++----- .../tsconfig.spec.json | 3 +- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular-elements/src/app/wrapper/wrapper.component.spec.ts b/projects/igniteui-angular-elements/src/app/wrapper/wrapper.component.spec.ts index dba92e93d40..12d9257360c 100644 --- a/projects/igniteui-angular-elements/src/app/wrapper/wrapper.component.spec.ts +++ b/projects/igniteui-angular-elements/src/app/wrapper/wrapper.component.spec.ts @@ -1,3 +1,4 @@ +import { Component, ViewChild, ViewContainerRef } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { html } from 'lit-html'; import { AsyncDirective, directive } from 'lit/async-directive.js'; @@ -13,22 +14,33 @@ class ToLowerAsyncDirective extends AsyncDirective { protected override disconnected(): void { directiveLog.push('disconnected'); } - } - export const toLowerAsync = directive(ToLowerAsyncDirective); +} +export const toLowerAsync = directive(ToLowerAsyncDirective); + +@Component({ + template: ``, + imports: [TemplateWrapperComponent] +}) +class TestTemplateWrapperComponent { + @ViewChild(TemplateWrapperComponent) + public templateWrapper; + + constructor(public viewContainerRef: ViewContainerRef) { } +} describe('WrapperComponent', () => { - let component: TemplateWrapperComponent; - let fixture: ComponentFixture; + let component: TestTemplateWrapperComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [TemplateWrapperComponent] + imports: [TestTemplateWrapperComponent] }) .compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(TemplateWrapperComponent); + fixture = TestBed.createComponent(TestTemplateWrapperComponent); component = fixture.componentInstance; fixture.detectChanges(); }); @@ -39,8 +51,9 @@ describe('WrapperComponent', () => { it('should render template', () => { const context = { text: "Oh hi" }; - const templateRef = component.addTemplate((ctx) => html`${ctx.text}`); + const templateRef = component.templateWrapper.addTemplate((ctx) => html`${ctx.text}`); const embeddedView = templateRef.createEmbeddedView(context); + component.viewContainerRef.insert(embeddedView); embeddedView.detectChanges(); const span = embeddedView.rootNodes[0].querySelector("#template1"); @@ -51,8 +64,9 @@ describe('WrapperComponent', () => { it('should update connectivity on template with AsyncDirective', () => { const context = { text: "OH HI" }; - const templateRef = component.addTemplate((ctx) => html`${toLowerAsync(ctx.text)}`); + const templateRef = component.templateWrapper.addTemplate((ctx) => html`${toLowerAsync(ctx.text)}`); const embeddedView = templateRef.createEmbeddedView(context); + component.viewContainerRef.insert(embeddedView); embeddedView.detectChanges(); const span = embeddedView.rootNodes[0].querySelector("#template1"); diff --git a/projects/igniteui-angular-elements/tsconfig.spec.json b/projects/igniteui-angular-elements/tsconfig.spec.json index 70dea48b858..d5c90d9aa8b 100644 --- a/projects/igniteui-angular-elements/tsconfig.spec.json +++ b/projects/igniteui-angular-elements/tsconfig.spec.json @@ -5,7 +5,8 @@ "allowJs": true, "outDir": "../../out-tsc/spec", "types": [ - "jasmine" + "jasmine", + "node" ] }, "files": [