Skip to content

Commit 4d6370c

Browse files
chore: crud application done
1 parent 302e82f commit 4d6370c

File tree

3 files changed

+188
-35
lines changed

3 files changed

+188
-35
lines changed
Lines changed: 125 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,139 @@
1-
import { HttpClient } from '@angular/common/http';
2-
import { Component, inject, OnInit } from '@angular/core';
3-
import { randText } from '@ngneat/falso';
1+
import { Component, OnInit, inject } from '@angular/core';
2+
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
3+
import { TodoListService } from './data-access/todo-list.service';
4+
import { Todo } from './model/todo.model';
45

56
@Component({
6-
imports: [],
77
selector: 'app-root',
8+
imports: [MatProgressSpinnerModule],
89
template: `
9-
@for (todo of todos; track todo.id) {
10-
{{ todo.title }}
11-
<button (click)="update(todo)">Update</button>
10+
@if (loading()) {
11+
<div class="overlay">
12+
<mat-progress-spinner mode="indeterminate" diameter="48" />
13+
</div>
1214
}
15+
16+
<main class="page">
17+
<h1>Todo list</h1>
18+
19+
@if (todos().length === 0) {
20+
<p class="muted">Loading...</p>
21+
} @else {
22+
<ul class="list">
23+
@for (todo of todos(); track todo.id) {
24+
<li class="item">
25+
<div>
26+
<p class="muted">#{{ todo.id }} · User {{ todo.userId }}</p>
27+
<p class="title">{{ todo.title }}</p>
28+
</div>
29+
<div class="actions">
30+
<button type="button" class="btn" (click)="update(todo)">
31+
Update
32+
</button>
33+
<button
34+
type="button"
35+
class="btn btn-delete"
36+
(click)="delete(todo)">
37+
Delete
38+
</button>
39+
</div>
40+
</li>
41+
}
42+
</ul>
43+
}
44+
</main>
1345
`,
14-
styles: [],
46+
styles: [
47+
`
48+
:host {
49+
display: block;
50+
font-family:
51+
system-ui,
52+
-apple-system,
53+
'Segoe UI',
54+
sans-serif;
55+
color: #0f172a;
56+
padding: 16px;
57+
}
58+
.page {
59+
max-width: 720px;
60+
margin: 0 auto;
61+
}
62+
h1 {
63+
margin: 0 0 10px;
64+
font-size: 24px;
65+
}
66+
.muted {
67+
margin: 0;
68+
color: #64748b;
69+
}
70+
.list {
71+
list-style: none;
72+
padding: 0;
73+
margin: 0;
74+
display: grid;
75+
gap: 8px;
76+
}
77+
.item {
78+
display: flex;
79+
justify-content: space-between;
80+
align-items: center;
81+
gap: 10px;
82+
padding: 12px;
83+
border-radius: 10px;
84+
border: 1px solid #e5e7eb;
85+
background: #f8fafc;
86+
}
87+
.title {
88+
margin: 6px 0 0;
89+
font-weight: 600;
90+
}
91+
.actions {
92+
display: flex;
93+
gap: 8px;
94+
}
95+
.btn {
96+
border: none;
97+
background: #475569;
98+
color: #fff;
99+
padding: 8px 12px;
100+
border-radius: 8px;
101+
cursor: pointer;
102+
font-weight: 600;
103+
transition: opacity 120ms ease;
104+
}
105+
.btn-delete {
106+
background: #ef4444;
107+
}
108+
.btn:hover {
109+
opacity: 0.9;
110+
}
111+
.overlay {
112+
position: fixed;
113+
inset: 0;
114+
display: grid;
115+
place-items: center;
116+
background: rgba(255, 255, 255, 0.6);
117+
backdrop-filter: blur(2px);
118+
z-index: 10;
119+
}
120+
`,
121+
],
15122
})
16123
export class AppComponent implements OnInit {
17-
private http = inject(HttpClient);
18-
19-
todos!: any[];
124+
private todoService = inject(TodoListService);
125+
todos = this.todoService.todos;
126+
loading = this.todoService.loading;
20127

21128
ngOnInit(): void {
22-
this.http
23-
.get<any[]>('https://jsonplaceholder.typicode.com/todos')
24-
.subscribe((todos) => {
25-
this.todos = todos;
26-
});
129+
this.todoService.loadTodos();
130+
}
131+
132+
update(todo: Todo) {
133+
this.todoService.updateTodo(todo);
27134
}
28135

29-
update(todo: any) {
30-
this.http
31-
.put<any>(
32-
`https://jsonplaceholder.typicode.com/todos/${todo.id}`,
33-
JSON.stringify({
34-
todo: todo.id,
35-
title: randText(),
36-
body: todo.body,
37-
userId: todo.userId,
38-
}),
39-
{
40-
headers: {
41-
'Content-type': 'application/json; charset=UTF-8',
42-
},
43-
},
44-
)
45-
.subscribe((todoUpdated: any) => {
46-
this.todos[todoUpdated.id - 1] = todoUpdated;
47-
});
136+
delete(todo: Todo) {
137+
this.todoService.deleteTodo(todo);
48138
}
49139
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { HttpClient } from '@angular/common/http';
2+
import { computed, inject, Injectable, signal } from '@angular/core';
3+
import { randText } from '@ngneat/falso';
4+
import { finalize } from 'rxjs/operators';
5+
import { Todo } from '../model/todo.model';
6+
7+
@Injectable({
8+
providedIn: 'root',
9+
})
10+
export class TodoListService {
11+
private http = inject(HttpClient);
12+
private _todos = signal<Todo[]>([]);
13+
private _loading = signal(false);
14+
15+
todos = computed(() => this._todos());
16+
loading = computed(() => this._loading());
17+
18+
loadTodos() {
19+
this._loading.set(true);
20+
return this.http
21+
.get<Todo[]>('https://jsonplaceholder.typicode.com/todos')
22+
.pipe(finalize(() => this._loading.set(false)))
23+
.subscribe((todos) => this._todos.set(todos));
24+
}
25+
26+
updateTodo(todo: Todo) {
27+
this._loading.set(true);
28+
return this.http
29+
.put<Todo>(
30+
`https://jsonplaceholder.typicode.com/todos/${todo.id}`,
31+
{
32+
todo: todo.id,
33+
title: randText(),
34+
body: todo.body,
35+
userId: todo.userId,
36+
},
37+
{ headers: { 'Content-type': 'application/json; charset=UTF-8' } },
38+
)
39+
.pipe(finalize(() => this._loading.set(false)))
40+
.subscribe((todoUpdated) =>
41+
this._todos.update((todos) =>
42+
todos.map((t) => (t.id === todoUpdated.id ? todoUpdated : t)),
43+
),
44+
);
45+
}
46+
47+
deleteTodo(todo: Todo) {
48+
this._loading.set(true);
49+
return this.http
50+
.delete(`https://jsonplaceholder.typicode.com/todos/${todo.id}`)
51+
.pipe(finalize(() => this._loading.set(false)))
52+
.subscribe(() => {
53+
this._todos.update((todos) => todos.filter((t) => t.id !== todo.id));
54+
});
55+
}
56+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface Todo {
2+
id: number;
3+
userId: number;
4+
title: string;
5+
completed: boolean;
6+
body?: string;
7+
}

0 commit comments

Comments
 (0)