From 13334b1e4cf0c12c121dc0ae55f80c1a4b19d941 Mon Sep 17 00:00:00 2001 From: Daniel Joo Date: Thu, 22 Jan 2026 11:04:52 -0800 Subject: [PATCH] feat: implement solution for content projection --- .../city-card/city-card.component.ts | 40 +++++++++- .../student-card/student-card.component.ts | 17 +++- .../teacher-card/teacher-card.component.ts | 16 +++- .../src/app/data-access/city.store.ts | 2 +- .../src/app/ui/card/card.component.ts | 79 +++++++++---------- .../app/ui/list-item/list-item.component.ts | 26 +++--- 6 files changed, 116 insertions(+), 64 deletions(-) diff --git a/apps/angular/1-projection/src/app/component/city-card/city-card.component.ts b/apps/angular/1-projection/src/app/component/city-card/city-card.component.ts index 8895c8c84..9fb7d4cb7 100644 --- a/apps/angular/1-projection/src/app/component/city-card/city-card.component.ts +++ b/apps/angular/1-projection/src/app/component/city-card/city-card.component.ts @@ -1,9 +1,41 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { CityStore } from '../../data-access/city.store'; +import { + FakeHttpService, + randomCity, +} from '../../data-access/fake-http.service'; +import { CardType } from '../../model/card.model'; +import { CardComponent } from '../../ui/card/card.component'; @Component({ selector: 'app-city-card', - template: 'TODO City', - imports: [], + template: ` + + `, + imports: [CardComponent], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class CityCardComponent {} +export class CityCardComponent { + private http = inject(FakeHttpService); + private store = inject(CityStore); + readonly cardType = CardType.CITY; + readonly cities = this.store.cities; + + ngOnInit(): void { + this.http.fetchCities$.subscribe((c) => this.store.addAll(c)); + } + + addNewItem() { + this.store.addOne(randomCity()); + } + + deleteItem(id: number) { + this.store.deleteOne(id); + } +} diff --git a/apps/angular/1-projection/src/app/component/student-card/student-card.component.ts b/apps/angular/1-projection/src/app/component/student-card/student-card.component.ts index bdfa4abd4..9c1d137d3 100644 --- a/apps/angular/1-projection/src/app/component/student-card/student-card.component.ts +++ b/apps/angular/1-projection/src/app/component/student-card/student-card.component.ts @@ -4,7 +4,10 @@ import { inject, OnInit, } from '@angular/core'; -import { FakeHttpService } from '../../data-access/fake-http.service'; +import { + FakeHttpService, + randStudent, +} from '../../data-access/fake-http.service'; import { StudentStore } from '../../data-access/student.store'; import { CardType } from '../../model/card.model'; import { CardComponent } from '../../ui/card/card.component'; @@ -15,6 +18,9 @@ import { CardComponent } from '../../ui/card/card.component'; `, styles: [ @@ -30,11 +36,18 @@ import { CardComponent } from '../../ui/card/card.component'; export class StudentCardComponent implements OnInit { private http = inject(FakeHttpService); private store = inject(StudentStore); - students = this.store.students; cardType = CardType.STUDENT; ngOnInit(): void { this.http.fetchStudents$.subscribe((s) => this.store.addAll(s)); } + + addNewItem() { + this.store.addOne(randStudent()); + } + + deleteItem(id: number) { + this.store.deleteOne(id); + } } diff --git a/apps/angular/1-projection/src/app/component/teacher-card/teacher-card.component.ts b/apps/angular/1-projection/src/app/component/teacher-card/teacher-card.component.ts index adf0ad3c1..c4cc294d0 100644 --- a/apps/angular/1-projection/src/app/component/teacher-card/teacher-card.component.ts +++ b/apps/angular/1-projection/src/app/component/teacher-card/teacher-card.component.ts @@ -1,5 +1,8 @@ import { Component, inject, OnInit } from '@angular/core'; -import { FakeHttpService } from '../../data-access/fake-http.service'; +import { + FakeHttpService, + randTeacher, +} from '../../data-access/fake-http.service'; import { TeacherStore } from '../../data-access/teacher.store'; import { CardType } from '../../model/card.model'; import { CardComponent } from '../../ui/card/card.component'; @@ -10,6 +13,9 @@ import { CardComponent } from '../../ui/card/card.component'; `, styles: [ @@ -31,4 +37,12 @@ export class TeacherCardComponent implements OnInit { ngOnInit(): void { this.http.fetchTeachers$.subscribe((t) => this.store.addAll(t)); } + + addNewItem() { + this.store.addOne(randTeacher()); + } + + deleteItem(id: number) { + this.store.deleteOne(id); + } } diff --git a/apps/angular/1-projection/src/app/data-access/city.store.ts b/apps/angular/1-projection/src/app/data-access/city.store.ts index a8b523569..9fbcb346b 100644 --- a/apps/angular/1-projection/src/app/data-access/city.store.ts +++ b/apps/angular/1-projection/src/app/data-access/city.store.ts @@ -5,7 +5,7 @@ import { City } from '../model/city.model'; providedIn: 'root', }) export class CityStore { - private cities = signal([]); + public cities = signal([]); addAll(cities: City[]) { this.cities.set(cities); diff --git a/apps/angular/1-projection/src/app/ui/card/card.component.ts b/apps/angular/1-projection/src/app/ui/card/card.component.ts index 1a6c3648c..218367a80 100644 --- a/apps/angular/1-projection/src/app/ui/card/card.component.ts +++ b/apps/angular/1-projection/src/app/ui/card/card.component.ts @@ -1,58 +1,57 @@ -import { NgOptimizedImage } from '@angular/common'; -import { Component, inject, input } from '@angular/core'; -import { randStudent, randTeacher } from '../../data-access/fake-http.service'; -import { StudentStore } from '../../data-access/student.store'; -import { TeacherStore } from '../../data-access/teacher.store'; +import { NgOptimizedImage, NgTemplateOutlet } from '@angular/common'; +import { Component, input, output } from '@angular/core'; import { CardType } from '../../model/card.model'; import { ListItemComponent } from '../list-item/list-item.component'; @Component({ selector: 'app-card', template: ` -
- @if (type() === CardType.TEACHER) { - - } - @if (type() === CardType.STUDENT) { - - } + +
+ +
+ @for (item of list(); track item) { + + } +
-
- @for (item of list(); track item) { - - } -
+ +
+
- -
+ `, - imports: [ListItemComponent, NgOptimizedImage], + imports: [ListItemComponent, NgOptimizedImage, NgTemplateOutlet], }) export class CardComponent { - private teacherStore = inject(TeacherStore); - private studentStore = inject(StudentStore); - readonly list = input(null); readonly type = input.required(); readonly customClass = input(''); - + readonly pictureType = input.required(); + addItem = output(); + deleteItem = output(); + readonly ctx = { cardType: this.type }; CardType = CardType; - addNewItem() { - const type = this.type(); - if (type === CardType.TEACHER) { - this.teacherStore.addOne(randTeacher()); - } else if (type === CardType.STUDENT) { - this.studentStore.addOne(randStudent()); - } + delete(id: number) { + this.deleteItem.emit(id); } } diff --git a/apps/angular/1-projection/src/app/ui/list-item/list-item.component.ts b/apps/angular/1-projection/src/app/ui/list-item/list-item.component.ts index 5d504f372..e4ce5ba5e 100644 --- a/apps/angular/1-projection/src/app/ui/list-item/list-item.component.ts +++ b/apps/angular/1-projection/src/app/ui/list-item/list-item.component.ts @@ -1,19 +1,18 @@ import { ChangeDetectionStrategy, Component, - inject, input, + output, } from '@angular/core'; -import { StudentStore } from '../../data-access/student.store'; -import { TeacherStore } from '../../data-access/teacher.store'; import { CardType } from '../../model/card.model'; @Component({ selector: 'app-list-item', template: `
+ {{ firstName() }} {{ lastName() }} {{ name() }} -
@@ -21,19 +20,14 @@ import { CardType } from '../../model/card.model'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class ListItemComponent { - private teacherStore = inject(TeacherStore); - private studentStore = inject(StudentStore); - readonly id = input.required(); - readonly name = input.required(); - readonly type = input.required(); + readonly type = input(); + readonly name = input(); + readonly firstName = input(''); + readonly lastName = input(''); + deleteItem = output(); - delete(id: number) { - const type = this.type(); - if (type === CardType.TEACHER) { - this.teacherStore.deleteOne(id); - } else if (type === CardType.STUDENT) { - this.studentStore.deleteOne(id); - } + onDelete() { + this.deleteItem.emit(this.id()); } }