From 72fa0cbe2c3e07eed3826790734fd3300987d54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n?= Date: Wed, 7 May 2025 12:10:28 +0200 Subject: [PATCH] feat(challenge 1): content projection my version --- .../city-card/city-card.component.ts | 47 +++++++++++- .../student-card/student-card.component.ts | 40 ++++++---- .../teacher-card/teacher-card.component.ts | 36 ++++++--- .../src/app/data-access/city.store.ts | 12 ++- .../src/app/data-access/student.store.ts | 10 ++- .../src/app/data-access/teacher.store.ts | 12 ++- .../src/app/ui/card/card.component.ts | 75 +++++++++---------- .../app/ui/list-item/list-item.component.ts | 40 +++++----- 8 files changed, 173 insertions(+), 99 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..f024207c6 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,48 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { NgOptimizedImage } from '@angular/common'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { CityStore } from '../../data-access/city.store'; +import { 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: ` + + + + `, + styles: [ + ` + .bg-light-blue { + background-color: rgba(0, 171, 250, 0.1); + } + `, + ], + imports: [CardComponent, NgOptimizedImage], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class CityCardComponent {} +export class CityCardComponent { + private store = inject(CityStore); + + readonly cities = this.store.cities; + readonly cardType = CardType.CITY; + + addCity() { + this.store.addOne(randomCity()); + } + + deleteCity(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..ead0344b1 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 @@ -1,10 +1,6 @@ -import { - ChangeDetectionStrategy, - Component, - inject, - OnInit, -} from '@angular/core'; -import { FakeHttpService } from '../../data-access/fake-http.service'; +import { NgOptimizedImage } from '@angular/common'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { 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,26 +11,38 @@ import { CardComponent } from '../../ui/card/card.component'; + (addNewItem)="addStudent()" + (delete)="deleteStudent($event)" + class="bg-light-green"> + + `, styles: [ ` - ::ng-deep .bg-light-green { + .bg-light-green { background-color: rgba(0, 250, 0, 0.1); } `, ], - imports: [CardComponent], + imports: [CardComponent, NgOptimizedImage], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class StudentCardComponent implements OnInit { - private http = inject(FakeHttpService); +export class StudentCardComponent { private store = inject(StudentStore); - students = this.store.students; - cardType = CardType.STUDENT; + readonly students = this.store.students; + readonly cardType = CardType.STUDENT; - ngOnInit(): void { - this.http.fetchStudents$.subscribe((s) => this.store.addAll(s)); + addStudent() { + this.store.addOne(randStudent()); + } + + deleteStudent(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..6652eafbe 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,6 @@ -import { Component, inject, OnInit } from '@angular/core'; -import { FakeHttpService } from '../../data-access/fake-http.service'; +import { NgOptimizedImage } from '@angular/common'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { 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,25 +11,38 @@ import { CardComponent } from '../../ui/card/card.component'; + (addNewItem)="addTeacher()" + (delete)="deleteTeacher($event)" + class="bg-light-red"> + + `, styles: [ ` - ::ng-deep .bg-light-red { + .bg-light-red { background-color: rgba(250, 0, 0, 0.1); } `, ], - imports: [CardComponent], + imports: [CardComponent, NgOptimizedImage], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class TeacherCardComponent implements OnInit { - private http = inject(FakeHttpService); +export class TeacherCardComponent { private store = inject(TeacherStore); - teachers = this.store.teachers; - cardType = CardType.TEACHER; + readonly teachers = this.store.teachers; + readonly cardType = CardType.TEACHER; - ngOnInit(): void { - this.http.fetchTeachers$.subscribe((t) => this.store.addAll(t)); + addTeacher() { + this.store.addOne(randTeacher()); + } + + deleteTeacher(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..b79734770 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 @@ -1,11 +1,19 @@ -import { Injectable, signal } from '@angular/core'; +import { effect, inject, Injectable, signal } from '@angular/core'; import { City } from '../model/city.model'; +import { FakeHttpService } from './fake-http.service'; @Injectable({ providedIn: 'root', }) export class CityStore { - private cities = signal([]); + private readonly http = inject(FakeHttpService); + public cities = signal([]); + + constructor() { + effect(() => { + this.http.fetchCities$.subscribe((c) => this.addAll(c)); + }); + } addAll(cities: City[]) { this.cities.set(cities); diff --git a/apps/angular/1-projection/src/app/data-access/student.store.ts b/apps/angular/1-projection/src/app/data-access/student.store.ts index 6e7f57022..e7adf2c4b 100644 --- a/apps/angular/1-projection/src/app/data-access/student.store.ts +++ b/apps/angular/1-projection/src/app/data-access/student.store.ts @@ -1,12 +1,20 @@ -import { Injectable, signal } from '@angular/core'; +import { effect, inject, Injectable, signal } from '@angular/core'; import { Student } from '../model/student.model'; +import { FakeHttpService } from './fake-http.service'; @Injectable({ providedIn: 'root', }) export class StudentStore { + private readonly http = inject(FakeHttpService); public students = signal([]); + constructor() { + effect(() => { + this.http.fetchStudents$.subscribe((s) => this.addAll(s)); + }); + } + addAll(students: Student[]) { this.students.set(students); } diff --git a/apps/angular/1-projection/src/app/data-access/teacher.store.ts b/apps/angular/1-projection/src/app/data-access/teacher.store.ts index 5f6dae989..0f8589eb8 100644 --- a/apps/angular/1-projection/src/app/data-access/teacher.store.ts +++ b/apps/angular/1-projection/src/app/data-access/teacher.store.ts @@ -1,11 +1,19 @@ -import { Injectable, signal } from '@angular/core'; +import { effect, inject, Injectable, signal } from '@angular/core'; import { Teacher } from '../model/teacher.model'; +import { FakeHttpService } from './fake-http.service'; @Injectable({ providedIn: 'root', }) export class TeacherStore { - public teachers = signal([]); + private readonly http = inject(FakeHttpService); + readonly teachers = signal([]); + + constructor() { + effect(() => { + this.http.fetchTeachers$.subscribe((t) => this.addAll(t)); + }); + } addAll(teachers: Teacher[]) { this.teachers.set(teachers); 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..7579f1a5e 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,51 @@ -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 { + ChangeDetectionStrategy, + 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], + host: { + class: 'flex w-fit flex-col gap-3 rounded-md border-2 border-black p-4', + }, + imports: [ListItemComponent], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class CardComponent { - private teacherStore = inject(TeacherStore); - private studentStore = inject(StudentStore); - readonly list = input(null); readonly type = input.required(); - readonly customClass = input(''); - CardType = CardType; + readonly addNewItem = output(); + readonly delete = output(); + + onAdd() { + this.addNewItem.emit(''); + } - addNewItem() { - const type = this.type(); - if (type === CardType.TEACHER) { - this.teacherStore.addOne(randTeacher()); - } else if (type === CardType.STUDENT) { - this.studentStore.addOne(randStudent()); - } + handleDelete(id: number) { + this.delete.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 cffabb451..94708618e 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,40 +1,36 @@ 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: ` -
- {{ name() }} - -
+ @if (type() === CardType.TEACHER || type() === CardType.STUDENT) { + {{ item().firstName }} + } @else if (type() === CardType.CITY) { + {{ item().name }} + } + + `, - standalone: true, + host: { class: 'border-grey-300 flex justify-between border px-2 py-1' }, changeDetection: ChangeDetectionStrategy.OnPush, }) export class ListItemComponent { - private teacherStore = inject(TeacherStore); - private studentStore = inject(StudentStore); - - readonly id = input.required(); - readonly name = input.required(); + readonly item = input.required(); readonly type = input.required(); - delete(id: number) { - const type = this.type(); - if (type === CardType.TEACHER) { - this.teacherStore.deleteOne(id); - } else if (type === CardType.STUDENT) { - this.studentStore.deleteOne(id); - } + readonly deleteItem = output(); + + CardType = CardType; + + onDelete(id: number) { + this.deleteItem.emit(id); } }