Skip to content

Commit ee70bbd

Browse files
committed
feat: refactoring CardComponent via ngTemplateOutlet
1 parent fbb5e82 commit ee70bbd

7 files changed

Lines changed: 138 additions & 102 deletions

File tree

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,57 @@
1-
import { ChangeDetectionStrategy, Component } from '@angular/core';
1+
import { NgOptimizedImage } from '@angular/common';
2+
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
3+
import { CityStore } from '../../data-access/city.store';
4+
import {
5+
FakeHttpService,
6+
randomCity,
7+
} from '../../data-access/fake-http.service';
8+
import { CardComponent } from '../../ui/card/card.component';
9+
import { ListItemComponent } from '../../ui/list-item/list-item.component';
210

311
@Component({
412
selector: 'app-city-card',
5-
template: 'TODO City',
6-
imports: [],
13+
template: `
14+
<app-card [list]="cities()" class="bg-light-yellow">
15+
<img ngSrc="assets/img/city.png" width="200" height="200" />
16+
<ng-template #rowRef let-city>
17+
<app-list-item>
18+
{{ city.name }}
19+
<button (click)="deleteCity(city.id)">
20+
<img class="h-5" src="assets/svg/trash.svg" />
21+
</button>
22+
</app-list-item>
23+
</ng-template>
24+
<button
25+
class="rounded-sm border border-blue-500 bg-blue-300 p-2"
26+
(click)="addCity()">
27+
Add
28+
</button>
29+
</app-card>
30+
`,
31+
styles: [
32+
`
33+
.bg-light-yellow {
34+
background-color: rgba(250, 250, 0, 0.1);
35+
}
36+
`,
37+
],
38+
imports: [CardComponent, NgOptimizedImage, ListItemComponent],
739
changeDetection: ChangeDetectionStrategy.OnPush,
840
})
9-
export class CityCardComponent {}
41+
export class CityCardComponent {
42+
private http = inject(FakeHttpService);
43+
private store = inject(CityStore);
44+
45+
cities = this.store.cities;
46+
47+
ngOnInit(): void {
48+
this.http.fetchCities$.subscribe((s) => this.store.addAll(s));
49+
}
50+
51+
addCity() {
52+
this.store.addOne(randomCity());
53+
}
54+
deleteCity(id: number) {
55+
this.store.deleteOne(id);
56+
}
57+
}
Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,63 @@
1+
import { NgOptimizedImage } from '@angular/common';
12
import {
23
ChangeDetectionStrategy,
34
Component,
45
inject,
56
OnInit,
67
} from '@angular/core';
7-
import { FakeHttpService } from '../../data-access/fake-http.service';
8+
import {
9+
FakeHttpService,
10+
randStudent,
11+
} from '../../data-access/fake-http.service';
812
import { StudentStore } from '../../data-access/student.store';
9-
import { CardType } from '../../model/card.model';
1013
import { CardComponent } from '../../ui/card/card.component';
14+
import { ListItemComponent } from '../../ui/list-item/list-item.component';
1115

1216
@Component({
1317
selector: 'app-student-card',
1418
template: `
15-
<app-card
16-
[list]="students()"
17-
[type]="cardType"
18-
customClass="bg-light-green" />
19+
<app-card [list]="students()" class="">
20+
<img ngSrc="assets/img/student.webp" width="200" height="200" />
21+
<ng-template #rowRef let-student>
22+
<app-list-item>
23+
{{ student.firstName }}
24+
<button (click)="deleteStudent(student.id)">
25+
<img class="h-5" src="assets/svg/trash.svg" />
26+
</button>
27+
</app-list-item>
28+
</ng-template>
29+
<button
30+
class="rounded-sm border border-blue-500 bg-blue-300 p-2"
31+
(click)="addStudent()">
32+
Add
33+
</button>
34+
</app-card>
1935
`,
2036
styles: [
2137
`
22-
::ng-deep .bg-light-green {
38+
.bg-light-green {
2339
background-color: rgba(0, 250, 0, 0.1);
2440
}
2541
`,
2642
],
27-
imports: [CardComponent],
43+
imports: [CardComponent, NgOptimizedImage, ListItemComponent],
2844
changeDetection: ChangeDetectionStrategy.OnPush,
2945
})
3046
export class StudentCardComponent implements OnInit {
3147
private http = inject(FakeHttpService);
3248
private store = inject(StudentStore);
3349

3450
students = this.store.students;
35-
cardType = CardType.STUDENT;
3651

3752
ngOnInit(): void {
3853
this.http.fetchStudents$.subscribe((s) => this.store.addAll(s));
3954
}
55+
56+
addStudent() {
57+
this.store.addOne(randStudent());
58+
}
59+
60+
deleteStudent(id: number) {
61+
this.store.deleteOne(id);
62+
}
4063
}
Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,57 @@
1+
import { NgOptimizedImage } from '@angular/common';
12
import { Component, inject, OnInit } from '@angular/core';
2-
import { FakeHttpService } from '../../data-access/fake-http.service';
3+
import {
4+
FakeHttpService,
5+
randTeacher,
6+
} from '../../data-access/fake-http.service';
37
import { TeacherStore } from '../../data-access/teacher.store';
4-
import { CardType } from '../../model/card.model';
58
import { CardComponent } from '../../ui/card/card.component';
9+
import { ListItemComponent } from '../../ui/list-item/list-item.component';
610

711
@Component({
812
selector: 'app-teacher-card',
913
template: `
10-
<app-card
11-
[list]="teachers()"
12-
[type]="cardType"
13-
customClass="bg-light-red"></app-card>
14+
<app-card [list]="teachers()" class="bg-light-red">
15+
<img ngSrc="assets/img/teacher.png" width="200" height="200" />
16+
<ng-template #rowRef let-teacher>
17+
<app-list-item>
18+
{{ teacher.firstName }}
19+
<button (click)="deleteTeacher(teacher.id)">
20+
<img class="h-5" src="assets/svg/trash.svg" />
21+
</button>
22+
</app-list-item>
23+
</ng-template>
24+
<button
25+
class="rounded-sm border border-blue-500 bg-blue-300 p-2"
26+
(click)="addTeacher()">
27+
Add
28+
</button>
29+
</app-card>
1430
`,
1531
styles: [
1632
`
17-
::ng-deep .bg-light-red {
33+
.bg-light-red {
1834
background-color: rgba(250, 0, 0, 0.1);
1935
}
2036
`,
2137
],
22-
imports: [CardComponent],
38+
imports: [CardComponent, NgOptimizedImage, ListItemComponent],
2339
})
2440
export class TeacherCardComponent implements OnInit {
2541
private http = inject(FakeHttpService);
2642
private store = inject(TeacherStore);
2743

2844
teachers = this.store.teachers;
29-
cardType = CardType.TEACHER;
3045

3146
ngOnInit(): void {
3247
this.http.fetchTeachers$.subscribe((t) => this.store.addAll(t));
3348
}
49+
50+
addTeacher() {
51+
this.store.addOne(randTeacher());
52+
}
53+
54+
deleteTeacher(id: number) {
55+
this.store.deleteOne(id);
56+
}
3457
}

apps/angular/1-projection/src/app/data-access/city.store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { City } from '../model/city.model';
55
providedIn: 'root',
66
})
77
export class CityStore {
8-
private cities = signal<City[]>([]);
8+
public cities = signal<City[]>([]);
99

1010
addAll(cities: City[]) {
1111
this.cities.set(cities);
Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,28 @@
1-
import { NgOptimizedImage } from '@angular/common';
2-
import { Component, inject, input } from '@angular/core';
3-
import { randStudent, randTeacher } from '../../data-access/fake-http.service';
4-
import { StudentStore } from '../../data-access/student.store';
5-
import { TeacherStore } from '../../data-access/teacher.store';
6-
import { CardType } from '../../model/card.model';
7-
import { ListItemComponent } from '../list-item/list-item.component';
1+
import { NgTemplateOutlet } from '@angular/common';
2+
import { Component, ContentChild, input, TemplateRef } from '@angular/core';
83

94
@Component({
105
selector: 'app-card',
116
template: `
12-
<div
13-
class="flex w-fit flex-col gap-3 rounded-md border-2 border-black p-4"
14-
[class]="customClass()">
15-
@if (type() === CardType.TEACHER) {
16-
<img ngSrc="assets/img/teacher.png" width="200" height="200" />
7+
<ng-content select="img" />
8+
<section>
9+
@for (item of list(); track item) {
10+
<ng-container
11+
*ngTemplateOutlet="
12+
rowRef;
13+
context: { $implicit: item }
14+
"></ng-container>
1715
}
18-
@if (type() === CardType.STUDENT) {
19-
<img ngSrc="assets/img/student.webp" width="200" height="200" />
20-
}
21-
22-
<section>
23-
@for (item of list(); track item) {
24-
<app-list-item
25-
[name]="item.firstName"
26-
[id]="item.id"
27-
[type]="type()"></app-list-item>
28-
}
29-
</section>
30-
31-
<button
32-
class="rounded-sm border border-blue-500 bg-blue-300 p-2"
33-
(click)="addNewItem()">
34-
Add
35-
</button>
36-
</div>
16+
</section>
17+
<ng-content />
3718
`,
38-
imports: [ListItemComponent, NgOptimizedImage],
19+
imports: [NgTemplateOutlet],
20+
host: {
21+
class: 'flex w-fit flex-col gap-3 rounded-md border-2 border-black p-4 ',
22+
},
3923
})
4024
export class CardComponent {
41-
private teacherStore = inject(TeacherStore);
42-
private studentStore = inject(StudentStore);
43-
25+
@ContentChild('rowRef') rowRef!: TemplateRef<any>;
4426
readonly list = input<any[] | null>(null);
45-
readonly type = input.required<CardType>();
4627
readonly customClass = input('');
47-
48-
CardType = CardType;
49-
50-
addNewItem() {
51-
const type = this.type();
52-
if (type === CardType.TEACHER) {
53-
this.teacherStore.addOne(randTeacher());
54-
} else if (type === CardType.STUDENT) {
55-
this.studentStore.addOne(randStudent());
56-
}
57-
}
5828
}
Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,11 @@
1-
import {
2-
ChangeDetectionStrategy,
3-
Component,
4-
inject,
5-
input,
6-
} from '@angular/core';
7-
import { StudentStore } from '../../data-access/student.store';
8-
import { TeacherStore } from '../../data-access/teacher.store';
9-
import { CardType } from '../../model/card.model';
10-
1+
import { ChangeDetectionStrategy, Component } from '@angular/core';
112
@Component({
123
selector: 'app-list-item',
134
template: `
145
<div class="border-grey-300 flex justify-between border px-2 py-1">
15-
{{ name() }}
16-
<button (click)="delete(id())">
17-
<img class="h-5" src="assets/svg/trash.svg" />
18-
</button>
6+
<ng-content></ng-content>
197
</div>
208
`,
219
changeDetection: ChangeDetectionStrategy.OnPush,
2210
})
23-
export class ListItemComponent {
24-
private teacherStore = inject(TeacherStore);
25-
private studentStore = inject(StudentStore);
26-
27-
readonly id = input.required<number>();
28-
readonly name = input.required<string>();
29-
readonly type = input.required<CardType>();
30-
31-
delete(id: number) {
32-
const type = this.type();
33-
if (type === CardType.TEACHER) {
34-
this.teacherStore.deleteOne(id);
35-
} else if (type === CardType.STUDENT) {
36-
this.studentStore.deleteOne(id);
37-
}
38-
}
39-
}
11+
export class ListItemComponent {}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"node": ">=20.19.6"
1616
},
1717
"private": true,
18-
"packageManager": "pnpm@10.23.0",
18+
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48",
1919
"dependencies": {
2020
"@actions/core": "^1.10.1",
2121
"@actions/github": "^6.0.0",

0 commit comments

Comments
 (0)