|
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'; |
4 | 5 |
|
5 | 6 | @Component({ |
6 | | - imports: [], |
7 | 7 | selector: 'app-root', |
| 8 | + imports: [MatProgressSpinnerModule], |
8 | 9 | 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> |
12 | 14 | } |
| 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> |
13 | 45 | `, |
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 | + ], |
15 | 122 | }) |
16 | 123 | 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; |
20 | 127 |
|
21 | 128 | 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); |
27 | 134 | } |
28 | 135 |
|
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); |
48 | 138 | } |
49 | 139 | } |
0 commit comments