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 30c8f88ec..c1c6693a3 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,13 +1,50 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, Signal } from '@angular/core';
+import { toSignal } from '@angular/core/rxjs-interop';
+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 { ListItemTemplateDirective } from '../../ui/list-item/list-item-template.directive';
+import { ListItemComponent } from '../../ui/list-item/list-item.component';
@Component({
selector: 'app-city-card',
- template: 'TODO City',
+ template: `
+
+
+
+
+
+ {{ city.name }}
+
+
+
+ `,
standalone: true,
- imports: [],
+ imports: [CardComponent, ListItemTemplateDirective, ListItemComponent],
})
export class CityCardComponent implements OnInit {
- constructor() {}
+ cities: Signal = toSignal(this.store.cities$, {
+ initialValue: [],
+ });
+
+ constructor(
+ private http: FakeHttpService,
+ private store: CityStore,
+ ) {}
+
+ ngOnInit(): void {
+ this.http.fetchCities$.subscribe((s) => this.store.addAll(s));
+ }
+
+ addCity() {
+ this.store.addOne(randomCity());
+ }
- ngOnInit(): void {}
+ 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 441cda189..d4909b66b 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,31 +1,41 @@
import { Component, OnInit } from '@angular/core';
-import { FakeHttpService } from '../../data-access/fake-http.service';
+import { toSignal } from '@angular/core/rxjs-interop';
+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 { ListItemTemplateDirective } from '../../ui/list-item/list-item-template.directive';
+import { ListItemComponent } from '../../ui/list-item/list-item.component';
@Component({
selector: 'app-student-card',
template: `
-
+
+
+
+
+
+ {{ student.firstName }}
+
+
+
`,
standalone: true,
styles: [
`
- ::ng-deep .bg-light-green {
+ .bg-light-green {
background-color: rgba(0, 250, 0, 0.1);
}
`,
],
- imports: [CardComponent],
+ imports: [CardComponent, ListItemComponent, ListItemTemplateDirective],
})
export class StudentCardComponent implements OnInit {
- students: Student[] = [];
- cardType = CardType.STUDENT;
+ students = toSignal(this.store.students$, {
+ initialValue: [],
+ });
constructor(
private http: FakeHttpService,
@@ -34,7 +44,13 @@ export class StudentCardComponent implements OnInit {
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(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 995cb7c2f..debbf3c66 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,31 +1,48 @@
-import { Component, OnInit } from '@angular/core';
-import { FakeHttpService } from '../../data-access/fake-http.service';
+import {
+ ChangeDetectionStrategy,
+ Component,
+ OnInit,
+ Signal,
+} from '@angular/core';
+import { toSignal } from '@angular/core/rxjs-interop';
+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 { ListItemTemplateDirective } from '../../ui/list-item/list-item-template.directive';
+import { ListItemComponent } from '../../ui/list-item/list-item.component';
@Component({
selector: 'app-teacher-card',
template: `
-
+
+
+
+
+
+ {{ teacher.firstName }}
+
+
+
`,
styles: [
`
- ::ng-deep .bg-light-red {
+ .bg-light-red {
background-color: rgba(250, 0, 0, 0.1);
}
`,
],
standalone: true,
- imports: [CardComponent],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ imports: [CardComponent, ListItemComponent, ListItemTemplateDirective],
})
export class TeacherCardComponent implements OnInit {
- teachers: Teacher[] = [];
- cardType = CardType.TEACHER;
+ teachers: Signal = toSignal(this.store.teachers$, {
+ initialValue: [],
+ });
constructor(
private http: FakeHttpService,
@@ -34,7 +51,13 @@ export class TeacherCardComponent implements OnInit {
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(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 711dad1d7..c2b6bd1db 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
@@ -13,8 +13,8 @@ export class CityStore {
this.cities.next(cities);
}
- addOne(student: City) {
- this.cities.next([...this.cities.value, student]);
+ addOne(city: City) {
+ this.cities.next([...this.cities.value, city]);
}
deleteOne(id: number) {
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 f06c9ae00..c9879d81a 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,61 +1,48 @@
-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 { NgTemplateOutlet } from '@angular/common';
+import {
+ ChangeDetectionStrategy,
+ Component,
+ contentChild,
+ input,
+ output,
+} from '@angular/core';
+import { ListItemTemplateDirective } from '../list-item/list-item-template.directive';
import { ListItemComponent } from '../list-item/list-item.component';
@Component({
selector: 'app-card',
template: `
-
-
-
+
-
+
+ @for (item of list(); track item) {
+
+ }
+
-
-
+
`,
standalone: true,
- imports: [NgIf, NgFor, ListItemComponent],
+ imports: [ListItemComponent, NgTemplateOutlet],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ host: {
+ class: 'flex w-fit flex-col gap-3 rounded-md border-2 border-black p-4',
+ },
})
-export class CardComponent {
- @Input() list: any[] | null = null;
- @Input() type!: CardType;
- @Input() customClass = '';
+export class CardComponent {
+ list = input([]);
- CardType = CardType;
+ itemTemplate = contentChild(ListItemTemplateDirective);
- constructor(
- private teacherStore: TeacherStore,
- private studentStore: StudentStore,
- ) {}
+ add = output();
addNewItem() {
- if (this.type === CardType.TEACHER) {
- this.teacherStore.addOne(randTeacher());
- } else if (this.type === CardType.STUDENT) {
- this.studentStore.addOne(randStudent());
- }
+ this.add.emit();
}
}
diff --git a/apps/angular/1-projection/src/app/ui/list-item/list-item-template.directive.ts b/apps/angular/1-projection/src/app/ui/list-item/list-item-template.directive.ts
new file mode 100644
index 000000000..95a002400
--- /dev/null
+++ b/apps/angular/1-projection/src/app/ui/list-item/list-item-template.directive.ts
@@ -0,0 +1,9 @@
+import { Directive, TemplateRef } from '@angular/core';
+
+@Directive({
+ selector: '[appListItemTemplate]',
+ standalone: true,
+})
+export class ListItemTemplateDirective {
+ constructor(public templateRef: TemplateRef) {}
+}
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..d7e371cf3 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,35 +1,20 @@
-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 { ChangeDetectionStrategy, Component, output } from '@angular/core';
@Component({
selector: 'app-list-item',
template: `
-
- {{ name }}
-
-
+
+
+
`,
standalone: true,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ host: {
+ class: 'border-grey-300 flex justify-between border px-2 py-1',
+ },
})
export class ListItemComponent {
- @Input() id!: number;
- @Input() name!: string;
- @Input() type!: CardType;
-
- 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);
- }
- }
+ deleteItem = output();
}