Skip to content

Commit 973a069

Browse files
committed
feat(Answer:1): first commit learn ng content and ng template use Solve First Challange
1 parent dbf0e8a commit 973a069

File tree

6 files changed

+163
-103
lines changed

6 files changed

+163
-103
lines changed
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, ItemRefDirective } 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 (addNewItem)="addCity()" [list]="cities()">
15+
<img ngSrc="assets/img/city.png" width="200" height="200" />
16+
<ng-template itemRef let-item>
17+
<app-list-item (onDelete)="deleteCity(item.id)">
18+
{{ item.name }}
19+
</app-list-item>
20+
</ng-template>
21+
</app-card>
22+
`,
23+
styles: [
24+
`
25+
app-card {
26+
background-color: rgba(0, 0, 250, 0.1);
27+
}
28+
`,
29+
],
30+
host: {
31+
class: 'flex justify-center p-4',
32+
},
33+
imports: [
34+
CardComponent,
35+
ListItemComponent,
36+
ItemRefDirective,
37+
NgOptimizedImage,
38+
],
739
changeDetection: ChangeDetectionStrategy.OnPush,
840
})
9-
export class CityCardComponent {}
41+
export class CityCardComponent {
42+
private http = inject(FakeHttpService);
43+
private store = inject(CityStore);
44+
cities = this.store.cities;
45+
46+
ngOnInit(): void {
47+
this.http.fetchCities$.subscribe((c) => this.store.addAll(c));
48+
}
49+
50+
deleteCity(id: number) {
51+
this.store.deleteOne(id);
52+
}
53+
54+
addCity() {
55+
this.store.addOne(randomCity());
56+
}
57+
}
Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,62 @@
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';
10-
import { CardComponent } from '../../ui/card/card.component';
13+
import { CardComponent, ItemRefDirective } 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 (addNewItem)="addStudent()" [list]="students()">
20+
<img ngSrc="assets/img/student.webp" width="200" height="200" priority />
21+
<ng-template itemRef let-item>
22+
<app-list-item (onDelete)="deleteStudent(item.id)">
23+
{{ item.firstName }}
24+
</app-list-item>
25+
</ng-template>
26+
</app-card>
1927
`,
2028
styles: [
2129
`
22-
::ng-deep .bg-light-green {
30+
app-card {
2331
background-color: rgba(0, 250, 0, 0.1);
2432
}
2533
`,
2634
],
27-
imports: [CardComponent],
35+
host: {
36+
class: 'flex justify-center p-4',
37+
},
38+
imports: [
39+
CardComponent,
40+
ListItemComponent,
41+
ItemRefDirective,
42+
NgOptimizedImage,
43+
],
2844
changeDetection: ChangeDetectionStrategy.OnPush,
2945
})
3046
export class StudentCardComponent implements OnInit {
3147
private http = inject(FakeHttpService);
3248
private store = inject(StudentStore);
33-
3449
students = this.store.students;
35-
cardType = CardType.STUDENT;
3650

3751
ngOnInit(): void {
3852
this.http.fetchStudents$.subscribe((s) => this.store.addAll(s));
3953
}
54+
55+
deleteStudent(id: number) {
56+
this.store.deleteOne(id);
57+
}
58+
59+
addStudent() {
60+
this.store.addOne(randStudent());
61+
}
4062
}
Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,55 @@
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';
5-
import { CardComponent } from '../../ui/card/card.component';
8+
import { CardComponent, ItemRefDirective } 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 (addNewItem)="addTeacher()" [list]="teachers()">
15+
<img ngSrc="assets/img/teacher.png" width="200" height="200" priority />
16+
<ng-template itemRef let-item>
17+
<app-list-item (onDelete)="deleteTeacher(item.id)">
18+
{{ item.firstName }}
19+
</app-list-item>
20+
</ng-template>
21+
</app-card>
1422
`,
1523
styles: [
1624
`
17-
::ng-deep .bg-light-red {
25+
app-card {
1826
background-color: rgba(250, 0, 0, 0.1);
1927
}
2028
`,
2129
],
22-
imports: [CardComponent],
30+
host: {
31+
class: 'flex justify-center p-4',
32+
},
33+
imports: [
34+
CardComponent,
35+
ListItemComponent,
36+
NgOptimizedImage,
37+
ItemRefDirective,
38+
],
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+
addTeacher() {
50+
this.store.addOne(randTeacher());
51+
}
52+
deleteTeacher(id: number) {
53+
this.store.deleteOne(id);
54+
}
3455
}

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+
cities = signal<City[]>([]);
99

1010
addAll(cities: City[]) {
1111
this.cities.set(cities);
Lines changed: 37 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,48 @@
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 {
3+
Component,
4+
ContentChild,
5+
Directive,
6+
input,
7+
output,
8+
TemplateRef,
9+
} from '@angular/core';
10+
11+
@Directive({
12+
selector: '[itemRef]',
13+
standalone: true,
14+
})
15+
export class ItemRefDirective {}
816

917
@Component({
1018
selector: 'app-card',
1119
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" />
20+
<ng-content select="img"></ng-content>
21+
<section>
22+
@for (item of list(); track item.id) {
23+
<ng-template
24+
[ngTemplateOutlet]="itemRef"
25+
[ngTemplateOutletContext]="{ $implicit: item }"></ng-template>
1726
}
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>
27+
</section>
28+
<button
29+
class="rounded-sm border border-blue-500 bg-blue-300 p-2"
30+
(click)="addNewItem.emit()">
31+
Add
32+
</button>
3733
`,
38-
imports: [ListItemComponent, NgOptimizedImage],
34+
host: {
35+
class: 'flex w-fit flex-col gap-3 rounded-md border-2 border-black p-4',
36+
},
37+
imports: [NgTemplateOutlet],
3938
})
4039
export class CardComponent {
41-
private teacherStore = inject(TeacherStore);
42-
private studentStore = inject(StudentStore);
43-
40+
addNewItem = output<void>();
4441
readonly list = input<any[] | null>(null);
45-
readonly type = input.required<CardType>();
46-
readonly customClass = input('');
47-
48-
CardType = CardType;
4942

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-
}
43+
// [For My Ref] we can leverage on ng-content and template to reduce duplication and better code managment with reusability without changing much
44+
// Using ContentChild to get the template reference from projected content read as TemplateRef without it doesnt give TemplateRef
45+
@ContentChild(ItemRefDirective, { read: TemplateRef }) itemRef!: TemplateRef<{
46+
$implicit: any;
47+
}>;
5848
}
Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,18 @@
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';
1+
import { ChangeDetectionStrategy, Component, output } from '@angular/core';
102

113
@Component({
124
selector: 'app-list-item',
135
template: `
14-
<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>
19-
</div>
6+
<ng-content></ng-content>
7+
<button (click)="onDelete.emit()">
8+
<img class="h-5" src="assets/svg/trash.svg" />
9+
</button>
2010
`,
11+
host: {
12+
class: 'border-grey-300 flex justify-between border px-2 py-1',
13+
},
2114
changeDetection: ChangeDetectionStrategy.OnPush,
2215
})
2316
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-
}
17+
onDelete = output<void>();
3918
}

0 commit comments

Comments
 (0)