Ответ 1
Я думаю, что эта статья может помочь вам https://nezhar.com/blog/create-a-loading-screen-for-angular-apps/
в основном вы должны реализовать различные перехватчики для загрузки компонентов и для запросов http
У меня есть глобальный загрузчик, который реализован так:
CoreModule:
router.events.pipe(
filter(x => x instanceof NavigationStart)
).subscribe(() => loaderService.show());
router.events.pipe(
filter(x => x instanceof NavigationEnd || x instanceof NavigationCancel || x instanceof NavigationError)
).subscribe(() => loaderService.hide());
LoaderService:
@Injectable({
providedIn: 'root'
})
export class LoaderService {
overlayRef: OverlayRef;
componentFactory: ComponentFactory<LoaderComponent>;
componentPortal: ComponentPortal<LoaderComponent>;
componentRef: ComponentRef<LoaderComponent>;
constructor(
private overlay: Overlay,
private componentFactoryResolver: ComponentFactoryResolver
) {
this.overlayRef = this.overlay.create(
{
hasBackdrop: true,
positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically()
}
);
this.componentFactory = this.componentFactoryResolver.resolveComponentFactory(LoaderComponent);
this.componentPortal = new ComponentPortal(this.componentFactory.componentType);
}
show(message?: string) {
this.componentRef = this.overlayRef.attach<LoaderComponent>(this.componentPortal);
this.componentRef.instance.message = message;
}
hide() {
this.overlayRef.detach();
}
}
При работе с Angular 7.0.2 поведение (которое я хотел) было:
Я обновил до Angular 7.2, теперь поведение:
LoaderComponent
при переходе к маршруту без какого-либо распознавателя Я добавил несколько журналов событий NavigationStart
и NavigationEnd
и обнаружил, что NavigationEnd
запускается сразу после NavigationStart
(что нормально), а Overlay исчезает примерно через 0,5 с после.
Я прочитал CHANGELOG.md
но не нашел ничего, что могло бы объяснить эту проблему. Любая идея приветствуется.
Редактировать:
После дальнейших исследований я восстановил предыдущее поведение, установив package.json
следующим образом:
"@angular/cdk": "~7.0.0",
"@angular/material": "~7.0.0",
вместо этого:
"@angular/cdk": "~7.2.0",
"@angular/material": "~7.2.0",
Я обнаружил неисправный коммит, выпущенный в версии 7.1.0, и опубликовал свою проблему в связанной проблеме GitHub. Это исправляет затухание анимации Overlay
.
Что такое совместимый с v7. 1+ способ получить желаемое поведение? По моему мнению, лучше всего было бы: показывать загрузчик только тогда, когда это необходимо, но NavigationStart
не содержит необходимой информации. Я хотел бы избежать в конечном итоге поведения отказов.
Я думаю, что эта статья может помочь вам https://nezhar.com/blog/create-a-loading-screen-for-angular-apps/
в основном вы должны реализовать различные перехватчики для загрузки компонентов и для запросов http
Реализует HttpInterceptor - Вы можете добавить глобальный загрузчик.
Вот что я закончил, поняв, что debounceTime
- хорошее решение с точки зрения UX, потому что позволяет загрузчику показывать только тогда, когда время загрузки стоит показывать загрузчик.
counter = 0;
router.events.pipe(
filter(x => x instanceof NavigationStart),
debounceTime(200),
).subscribe(() => {
/*
If this condition is true, then the event corresponding to the end of this NavigationStart
has not passed yet so we show the loader
*/
if (this.counter === 0) {
loaderService.show();
}
this.counter++;
});
router.events.pipe(
filter(x => x instanceof NavigationEnd || x instanceof NavigationCancel || x instanceof NavigationError)
).subscribe(() => {
this.counter--;
loaderService.hide();
});
Как мы реализуем загрузчик в нашей системе со списком исключений:
export class LoaderInterceptor implements HttpInterceptor {
requestCount = 0;
constructor(private loaderService: LoaderService) {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!(REQUEST_LOADER_EXCEPTIONS.find(r => request.url.includes(r)))) {
this.loaderService.setLoading(true);
this.requestCount++;
}
return next.handle(request).pipe(
tap(res => {
if (res instanceof HttpResponse) {
if (!(REQUEST_LOADER_EXCEPTIONS.find(r => request.url.includes(r)))) {
this.requestCount--;
}
if (this.requestCount <= 0) {
this.loaderService.setLoading(false);
}
}
}),
catchError(err => {
this.loaderService.setLoading(false);
this.requestCount = 0;
throw err;
})
);
}
}
и сервис загрузчика просто:
export class LoaderService {
loadingRequest = new BehaviorSubject(false);
private timeout: any;
constructor() {
}
setLoading(val: boolean): void {
if (!val) {
this.timeout = setTimeout(() => {
this.loadingRequest.next(val);
}, 300);
} else {
clearTimeout(this.timeout);
this.loadingRequest.next(val);
}
}
}
В этой сущности есть пример моего загрузчика.
https://gist.github.com/borjapazr/88e06f2e9159778ee7991cef14a130f2
app.component.ts
<custom-loader></custom-loader>
custom-loader.component.html
<div [class.hidden]="!show">
<div *ngIf="show" class="loader-overlay">
<div class="loader-bar"></div>
<div class="loader-spinner"></div>
</div>
</div>
custom-loader.component.css
.hidden {
visibility: hidden;
}
.loader-overlay {
position: fixed;
background: rgba(0,0,0,0.4);
bottom: 0;
height: 100%;
left: 0;
right: 0;
top: 0;
width: 100%;
display:block;
margin: 0 auto;
overflow: hidden;
z-index: 9999;
}
.loader-bar {
height: 4px;
width: 100%;
position: relative;
overflow: hidden;
background-color: #000000;
&:before {
display: block;
position: absolute;
content: "";
left: -200px;
width: 200px;
height: 4px;
background-color: #ffffff;
animation: loading 2s linear infinite;
}
}
.loader-spinner {
position: fixed;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
z-index: 9999;
background: url('/assets/img/loader/spinner.gif') 50% 50% no-repeat;
opacity: 0.7;
background-size: 75px 75px;
}
@keyframes loading {
from {left: -200px; width: 30%;}
50% {width: 30%;}
70% {width: 70%;}
80% {left: 50%;}
95% {left: 120%;}
to {left: 100%;}
}
custom-loader.component.ts
@Component({
selector: 'custom-loader',
templateUrl: './custom-loader.component.html',
styleUrls: ['./custom-loader.component.scss']
})
export class CustomLoaderComponent implements OnInit, OnDestroy {
public destroy$: Subject<boolean> = new Subject<boolean>();
public show = false;
constructor(private loaderService: CustomLoaderService) { }
ngOnInit() {
this.loaderService.getLoaderState()
.pipe(
takeUntil(this.destroy$)
)
.subscribe(state => {
this.show = state.show;
this.setScrollBarVisibility(!this.show);
});
}
private setScrollBarVisibility(visible: boolean) {
document.body.style.overflow = visible ? 'auto' : 'hidden';
}
ngOnDestroy() {
this.destroy$.next(true);
this.destroy$.unsubscribe();
}
}
custom-loader.service.ts
@Injectable({
providedIn: 'root'
})
export class CustomLoaderService {
private loaderSubject = new Subject<ILoaderState>();
private loaderState = this.loaderSubject.asObservable();
private timeout: any;
constructor() { }
public getLoaderState(): Observable<ILoaderState> {
return this.loaderState;
}
public show(delay?: number) {
if (delay) {
this.timeout = setTimeout(() => this.loaderSubject.next(<ILoaderState>{ show: true }), delay);
} else {
this.loaderSubject.next(<ILoaderState>{ show: true });
}
}
public hide() {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.loaderSubject.next(<ILoaderState>{ show: false });
}
}
вы не можете, потому что angular - это не Vue.JS, и вы пытаетесь уйти от Rust and Go, этой чертовски глупой, чертовски глупой