From bfa96d88b309303e9b9442441c4a89ac88977078 Mon Sep 17 00:00:00 2001 From: Riccardo Perra Date: Sun, 30 Jun 2024 01:32:27 +0200 Subject: [PATCH] fix(angular-table): View is not updated anymore when flexRenderDirective is instantiated the first time with an empty value (#5626) * fix: update view when content type change --- packages/angular-table/src/flex-render.ts | 35 +++++++++---------- .../angular-table/tests/flex-render.test.ts | 29 +++++++++------ 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/packages/angular-table/src/flex-render.ts b/packages/angular-table/src/flex-render.ts index be1d114dd1..86f97ae6cb 100644 --- a/packages/angular-table/src/flex-render.ts +++ b/packages/angular-table/src/flex-render.ts @@ -4,16 +4,16 @@ import { Directive, EmbeddedViewRef, Inject, + inject, InjectionToken, Injector, Input, + isSignal, + type OnChanges, + type SimpleChanges, TemplateRef, Type, ViewContainerRef, - inject, - isSignal, - type DoCheck, - type OnInit, } from '@angular/core' export type FlexRenderContent> = @@ -30,13 +30,14 @@ export type FlexRenderContent> = standalone: true, }) export class FlexRenderDirective> - implements OnInit, DoCheck + implements OnChanges { @Input({ required: true, alias: 'flexRender' }) content: | number | string | ((props: TProps) => FlexRenderContent) + | null | undefined = undefined @Input({ required: true, alias: 'flexRenderProps' }) @@ -54,32 +55,28 @@ export class FlexRenderDirective> ref?: ComponentRef | EmbeddedViewRef | null = null - ngOnInit(): void { - this.ref = this.render() - } - - ngDoCheck() { + ngOnChanges(changes: SimpleChanges) { if (this.ref instanceof ComponentRef) { this.ref.injector.get(ChangeDetectorRef).markForCheck() - } else if (this.ref instanceof EmbeddedViewRef) { - this.ref.markForCheck() } + if (!changes['content']) { + return + } + this.render() } render() { this.viewContainerRef.clear() const { content, props } = this - if (!this.content) { - return null - } - - if (typeof content === 'string' || typeof content === 'number') { - return this.renderStringContent() + if (content === null || content === undefined) { + this.ref = null + return } if (typeof content === 'function') { return this.renderContent(content(props)) + } else { + return this.renderContent(content) } - return null } private renderContent(content: FlexRenderContent) { diff --git a/packages/angular-table/tests/flex-render.test.ts b/packages/angular-table/tests/flex-render.test.ts index ee5336690c..2cb4ecde82 100644 --- a/packages/angular-table/tests/flex-render.test.ts +++ b/packages/angular-table/tests/flex-render.test.ts @@ -1,7 +1,6 @@ import { Component, ViewChild, input, type TemplateRef } from '@angular/core' import { TestBed, type ComponentFixture } from '@angular/core/testing' import { createColumnHelper } from '@tanstack/table-core' -import { skip } from 'node:test' import { describe, expect, test } from 'vitest' import { FlexRenderComponent, @@ -24,6 +23,20 @@ describe('FlexRenderDirective', () => { test('should render primitives', async () => { const fixture = TestBed.createComponent(TestRenderComponent) + // Null + setFixtureSignalInputs(fixture, { + content: () => null, + context: {}, + }) + expect((fixture.nativeElement as HTMLElement).matches(':empty')).toBe(true) + + // Undefined + setFixtureSignalInputs(fixture, { + content: () => undefined, + context: {}, + }) + expect((fixture.nativeElement as HTMLElement).matches(':empty')).toBe(true) + // String setFixtureSignalInputs(fixture, { content: 'My value', @@ -45,19 +58,12 @@ describe('FlexRenderDirective', () => { }) expectPrimitiveValueIs(fixture, 'My value 2') - // Null + // Set again to null to be sure content has been destroyed setFixtureSignalInputs(fixture, { content: () => null, context: {}, }) - expectPrimitiveValueIs(fixture, '') - - // Undefined - setFixtureSignalInputs(fixture, { - content: () => undefined, - context: {}, - }) - expectPrimitiveValueIs(fixture, '') + expect((fixture.nativeElement as HTMLElement).matches(':empty')).toBe(true) }) test('should render TemplateRef', () => { @@ -118,7 +124,7 @@ describe('FlexRenderDirective', () => { // Skip for now, test framework (using ComponentRef.setInput) cannot recognize signal inputs // as component inputs - skip('should render custom components', () => { + test.skip('should render custom components', () => { @Component({ template: `{{ row().property }}`, standalone: true, @@ -172,6 +178,7 @@ function expectPrimitiveValueIs( fixture: ComponentFixture, value: unknown ) { + expect(fixture.nativeElement.matches(':empty')).toBe(false) const span = fixture.nativeElement.querySelector('span') expect(span).toBeDefined() expect(span.innerHTML).toEqual(value)