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 47b089650..c8054441a 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,12 +1,48 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, inject, OnInit, Signal } from '@angular/core'; +import { CityStore } from '../../data-access/city.store'; +import { + FakeHttpService, + randomCity, +} from '../../data-access/fake-http.service'; +import { City } from '../../model/city.model'; +import { CardComponent } from '../../ui/card/card.component'; +import { ListItemComponent } from '../../ui/list-item/list-item.component'; @Component({ selector: 'app-city-card', - template: 'TODO City', - imports: [], + template: ` + + + + + + + + `, + imports: [CardComponent, ListItemComponent], }) export class CityCardComponent implements OnInit { - constructor() {} + private http: FakeHttpService = inject(FakeHttpService); + private store: CityStore = inject(CityStore); + + cities: Signal = this.store.elements$; + + ngOnInit(): void { + this.http.fetchCities$.subscribe((c) => this.store.addAll(c)); + } + + addCity() { + this.store.addOne(randomCity()); + } - ngOnInit(): void {} + deleteCity(cityId: number) { + this.store.deleteOne(cityId); + } } 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 dae48a2d5..1a09e3b9a 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,39 +1,48 @@ -import { Component, OnInit } from '@angular/core'; -import { FakeHttpService } from '../../data-access/fake-http.service'; +import { Component, inject, OnInit, Signal } from '@angular/core'; +import { + FakeHttpService, + randStudent, +} from '../../data-access/fake-http.service'; import { StudentStore } from '../../data-access/student.store'; -import { CardType } from '../../model/card.model'; import { Student } from '../../model/student.model'; import { CardComponent } from '../../ui/card/card.component'; +import { ListItemComponent } from '../../ui/list-item/list-item.component'; @Component({ selector: 'app-student-card', template: ` + [list]="students()" + [customClass]="'rgba(0, 250, 0, 0.1)'" + [templateRef]="student" + (add)="addStudent()"> + + + + + + `, - styles: [ - ` - ::ng-deep .bg-light-green { - background-color: rgba(0, 250, 0, 0.1); - } - `, - ], - imports: [CardComponent], + imports: [CardComponent, ListItemComponent], }) export class StudentCardComponent implements OnInit { - students: Student[] = []; - cardType = CardType.STUDENT; + private http: FakeHttpService = inject(FakeHttpService); + private store: StudentStore = inject(StudentStore); - constructor( - private http: FakeHttpService, - private store: StudentStore, - ) {} + students: Signal = this.store.elements$; ngOnInit(): void { this.http.fetchStudents$.subscribe((s) => this.store.addAll(s)); + } + + addStudent() { + this.store.addOne(randStudent()); + } - this.store.students$.subscribe((s) => (this.students = s)); + deleteStudent(studentId: number) { + this.store.deleteOne(studentId); } } 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 815cde9d5..8d79abf1a 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,39 +1,48 @@ -import { Component, OnInit } from '@angular/core'; -import { FakeHttpService } from '../../data-access/fake-http.service'; +import { Component, inject, OnInit, Signal } from '@angular/core'; +import { + FakeHttpService, + randTeacher, +} from '../../data-access/fake-http.service'; import { TeacherStore } from '../../data-access/teacher.store'; -import { CardType } from '../../model/card.model'; import { Teacher } from '../../model/teacher.model'; import { CardComponent } from '../../ui/card/card.component'; +import { ListItemComponent } from '../../ui/list-item/list-item.component'; @Component({ selector: 'app-teacher-card', template: ` + [list]="teachers()" + [customClass]="'rgba(250, 0, 0, 0.1)'" + [templateRef]="teacher" + (add)="addTeacher()"> + + + + + + `, - styles: [ - ` - ::ng-deep .bg-light-red { - background-color: rgba(250, 0, 0, 0.1); - } - `, - ], - imports: [CardComponent], + imports: [CardComponent, ListItemComponent], }) export class TeacherCardComponent implements OnInit { - teachers: Teacher[] = []; - cardType = CardType.TEACHER; + private http: FakeHttpService = inject(FakeHttpService); + private store: TeacherStore = inject(TeacherStore); - constructor( - private http: FakeHttpService, - private store: TeacherStore, - ) {} + teachers: Signal = this.store.elements$; ngOnInit(): void { this.http.fetchTeachers$.subscribe((t) => this.store.addAll(t)); + } + + addTeacher() { + this.store.addOne(randTeacher()); + } - this.store.teachers$.subscribe((t) => (this.teachers = t)); + deleteTeacher(teacherId: number) { + this.store.deleteOne(teacherId); } } 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 711dad1d7..f9650829d 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,23 +1,8 @@ import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; import { City } from '../model/city.model'; +import { Store } from './store'; @Injectable({ providedIn: 'root', }) -export class CityStore { - private cities = new BehaviorSubject([]); - cities$ = this.cities.asObservable(); - - addAll(cities: City[]) { - this.cities.next(cities); - } - - addOne(student: City) { - this.cities.next([...this.cities.value, student]); - } - - deleteOne(id: number) { - this.cities.next(this.cities.value.filter((s) => s.id !== id)); - } -} +export class CityStore extends Store {} diff --git a/apps/angular/1-projection/src/app/data-access/store.ts b/apps/angular/1-projection/src/app/data-access/store.ts new file mode 100644 index 000000000..f5fcb7fc0 --- /dev/null +++ b/apps/angular/1-projection/src/app/data-access/store.ts @@ -0,0 +1,23 @@ +import { signal } from '@angular/core'; + +export class Store { + private elements = signal([]); + elements$ = this.elements.asReadonly(); + + addAll(elements: T[]) { + this.elements.update((currentElements) => [ + ...currentElements, + ...elements, + ]); + } + + addOne(element: T) { + this.elements.update((currentElements) => [...currentElements, element]); + } + + deleteOne(id: number) { + this.elements.update((currentElements) => + currentElements.filter((t) => t.id !== id), + ); + } +} 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 7918118c3..734c4c882 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,23 +1,8 @@ import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; import { Student } from '../model/student.model'; +import { Store } from './store'; @Injectable({ providedIn: 'root', }) -export class StudentStore { - private students = new BehaviorSubject([]); - students$ = this.students.asObservable(); - - addAll(students: Student[]) { - this.students.next(students); - } - - addOne(student: Student) { - this.students.next([...this.students.value, student]); - } - - deleteOne(id: number) { - this.students.next(this.students.value.filter((s) => s.id !== id)); - } -} +export class StudentStore extends Store {} 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 93f68c4b1..30b3c7f38 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,23 +1,8 @@ import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; import { Teacher } from '../model/teacher.model'; +import { Store } from './store'; @Injectable({ providedIn: 'root', }) -export class TeacherStore { - private teachers = new BehaviorSubject([]); - teachers$ = this.teachers.asObservable(); - - addAll(teachers: Teacher[]) { - this.teachers.next(teachers); - } - - addOne(teacher: Teacher) { - this.teachers.next([...this.teachers.value, teacher]); - } - - deleteOne(id: number) { - this.teachers.next(this.teachers.value.filter((t) => t.id !== id)); - } -} +export class TeacherStore extends Store {} 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 ca3c661de..c35c4983f 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,60 +1,35 @@ -import { NgFor, NgIf } from '@angular/common'; -import { Component, 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 { CardType } from '../../model/card.model'; -import { ListItemComponent } from '../list-item/list-item.component'; +import { NgTemplateOutlet } from '@angular/common'; +import { Component, input, output, TemplateRef } from '@angular/core'; @Component({ selector: 'app-card', template: `
- - + [style.background-color]="customClass()"> +
- + @for (item of list(); track item.id) { + + }
`, - imports: [NgIf, NgFor, ListItemComponent], + imports: [NgTemplateOutlet], }) export class CardComponent { - @Input() list: any[] | null = null; - @Input() type!: CardType; - @Input() customClass = ''; + list = input<{ id: number }[] | null>(null); + customClass = input(''); + templateRef = input.required>(); - CardType = CardType; - - constructor( - private teacherStore: TeacherStore, - private studentStore: StudentStore, - ) {} - - addNewItem() { - if (this.type === CardType.TEACHER) { - this.teacherStore.addOne(randTeacher()); - } else if (this.type === CardType.STUDENT) { - this.studentStore.addOne(randStudent()); - } - } + add = output(); } 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 c0f9cff7f..35df0544e 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,14 +1,11 @@ -import { Component, Input } from '@angular/core'; -import { StudentStore } from '../../data-access/student.store'; -import { TeacherStore } from '../../data-access/teacher.store'; -import { CardType } from '../../model/card.model'; +import { Component, input, output } from '@angular/core'; @Component({ selector: 'app-list-item', template: `
- {{ name }} -
@@ -16,20 +13,8 @@ import { CardType } from '../../model/card.model'; standalone: true, }) export class ListItemComponent { - @Input() id!: number; - @Input() name!: string; - @Input() type!: CardType; + id = input.required(); + name = input.required(); - constructor( - private teacherStore: TeacherStore, - private studentStore: StudentStore, - ) {} - - delete(id: number) { - if (this.type === CardType.TEACHER) { - this.teacherStore.deleteOne(id); - } else if (this.type === CardType.STUDENT) { - this.studentStore.deleteOne(id); - } - } + delete = output(); }