diff --git a/apps/angular/projection/src/app/component/student-card/student-card.component.ts b/apps/angular/projection/src/app/component/student-card/student-card.component.ts
index 441cda189..569e6d6af 100644
--- a/apps/angular/projection/src/app/component/student-card/student-card.component.ts
+++ b/apps/angular/projection/src/app/component/student-card/student-card.component.ts
@@ -1,40 +1,52 @@
-import { Component, OnInit } from '@angular/core';
-import { FakeHttpService } from '../../data-access/fake-http.service';
+import { AsyncPipe } from '@angular/common';
+import { ChangeDetectionStrategy, Component, inject } 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 { CardRowDirective } from '../../ui/card/card-row.directive';
import { CardComponent } from '../../ui/card/card.component';
+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, AsyncPipe, CardRowDirective],
+ changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class StudentCardComponent implements OnInit {
- students: Student[] = [];
- cardType = CardType.STUDENT;
+export class StudentCardComponent {
+ private http = inject(FakeHttpService);
+ private store = inject(StudentStore);
- constructor(
- private http: FakeHttpService,
- private store: StudentStore,
- ) {}
+ students = this.store.students;
- ngOnInit(): void {
+ constructor() {
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/projection/src/app/component/teacher-card/teacher-card.component.ts b/apps/angular/projection/src/app/component/teacher-card/teacher-card.component.ts
index 995cb7c2f..ed18d3fd2 100644
--- a/apps/angular/projection/src/app/component/teacher-card/teacher-card.component.ts
+++ b/apps/angular/projection/src/app/component/teacher-card/teacher-card.component.ts
@@ -1,40 +1,52 @@
-import { Component, OnInit } from '@angular/core';
-import { FakeHttpService } from '../../data-access/fake-http.service';
+import { AsyncPipe } from '@angular/common';
+import { ChangeDetectionStrategy, Component, inject } 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 { CardRowDirective } from '../../ui/card/card-row.directive';
import { CardComponent } from '../../ui/card/card.component';
+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],
+ imports: [ListItemComponent, AsyncPipe, CardRowDirective, CardComponent],
+ changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class TeacherCardComponent implements OnInit {
- teachers: Teacher[] = [];
- cardType = CardType.TEACHER;
+export class TeacherCardComponent {
+ private http = inject(FakeHttpService);
+ private store = inject(TeacherStore);
- constructor(
- private http: FakeHttpService,
- private store: TeacherStore,
- ) {}
+ teachers = this.store.teachers;
- ngOnInit(): void {
+ constructor() {
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/projection/src/app/data-access/student.store.ts b/apps/angular/projection/src/app/data-access/student.store.ts
index 7918118c3..47e748162 100644
--- a/apps/angular/projection/src/app/data-access/student.store.ts
+++ b/apps/angular/projection/src/app/data-access/student.store.ts
@@ -1,23 +1,21 @@
-import { Injectable } from '@angular/core';
-import { BehaviorSubject } from 'rxjs';
+import { Injectable, signal } from '@angular/core';
import { Student } from '../model/student.model';
@Injectable({
providedIn: 'root',
})
export class StudentStore {
- private students = new BehaviorSubject([]);
- students$ = this.students.asObservable();
+ students = signal([]);
addAll(students: Student[]) {
- this.students.next(students);
+ this.students.set(students);
}
addOne(student: Student) {
- this.students.next([...this.students.value, student]);
+ this.students.set([...this.students(), student]);
}
deleteOne(id: number) {
- this.students.next(this.students.value.filter((s) => s.id !== id));
+ this.students.set(this.students().filter((s) => s.id !== id));
}
}
diff --git a/apps/angular/projection/src/app/data-access/teacher.store.ts b/apps/angular/projection/src/app/data-access/teacher.store.ts
index 93f68c4b1..c30b6e0ec 100644
--- a/apps/angular/projection/src/app/data-access/teacher.store.ts
+++ b/apps/angular/projection/src/app/data-access/teacher.store.ts
@@ -1,23 +1,21 @@
-import { Injectable } from '@angular/core';
-import { BehaviorSubject } from 'rxjs';
+import { Injectable, signal } from '@angular/core';
import { Teacher } from '../model/teacher.model';
@Injectable({
providedIn: 'root',
})
export class TeacherStore {
- private teachers = new BehaviorSubject([]);
- teachers$ = this.teachers.asObservable();
+ teachers = signal([]);
addAll(teachers: Teacher[]) {
- this.teachers.next(teachers);
+ this.teachers.set(teachers);
}
addOne(teacher: Teacher) {
- this.teachers.next([...this.teachers.value, teacher]);
+ this.teachers.set([...this.teachers(), teacher]);
}
deleteOne(id: number) {
- this.teachers.next(this.teachers.value.filter((t) => t.id !== id));
+ this.teachers.set(this.teachers().filter((t) => t.id !== id));
}
}
diff --git a/apps/angular/projection/src/app/model/card.model.ts b/apps/angular/projection/src/app/model/card.model.ts
index 740cd2ae4..e69de29bb 100644
--- a/apps/angular/projection/src/app/model/card.model.ts
+++ b/apps/angular/projection/src/app/model/card.model.ts
@@ -1,5 +0,0 @@
-export enum CardType {
- TEACHER,
- STUDENT,
- CITY,
-}
diff --git a/apps/angular/projection/src/app/ui/card/card-row.directive.ts b/apps/angular/projection/src/app/ui/card/card-row.directive.ts
new file mode 100644
index 000000000..ce3d637cb
--- /dev/null
+++ b/apps/angular/projection/src/app/ui/card/card-row.directive.ts
@@ -0,0 +1,17 @@
+import { Directive, input } from '@angular/core';
+
+interface CardRowContext {
+ $implicit: T;
+}
+
+@Directive({ selector: 'ng-template[cardRow]', standalone: true })
+export class CardRowDirective {
+ cardRow = input.required();
+
+ static ngTemplateContextGuard(
+ dir: CardRowDirective,
+ ctx: unknown,
+ ): ctx is CardRowContext {
+ return true;
+ }
+}
diff --git a/apps/angular/projection/src/app/ui/card/card.component.ts b/apps/angular/projection/src/app/ui/card/card.component.ts
index f06c9ae00..499ef670e 100644
--- a/apps/angular/projection/src/app/ui/card/card.component.ts
+++ b/apps/angular/projection/src/app/ui/card/card.component.ts
@@ -1,61 +1,43 @@
-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 {
+ ChangeDetectionStrategy,
+ Component,
+ contentChild,
+ input,
+ output,
+ TemplateRef,
+} from '@angular/core';
+import { CardRowDirective } from './card-row.directive';
@Component({
selector: 'app-card',
template: `
-
-
-
+
-
+
+ @for (item of items(); track item.id) {
+
+ }
+
-
-
+
`,
standalone: true,
- imports: [NgIf, NgFor, ListItemComponent],
+ imports: [NgTemplateOutlet],
+ host: {
+ class: 'border-2 border-black rounded-md p-4 w-fit flex flex-col gap-3',
+ },
+ changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class CardComponent {
- @Input() list: any[] | null = null;
- @Input() type!: CardType;
- @Input() customClass = '';
+export class CardComponent {
+ items = input.required();
+ add = output();
- 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());
- }
- }
+ rowTemplate = contentChild.required(CardRowDirective, { read: TemplateRef });
}
diff --git a/apps/angular/projection/src/app/ui/list-item/list-item.component.ts b/apps/angular/projection/src/app/ui/list-item/list-item.component.ts
index c0f9cff7f..45b1f7a78 100644
--- a/apps/angular/projection/src/app/ui/list-item/list-item.component.ts
+++ b/apps/angular/projection/src/app/ui/list-item/list-item.component.ts
@@ -1,35 +1,18 @@
-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,
})
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);
- }
- }
+ delete = output();
}