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..8605256b8 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,73 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { NgOptimizedImage } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + inject, + OnInit, +} from '@angular/core'; +import { CityStore } from '../../data-access/city.store'; +import { + FakeHttpService, + randomCity, +} from '../../data-access/fake-http.service'; +import { CardActions } from '../../model'; +import { + CardComponent, + CardSectionDirective, + ListItemComponent, +} from '../../ui'; @Component({ selector: 'app-city-card', - template: 'TODO City', - imports: [], + template: ` + + + + + + + + + + `, + imports: [ + CardComponent, + NgOptimizedImage, + ListItemComponent, + CardSectionDirective, + ], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class CityCardComponent {} +export class CityCardComponent implements OnInit, CardActions { + private readonly http = inject(FakeHttpService); + private readonly store = inject(CityStore); + + cities = this.store.cities; + + ngOnInit(): void { + this.http.fetchCities$.subscribe((cities) => this.store.addAll(cities)); + } + + onAddNewItem() { + this.store.addOne(randomCity()); + } + + onDeleteItem(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..82f99ca2a 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,40 +1,72 @@ +import { NgOptimizedImage } from '@angular/common'; import { ChangeDetectionStrategy, Component, 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'; - +import { CardActions } from '../../model'; +import { + CardComponent, + CardSectionDirective, + ListItemComponent, +} from '../../ui'; @Component({ selector: 'app-student-card', template: ` + customClass="bg-light-green" + [cardItemTemplate]="cardItemTemplate"> + + + + + + + + `, - styles: [ - ` - ::ng-deep .bg-light-green { - background-color: rgba(0, 250, 0, 0.1); - } - `, + imports: [ + CardComponent, + NgOptimizedImage, + ListItemComponent, + CardSectionDirective, ], - imports: [CardComponent], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class StudentCardComponent implements OnInit { - private http = inject(FakeHttpService); - private store = inject(StudentStore); +export class StudentCardComponent implements OnInit, CardActions { + private readonly http = inject(FakeHttpService); + private readonly store = inject(StudentStore); students = this.store.students; - cardType = CardType.STUDENT; ngOnInit(): void { this.http.fetchStudents$.subscribe((s) => this.store.addAll(s)); } + + onAddNewItem() { + this.store.addOne(randStudent()); + } + + onDeleteItem(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..1e8914cb2 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,34 +1,67 @@ +import { NgOptimizedImage } from '@angular/common'; 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'; - +import { CardActions } from '../../model'; +import { + CardComponent, + CardSectionDirective, + ListItemComponent, +} from '../../ui'; @Component({ selector: 'app-teacher-card', template: ` + customClass="bg-light-red" + [cardItemTemplate]="cardItemTemplate"> + + + + + + + + `, - styles: [ - ` - ::ng-deep .bg-light-red { - background-color: rgba(250, 0, 0, 0.1); - } - `, + imports: [ + CardComponent, + NgOptimizedImage, + ListItemComponent, + CardSectionDirective, ], - imports: [CardComponent], }) -export class TeacherCardComponent implements OnInit { - private http = inject(FakeHttpService); - private store = inject(TeacherStore); +export class TeacherCardComponent implements OnInit, CardActions { + private readonly http = inject(FakeHttpService); + private readonly store = inject(TeacherStore); teachers = this.store.teachers; - cardType = CardType.TEACHER; ngOnInit(): void { this.http.fetchTeachers$.subscribe((t) => this.store.addAll(t)); } + + onDeleteItem(id: number) { + this.store.deleteOne(id); + } + + onAddNewItem() { + this.store.addOne(randTeacher()); + } } 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/model/base.model.ts b/apps/angular/1-projection/src/app/model/base.model.ts new file mode 100644 index 000000000..1f8f62227 --- /dev/null +++ b/apps/angular/1-projection/src/app/model/base.model.ts @@ -0,0 +1,3 @@ +export interface BaseItem { + id: number; +} diff --git a/apps/angular/1-projection/src/app/model/card-actions.interface.ts b/apps/angular/1-projection/src/app/model/card-actions.interface.ts new file mode 100644 index 000000000..bc40a520b --- /dev/null +++ b/apps/angular/1-projection/src/app/model/card-actions.interface.ts @@ -0,0 +1,4 @@ +export interface CardActions { + onDeleteItem(id: number): void; + onAddNewItem(): void; +} diff --git a/apps/angular/1-projection/src/app/model/city.model.ts b/apps/angular/1-projection/src/app/model/city.model.ts index dd26b1957..d7121d46a 100644 --- a/apps/angular/1-projection/src/app/model/city.model.ts +++ b/apps/angular/1-projection/src/app/model/city.model.ts @@ -1,5 +1,6 @@ -export interface City { - id: number; +import { BaseItem } from './base.model'; + +export interface City extends BaseItem { name: string; country: string; } diff --git a/apps/angular/1-projection/src/app/model/index.ts b/apps/angular/1-projection/src/app/model/index.ts new file mode 100644 index 000000000..70b682773 --- /dev/null +++ b/apps/angular/1-projection/src/app/model/index.ts @@ -0,0 +1,5 @@ +export * from './card-actions.interface'; +export * from './card.model'; +export * from './city.model'; +export * from './student.model'; +export * from './teacher.model'; diff --git a/apps/angular/1-projection/src/app/model/student.model.ts b/apps/angular/1-projection/src/app/model/student.model.ts index bc18e464a..ece603cc4 100644 --- a/apps/angular/1-projection/src/app/model/student.model.ts +++ b/apps/angular/1-projection/src/app/model/student.model.ts @@ -1,7 +1,7 @@ +import { BaseItem } from './base.model'; import { Teacher } from './teacher.model'; -export interface Student { - id: number; +export interface Student extends BaseItem { firstName: string; lastName: string; mainTeacher: Teacher; diff --git a/apps/angular/1-projection/src/app/model/teacher.model.ts b/apps/angular/1-projection/src/app/model/teacher.model.ts index 34b4241be..b0c6e96ac 100644 --- a/apps/angular/1-projection/src/app/model/teacher.model.ts +++ b/apps/angular/1-projection/src/app/model/teacher.model.ts @@ -1,3 +1,5 @@ +import { BaseItem } from './base.model'; + export const subject = [ 'Sciences', 'History', @@ -5,10 +7,10 @@ export const subject = [ 'Maths', 'Sport', ] as const; + export type Subject = (typeof subject)[number]; -export interface Teacher { - id: number; +export interface Teacher extends BaseItem { firstName: string; lastName: string; subject: Subject; 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..89c6a624a 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,33 @@ -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 { CardType } from '../../model/card.model'; -import { ListItemComponent } from '../list-item/list-item.component'; - +import { CommonModule } from '@angular/common'; +import { Component, input, TemplateRef } from '@angular/core'; +import { BaseItem } from '../../model/base.model'; @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.id) { + } -
+
- + `, - imports: [ListItemComponent, NgOptimizedImage], + imports: [CommonModule], }) -export class CardComponent { - private teacherStore = inject(TeacherStore); - private studentStore = inject(StudentStore); - - readonly list = input(null); - readonly type = input.required(); +export class CardComponent { + readonly list = input(null); readonly customClass = input(''); - - CardType = CardType; - - addNewItem() { - const type = this.type(); - if (type === CardType.TEACHER) { - this.teacherStore.addOne(randTeacher()); - } else if (type === CardType.STUDENT) { - this.studentStore.addOne(randStudent()); - } - } + readonly cardItemTemplate = input.required>(); } diff --git a/apps/angular/1-projection/src/app/ui/card/directive/card-section.directive.ts b/apps/angular/1-projection/src/app/ui/card/directive/card-section.directive.ts new file mode 100644 index 000000000..7eb6dd793 --- /dev/null +++ b/apps/angular/1-projection/src/app/ui/card/directive/card-section.directive.ts @@ -0,0 +1,8 @@ +import { Directive, input } from '@angular/core'; + +@Directive({ + selector: '[cardSection]', +}) +export class CardSectionDirective { + cardSection = input.required<['header', 'footer'][number]>(); +} diff --git a/apps/angular/1-projection/src/app/ui/index.ts b/apps/angular/1-projection/src/app/ui/index.ts new file mode 100644 index 000000000..b79e94ad3 --- /dev/null +++ b/apps/angular/1-projection/src/app/ui/index.ts @@ -0,0 +1,3 @@ +export * from './card/card.component'; +export * from './card/directive/card-section.directive'; +export * from './list-item/list-item.component'; 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..ac66aea64 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,17 +1,14 @@ 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() }}