From 6b0287d33373261db1833d84dcf25abbfe33906e Mon Sep 17 00:00:00 2001 From: Nicolas Frizzarin Date: Tue, 7 May 2024 16:05:42 +0200 Subject: [PATCH] feat: migrate on signals component --- .../components/header/header.component.html | 2 +- .../components/header/header.component.ts | 4 +- .../app/feature/home/home.component.spec.ts | 8 +-- .../search/search.component.spec.ts | 19 ++++--- .../components/search/search.component.ts | 24 ++++----- .../feature/people/people.component.spec.ts | 8 +-- .../update-person.component.spec.ts | 4 +- .../components/card/card.component.spec.ts | 14 +++-- .../shared/components/card/card.component.ts | 11 ++-- .../custom-input/custom-input.component.html | 4 +- .../custom-input/custom-input.component.ts | 52 ++++++++++--------- .../components/form/form.component.spec.ts | 8 ++- .../shared/components/form/form.component.ts | 20 +++---- .../shared/directives/badge.directive.spec.ts | 6 +-- .../app/shared/directives/badge.directive.ts | 13 ++--- .../directives/display.directive.spec.ts | 13 ++--- .../shared/directives/display.directive.ts | 10 ++-- 17 files changed, 115 insertions(+), 105 deletions(-) diff --git a/apps/34-signal-solution/src/app/core/components/header/header.component.html b/apps/34-signal-solution/src/app/core/components/header/header.component.html index 5e9dc016..65bbbb35 100644 --- a/apps/34-signal-solution/src/app/core/components/header/header.component.html +++ b/apps/34-signal-solution/src/app/core/components/header/header.component.html @@ -1,3 +1,3 @@
- +
diff --git a/apps/34-signal-solution/src/app/core/components/header/header.component.ts b/apps/34-signal-solution/src/app/core/components/header/header.component.ts index cada541e..59ddae46 100644 --- a/apps/34-signal-solution/src/app/core/components/header/header.component.ts +++ b/apps/34-signal-solution/src/app/core/components/header/header.component.ts @@ -1,5 +1,5 @@ import { NgTemplateOutlet } from '@angular/common'; -import { Component, Input, TemplateRef } from '@angular/core'; +import { Component, input, TemplateRef } from '@angular/core'; import { MatToolbar } from '@angular/material/toolbar'; @Component({ @@ -10,5 +10,5 @@ import { MatToolbar } from '@angular/material/toolbar'; imports: [NgTemplateOutlet], }) export class HeaderComponent { - @Input() headerTemplate: TemplateRef; + headerTemplate = input.required>(); } diff --git a/apps/34-signal-solution/src/app/feature/home/home.component.spec.ts b/apps/34-signal-solution/src/app/feature/home/home.component.spec.ts index d3caaee5..eb82f6ca 100644 --- a/apps/34-signal-solution/src/app/feature/home/home.component.spec.ts +++ b/apps/34-signal-solution/src/app/feature/home/home.component.spec.ts @@ -1,5 +1,5 @@ import { CommonModule } from '@angular/common'; -import { Component, DebugElement, Input } from '@angular/core'; +import { Component, DebugElement, input } from '@angular/core'; import { ComponentFixture, fakeAsync } from '@angular/core/testing'; import { MatButtonModule } from '@angular/material/button'; import { By } from '@angular/platform-browser'; @@ -16,7 +16,7 @@ import { HomeComponent } from './home.component'; standalone: true, }) class MockCardComponent { - @Input() person: People; + person = input(); } const PEOPLE_SERVICE = { @@ -57,7 +57,7 @@ describe('HomeComponent', () => { }); test('should pass the input person', () => { const sfeirCard: CardComponent = debugElement.query(By.css('sfeir-card')).componentInstance; - expect(sfeirCard.person).toEqual(PERSON); + expect(sfeirCard.person()).toEqual(PERSON); }); test('should call the getRandomPerson', () => { const spy = jest.spyOn(component, 'getRandomPerson'); @@ -80,6 +80,6 @@ describe('HomeComponent', () => { await componentFixture.whenStable(); componentFixture.detectChanges(); const sfeirCard: CardComponent = debugElement.query(By.css('sfeir-card')).componentInstance; - expect(sfeirCard.person).toEqual(RANDOM_PERSON); + expect(sfeirCard.person()).toEqual(RANDOM_PERSON); })); }); diff --git a/apps/34-signal-solution/src/app/feature/people/components/search/search.component.spec.ts b/apps/34-signal-solution/src/app/feature/people/components/search/search.component.spec.ts index 7f6fc68c..e0bd0c79 100644 --- a/apps/34-signal-solution/src/app/feature/people/components/search/search.component.spec.ts +++ b/apps/34-signal-solution/src/app/feature/people/components/search/search.component.spec.ts @@ -1,8 +1,7 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { fireEvent, render } from '@testing-library/angular'; import { SearchComponent } from './search.component'; - -const SEARCH_SPY = jest.fn(); +import { fakeAsync, flush } from '@angular/core/testing'; describe('SearchComponent', () => { let container: Element; @@ -16,11 +15,8 @@ describe('SearchComponent', () => { rerender: reload, } = await render(SearchComponent, { schemas: [NO_ERRORS_SCHEMA], - componentProperties: { + componentInputs: { searchText: 'SFEIR', - search: { - emit: SEARCH_SPY, - } as any, }, }); container = hostContainer; @@ -37,12 +33,15 @@ describe('SearchComponent', () => { }); test('should refresh the search control', async () => { const spy = jest.spyOn(component.searchControl, 'patchValue'); - await rerender({ componentProperties: { searchText: 'sfeir' } }); + await rerender({ componentInputs: { searchText: 'sfeir' } }); expect(spy).toHaveBeenCalledWith('sfeir', { emitEvent: false }); }); - test('should emit the search event', () => { + test('should emit the search event', fakeAsync(() => { const element = container.querySelector('input[placeholder="Person Searcher"]'); fireEvent.input(element, { target: { value: 'test' } }); - expect(SEARCH_SPY).toHaveBeenCalledWith('test'); - }); + flush(); + component.search.subscribe(input => { + expect(input).toBe('test'); + }); + })); }); diff --git a/apps/34-signal-solution/src/app/feature/people/components/search/search.component.ts b/apps/34-signal-solution/src/app/feature/people/components/search/search.component.ts index 1e6b16ea..ad85e61b 100644 --- a/apps/34-signal-solution/src/app/feature/people/components/search/search.component.ts +++ b/apps/34-signal-solution/src/app/feature/people/components/search/search.component.ts @@ -1,7 +1,7 @@ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; +import { Component, effect, input } from '@angular/core'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; -import { Subject, takeUntil } from 'rxjs'; import { MatInputModule } from '@angular/material/input'; +import { outputFromObservable } from '@angular/core/rxjs-interop'; @Component({ selector: 'sfeir-search', @@ -10,19 +10,13 @@ import { MatInputModule } from '@angular/material/input'; standalone: true, imports: [MatInputModule, ReactiveFormsModule], }) -export class SearchComponent implements OnChanges, OnInit { - @Input() searchText: string; - @Output() search: EventEmitter = new EventEmitter(); - searchControl: FormControl; - private unsubscribe$: Subject = new Subject(); +export class SearchComponent { + searchControl: FormControl = new FormControl(null); - ngOnChanges() { - this.searchControl - ? this.searchControl.patchValue(this.searchText, { emitEvent: false }) - : (this.searchControl = new FormControl(this.searchText)); - } + searchText = input(''); + search = outputFromObservable(this.searchControl.valueChanges); - ngOnInit(): void { - this.searchControl.valueChanges.pipe(takeUntil(this.unsubscribe$)).subscribe(search => this.search.emit(search)); - } + #updateSearchControlEffect = effect(() => { + this.searchControl.patchValue(this.searchText(), { emitEvent: false }); + }); } diff --git a/apps/34-signal-solution/src/app/feature/people/people.component.spec.ts b/apps/34-signal-solution/src/app/feature/people/people.component.spec.ts index 7ba24327..6891ca3b 100644 --- a/apps/34-signal-solution/src/app/feature/people/people.component.spec.ts +++ b/apps/34-signal-solution/src/app/feature/people/people.component.spec.ts @@ -1,5 +1,5 @@ import { CommonModule } from '@angular/common'; -import { Component, DebugElement, Input, NO_ERRORS_SCHEMA, signal } from '@angular/core'; +import { Component, DebugElement, input, NO_ERRORS_SCHEMA, signal } from '@angular/core'; import { ComponentFixture, fakeAsync, flush } from '@angular/core/testing'; import { MatDialog } from '@angular/material/dialog'; import { MatListModule } from '@angular/material/list'; @@ -20,7 +20,7 @@ import { PeopleComponent } from './people.component'; template: '', }) class MockCardComponent { - @Input() person: People; + person = input(); } const PEOPLE = [{ id: '1' }, { id: '2' }] as Array; @@ -100,8 +100,8 @@ describe('PeopleComponent', () => { }); test('should pass the correct person', () => { const [sfeirCard1, sfeirCard2] = debugElement.queryAll(By.css('sfeir-card')); - expect(sfeirCard1.componentInstance.person).toEqual(PEOPLE[0]); - expect(sfeirCard2.componentInstance.person).toEqual(PEOPLE[1]); + expect(sfeirCard1.componentInstance.person()).toEqual(PEOPLE[0]); + expect(sfeirCard2.componentInstance.person()).toEqual(PEOPLE[1]); }); test('should call the delete method', () => { jest.spyOn(PEOPLE_STORE_SERVICE, 'deletePerson'); diff --git a/apps/34-signal-solution/src/app/feature/update-person/update-person.component.spec.ts b/apps/34-signal-solution/src/app/feature/update-person/update-person.component.spec.ts index 84fe6ab5..b4a030ab 100644 --- a/apps/34-signal-solution/src/app/feature/update-person/update-person.component.spec.ts +++ b/apps/34-signal-solution/src/app/feature/update-person/update-person.component.spec.ts @@ -1,5 +1,5 @@ import { CommonModule } from '@angular/common'; -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { fireEvent, render } from '@testing-library/angular'; import { of } from 'rxjs'; import { PeopleService } from '../../core/providers/people.service'; @@ -12,7 +12,7 @@ import { UpdatePersonComponent } from './update-person.component'; template: '', }) class MockFormComponent { - @Input() person: People; + person = input(); } const LOCATION = { diff --git a/apps/34-signal-solution/src/app/shared/components/card/card.component.spec.ts b/apps/34-signal-solution/src/app/shared/components/card/card.component.spec.ts index 96743654..4a0fd442 100644 --- a/apps/34-signal-solution/src/app/shared/components/card/card.component.spec.ts +++ b/apps/34-signal-solution/src/app/shared/components/card/card.component.spec.ts @@ -9,6 +9,7 @@ import { MatIconModule } from '@angular/material/icon'; import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { DisplayDirective } from '../../directives/display.directive'; +import { ComponentFixture } from '@angular/core/testing'; const PEOPLE: People = { firstname: 'John', @@ -25,6 +26,7 @@ const PERSON_DELETE = jest.fn(); describe('CardComponent', () => { let component: CardComponent; + let componentFixture: ComponentFixture; let debugElement: DebugElement; let rerender: any; @@ -33,8 +35,12 @@ describe('CardComponent', () => { imports: [MatButtonModule], componentImports: [NaPipe, MatCardModule, MatIconModule, MatButtonModule, CommonModule, DisplayDirective], schemas: [NO_ERRORS_SCHEMA], - componentProperties: { _person: PEOPLE, personDelete: { emit: PERSON_DELETE } as any }, + componentInputs: { + person: PEOPLE, + }, + componentOutputs: { personDelete: { emit: PERSON_DELETE } as any }, }); + componentFixture = fixture; component = fixture.componentInstance; debugElement = fixture.debugElement; rerender = reload; @@ -96,7 +102,7 @@ describe('CardComponent', () => { }); test('should display another person', async () => { const newPerson = { ...PEOPLE, firstname: 'Jane', lastname: 'Doe', photo: 'jane-doe.jpg' }; - await rerender({ componentProperties: { _person: newPerson } }); + await rerender({ componentInputs: { person: newPerson } }); const image: HTMLImageElement = screen.getByAltText('person-photo'); expect((image as any).ngSrc).toEqual(newPerson.photo); }); @@ -111,7 +117,9 @@ describe('CardComponent', () => { expect(PERSON_DELETE).toHaveBeenCalledWith(PEOPLE); }); test('should not display the button edit and delete if the person is a manager', async () => { - await rerender({ componentProperties: { _person: { ...PEOPLE, isManager: true } } }); + await rerender({ componentInputs: { person: { ...PEOPLE, isManager: true } } }); + componentFixture.detectChanges(); + await componentFixture.whenStable(); const editButton = screen.queryByTitle('Edit'); const deleteButton = screen.queryByTitle('Delete'); expect(editButton).toBeFalsy(); diff --git a/apps/34-signal-solution/src/app/shared/components/card/card.component.ts b/apps/34-signal-solution/src/app/shared/components/card/card.component.ts index 57cf847a..f1a0fe53 100644 --- a/apps/34-signal-solution/src/app/shared/components/card/card.component.ts +++ b/apps/34-signal-solution/src/app/shared/components/card/card.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output, signal } from '@angular/core'; +import { Component, input, output } from '@angular/core'; import { People } from '../../models/people.model'; import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; @@ -16,13 +16,8 @@ import { DatePipe, NgOptimizedImage } from '@angular/common'; imports: [MatCardModule, MatIconModule, MatButtonModule, DisplayDirective, NaPipe, RouterLink, NgOptimizedImage, DatePipe], }) export class CardComponent { - @Input({ alias: 'person' }) set _person(person: People | undefined) { - person && this.person.set(person); - } - - @Output() personDelete: EventEmitter = new EventEmitter(); - - person = signal({} as People); + person = input.required(); + personDelete = output(); deletePerson(person: People): void { this.personDelete.emit(person); diff --git a/apps/34-signal-solution/src/app/shared/components/custom-input/custom-input.component.html b/apps/34-signal-solution/src/app/shared/components/custom-input/custom-input.component.html index 29b82bdb..5f490a91 100644 --- a/apps/34-signal-solution/src/app/shared/components/custom-input/custom-input.component.html +++ b/apps/34-signal-solution/src/app/shared/components/custom-input/custom-input.component.html @@ -1,7 +1,7 @@
- {{ placeholder }} - + {{ placeholder() }} + @if (userLoseFocus$()) { } diff --git a/apps/34-signal-solution/src/app/shared/components/custom-input/custom-input.component.ts b/apps/34-signal-solution/src/app/shared/components/custom-input/custom-input.component.ts index 1bdb3f40..992157cb 100644 --- a/apps/34-signal-solution/src/app/shared/components/custom-input/custom-input.component.ts +++ b/apps/34-signal-solution/src/app/shared/components/custom-input/custom-input.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, Renderer2, signal, ViewChild } from '@angular/core'; +import { afterNextRender, AfterRenderPhase, Component, ElementRef, forwardRef, input, OnDestroy, Renderer2, signal, viewChild } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { fromEvent, merge, Subject, takeUntil, tap } from 'rxjs'; import { MatFormFieldModule } from '@angular/material/form-field'; @@ -12,34 +12,38 @@ import { MatInputModule } from '@angular/material/input'; styleUrls: ['./custom-input.component.scss'], providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CustomInputComponent), multi: true }], }) -export class CustomInputComponent implements OnInit, OnDestroy, ControlValueAccessor { - @Input() placeholder = ''; - @Input() inputType = 'text'; - @ViewChild('InputElement', { static: true }) inputElement: ElementRef; +export class CustomInputComponent implements OnDestroy, ControlValueAccessor { + placeholder = input(''); + inputType = input('text'); + inputElement = viewChild.required>('InputElement'); userLoseFocus$ = signal(false); + private _onChange: (x: string | number) => void; private _onTouched: () => void; private unsubscribe$: Subject = new Subject(); - constructor(private readonly renderer: Renderer2) {} + #listener = afterNextRender( + () => { + const inputListener$ = fromEvent(this.inputElement().nativeElement, 'input').pipe( + tap(() => { + this._onChange(this.inputElement().nativeElement.value); + this._onTouched(); + }), + ); - ngOnInit(): void { - const inputListener$ = fromEvent(this.inputElement.nativeElement, 'input').pipe( - tap(() => { - this._onChange(this.inputElement.nativeElement.value); - this._onTouched(); - }), - ); - - const blurListener$ = fromEvent(this.inputElement.nativeElement, 'blur').pipe( - tap(() => { - this._onTouched(); - this.userLoseFocus$.set(true); - }), - ); - - merge(inputListener$, blurListener$).pipe(takeUntil(this.unsubscribe$)).subscribe(); - } + const blurListener$ = fromEvent(this.inputElement().nativeElement, 'blur').pipe( + tap(() => { + this._onTouched(); + this.userLoseFocus$.set(true); + }), + ); + + merge(inputListener$, blurListener$).pipe(takeUntil(this.unsubscribe$)).subscribe(); + }, + { phase: AfterRenderPhase.Read }, + ); + + constructor(private readonly renderer: Renderer2) {} ngOnDestroy(): void { this.unsubscribe$.next(true); @@ -47,7 +51,7 @@ export class CustomInputComponent implements OnInit, OnDestroy, ControlValueAcce } writeValue(value: string | number): void { - this.renderer.setProperty(this.inputElement.nativeElement, 'value', value ?? null); + this.renderer.setProperty(this.inputElement().nativeElement, 'value', value ?? null); } registerOnTouched(fn: () => void) { diff --git a/apps/34-signal-solution/src/app/shared/components/form/form.component.spec.ts b/apps/34-signal-solution/src/app/shared/components/form/form.component.spec.ts index 61b3818f..3d89cc42 100644 --- a/apps/34-signal-solution/src/app/shared/components/form/form.component.spec.ts +++ b/apps/34-signal-solution/src/app/shared/components/form/form.component.spec.ts @@ -24,8 +24,10 @@ describe('FormComponent', () => { } = await render(FormComponent, { componentImports: [MatInputModule, ReactiveFormsModule, CustomInputComponent, CommonModule, NgOptimizedImage], schemas: [NO_ERRORS_SCHEMA], - componentProperties: { + componentInputs: { person: null, + }, + componentOutputs: { cancel: { emit: CANCEL_SPY, } as any, @@ -59,8 +61,10 @@ describe('FormComponent', () => { const spy = jest.spyOn(component.personForm, 'patchValue'); const person = { lastname: 'Doe', firstname: 'John' } as People; await reload({ - componentProperties: { + componentInputs: { person, + }, + componentProperties: { cancel: { emit: CANCEL_SPY, } as any, diff --git a/apps/34-signal-solution/src/app/shared/components/form/form.component.ts b/apps/34-signal-solution/src/app/shared/components/form/form.component.ts index 48addebd..3058d84e 100644 --- a/apps/34-signal-solution/src/app/shared/components/form/form.component.ts +++ b/apps/34-signal-solution/src/app/shared/components/form/form.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; +import { Component, effect, input, output } from '@angular/core'; import { People, PeopleForm } from '../../models/people.model'; import { PersonForm } from './form'; import { ReactiveFormsModule } from '@angular/forms'; @@ -14,18 +14,18 @@ import { MatButtonModule } from '@angular/material/button'; templateUrl: './form.component.html', styleUrls: ['./form.component.scss'], }) -export class FormComponent implements OnChanges { - @Input() person: People; - @Output() cancel: EventEmitter = new EventEmitter(); - @Output() save: EventEmitter = new EventEmitter(); +export class FormComponent { + person = input(); + cancel = output(); + save = output(); personForm = new PersonForm(); - ngOnChanges(changes: SimpleChanges): void { - const { person } = changes; - if (person.currentValue !== person.previousValue) { - this.personForm.patchValue(this.person); + #updatePersonFormEffect = effect(() => { + const person = this.person(); + if (person) { + this.personForm.patchValue(person); } - } + }); onSave(): void { this.save.emit(this.personForm.getRawValue()); diff --git a/apps/34-signal-solution/src/app/shared/directives/badge.directive.spec.ts b/apps/34-signal-solution/src/app/shared/directives/badge.directive.spec.ts index e52c29f1..b1279853 100644 --- a/apps/34-signal-solution/src/app/shared/directives/badge.directive.spec.ts +++ b/apps/34-signal-solution/src/app/shared/directives/badge.directive.spec.ts @@ -1,4 +1,4 @@ -import { Component, DebugElement, Input } from '@angular/core'; +import { Component, DebugElement, input } from '@angular/core'; import { fireEvent, render } from '@testing-library/angular'; import { BadgeDirective } from './badge.directive'; import { By } from '@angular/platform-browser'; @@ -74,8 +74,8 @@ describe('BadgeDirective', () => { @Component({ standalone: true, imports: [BadgeDirective], - template: ``, + template: ``, }) export class HostDirectiveComponent { - @Input() person!: { isManager: boolean }; + person = input.required<{ isManager: boolean }>(); } diff --git a/apps/34-signal-solution/src/app/shared/directives/badge.directive.ts b/apps/34-signal-solution/src/app/shared/directives/badge.directive.ts index d0b7513e..cd9e0315 100644 --- a/apps/34-signal-solution/src/app/shared/directives/badge.directive.ts +++ b/apps/34-signal-solution/src/app/shared/directives/badge.directive.ts @@ -1,11 +1,11 @@ -import { Directive, ElementRef, HostBinding, HostListener, Input, OnInit, Renderer2 } from '@angular/core'; +import { Directive, effect, ElementRef, HostBinding, HostListener, input, Renderer2 } from '@angular/core'; @Directive({ standalone: true, selector: '[sfeirBadge]', }) -export class BadgeDirective implements OnInit { - @Input('sfeirBadge') isManager: boolean; +export class BadgeDirective { + isManager = input(false, { alias: 'sfeirBadge' }); @HostBinding('style.color') private iconColor = 'black'; constructor( @@ -13,11 +13,12 @@ export class BadgeDirective implements OnInit { private readonly renderer: Renderer2, ) {} - ngOnInit(): void { - if (this.isManager) { + #isManagerEffect = effect(() => { + const isManager = this.isManager(); + if (isManager) { this.renderer.setProperty(this.element.nativeElement, 'innerHTML', 'supervisor_account'); } - } + }); @HostListener('mouseover', ['$event']) onMouseOver(event: MouseEvent): void { diff --git a/apps/34-signal-solution/src/app/shared/directives/display.directive.spec.ts b/apps/34-signal-solution/src/app/shared/directives/display.directive.spec.ts index ee8e5b81..ad3700e7 100644 --- a/apps/34-signal-solution/src/app/shared/directives/display.directive.spec.ts +++ b/apps/34-signal-solution/src/app/shared/directives/display.directive.spec.ts @@ -1,11 +1,11 @@ -import { Component } from '@angular/core'; +import { Component, signal } from '@angular/core'; import { ComponentFixture } from '@angular/core/testing'; import { render } from '@testing-library/angular'; import { DisplayDirective } from './display.directive'; -@Component({ standalone: true, imports: [DisplayDirective], template: `Hello Sfeir` }) +@Component({ standalone: true, imports: [DisplayDirective], template: `Hello Sfeir` }) class HostDirectiveComponent { - person = { isManager: true }; + person = signal({ isManager: true }); } describe('SfeirDisplayDirective', () => { @@ -24,9 +24,10 @@ describe('SfeirDisplayDirective', () => { const span = container.querySelector('span'); expect(span.textContent).toBe('Hello Sfeir'); }); - test('should not display the Helle Sfeir text', () => { - component.person = { isManager: false }; - componentFixture.detectChanges(); + test('should not display the Helle Sfeir text', async () => { + component.person.set({ isManager: false }); + componentFixture.autoDetectChanges(); + await componentFixture.whenStable(); const span = container.querySelector('span'); expect(span).toBeFalsy(); }); diff --git a/apps/34-signal-solution/src/app/shared/directives/display.directive.ts b/apps/34-signal-solution/src/app/shared/directives/display.directive.ts index 031de74d..c313b726 100644 --- a/apps/34-signal-solution/src/app/shared/directives/display.directive.ts +++ b/apps/34-signal-solution/src/app/shared/directives/display.directive.ts @@ -1,14 +1,18 @@ import { NgIfContext } from '@angular/common'; -import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; +import { Directive, effect, input, TemplateRef, ViewContainerRef } from '@angular/core'; @Directive({ standalone: true, selector: '[sfeirDisplay]', }) export class DisplayDirective { - @Input('sfeirDisplay') set condition(condition: boolean) { + condition = input.required({ alias: 'sfeirDisplay' }); + + #displayEffect = effect(() => { + const condition = this.condition(); condition ? this.viewContainerRef.createEmbeddedView(this.templateRef) : this.viewContainerRef.clear(); - } + }); + constructor( private readonly templateRef: TemplateRef, private readonly viewContainerRef: ViewContainerRef,