diff --git a/apps/performance/34-default-vs-onpush/src/app/app.component.ts b/apps/performance/34-default-vs-onpush/src/app/app.component.ts index 88b0a6571..173965b6a 100644 --- a/apps/performance/34-default-vs-onpush/src/app/app.component.ts +++ b/apps/performance/34-default-vs-onpush/src/app/app.component.ts @@ -1,21 +1,46 @@ +import { AsyncPipe } from '@angular/common'; import { Component } from '@angular/core'; import { randFirstName } from '@ngneat/falso'; -import { PersonListComponent } from './person-list.component'; -import { RandomComponent } from './random.component'; +import { BehaviorSubject } from 'rxjs'; +import { RandomComponent } from './components/random.component'; +import { PersonListComponent } from './container/person-list.component'; @Component({ - imports: [PersonListComponent, RandomComponent], + imports: [AsyncPipe, PersonListComponent, RandomComponent], selector: 'app-root', template: `
- - + +
`, }) export class AppComponent { - girlList = randFirstName({ gender: 'female', length: 10 }); - boyList = randFirstName({ gender: 'male', length: 10 }); + private girlListSubject = new BehaviorSubject( + randFirstName({ gender: 'female', length: 10 }), + ); + private boyListSubject = new BehaviorSubject( + randFirstName({ gender: 'male', length: 10 }), + ); + + girlList$ = this.girlListSubject.asObservable(); + boyList$ = this.boyListSubject.asObservable(); + + addName({ title, name }: { title: string; name: string }) { + if (title === 'Female') { + const current = this.girlListSubject.value; + this.girlListSubject.next([name, ...current]); + } else { + const current = this.boyListSubject.value; + this.boyListSubject.next([name, ...current]); + } + } } diff --git a/apps/performance/34-default-vs-onpush/src/app/app.config.ts b/apps/performance/34-default-vs-onpush/src/app/app.config.ts index 59198e627..6488e30ce 100644 --- a/apps/performance/34-default-vs-onpush/src/app/app.config.ts +++ b/apps/performance/34-default-vs-onpush/src/app/app.config.ts @@ -1,6 +1,7 @@ -import { ApplicationConfig } from '@angular/core'; -import { provideAnimations } from '@angular/platform-browser/animations'; +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; export const appConfig: ApplicationConfig = { - providers: [provideAnimations()], + providers: [ + provideZoneChangeDetection({ eventCoalescing: true, runCoalescing: true }), + ], }; diff --git a/apps/performance/34-default-vs-onpush/src/app/components/list.component.ts b/apps/performance/34-default-vs-onpush/src/app/components/list.component.ts new file mode 100644 index 000000000..a31393e8d --- /dev/null +++ b/apps/performance/34-default-vs-onpush/src/app/components/list.component.ts @@ -0,0 +1,21 @@ +import { CDFlashingDirective } from '@angular-challenges/shared/directives'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { MatListModule } from '@angular/material/list'; + +@Component({ + selector: 'app-list', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [MatListModule, CDFlashingDirective], + template: ` + +
+

+ {{ name }} +

+
+
+ `, +}) +export class ListComponent { + @Input() name!: string; +} diff --git a/apps/performance/34-default-vs-onpush/src/app/random.component.ts b/apps/performance/34-default-vs-onpush/src/app/components/random.component.ts similarity index 68% rename from apps/performance/34-default-vs-onpush/src/app/random.component.ts rename to apps/performance/34-default-vs-onpush/src/app/components/random.component.ts index 71479e28d..a37f097c9 100644 --- a/apps/performance/34-default-vs-onpush/src/app/random.component.ts +++ b/apps/performance/34-default-vs-onpush/src/app/components/random.component.ts @@ -1,8 +1,9 @@ import { CDFlashingDirective } from '@angular-challenges/shared/directives'; -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ selector: 'app-random', + changeDetection: ChangeDetectionStrategy.OnPush, template: `
I do nothing but I'm here
`, diff --git a/apps/performance/34-default-vs-onpush/src/app/components/search.component.ts b/apps/performance/34-default-vs-onpush/src/app/components/search.component.ts new file mode 100644 index 000000000..f69415dfa --- /dev/null +++ b/apps/performance/34-default-vs-onpush/src/app/components/search.component.ts @@ -0,0 +1,37 @@ +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Output, +} from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; + +@Component({ + selector: 'app-search', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [MatFormFieldModule, MatInputModule, FormsModule], + template: ` + + + + `, +}) +export class SearchComponent { + @Output() newName = new EventEmitter(); + + label = ''; + + handleKey(event: KeyboardEvent) { + if (event.key === 'Enter') { + this.newName.emit(this.label); + this.label = ''; + } + } +} diff --git a/apps/performance/34-default-vs-onpush/src/app/container/person-list.component.ts b/apps/performance/34-default-vs-onpush/src/app/container/person-list.component.ts new file mode 100644 index 000000000..22f1b5014 --- /dev/null +++ b/apps/performance/34-default-vs-onpush/src/app/container/person-list.component.ts @@ -0,0 +1,57 @@ +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; + +import { CDFlashingDirective } from '@angular-challenges/shared/directives'; +import { TitleCasePipe } from '@angular/common'; +import { MatListModule } from '@angular/material/list'; +import { ListComponent } from '../components/list.component'; +import { SearchComponent } from '../components/search.component'; + +@Component({ + selector: 'app-person-list', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + TitleCasePipe, + MatListModule, + CDFlashingDirective, + SearchComponent, + ListComponent, + ], + template: ` +

+ {{ title | titlecase }} +

+ + + + + @if (names?.length === 0) { +
Empty list
+ } + @for (name of names; track $index) { + + } + @if (names?.length !== 0) { + + } +
+ `, + host: { + class: 'w-full flex flex-col items-center', + }, +}) +export class PersonListComponent { + @Input() names!: ReadonlyArray | null; + @Input() title!: string; + + @Output() newName = new EventEmitter<{ title: string; name: string }>(); + + handleNewName(name: string) { + this.newName.emit({ title: this.title, name }); + } +} diff --git a/apps/performance/34-default-vs-onpush/src/app/person-list.component.ts b/apps/performance/34-default-vs-onpush/src/app/person-list.component.ts deleted file mode 100644 index 4cd92396a..000000000 --- a/apps/performance/34-default-vs-onpush/src/app/person-list.component.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Component, Input } from '@angular/core'; - -import { CDFlashingDirective } from '@angular-challenges/shared/directives'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { MatListModule } from '@angular/material/list'; - -@Component({ - selector: 'app-person-list', - imports: [ - CommonModule, - FormsModule, - MatListModule, - MatFormFieldModule, - MatInputModule, - MatChipsModule, - CDFlashingDirective, - ], - template: ` -

- {{ title | titlecase }} -

- - - - - - -
Empty list
- -
-

- {{ name }} -

-
-
- -
- `, - host: { - class: 'w-full flex flex-col items-center', - }, -}) -export class PersonListComponent { - @Input() names: string[] = []; - @Input() title = ''; - - label = ''; - - handleKey(event: KeyboardEvent) { - if (event.key === 'Enter') { - this.names?.unshift(this.label); - this.label = ''; - } - } -}