Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/angular/1-projection/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { TeacherCardComponent } from './component/teacher-card/teacher-card.comp
selector: 'app-root',
template: `
<div class="grid grid-cols-3 gap-3">
<!--los componentes ya tienen dentro la configuracion para usar todos el card sin condiciones if-->
<app-teacher-card />
<app-student-card />
<app-city-card />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,47 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { CityStore } from '../../data-access/city.store';
import {
FakeHttpService,
randomCity,
} from '../../data-access/fake-http.service';
import { CardComponent } from '../../ui/card/card.component';
import { ListItemComponent } from '../../ui/list-item/list-item.component';

@Component({
selector: 'app-city-card',
template: 'TODO City',
imports: [],
template: `
<app-card [list]="cities()" [itemTemplate]="cityItem" [onAdd]="addCity">
<p>Titulo para city</p>
<img ngSrc="assets/img/teacher.png" width="200" height="200" />
</app-card>

<!--Template para teacher que se mostrará dentro de app-card -->
<ng-template #cityItem let-item>
<app-list-item
[id]="item.id"
[name]="item.name"
(delete)="deleteCity($event)"></app-list-item>
</ng-template>
`,
imports: [CardComponent, ListItemComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CityCardComponent {}
export class CityCardComponent {
//Inyectamos el store de cityes
private store = inject(CityStore);
private http = inject(FakeHttpService);

cities = this.store.cities;
citiesStore = this.store;

ngOnInit(): void {
this.http.fetchCities$.subscribe((t) => this.store.addAll(t));
}

addCity = () => {
this.citiesStore.addOne(randomCity());
};
deleteCity(id: number) {
this.citiesStore.deleteOne(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,59 @@ import {
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 { ListItemComponent } from '../../ui/list-item/list-item.component';

@Component({
selector: 'app-student-card',
template: `
<app-card
[list]="students()"
[type]="cardType"
customClass="bg-light-green" />
[itemTemplate]="studentsItem"
[onAdd]="addStudent">
<p>Titulo para student</p>
<img ngSrc="assets/img/student.webp" width="200" height="200" />
</app-card>

<!--Template para teacher que se mostrará dentro de app-card -->
<ng-template #studentsItem let-item>
<app-list-item
[id]="item.id"
[name]="item.firstName"
(delete)="deleteStudent($event)"></app-list-item>
</ng-template>
`,
styles: [
`
::ng-deep .bg-light-green {
background-color: rgba(0, 250, 0, 0.1);
}
`,
],
imports: [CardComponent],
// styles: [
// `
// ::ng-deep .bg-light-green {
// background-color: rgba(0, 250, 0, 0.1);
// }
// `,
// ],
imports: [CardComponent, ListItemComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class StudentCardComponent implements OnInit {
private http = inject(FakeHttpService);
private store = inject(StudentStore);

students = this.store.students;
cardType = CardType.STUDENT;
studentsStore = this.store;
//cardType = CardType.STUDENT;

ngOnInit(): void {
this.http.fetchStudents$.subscribe((s) => this.store.addAll(s));
}

addStudent = () => {
this.studentsStore.addOne(randStudent());
};
deleteStudent(id: number) {
this.studentsStore.deleteOne(id);
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,58 @@
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 { ListItemComponent } from '../../ui/list-item/list-item.component';

@Component({
selector: 'app-teacher-card',
template: `
<app-card
[list]="teachers()"
[type]="cardType"
customClass="bg-light-red"></app-card>
[itemTemplate]="teacherItem"
[onAdd]="addTeacher">
<!--la img y el p, como están dentro del componente se visualizará en el ng-content del componente card-->
<p>Titulo para teacher</p>
<img ngSrc="assets/img/teacher.png" width="200" height="200" />
</app-card>

<!--Template para teacher que se mostrará dentro de app-card -->
<ng-template #teacherItem let-item>
<!--encargado de repetir los list-item es el card, encargado de decidir cómo se pinta es el teacher-card-->
<app-list-item
[id]="item.id"
[name]="item.firstName"
(delete)="deleteTeacher($event)"></app-list-item>
</ng-template>
`,
styles: [
`
::ng-deep .bg-light-red {
background-color: rgba(250, 0, 0, 0.1);
}
`,
],
imports: [CardComponent],
// styles: [
// `
// ::ng-deep .bg-light-red {
// background-color: rgba(250, 0, 0, 0.1);
// }
// `,
// ],
imports: [CardComponent, ListItemComponent],
})
export class TeacherCardComponent implements OnInit {
private http = inject(FakeHttpService);
private store = inject(TeacherStore);

teachers = this.store.teachers;
cardType = CardType.TEACHER;
teacherStore = this.store;
// cardType = CardType.TEACHER;

ngOnInit(): void {
this.http.fetchTeachers$.subscribe((t) => this.store.addAll(t));
}

addTeacher = () => {
this.teacherStore.addOne(randTeacher());
};
deleteTeacher(id: number) {
this.teacherStore.deleteOne(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { City } from '../model/city.model';
providedIn: 'root',
})
export class CityStore {
private cities = signal<City[]>([]);
public cities = signal<City[]>([]);

addAll(cities: City[]) {
this.cities.set(cities);
Expand Down
77 changes: 51 additions & 26 deletions apps/angular/1-projection/src/app/ui/card/card.component.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,83 @@
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 { CommonModule, NgOptimizedImage } from '@angular/common';
import { Component, input, TemplateRef } from '@angular/core';
import { CardType } from '../../model/card.model';
import { ListItemComponent } from '../list-item/list-item.component';

//La template que tengo hay ahora mismo es poco reutilizable porque cuando queramos añadir otro tipo de card
//tenemos que poner más condiciones de if y más imágenes en el img y añadir más condiciones de if en la funcion de addNewItem

/*
Esto se hace con:

<ng-content> para contenido estático

<ng-template> + ngTemplateOutlet para contenido dinámico
*/
@Component({
selector: 'app-card',
template: `
<div
class="flex w-fit flex-col gap-3 rounded-md border-2 border-black p-4"
[class]="customClass()">
@if (type() === CardType.TEACHER) {
<div class="flex w-fit flex-col gap-3 rounded-md border-2 border-black p-4">
<!--Eliminamos los if-->
<!-- @if (type() === CardType.TEACHER) {
<img ngSrc="assets/img/teacher.png" width="200" height="200" />
}
@if (type() === CardType.STUDENT) {
<img ngSrc="assets/img/student.webp" width="200" height="200" />
}
} -->
<!--ponermos un <ng-content>; contenido proyectado imagen-->
<ng-content>
<!-- ng-content; hueco donde el padre mete aqui html tal cual, en este caso las img que pasamos dentro del app-card cuando lo llamamos
en teacher, student o city (el img se "inyecta" dentro del ng-content)-->
</ng-content>

<section>
@for (item of list(); track item) {
<app-list-item
<!-- <app-list-item
[name]="item.firstName"
[id]="item.id"
[type]="type()"></app-list-item>
[type]="type()"></app-list-item> -->
<!--el ng-container lo que hace es en este caso
repetir un template (la definida en el teacher, student y city)
para cada elemento de la lista que pasemos al card (de esta forma el card no sabe nada por asi decirlo, solo
sabe que tiene que representar una template de forma repetida-->
<ng-container
[ngTemplateOutlet]="itemTemplate()"
[ngTemplateOutletContext]="{ $implicit: item }"></ng-container>
}
</section>

<button
class="rounded-sm border border-blue-500 bg-blue-300 p-2"
(click)="addNewItem()">
(click)="onAdd()">
Add
</button>
</div>
`,
imports: [ListItemComponent, NgOptimizedImage],
imports: [CommonModule, ListItemComponent, NgOptimizedImage],
})
export class CardComponent {
private teacherStore = inject(TeacherStore);
private studentStore = inject(StudentStore);
//Input que permite que el padre decida como se ve cada item de la lista
//@Input() itemTemplate!: TemplateRef<any>;
//Usamos un input signal mejor -> en el ngContainer se llama con () por ser un signal:
readonly itemTemplate = input<TemplateRef<any> | null>(null);
//Input signal para recibir una funcion
readonly onAdd = input<() => void>();

// private teacherStore = inject(TeacherStore);
// private studentStore = inject(StudentStore);

readonly list = input<any[] | null>(null);
readonly type = input.required<CardType>();
readonly customClass = input('');
// readonly type = input.required<CardType>();
// 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());
}
}
// addNewItem() {
// const type = this.type();
// if (type === CardType.TEACHER) {
// this.teacherStore.addOne(randTeacher());
// } else if (type === CardType.STUDENT) {
// this.studentStore.addOne(randStudent());
// }
// }
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
import {
ChangeDetectionStrategy,
Component,
inject,
EventEmitter,
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: `
<div class="border-grey-300 flex justify-between border px-2 py-1">
{{ name() }}
<button (click)="delete(id())">
<button (click)="deleteItem()">
<img class="h-5" src="assets/svg/trash.svg" />
</button>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ListItemComponent {
private teacherStore = inject(TeacherStore);
private studentStore = inject(StudentStore);
@Output() delete = new EventEmitter<number>();

// private teacherStore = inject(TeacherStore);
// private studentStore = inject(StudentStore);

readonly id = input.required<number>();
readonly name = input.required<string>();
readonly type = input.required<CardType>();
// readonly type = input.required<CardType>();

delete(id: number) {
const type = this.type();
deleteItem() {
this.delete.emit(this.id());
/*const type = this.type();
if (type === CardType.TEACHER) {
this.teacherStore.deleteOne(id);
} else if (type === CardType.STUDENT) {
this.studentStore.deleteOne(id);
}
}*/
}
}