@ichgamer999/wmctest 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/backend/backend-demo.component.d.ts +2 -3
- package/dist/app/backend/backend-demo.component.d.ts.map +1 -1
- package/dist/app/backend/backend-demo.component.js +56 -46
- package/dist/app/backend/backend.service.d.ts +5 -0
- package/dist/app/backend/backend.service.d.ts.map +1 -1
- package/dist/app/backend/backend.service.js +73 -10
- package/package.json +2 -2
- package/src/app/backend/backend-demo.component.ts +57 -48
- package/src/app/backend/backend.service.ts +112 -10
- package/dist/wmctest/3rdpartylicenses.txt +0 -355
- package/dist/wmctest/browser/chunk-AAHIOVR7.js +0 -1
- package/dist/wmctest/browser/chunk-IOFOGN3P.js +0 -1
- package/dist/wmctest/browser/chunk-LVFMTH2I.js +0 -5
- package/dist/wmctest/browser/chunk-U3WW7TST.js +0 -1
- package/dist/wmctest/browser/chunk-WM5SAM2A.js +0 -1
- package/dist/wmctest/browser/favicon.ico +0 -0
- package/dist/wmctest/browser/index.html +0 -13
- package/dist/wmctest/browser/main-EHFDL22K.js +0 -1
- package/dist/wmctest/browser/styles-5INURTSO.css +0 -0
- package/dist/wmctest/prerendered-routes.json +0 -3
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import { BackendService } from './backend.service';
|
|
1
2
|
export declare class BackendDemoComponent {
|
|
2
|
-
|
|
3
|
-
loading: import("@angular/core").WritableSignal<boolean>;
|
|
4
|
-
error: import("@angular/core").WritableSignal<string>;
|
|
3
|
+
backendService: BackendService;
|
|
5
4
|
response: import("@angular/core").WritableSignal<any>;
|
|
6
5
|
getUsers(): void;
|
|
7
6
|
createUser(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend-demo.component.d.ts","sourceRoot":"","sources":["../../../src/app/backend/backend-demo.component.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"backend-demo.component.d.ts","sourceRoot":"","sources":["../../../src/app/backend/backend-demo.component.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAQnD,qBAiIa,oBAAoB;IAC/B,cAAc,iBAA0B;IACxC,QAAQ,8CAAqB;IAE7B,QAAQ;IAQR,UAAU;IAaV,UAAU;IAaV,UAAU;CAOX"}
|
|
@@ -4,71 +4,37 @@ import { CommonModule } from '@angular/common';
|
|
|
4
4
|
import { BackendService } from './backend.service';
|
|
5
5
|
let BackendDemoComponent = class BackendDemoComponent {
|
|
6
6
|
backendService = inject(BackendService);
|
|
7
|
-
loading = signal(false);
|
|
8
|
-
error = signal('');
|
|
9
7
|
response = signal(null);
|
|
10
8
|
getUsers() {
|
|
11
|
-
this.
|
|
12
|
-
this.error.set('');
|
|
9
|
+
this.response.set(null);
|
|
13
10
|
this.backendService.get('/users').subscribe({
|
|
14
|
-
next: (users) =>
|
|
15
|
-
this.loading.set(false);
|
|
16
|
-
this.response.set(users);
|
|
17
|
-
},
|
|
18
|
-
error: (err) => {
|
|
19
|
-
this.loading.set(false);
|
|
20
|
-
this.error.set(err.message || 'Failed to fetch users');
|
|
21
|
-
}
|
|
11
|
+
next: (users) => this.response.set(users)
|
|
22
12
|
});
|
|
23
13
|
}
|
|
24
14
|
createUser() {
|
|
25
|
-
this.
|
|
26
|
-
this.error.set('');
|
|
15
|
+
this.response.set(null);
|
|
27
16
|
const newUser = {
|
|
28
17
|
name: 'John Doe',
|
|
29
18
|
email: 'john@example.com'
|
|
30
19
|
};
|
|
31
20
|
this.backendService.post('/users', newUser).subscribe({
|
|
32
|
-
next: (user) =>
|
|
33
|
-
this.loading.set(false);
|
|
34
|
-
this.response.set(user);
|
|
35
|
-
},
|
|
36
|
-
error: (err) => {
|
|
37
|
-
this.loading.set(false);
|
|
38
|
-
this.error.set(err.message || 'Failed to create user');
|
|
39
|
-
}
|
|
21
|
+
next: (user) => this.response.set(user)
|
|
40
22
|
});
|
|
41
23
|
}
|
|
42
24
|
updateUser() {
|
|
43
|
-
this.
|
|
44
|
-
this.error.set('');
|
|
25
|
+
this.response.set(null);
|
|
45
26
|
const updatedUser = {
|
|
46
27
|
name: 'Jane Doe',
|
|
47
28
|
email: 'jane@example.com'
|
|
48
29
|
};
|
|
49
30
|
this.backendService.put('/users/1', updatedUser).subscribe({
|
|
50
|
-
next: (user) =>
|
|
51
|
-
this.loading.set(false);
|
|
52
|
-
this.response.set(user);
|
|
53
|
-
},
|
|
54
|
-
error: (err) => {
|
|
55
|
-
this.loading.set(false);
|
|
56
|
-
this.error.set(err.message || 'Failed to update user');
|
|
57
|
-
}
|
|
31
|
+
next: (user) => this.response.set(user)
|
|
58
32
|
});
|
|
59
33
|
}
|
|
60
34
|
deleteUser() {
|
|
61
|
-
this.
|
|
62
|
-
this.error.set('');
|
|
35
|
+
this.response.set(null);
|
|
63
36
|
this.backendService.delete('/users/1').subscribe({
|
|
64
|
-
next: () => {
|
|
65
|
-
this.loading.set(false);
|
|
66
|
-
this.response.set({ message: 'User deleted successfully' });
|
|
67
|
-
},
|
|
68
|
-
error: (err) => {
|
|
69
|
-
this.loading.set(false);
|
|
70
|
-
this.error.set(err.message || 'Failed to delete user');
|
|
71
|
-
}
|
|
37
|
+
next: () => this.response.set({ message: 'User deleted successfully' })
|
|
72
38
|
});
|
|
73
39
|
}
|
|
74
40
|
};
|
|
@@ -80,6 +46,7 @@ BackendDemoComponent = __decorate([
|
|
|
80
46
|
template: `
|
|
81
47
|
<div class="demo-container">
|
|
82
48
|
<h2>Backend Service Demo</h2>
|
|
49
|
+
<p class="subtitle">Using Signals for reactive state management</p>
|
|
83
50
|
|
|
84
51
|
<div class="actions">
|
|
85
52
|
<button (click)="getUsers()">Get Users</button>
|
|
@@ -88,12 +55,15 @@ BackendDemoComponent = __decorate([
|
|
|
88
55
|
<button (click)="deleteUser()">Delete User</button>
|
|
89
56
|
</div>
|
|
90
57
|
|
|
91
|
-
@if (loading()) {
|
|
58
|
+
@if (backendService.loading()) {
|
|
92
59
|
<div class="loading">Loading...</div>
|
|
93
60
|
}
|
|
94
61
|
|
|
95
|
-
@if (error()) {
|
|
96
|
-
<div class="error">
|
|
62
|
+
@if (backendService.error()) {
|
|
63
|
+
<div class="error">
|
|
64
|
+
{{ backendService.error() }}
|
|
65
|
+
<button (click)="backendService.clearError()">✕</button>
|
|
66
|
+
</div>
|
|
97
67
|
}
|
|
98
68
|
|
|
99
69
|
@if (response()) {
|
|
@@ -102,6 +72,16 @@ BackendDemoComponent = __decorate([
|
|
|
102
72
|
<pre>{{ response() | json }}</pre>
|
|
103
73
|
</div>
|
|
104
74
|
}
|
|
75
|
+
|
|
76
|
+
<div class="info">
|
|
77
|
+
<h3>Features:</h3>
|
|
78
|
+
<ul>
|
|
79
|
+
<li>✅ Global loading state with Signal</li>
|
|
80
|
+
<li>✅ Global error state with Signal</li>
|
|
81
|
+
<li>✅ Automatic loading/error management</li>
|
|
82
|
+
<li>✅ Type-safe API calls</li>
|
|
83
|
+
</ul>
|
|
84
|
+
</div>
|
|
105
85
|
</div>
|
|
106
86
|
`,
|
|
107
87
|
styles: [`
|
|
@@ -111,6 +91,11 @@ BackendDemoComponent = __decorate([
|
|
|
111
91
|
margin: 0 auto;
|
|
112
92
|
}
|
|
113
93
|
|
|
94
|
+
.subtitle {
|
|
95
|
+
color: #666;
|
|
96
|
+
margin-bottom: 2rem;
|
|
97
|
+
}
|
|
98
|
+
|
|
114
99
|
.actions {
|
|
115
100
|
display: flex;
|
|
116
101
|
gap: 1rem;
|
|
@@ -131,7 +116,7 @@ BackendDemoComponent = __decorate([
|
|
|
131
116
|
background: #0056b3;
|
|
132
117
|
}
|
|
133
118
|
|
|
134
|
-
.loading, .error, .response {
|
|
119
|
+
.loading, .error, .response, .info {
|
|
135
120
|
padding: 1rem;
|
|
136
121
|
margin: 1rem 0;
|
|
137
122
|
border-radius: 4px;
|
|
@@ -140,11 +125,22 @@ BackendDemoComponent = __decorate([
|
|
|
140
125
|
.loading {
|
|
141
126
|
background: #e3f2fd;
|
|
142
127
|
color: #1976d2;
|
|
128
|
+
font-weight: 500;
|
|
143
129
|
}
|
|
144
130
|
|
|
145
131
|
.error {
|
|
146
132
|
background: #ffebee;
|
|
147
133
|
color: #c62828;
|
|
134
|
+
display: flex;
|
|
135
|
+
justify-content: space-between;
|
|
136
|
+
align-items: center;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.error button {
|
|
140
|
+
background: transparent;
|
|
141
|
+
color: #c62828;
|
|
142
|
+
padding: 0.25rem 0.5rem;
|
|
143
|
+
font-size: 1.2rem;
|
|
148
144
|
}
|
|
149
145
|
|
|
150
146
|
.response {
|
|
@@ -152,6 +148,20 @@ BackendDemoComponent = __decorate([
|
|
|
152
148
|
border: 1px solid #ddd;
|
|
153
149
|
}
|
|
154
150
|
|
|
151
|
+
.info {
|
|
152
|
+
background: #e8f5e9;
|
|
153
|
+
border-left: 4px solid #4caf50;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.info ul {
|
|
157
|
+
margin: 0.5rem 0 0;
|
|
158
|
+
padding-left: 1.5rem;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.info li {
|
|
162
|
+
margin: 0.5rem 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
155
165
|
pre {
|
|
156
166
|
overflow-x: auto;
|
|
157
167
|
margin: 0.5rem 0 0;
|
|
@@ -7,6 +7,10 @@ export interface ApiResponse<T> {
|
|
|
7
7
|
export declare class BackendService {
|
|
8
8
|
private readonly http;
|
|
9
9
|
baseUrl: string;
|
|
10
|
+
private _loading;
|
|
11
|
+
private _error;
|
|
12
|
+
readonly loading: import("@angular/core").Signal<boolean>;
|
|
13
|
+
readonly error: import("@angular/core").Signal<string>;
|
|
10
14
|
get<T>(endpoint: string, params?: Record<string, string | number>): Observable<T>;
|
|
11
15
|
post<T>(endpoint: string, body: any): Observable<T>;
|
|
12
16
|
put<T>(endpoint: string, body: any): Observable<T>;
|
|
@@ -16,5 +20,6 @@ export declare class BackendService {
|
|
|
16
20
|
postWithResponse<T>(endpoint: string, body: any): Observable<ApiResponse<T>>;
|
|
17
21
|
uploadFile<T>(endpoint: string, file: File, additionalData?: Record<string, any>): Observable<T>;
|
|
18
22
|
downloadFile(endpoint: string): Observable<Blob>;
|
|
23
|
+
clearError(): void;
|
|
19
24
|
}
|
|
20
25
|
//# sourceMappingURL=backend.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.service.d.ts","sourceRoot":"","sources":["../../../src/app/backend/backend.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"backend.service.d.ts","sourceRoot":"","sources":["../../../src/app/backend/backend.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAIlC,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,IAAI,EAAE,CAAC,CAAC;IACR,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAGa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;IAE3C,OAAO,SAA+B;IAEtC,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,MAAM,CAAsB;IAEpC,SAAgB,OAAO,0CAA8B;IACrD,SAAgB,KAAK,yCAA4B;IAEjD,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAejF,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC;IAcnD,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC;IAclD,KAAK,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC;IAcpD,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC;IAc1C,eAAe,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAe1G,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAc5E,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAuBhG,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC;IAgBhD,UAAU,IAAI,IAAI;CAGnB"}
|
|
@@ -1,33 +1,83 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
|
-
import { Injectable, inject } from '@angular/core';
|
|
2
|
+
import { Injectable, inject, signal } from '@angular/core';
|
|
3
3
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
|
4
|
+
import { tap, catchError } from 'rxjs/operators';
|
|
5
|
+
import { throwError } from 'rxjs';
|
|
4
6
|
let BackendService = class BackendService {
|
|
5
7
|
http = inject(HttpClient);
|
|
6
8
|
baseUrl = 'http://localhost:3000/api';
|
|
9
|
+
_loading = signal(false);
|
|
10
|
+
_error = signal('');
|
|
11
|
+
loading = this._loading.asReadonly();
|
|
12
|
+
error = this._error.asReadonly();
|
|
7
13
|
get(endpoint, params) {
|
|
14
|
+
this._loading.set(true);
|
|
15
|
+
this._error.set('');
|
|
8
16
|
const httpParams = params ? new HttpParams({ fromObject: params }) : undefined;
|
|
9
|
-
return this.http.get(`${this.baseUrl}${endpoint}`, { params: httpParams })
|
|
17
|
+
return this.http.get(`${this.baseUrl}${endpoint}`, { params: httpParams }).pipe(tap(() => this._loading.set(false)), catchError(err => {
|
|
18
|
+
this._loading.set(false);
|
|
19
|
+
this._error.set(err.message || 'Request failed');
|
|
20
|
+
return throwError(() => err);
|
|
21
|
+
}));
|
|
10
22
|
}
|
|
11
23
|
post(endpoint, body) {
|
|
12
|
-
|
|
24
|
+
this._loading.set(true);
|
|
25
|
+
this._error.set('');
|
|
26
|
+
return this.http.post(`${this.baseUrl}${endpoint}`, body).pipe(tap(() => this._loading.set(false)), catchError(err => {
|
|
27
|
+
this._loading.set(false);
|
|
28
|
+
this._error.set(err.message || 'Request failed');
|
|
29
|
+
return throwError(() => err);
|
|
30
|
+
}));
|
|
13
31
|
}
|
|
14
32
|
put(endpoint, body) {
|
|
15
|
-
|
|
33
|
+
this._loading.set(true);
|
|
34
|
+
this._error.set('');
|
|
35
|
+
return this.http.put(`${this.baseUrl}${endpoint}`, body).pipe(tap(() => this._loading.set(false)), catchError(err => {
|
|
36
|
+
this._loading.set(false);
|
|
37
|
+
this._error.set(err.message || 'Request failed');
|
|
38
|
+
return throwError(() => err);
|
|
39
|
+
}));
|
|
16
40
|
}
|
|
17
41
|
patch(endpoint, body) {
|
|
18
|
-
|
|
42
|
+
this._loading.set(true);
|
|
43
|
+
this._error.set('');
|
|
44
|
+
return this.http.patch(`${this.baseUrl}${endpoint}`, body).pipe(tap(() => this._loading.set(false)), catchError(err => {
|
|
45
|
+
this._loading.set(false);
|
|
46
|
+
this._error.set(err.message || 'Request failed');
|
|
47
|
+
return throwError(() => err);
|
|
48
|
+
}));
|
|
19
49
|
}
|
|
20
50
|
delete(endpoint) {
|
|
21
|
-
|
|
51
|
+
this._loading.set(true);
|
|
52
|
+
this._error.set('');
|
|
53
|
+
return this.http.delete(`${this.baseUrl}${endpoint}`).pipe(tap(() => this._loading.set(false)), catchError(err => {
|
|
54
|
+
this._loading.set(false);
|
|
55
|
+
this._error.set(err.message || 'Request failed');
|
|
56
|
+
return throwError(() => err);
|
|
57
|
+
}));
|
|
22
58
|
}
|
|
23
59
|
getWithResponse(endpoint, params) {
|
|
60
|
+
this._loading.set(true);
|
|
61
|
+
this._error.set('');
|
|
24
62
|
const httpParams = params ? new HttpParams({ fromObject: params }) : undefined;
|
|
25
|
-
return this.http.get(`${this.baseUrl}${endpoint}`, { params: httpParams })
|
|
63
|
+
return this.http.get(`${this.baseUrl}${endpoint}`, { params: httpParams }).pipe(tap(() => this._loading.set(false)), catchError(err => {
|
|
64
|
+
this._loading.set(false);
|
|
65
|
+
this._error.set(err.message || 'Request failed');
|
|
66
|
+
return throwError(() => err);
|
|
67
|
+
}));
|
|
26
68
|
}
|
|
27
69
|
postWithResponse(endpoint, body) {
|
|
28
|
-
|
|
70
|
+
this._loading.set(true);
|
|
71
|
+
this._error.set('');
|
|
72
|
+
return this.http.post(`${this.baseUrl}${endpoint}`, body).pipe(tap(() => this._loading.set(false)), catchError(err => {
|
|
73
|
+
this._loading.set(false);
|
|
74
|
+
this._error.set(err.message || 'Request failed');
|
|
75
|
+
return throwError(() => err);
|
|
76
|
+
}));
|
|
29
77
|
}
|
|
30
78
|
uploadFile(endpoint, file, additionalData) {
|
|
79
|
+
this._loading.set(true);
|
|
80
|
+
this._error.set('');
|
|
31
81
|
const formData = new FormData();
|
|
32
82
|
formData.append('file', file);
|
|
33
83
|
if (additionalData) {
|
|
@@ -35,12 +85,25 @@ let BackendService = class BackendService {
|
|
|
35
85
|
formData.append(key, additionalData[key]);
|
|
36
86
|
});
|
|
37
87
|
}
|
|
38
|
-
return this.http.post(`${this.baseUrl}${endpoint}`, formData)
|
|
88
|
+
return this.http.post(`${this.baseUrl}${endpoint}`, formData).pipe(tap(() => this._loading.set(false)), catchError(err => {
|
|
89
|
+
this._loading.set(false);
|
|
90
|
+
this._error.set(err.message || 'Upload failed');
|
|
91
|
+
return throwError(() => err);
|
|
92
|
+
}));
|
|
39
93
|
}
|
|
40
94
|
downloadFile(endpoint) {
|
|
95
|
+
this._loading.set(true);
|
|
96
|
+
this._error.set('');
|
|
41
97
|
return this.http.get(`${this.baseUrl}${endpoint}`, {
|
|
42
98
|
responseType: 'blob'
|
|
43
|
-
})
|
|
99
|
+
}).pipe(tap(() => this._loading.set(false)), catchError(err => {
|
|
100
|
+
this._loading.set(false);
|
|
101
|
+
this._error.set(err.message || 'Download failed');
|
|
102
|
+
return throwError(() => err);
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
clearError() {
|
|
106
|
+
this._error.set('');
|
|
44
107
|
}
|
|
45
108
|
};
|
|
46
109
|
BackendService = __decorate([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ichgamer999/wmctest",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Minimalist Angular authentication template with signals, JWT token handling, and HTTP interceptors",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"signals",
|
|
10
10
|
"template"
|
|
11
11
|
],
|
|
12
|
-
"author": "
|
|
12
|
+
"author": "ICHGamer999",
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
@@ -15,6 +15,7 @@ interface User {
|
|
|
15
15
|
template: `
|
|
16
16
|
<div class="demo-container">
|
|
17
17
|
<h2>Backend Service Demo</h2>
|
|
18
|
+
<p class="subtitle">Using Signals for reactive state management</p>
|
|
18
19
|
|
|
19
20
|
<div class="actions">
|
|
20
21
|
<button (click)="getUsers()">Get Users</button>
|
|
@@ -23,12 +24,15 @@ interface User {
|
|
|
23
24
|
<button (click)="deleteUser()">Delete User</button>
|
|
24
25
|
</div>
|
|
25
26
|
|
|
26
|
-
@if (loading()) {
|
|
27
|
+
@if (backendService.loading()) {
|
|
27
28
|
<div class="loading">Loading...</div>
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
@if (error()) {
|
|
31
|
-
<div class="error">
|
|
31
|
+
@if (backendService.error()) {
|
|
32
|
+
<div class="error">
|
|
33
|
+
{{ backendService.error() }}
|
|
34
|
+
<button (click)="backendService.clearError()">✕</button>
|
|
35
|
+
</div>
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
@if (response()) {
|
|
@@ -37,6 +41,16 @@ interface User {
|
|
|
37
41
|
<pre>{{ response() | json }}</pre>
|
|
38
42
|
</div>
|
|
39
43
|
}
|
|
44
|
+
|
|
45
|
+
<div class="info">
|
|
46
|
+
<h3>Features:</h3>
|
|
47
|
+
<ul>
|
|
48
|
+
<li>✅ Global loading state with Signal</li>
|
|
49
|
+
<li>✅ Global error state with Signal</li>
|
|
50
|
+
<li>✅ Automatic loading/error management</li>
|
|
51
|
+
<li>✅ Type-safe API calls</li>
|
|
52
|
+
</ul>
|
|
53
|
+
</div>
|
|
40
54
|
</div>
|
|
41
55
|
`,
|
|
42
56
|
styles: [`
|
|
@@ -46,6 +60,11 @@ interface User {
|
|
|
46
60
|
margin: 0 auto;
|
|
47
61
|
}
|
|
48
62
|
|
|
63
|
+
.subtitle {
|
|
64
|
+
color: #666;
|
|
65
|
+
margin-bottom: 2rem;
|
|
66
|
+
}
|
|
67
|
+
|
|
49
68
|
.actions {
|
|
50
69
|
display: flex;
|
|
51
70
|
gap: 1rem;
|
|
@@ -66,7 +85,7 @@ interface User {
|
|
|
66
85
|
background: #0056b3;
|
|
67
86
|
}
|
|
68
87
|
|
|
69
|
-
.loading, .error, .response {
|
|
88
|
+
.loading, .error, .response, .info {
|
|
70
89
|
padding: 1rem;
|
|
71
90
|
margin: 1rem 0;
|
|
72
91
|
border-radius: 4px;
|
|
@@ -75,11 +94,22 @@ interface User {
|
|
|
75
94
|
.loading {
|
|
76
95
|
background: #e3f2fd;
|
|
77
96
|
color: #1976d2;
|
|
97
|
+
font-weight: 500;
|
|
78
98
|
}
|
|
79
99
|
|
|
80
100
|
.error {
|
|
81
101
|
background: #ffebee;
|
|
82
102
|
color: #c62828;
|
|
103
|
+
display: flex;
|
|
104
|
+
justify-content: space-between;
|
|
105
|
+
align-items: center;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.error button {
|
|
109
|
+
background: transparent;
|
|
110
|
+
color: #c62828;
|
|
111
|
+
padding: 0.25rem 0.5rem;
|
|
112
|
+
font-size: 1.2rem;
|
|
83
113
|
}
|
|
84
114
|
|
|
85
115
|
.response {
|
|
@@ -87,6 +117,20 @@ interface User {
|
|
|
87
117
|
border: 1px solid #ddd;
|
|
88
118
|
}
|
|
89
119
|
|
|
120
|
+
.info {
|
|
121
|
+
background: #e8f5e9;
|
|
122
|
+
border-left: 4px solid #4caf50;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.info ul {
|
|
126
|
+
margin: 0.5rem 0 0;
|
|
127
|
+
padding-left: 1.5rem;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.info li {
|
|
131
|
+
margin: 0.5rem 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
90
134
|
pre {
|
|
91
135
|
overflow-x: auto;
|
|
92
136
|
margin: 0.5rem 0 0;
|
|
@@ -94,31 +138,19 @@ interface User {
|
|
|
94
138
|
`]
|
|
95
139
|
})
|
|
96
140
|
export class BackendDemoComponent {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
loading = signal(false);
|
|
100
|
-
error = signal('');
|
|
141
|
+
backendService = inject(BackendService);
|
|
101
142
|
response = signal<any>(null);
|
|
102
143
|
|
|
103
144
|
getUsers() {
|
|
104
|
-
this.
|
|
105
|
-
this.error.set('');
|
|
145
|
+
this.response.set(null);
|
|
106
146
|
|
|
107
147
|
this.backendService.get<User[]>('/users').subscribe({
|
|
108
|
-
next: (users) =>
|
|
109
|
-
this.loading.set(false);
|
|
110
|
-
this.response.set(users);
|
|
111
|
-
},
|
|
112
|
-
error: (err) => {
|
|
113
|
-
this.loading.set(false);
|
|
114
|
-
this.error.set(err.message || 'Failed to fetch users');
|
|
115
|
-
}
|
|
148
|
+
next: (users) => this.response.set(users)
|
|
116
149
|
});
|
|
117
150
|
}
|
|
118
151
|
|
|
119
152
|
createUser() {
|
|
120
|
-
this.
|
|
121
|
-
this.error.set('');
|
|
153
|
+
this.response.set(null);
|
|
122
154
|
|
|
123
155
|
const newUser = {
|
|
124
156
|
name: 'John Doe',
|
|
@@ -126,20 +158,12 @@ export class BackendDemoComponent {
|
|
|
126
158
|
};
|
|
127
159
|
|
|
128
160
|
this.backendService.post<User>('/users', newUser).subscribe({
|
|
129
|
-
next: (user) =>
|
|
130
|
-
this.loading.set(false);
|
|
131
|
-
this.response.set(user);
|
|
132
|
-
},
|
|
133
|
-
error: (err) => {
|
|
134
|
-
this.loading.set(false);
|
|
135
|
-
this.error.set(err.message || 'Failed to create user');
|
|
136
|
-
}
|
|
161
|
+
next: (user) => this.response.set(user)
|
|
137
162
|
});
|
|
138
163
|
}
|
|
139
164
|
|
|
140
165
|
updateUser() {
|
|
141
|
-
this.
|
|
142
|
-
this.error.set('');
|
|
166
|
+
this.response.set(null);
|
|
143
167
|
|
|
144
168
|
const updatedUser = {
|
|
145
169
|
name: 'Jane Doe',
|
|
@@ -147,30 +171,15 @@ export class BackendDemoComponent {
|
|
|
147
171
|
};
|
|
148
172
|
|
|
149
173
|
this.backendService.put<User>('/users/1', updatedUser).subscribe({
|
|
150
|
-
next: (user) =>
|
|
151
|
-
this.loading.set(false);
|
|
152
|
-
this.response.set(user);
|
|
153
|
-
},
|
|
154
|
-
error: (err) => {
|
|
155
|
-
this.loading.set(false);
|
|
156
|
-
this.error.set(err.message || 'Failed to update user');
|
|
157
|
-
}
|
|
174
|
+
next: (user) => this.response.set(user)
|
|
158
175
|
});
|
|
159
176
|
}
|
|
160
177
|
|
|
161
178
|
deleteUser() {
|
|
162
|
-
this.
|
|
163
|
-
this.error.set('');
|
|
179
|
+
this.response.set(null);
|
|
164
180
|
|
|
165
181
|
this.backendService.delete<void>('/users/1').subscribe({
|
|
166
|
-
next: () => {
|
|
167
|
-
this.loading.set(false);
|
|
168
|
-
this.response.set({ message: 'User deleted successfully' });
|
|
169
|
-
},
|
|
170
|
-
error: (err) => {
|
|
171
|
-
this.loading.set(false);
|
|
172
|
-
this.error.set(err.message || 'Failed to delete user');
|
|
173
|
-
}
|
|
182
|
+
next: () => this.response.set({ message: 'User deleted successfully' })
|
|
174
183
|
});
|
|
175
184
|
}
|
|
176
185
|
}
|