Skip to content

Commit

Permalink
fix(angular-table): View is not updated anymore when flexRenderDirect…
Browse files Browse the repository at this point in the history
…ive is instantiated the first time with an empty value (#5626)

* fix: update view when content type change
  • Loading branch information
riccardoperra committed Jun 29, 2024
1 parent 78b9012 commit bfa96d8
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 30 deletions.
35 changes: 16 additions & 19 deletions packages/angular-table/src/flex-render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<TProps extends NonNullable<unknown>> =
Expand All @@ -30,13 +30,14 @@ export type FlexRenderContent<TProps extends NonNullable<unknown>> =
standalone: true,
})
export class FlexRenderDirective<TProps extends NonNullable<unknown>>
implements OnInit, DoCheck
implements OnChanges
{
@Input({ required: true, alias: 'flexRender' })
content:
| number
| string
| ((props: TProps) => FlexRenderContent<TProps>)
| null
| undefined = undefined

@Input({ required: true, alias: 'flexRenderProps' })
Expand All @@ -54,32 +55,28 @@ export class FlexRenderDirective<TProps extends NonNullable<unknown>>

ref?: ComponentRef<unknown> | EmbeddedViewRef<unknown> | 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<TProps>) {
Expand Down
29 changes: 18 additions & 11 deletions packages/angular-table/tests/flex-render.test.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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',
Expand All @@ -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', () => {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -172,6 +178,7 @@ function expectPrimitiveValueIs(
fixture: ComponentFixture<unknown>,
value: unknown
) {
expect(fixture.nativeElement.matches(':empty')).toBe(false)
const span = fixture.nativeElement.querySelector('span')
expect(span).toBeDefined()
expect(span.innerHTML).toEqual(value)
Expand Down

0 comments on commit bfa96d8

Please sign in to comment.