После обновления до angular 17 и преобразования в автономный перехватчик загрузчика, его компонент не работает

Проект построен в Angular Verison:17. Мы создали loading.comComponent.ts и loader.service.ts и привязали их в app.comComponent.html и в файле Progress.interceptor.ts, как вы можете видеть в коде. Загрузчик работал хорошо в версии Angular 14, но после обновления до Angular 17 он перестал работать. А Angular версии 17 был обновлен автономным компонентом, и мы обновили код согласно версии 17. Загрузчик должен работать при каждой навигации по маршрутизатору и вызове API.

Также появляется ошибка «Невозможно прочитать свойства неопределенного значения (чтение «применить»)» после каждого вызова API.

loading.comComponent.ts

      import { Component, OnInit, OnDestroy } from '@angular/core';
        import { LoaderService } from '../../service';
        import { Subscription } from 'rxjs';
        import { LoaderState } from '../../models/loader.model'
        import { CommonModule } from '@angular/common';
        @Component({
        selector: 'loading',
        templateUrl: './loading.component.html',
        styleUrls: ['./loading.component.css'],
        standalone: true,
        imports: [CommonModule],
        providers:[LoaderService]
        })
        export class LoadingComponent implements OnInit, OnDestroy {
        show = false;
        subscription: Subscription;
        constructor(private loaderSvc: LoaderService) { }

        ngOnInit() {
        this.subscription = this.loaderSvc.loaderState.subscribe((state: LoaderState) => {
        this.show = state.show; //setTimeout(() => this.show = state.show);
        })
        }

        ngOnDestroy() {
        if (this.subscription) {
        this.subscription.unsubscribe();
        }
        }
        }

loading.comComponent.html

<div *ngIf = "show">
 <div class = "loader-overlay" style = "position: relative; top: 45vh;">
    <div class = "spinner">
      <div class = "bounce1"></div>
      <div class = "bounce2"></div>
      <div class = "bounce3"></div>
    </div>
  </div>

app.comComponent.html

<loading></loading> 
<router-outlet></router-outlet>

app.comComponent.ts

import { AfterViewInit, Component, OnInit, Inject, PLATFORM_ID } from '@angular/core';
import { Router, ActivatedRoute, NavigationEnd, RouterModule } from '@angular/router';
import { LoadingComponent } from './shared/components';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
standalone: true,
imports: [RouterModule, NgIdleKeepaliveModule, LoadingComponent],
providers: [{ provide: WINDOW, useValue: {} }]

constructor(public loaderSVC: LoaderService){}

ngOnInit() {
this.router.events.subscribe((evt) => {
this.loaderSVC.startLoading();
// console.info("start")
if (!(evt instanceof NavigationEnd)) {
return;
}
setTimeout(() => {
this.loaderSVC.stopLoading();
// console.info("stop")
});
})

loader.service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { LoaderState } from '../models/loader.model';

@Injectable({
providedIn: 'root'
})
export class LoaderService {
private loaderSubject = new Subject<LoaderState>();
loaderState = this.loaderSubject.asObservable();
constructor() { }

startLoading() {  
this.loaderSubject.next(<LoaderState>{show: true});
}

stopLoading() {
this.loaderSubject.next(<LoaderState>{show: false});
}
}

Progress.interceptor.ts

import {Injectable} from '@angular/core';
import {HttpRequest, HttpHandler, HttpEvent, HttpInterceptor} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/finally';
import { LoaderService } from '../service';

@Injectable()
export class ProgressInterceptor implements HttpInterceptor {
constructor(private loaderSvc: LoaderService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (request.params.has('ignoreLoadingBar')) {
return next.handle(request.clone({ headers: request.headers.delete('ignoreLoadingBar') }));
}
this.loaderSvc.startLoading();
return next.handle(request).finally(() => this.loaderSvc.stopLoading());
}
}

main.ts

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import './icons';
import { environment } from './environments/environment';
import { AppComponent } from '@app/app.component';
import { appConfig } from '@app/app.config.server';
import { bootstrapApplication } from '@angular/platform-browser';

if (environment.production) {
enableProdMode();
}

bootstrapApplication(AppComponent, appConfig).catch(err => console.error(err));

app.config.server.ts

import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './routes/routes';
import { provideClientHydration, withHttpTransferCacheOptions } from '@angular/platform-browser';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { ReactiveFormsModule } from '@angular/forms';
import { provideAnimations } from '@angular/platform-browser/animations';
import { CookieService } from 'ngx-cookie-service';
import { provideNgxMask } from 'ngx-mask';
import { provideToastr } from 'ngx-toastr';
import { ProgressInterceptor, BasicAuthInterceptor } from './shared/helpers';
import { StorageService } from './shared/service';

export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes), provideClientHydration(withHttpTransferCacheOptions({
includePostRequests: true
})), provideNgxMask(), {
provide: HTTP_INTERCEPTORS,
useClass: ProgressInterceptor,
multi: true
}, {
provide: HTTP_INTERCEPTORS,
useClass: BasicAuthInterceptor,
multi: true
}, provideAnimations(),
CookieService, StorageService, provideToastr({ positionClass: 'toast-top-right' }),
importProvidersFrom(ReactiveFormsModule, HttpClientModule),]
};
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
1
0
288
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

loading.component.ts имеет LoaderService в массиве поставщиков, это означает, что был создан отдельный экземпляр, предназначенный только для этого компонента. Поэтому другие выбросы не получены.

...
@Component({
    selector: 'loading',
    templateUrl: './loading.component.html',
    styleUrls: ['./loading.component.css'],
    standalone: true,
    imports: [CommonModule],
    // providers:[LoaderService] // <- changed here!
})
export class LoadingComponent implements OnInit, OnDestroy {
    ...

Вам нужно преобразовать progress.interceptor.ts в interceptorFn

import { inject } from '@angular/core';
import { HttpInterceptorFn } from '@angular/common/http';
import { LoaderService } from '../service';

export const demoInterceptor: HttpInterceptorFn = (req, next) => {
  const loaderSvc = inject(LoaderService);
  const authToken = 'YOUR_AUTH_TOKEN_HERE';

  // Pass the cloned request with the updated header to the next handler
  if (req.params.has('ignoreLoadingBar')) {
      // Clone the request and add the authorization header
      const authReq = req.clone({ 
           headers: request.headers.delete('ignoreLoadingBar') 
      });
      return next(authReq);
  }
  this.loaderSvc.startLoading();
  return next(request).finally(() => this.loaderSvc.stopLoading());
};

Наконец, в app.config.server.ts нам нужно использовать provideHttpClient(withInterceptors([demoInterceptor])) для настройки перехватчиков.

import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './routes/routes';
import { provideClientHydration, withHttpTransferCacheOptions } from '@angular/platform-browser';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { ReactiveFormsModule } from '@angular/forms';
import { provideAnimations } from '@angular/platform-browser/animations';
import { CookieService } from 'ngx-cookie-service';
import { provideNgxMask } from 'ngx-mask';
import { provideToastr } from 'ngx-toastr';
import { ProgressInterceptor, BasicAuthInterceptor } from './shared/helpers';
import { StorageService } from './shared/service';

export const appConfig: ApplicationConfig = {
    providers: [
        provideRouter(routes), 
        provideClientHydration(withHttpTransferCacheOptions({
            includePostRequests: true
        })), 
        provideNgxMask(), 
        provideHttpClient(withInterceptors([ProgressInterceptor, 
             BasicAuthInterceptor])),
        provideAnimations(),
        CookieService, 
        StorageService, 
        provideToastr({ positionClass: 'toast-top-right' }),
        importProvidersFrom(ReactiveFormsModule, HttpClientModule),
    ]
};

Благодарю, решение работает нормально. Большое спасибо за помощь. :)

Palak Gandhi 11.04.2024 08:39

Другие вопросы по теме