Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Answer: 5 #1103

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
66 changes: 33 additions & 33 deletions apps/angular/5-crud-application/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
import { CommonModule } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { randText } from '@ngneat/falso';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { Todo } from './todo.interface';
import { TodoService } from './todo.service';

@Component({
standalone: true,
imports: [CommonModule],
imports: [CommonModule, MatSnackBarModule, MatProgressSpinnerModule],
selector: 'app-root',
template: `
<div *ngFor="let todo of todos">
{{ todo.title }}
<button (click)="update(todo)">Update</button>
</div>
@if (loadingSignal()) {
<mat-spinner></mat-spinner>
} @else {
<div *ngFor="let todo of todosSignal()">
{{ todo.title }}
<button (click)="updateTodo(todo)">Update</button>
<button (click)="deleteTodo(todo.id)">Delete</button>
</div>
}
`,
styles: [],
providers: [],
})
export class AppComponent implements OnInit {
todos!: any[];
todosSignal = this.todoService.todosSignal;
loadingSignal = this.todoService.loadingSignal;

constructor(private http: HttpClient) {}
constructor(private todoService: TodoService) {}

ngOnInit(): void {
this.http
.get<any[]>('https://jsonplaceholder.typicode.com/todos')
.subscribe((todos) => {
this.todos = todos;
});
this.todoService.getTodos();
}

update(todo: any) {
this.http
.put<any>(
`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;
});
updateTodo(todo: Todo) {
this.todoService.updateTodo(todo).subscribe((todoUpdated: Todo) => {
const updatedTodos = this.todosSignal().filter(
(todo) => todo.id !== todoUpdated.id,
);
this.todosSignal.set(updatedTodos);
});
}

deleteTodo(id: number) {
this.todoService.deleteTodo(id).subscribe(() => {
const allTodos = this.todosSignal().filter((t) => t.id !== id);
this.todosSignal.set(allTodos);
});
}
}
14 changes: 12 additions & 2 deletions apps/angular/5-crud-application/src/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { provideHttpClient } from '@angular/common/http';
import { HTTP_INTERCEPTORS, provideHttpClient } from '@angular/common/http';
import { ApplicationConfig } from '@angular/core';
import { provideAnimations } from '@angular/platform-browser/animations';
import { GlobalErrorInterceptor } from './error.interceptor';

export const appConfig: ApplicationConfig = {
providers: [provideHttpClient()],
providers: [
provideHttpClient(),
provideAnimations(),
{
provide: HTTP_INTERCEPTORS,
useClass: GlobalErrorInterceptor,
multi: true,
},
],
};
59 changes: 59 additions & 0 deletions apps/angular/5-crud-application/src/app/error.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
HttpErrorResponse,
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class GlobalErrorInterceptor implements HttpInterceptor {
constructor(private snackBar: MatSnackBar) {}

intercept(
request: HttpRequest<any>,
next: HttpHandler,
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
// Handle different types of errors here
let errorMessage = 'An unknown error occurred';

if (error.error instanceof ErrorEvent) {
// Client-side error
errorMessage = `Error: ${error.error.message}`;
} else {
// Server-side error
switch (error.status) {
case 400:
errorMessage = 'Bad request';
break;
case 401:
errorMessage = 'Unauthorized, please log in';
break;
case 404:
errorMessage = 'Resource not found';
break;
case 500:
errorMessage = 'Internal server error';
break;
default:
errorMessage = `Error: ${error.message}`;
break;
}
}

// Show the error message using MatSnackBar
this.snackBar.open(errorMessage, 'Close', {
duration: 3000,
});

return throwError(() => new Error());
}),
);
}
}
7 changes: 7 additions & 0 deletions apps/angular/5-crud-application/src/app/todo.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface Todo {
id: number;
title: string;
completed: boolean;
body: string;
userId: number;
}
16 changes: 16 additions & 0 deletions apps/angular/5-crud-application/src/app/todo.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';

import { TodoService } from './todo.service';

describe('TodoService', () => {
let service: TodoService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(TodoService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});
});
51 changes: 51 additions & 0 deletions apps/angular/5-crud-application/src/app/todo.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { HttpClient } from '@angular/common/http';
import { Injectable, signal } from '@angular/core';
import { randText } from '@ngneat/falso';
import { finalize, Observable } from 'rxjs';
import { Todo } from './todo.interface';

@Injectable({
providedIn: 'root',
})
export class TodoService {
private apiUrl = 'https://jsonplaceholder.typicode.com/todos';
todosSignal = signal<Todo[]>([]);
loadingSignal = signal<boolean>(false);

constructor(private http: HttpClient) {}

getTodos(): void {
this.loadingSignal.set(true);
this.http
.get<Todo[]>(this.apiUrl)
.pipe(finalize(() => this.loadingSignal.set(false)))
.subscribe((data) => this.todosSignal.set(data));
}

updateTodo(todo: Todo): Observable<Todo> {
this.loadingSignal.set(true);
return this.http
.put<Todo>(
`${this.apiUrl}/${todo.id}`,
JSON.stringify({
todo: todo.id,
title: randText(),
body: todo.body,
userId: todo.userId,
}),
{
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
},
)
.pipe(finalize(() => this.loadingSignal.set(false)));
}

deleteTodo(id: number): Observable<void> {
this.loadingSignal.set(true);
return this.http
.delete<void>(`${this.apiUrl}/${id}`)
.pipe(finalize(() => this.loadingSignal.set(false)));
}
}