加班到快斷賽,還無法發文(打第二次了)![]()
import { Injectable, signal } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class LoadingService {
private readonly activeRequests = signal<number>(0);
readonly isLoading = signal<boolean>(false);
show(): void {
this.activeRequests.update(count => count + 1);
this.isLoading.set(true);
}
hide(): void {
this.activeRequests.update(count => Math.max(0, count - 1));
if (this.activeRequests() === 0) {
this.isLoading.set(false);
}
}
}
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { finalize } from 'rxjs';
import { LoadingService } from '../services/loading.service';
export const loadingInterceptor: HttpInterceptorFn = (req, next) => {
const loadingService = inject(LoadingService);
loadingService.show();
return next(req).pipe(
finalize(() => loadingService.hide())
);
};
import { Component, inject } from '@angular/core';
import { LoadingService } from '@app/core/services/loading.service';
@Component({
selector: 'app-global-loading',
standalone: true,
template: `
@if (loadingService.isLoading()) {
<div class="loading-overlay">
<div class="spinner"></div>
</div>
}
`,
styles: [`
.loading-overlay {
position: fixed;
top: 0; left: 0; width: 100vw; height: 100vh;
background: rgba(0, 0, 0, 0.4);
display: flex; justify-content: center; align-items: center;
z-index: 9999;
}
.spinner {
width: 48px; height: 48px;
border: 5px solid #FFF;
border-bottom-color: transparent;
border-radius: 50%;
animation: rotation 1s linear infinite;
}
@keyframes rotation {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`]
})
export class GlobalLoading {
protected readonly loadingService = inject(LoadingService);
}
在最外層的 App 元件匯入 Loading 元件。
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { GlobalLoading } from '@shared-components';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, GlobalLoading],
template: `
<router-outlet />
<app-global-loading />
`
})
export class AppComponent {}
app.config.ts 設定攔截器,每當呼叫 API 便會執行 Loading 遮罩。
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { loadingInterceptor } from '@app/core/interceptors/loading.interceptor';
import { authInterceptor } from '@app/core/interceptors/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([
loadingInterceptor, // 全域 Loading 觸發
authInterceptor, // Token 與 Error 處理
])
),
// ...
]
};
今天就這樣啦!