diff --git a/.postcssrc.json b/.postcssrc.json
new file mode 100644
index 000000000..e092dc7c1
--- /dev/null
+++ b/.postcssrc.json
@@ -0,0 +1,5 @@
+{
+ "plugins": {
+ "@tailwindcss/postcss": {}
+ }
+}
diff --git a/apps/angular/1-projection/src/app/app.component.ts b/apps/angular/1-projection/src/app/app.component.ts
index df654bbc2..75dc670bc 100644
--- a/apps/angular/1-projection/src/app/app.component.ts
+++ b/apps/angular/1-projection/src/app/app.component.ts
@@ -5,6 +5,7 @@ import { TeacherCardComponent } from './component/teacher-card/teacher-card.comp
@Component({
selector: 'app-root',
+ standalone: true,
template: `
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..07de0d625 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,38 @@
-import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Component, OnInit } from '@angular/core';
+import { CityStore } from '../../data-access/city.store';
+import { FakeHttpService } from '../../data-access/fake-http.service';
+import { CardType } from '../../model/card.model';
+import { CardComponent } from '../../ui/card/card.component';
@Component({
selector: 'app-city-card',
- template: 'TODO City',
- imports: [],
- changeDetection: ChangeDetectionStrategy.OnPush,
+ template: `
+
+
+
+
+
+ `,
+ standalone: true,
+ imports: [CommonModule, CardComponent],
+ // changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class CityCardComponent {}
+export class CityCardComponent implements OnInit {
+ // private http = inject(FakeHttpService);
+ // private store = inject(CityStore);
+
+ constructor(
+ private http: FakeHttpService,
+ private store: CityStore,
+ ) {}
+
+ cities = this.store.cities;
+ cardType = CardType.CITY;
+
+ ngOnInit() {
+ this.http.fetchCities$.subscribe((c) => {
+ this.store.addAll(c);
+ });
+ }
+}
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..9a2df8a4b 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,3 +1,4 @@
+import { CommonModule } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
@@ -15,7 +16,12 @@ import { CardComponent } from '../../ui/card/card.component';
+
+
+
+
`,
styles: [
`
@@ -24,7 +30,8 @@ import { CardComponent } from '../../ui/card/card.component';
}
`,
],
- imports: [CardComponent],
+ imports: [CardComponent, CommonModule],
+ standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class StudentCardComponent implements OnInit {
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..2ef8a5576 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,3 +1,4 @@
+import { CommonModule } from '@angular/common';
import { Component, inject, OnInit } from '@angular/core';
import { FakeHttpService } from '../../data-access/fake-http.service';
import { TeacherStore } from '../../data-access/teacher.store';
@@ -5,12 +6,18 @@ import { CardType } from '../../model/card.model';
import { CardComponent } from '../../ui/card/card.component';
@Component({
+ standalone: true,
selector: 'app-teacher-card',
template: `
+ [template]="images"
+ customClass="bg-light-red">
+
+
+
+
`,
styles: [
`
@@ -19,7 +26,7 @@ import { CardComponent } from '../../ui/card/card.component';
}
`,
],
- imports: [CardComponent],
+ imports: [CommonModule, CardComponent],
})
export class TeacherCardComponent implements OnInit {
private http = inject(FakeHttpService);
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/ui/card/card.component.ts b/apps/angular/1-projection/src/app/ui/card/card.component.ts
index 1a6c3648c..0ea649fcb 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,6 +1,11 @@
-import { NgOptimizedImage } from '@angular/common';
-import { Component, inject, input } from '@angular/core';
-import { randStudent, randTeacher } from '../../data-access/fake-http.service';
+import { CommonModule } from '@angular/common';
+import { Component, inject, input, Input, TemplateRef } from '@angular/core';
+import { CityStore } from '../../data-access/city.store';
+import {
+ randomCity,
+ 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';
@@ -12,12 +17,8 @@ import { ListItemComponent } from '../list-item/list-item.component';
- @if (type() === CardType.TEACHER) {
-
![]()
- }
- @if (type() === CardType.STUDENT) {
-
![]()
- }
+ <
+
@for (item of list(); track item) {
@@ -27,7 +28,7 @@ import { ListItemComponent } from '../list-item/list-item.component';
[type]="type()">
}
-
+
`,
- imports: [ListItemComponent, NgOptimizedImage],
+ imports: [ListItemComponent, CommonModule],
+ standalone: true,
})
export class CardComponent {
+ @Input() template!: TemplateRef;
private teacherStore = inject(TeacherStore);
private studentStore = inject(StudentStore);
+ private cityStore = inject(CityStore);
+ store = input(null);
readonly list = input(null);
readonly type = input.required();
@@ -53,6 +58,8 @@ export class CardComponent {
this.teacherStore.addOne(randTeacher());
} else if (type === CardType.STUDENT) {
this.studentStore.addOne(randStudent());
+ } else if (type === CardType.CITY) {
+ this.cityStore.addOne(randomCity());
}
}
}
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..aabe9bd95 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,15 +1,12 @@
-import {
- ChangeDetectionStrategy,
- Component,
- inject,
- input,
-} from '@angular/core';
-import { StudentStore } from '../../data-access/student.store';
-import { TeacherStore } from '../../data-access/teacher.store';
+import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { CardType } from '../../model/card.model';
+import { CityStore } from './../../data-access/city.store';
+import { StudentStore } from './../../data-access/student.store';
+import { TeacherStore } from './../../data-access/teacher.store';
@Component({
selector: 'app-list-item',
+ standalone: true,
template: `
{{ name() }}
@@ -21,8 +18,15 @@ import { CardType } from '../../model/card.model';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ListItemComponent {
- private teacherStore = inject(TeacherStore);
- private studentStore = inject(StudentStore);
+ // private teacherStore = inject(TeacherStore);
+ // private studentStore = inject(StudentStore);
+ // private CityStore= inject(CityStore)
+
+ constructor(
+ private teacherStore: TeacherStore,
+ private studentStore: StudentStore,
+ private cityStore: CityStore,
+ ) {}
readonly id = input.required
();
readonly name = input.required();
@@ -32,6 +36,8 @@ export class ListItemComponent {
const type = this.type();
if (type === CardType.TEACHER) {
this.teacherStore.deleteOne(id);
+ } else if (type === CardType.CITY) {
+ this.cityStore.deleteOne(id);
} else if (type === CardType.STUDENT) {
this.studentStore.deleteOne(id);
}
diff --git a/apps/angular/5-crud-application/src/app/app.component.ts b/apps/angular/5-crud-application/src/app/app.component.ts
index 3836dd6c7..c79df1785 100644
--- a/apps/angular/5-crud-application/src/app/app.component.ts
+++ b/apps/angular/5-crud-application/src/app/app.component.ts
@@ -1,50 +1,50 @@
import { CommonModule } from '@angular/common';
import { HttpClient } from '@angular/common/http';
-import { Component, inject, OnInit } from '@angular/core';
-import { randText } from '@ngneat/falso';
+import { Component, OnInit } from '@angular/core';
+import { randText, Todo } from '@ngneat/falso';
+import { AppService } from '../services/app.service';
@Component({
imports: [CommonModule],
selector: 'app-root',
template: `
- @for (todo of todos; track todo.id) {
- {{ todo.title }}
-
- }
+
+ @for (todo of todos; track todo.id) {
+
+ {{ todo.title }}
+
+
+
+ }
+
`,
styles: [],
})
export class AppComponent implements OnInit {
- private http = inject(HttpClient);
+ constructor(
+ private appService: AppService,
+ private http: HttpClient,
+ ) {}
todos!: any[];
ngOnInit(): void {
- this.http
- .get('https://jsonplaceholder.typicode.com/todos')
- .subscribe((todos) => {
- this.todos = todos;
- });
+ this.appService.getTodos();
+ this.appService.todosBehavior.subscribe((todos) => {
+ this.todos = todos;
+ });
}
- update(todo: any) {
- this.http
- .put(
- `https://jsonplaceholder.typicode.com/todos/${todo.id}`,
- JSON.stringify({
- todo: todo.id,
- title: randText(),
- body: todo.body,
- userId: todo.userId,
- }),
- {
- headers: {
- 'Content-type': 'application/json; charset=UTF-8',
- },
- },
- )
- .subscribe((todoUpdated: any) => {
- this.todos[todoUpdated.id - 1] = todoUpdated;
- });
+ update(todo: Todo) {
+ const updatedTodo: Todo = {
+ id: todo.id,
+ title: randText(),
+ completed: false,
+ };
+
+ this.appService.updateTodo(todo.id, updatedTodo);
+ }
+ delete(todo: Todo) {
+ this.appService.deleteTodo(todo.id);
}
}
diff --git a/apps/angular/5-crud-application/src/app/app.config.ts b/apps/angular/5-crud-application/src/app/app.config.ts
index 1c0c9422f..e978e448e 100644
--- a/apps/angular/5-crud-application/src/app/app.config.ts
+++ b/apps/angular/5-crud-application/src/app/app.config.ts
@@ -1,6 +1,11 @@
import { provideHttpClient } from '@angular/common/http';
import { ApplicationConfig } from '@angular/core';
+import {
+ provideTanStackQuery,
+ QueryClient,
+} from '@tanstack/angular-query-experimental';
+
export const appConfig: ApplicationConfig = {
- providers: [provideHttpClient()],
+ providers: [provideHttpClient(), provideTanStackQuery(new QueryClient())],
};
diff --git a/apps/angular/5-crud-application/src/app/todo-item.component.ts b/apps/angular/5-crud-application/src/app/todo-item.component.ts
new file mode 100644
index 000000000..a301b5b55
--- /dev/null
+++ b/apps/angular/5-crud-application/src/app/todo-item.component.ts
@@ -0,0 +1,105 @@
+import {
+ ChangeDetectionStrategy,
+ Component,
+ inject,
+ Input,
+ signal,
+ WritableSignal,
+} from '@angular/core';
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import {
+ injectMutation,
+ QueryClient,
+} from '@tanstack/angular-query-experimental';
+import { lastValueFrom } from 'rxjs';
+import { Todo } from '../model/todo';
+import { todoKeys } from './todo.factory';
+import { TodoService } from './todo.service';
+
+@Component({
+ selector: 'app-todo-item',
+ imports: [MatProgressSpinnerModule],
+ template: `
+ @if (updateTodo.isPending() || deleteTodo.isPending()) {
+
+ }
+ @if (updateTodo.isError()) {
+ An error has occured: {{ updateTodo.error() }}
+ }
+ @if (deleteTodo.isError()) {
+ An error has occured: {{ deleteTodo.error() }}
+ }
+ @if (todoSignal() && !updateTodo.isPending() && !deleteTodo.isPending()) {
+ {{ todoSignal()!.title }}
+
+
+ }
+ `,
+ styles: [
+ `
+ :host {
+ display: flex;
+ gap: 3px;
+ .error {
+ color: red;
+ }
+ }
+ `,
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: true,
+})
+export class TodoItemComponent {
+ //Cach 1
+ todoSignal: WritableSignal = signal(undefined);
+
+ @Input({ required: true }) set todo(todo: Todo) {
+ this.todoSignal.set(todo);
+ }
+ queryClient = inject(QueryClient);
+
+ constructor(private todoService: TodoService) {}
+
+ updateTodo = injectMutation(() => ({
+ mutationFn: (todoId: number) => {
+ return lastValueFrom(this.todoService.updateTodo(todoId));
+ },
+ onSuccess: (data) => {
+ this.queryClient.setQueryData(todoKeys.all, (oldTodos: Todo[]) => {
+ return oldTodos
+ ? oldTodos.map((t) => (t.id === data.id ? data : t))
+ : oldTodos;
+ });
+ },
+ onSettled: (data, error, variables, context) => {
+ const todos: Todo[] | undefined = this.queryClient.getQueryData(
+ todoKeys.all,
+ );
+ if (todos) {
+ console.log(todos[variables]);
+ }
+ },
+ }));
+
+ deleteTodo = injectMutation(() => ({
+ mutationFn: (todoId: number) => {
+ return lastValueFrom(this.todoService.deleteTodo(todoId));
+ },
+ onSuccess: (_, variable) => {
+ this.queryClient.setQueryData(todoKeys.all, (oldTodos: Todo[]) => {
+ return oldTodos ? oldTodos.filter((t) => t.id !== variable) : oldTodos;
+ });
+ },
+ onSettled: (data, error, variables, context) => {
+ console.log(this.queryClient.getQueryData(todoKeys.all));
+ },
+ }));
+
+ updateTodos(todoId: number) {
+ this.updateTodo.mutate(todoId);
+ }
+
+ deleteTodos(todoId: number) {
+ this.deleteTodo.mutate(todoId);
+ }
+}
diff --git a/apps/angular/5-crud-application/src/app/todo.factory.ts b/apps/angular/5-crud-application/src/app/todo.factory.ts
new file mode 100644
index 000000000..f48f75764
--- /dev/null
+++ b/apps/angular/5-crud-application/src/app/todo.factory.ts
@@ -0,0 +1,3 @@
+export const todoKeys = {
+ all: ['todos'] as const,
+};
diff --git a/apps/angular/5-crud-application/src/app/todo.service.ts b/apps/angular/5-crud-application/src/app/todo.service.ts
new file mode 100644
index 000000000..7b90a209f
--- /dev/null
+++ b/apps/angular/5-crud-application/src/app/todo.service.ts
@@ -0,0 +1,38 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { randText } from '@ngneat/falso';
+import { Todo } from '../model/todo';
+
+const baseURI = 'https://jsonplaceholder.typicode.com/todos';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class TodoService {
+ constructor(private http: HttpClient) {}
+
+ getTodos() {
+ return this.http.get(`${baseURI}`);
+ }
+
+ updateTodo(id: number) {
+ const updatedTodo = {
+ id,
+ title: randText(),
+ };
+
+ return this.http.put(
+ `${baseURI}/${id}`,
+ JSON.stringify(updatedTodo),
+ {
+ headers: {
+ 'Content-type': 'application/json; charset=UTF-8',
+ },
+ },
+ );
+ }
+
+ deleteTodo(id: number) {
+ return this.http.delete(`${baseURI}/${id}`);
+ }
+}
diff --git a/apps/angular/5-crud-application/src/app/todos.component.ts b/apps/angular/5-crud-application/src/app/todos.component.ts
new file mode 100644
index 000000000..718c61e93
--- /dev/null
+++ b/apps/angular/5-crud-application/src/app/todos.component.ts
@@ -0,0 +1,38 @@
+import { CommonModule } from '@angular/common';
+import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
+import { injectQuery } from '@tanstack/angular-query-experimental';
+import { lastValueFrom } from 'rxjs';
+import { TodoItemComponent } from './todo-item.component';
+import { TodoService } from './todo.service';
+
+@Component({
+ selector: 'app-todos',
+ template: `
+ Todos
+ @if (query.isPending()) {
+ Loading...
+ }
+ @if (query.isError()) {
+ Error: {{ query.error().message }}
+ }
+ @if (query.data(); as data) {
+
+ @for (todo of query.data(); track todo.id) {
+
+ }
+
+ }
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: true,
+ imports: [CommonModule, TodoItemComponent],
+})
+export class TodosComponent {
+ private todoServiec = inject(TodoService);
+
+ query = injectQuery(() => ({
+ queryKey: ['todos'],
+ queryFn: () => lastValueFrom(this.todoServiec.getTodos()),
+ }));
+}
diff --git a/apps/angular/5-crud-application/src/index.html b/apps/angular/5-crud-application/src/index.html
index b9ec0b609..1eebd474d 100644
--- a/apps/angular/5-crud-application/src/index.html
+++ b/apps/angular/5-crud-application/src/index.html
@@ -15,6 +15,6 @@
rel="stylesheet" />
-
+