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 = '';
- }
- }
-}