8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

Angular 18:在 APP_INITIALIZER 提供程序之后设置 InjectionToken

andrepz 3月前

74 0

在 angular 18 中,我需要在应用程序初始化后设置 API URL(因为出于显而易见的原因,需要从 json 文件中获取 URL)。但是它没有设置。实际上 InjectionToken 之前已经设置过了

在 angular 18 中,我需要在应用程序初始化后设置 API URL(因为出于显而易见的原因,需要从 json 文件中获取 URL)。但是它没有设置。实际上,它 InjectionToken 之前已经设置过, APP_INITIALIZER 尽管它是后来添加到 providers 数组 app.config.ts

参考: https://angular.dev/api/core/APP_INITIALIZER?tab=usage-notes

以下是 Stackblitz

期望设置 API_URL_TOKEN 为 url 值并将其用于所有 API 调用。存储在 localStorage 中并分配。

private apiUrl = inject(API_URL_TOKEN);

loginUser(data: { username: string; password: string }) {
    return this.http.post(`${this.apiUrl}/auth`, data).pipe(
      tap((res) => {
        if (res.success) {
          this.addToken(res.data);
        }
      })
    );
  }

这是我尝试过的

主要内容

bootstrapApplication(AppComponent, appConfig)

应用程序.config.ts

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(withFetch(), withInterceptors([authInterceptor, errorInterceptor])),
    provideAppInitializer(),
    provideApiUrl(),
    provideRouter(routes),
    provideAnimationsAsync(),
    provideCharts(withDefaultRegisterables()),
  ],
};

应用程序初始化器.ts

const CONFIG_URL = '/config.json';

function appInitializer(http: HttpClient, storageService: StorageService) {
  return async () => {
    try {
      const config = await firstValueFrom(http.get<{apiUrl: string}>(CONFIG_URL));
      storageService.apiUrl = config.apiUrl;
      console.log('Config loaded successfully');
    } catch (error) {
      console.error('Error loading configuration:', error);
      throw error;
    }
  };
}

export function provideAppInitializer(): Provider {
  return {
    provide: APP_INITIALIZER,
    useFactory: appInitializer,
    deps: [HttpClient, StorageService],
    multi: true,
  };
}

应用程序-url-token.ts

export const API_URL_TOKEN = new InjectionToken<string>('API_URL_TOKEN');

function apiUrlFactory(storageService: StorageService): string {
  const apiUrl = storageService.apiUrl;
  if (apiUrl) {
    return apiUrl;
  }
  throw new Error('API URL not found in configuration');
}

export function provideApiUrl(): Provider {
  return {
    provide: API_URL_TOKEN,
    useFactory: apiUrlFactory,
    deps: [StorageService],
    multi: false,
  };
}

/* 其他方式 */

export const API_URL_TOKEN = new InjectionToken<string>('API_URL_TOKEN');

export function provideApiUrl(): Provider {
  return {
    provide: API_URL_TOKEN,
    useValue: (storageService: StorageService) => storageService.getApiUrl,
    deps: [StorageService],
  };
}

存储.服务.ts

import { inject, Injectable, InjectionToken } from '@angular/core';

const LOCAL_STORAGE = new InjectionToken<Storage>('Browser Storage', {
  providedIn: 'root',
  factory: () => localStorage,
});

const API_URL = 'apiUrl';

@Injectable({
  providedIn: 'root',
})
export class StorageService {
  private readonly storage = inject<Storage>(LOCAL_STORAGE);

  get(key: string) {
    return this.storage.getItem(key);
  }
  set(key: string, value: string): void {
    this.storage.setItem(key, value);
  }

  remove(key: string): void {
    return this.storage.removeItem(key);
  }

  clear() {
    return this.storage.clear();
  }

  get apiUrl(): string | null {
    return this.storage.getItem(API_URL) || null;
  }

  set apiUrl(url: string) {
    this.storage.setItem(API_URL, url);
  }
}

启动时

enter image description here

刷新时

enter image description here

在第二种情况下,很明显,它在初始化之前

关于 config.json

enter image description here

帖子版权声明 1、本帖标题:Angular 18:在 APP_INITIALIZER 提供程序之后设置 InjectionToken
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由andrepz在本站《angular》版块原创发布, 转载请注明出处!
最新回复 (0)
  • Beu 3月前 0 只看Ta
    引用 2

    下面的 github 问题帮助我了解了问题出在哪里。

    HTTP_INTERCEPTOR 不等待 APP_INITIALLIZER

    由于您的拦截器使用 HttpClient ,这反过来又调用了拦截器,因此您会遇到此问题。此问题的解决方案是使用 HttpBackend HttpBackend HttpRequest

    function appInitializer(http: HttpBackend, storageService: StorageService) {
      return () => {
        return http.handle(new HttpRequest('GET', CONFIG_URL)).pipe(
          map((config: any) => {
            storageService.apiUrl = config.apiUrl;
            console.log('Config loaded successfully');
          }),
          catchError((error: any) => {
            console.error('Error loading configuration:', error);
            throw error;
          })
        );
      };
    }
    
    export function provideAppInitializer(): Provider {
      return {
        provide: APP_INITIALIZER,
        useFactory: appInitializer,
        deps: [HttpBackend, StorageService],
        multi: true,
      };
    }
    

    将您的内容放在 config.json 资产文件夹中,以获取数据。然后在 angular.json 的资产文件夹中进行配置,以便可以发现它。如 stackblitz 中所示。

       ...
       },
          "options": {
            "assets": ["src/assets"],
            "index": "src/index.html",
            "browser": "src/main.ts",
            ...
    

    您可以摆脱它 async await 并使用纯 rxjs 来执行此操作。我们可以用来 map 将值分配给存储并用来 catchError 捕获任何异常。

    function appInitializer(http: HttpClient, storageService: StorageService) {
      return () => {
        return http.get<XiConfig>(CONFIG_URL).pipe(
          map((config: any) => {
            storageService.apiUrl = config.apiUrl;
            console.log('Config loaded successfully');
          }),
          catchError((error: any) => {
            console.error('Error loading configuration:', error);
            throw error;
          })
        );
      };
    }
    

    还有一件事是您需要将应用程序配置导入到引导应用程序。

    bootstrapApplication(App, appConfig);
    

    然后最后在组件上访问它。

    @Component({
      selector: 'app-root',
      standalone: true,
      template: `
        <h1>Hello from {{ name }}!</h1>
        <a target="_blank" href="https://angular.dev/overview">
          Learn more about Angular
        </a>
      `,
    })
    export class App {
      name = 'Angular';
    
      constructor(@Inject(API_URL_TOKEN) private urlToken: string) {
        console.log(urlToken);
      }
    }
    

    完整代码:

    主程序

    import { Component, Inject } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import 'zone.js';
    import { appConfig } from './app/app.config';
    import { API_URL_TOKEN } from './app/app-url-token';
    
        @Component({
          selector: 'app-root',
          standalone: true,
          template: `
            <h1>Hello from {{ name }}!</h1>
            <a target="_blank" href="https://angular.dev/overview">
              Learn more about Angular
            </a>
          `,
        })
        export class App {
          name = 'Angular';
    
          constructor(@Inject(API_URL_TOKEN) private urlToken: string) {
            console.log(urlToken);
          }
        }
    
    bootstrapApplication(App, appConfig);
    

    应用程序.config.ts

    import { ApplicationConfig } from '@angular/core';
    import {
      provideHttpClient,
      withFetch,
      withInterceptors,
    } from '@angular/common/http';
    import { provideAppInitializer } from './app-initializer';
    import { provideApiUrl } from './app-url-token';
    //import { provideRouter } from '@angular/router';
    //import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
    //import { provideCharts, withDefaultRegisterables } from 'ng2-charts';
    //import { routes } from './app.routes';
    //import { authInterceptor } from './interceptors/auth.interceptor';
    //import { errorInterceptor } from './interceptors/error.interceptor';
    
    export const appConfig: ApplicationConfig = {
      providers: [
        provideHttpClient(withFetch(), withInterceptors([])),
        provideAppInitializer(),
        provideApiUrl(),
        //provideRouter(routes),
        //provideAnimationsAsync(),
        //provideCharts(withDefaultRegisterables()),
      ],
    };
    

    应用程序.url.token

    import { InjectionToken, Provider } from '@angular/core';
    import { StorageService } from './storage.service';
    
    export const API_URL_TOKEN = new InjectionToken<string>('API_URL_TOKEN');
    
    function apiUrlFactory(storageService: StorageService): any {
      console.log(storageService.apiUrl);
      const apiUrl = storageService.apiUrl;
      if (apiUrl) {
        console.log('set apiUrl');
        return apiUrl;
      }
      throw new Error('API URL not found in configuration');
    }
    
    /*
    
    export const API_URL_TOKEN = new InjectionToken<string>('API_URL_TOKEN');
    
    export function provideApiUrl(): Provider {
      return {
        provide: API_URL_TOKEN,
        useValue: (storageService: StorageService) => storageService.getApiUrl,
        deps: [StorageService],
      };
    }
    
    
    */
    
    export function provideApiUrl(): Provider {
      return {
        provide: API_URL_TOKEN,
        useFactory: apiUrlFactory,
        deps: [StorageService],
        multi: false,
      };
    }
    

    应用程序初始化程序:

    import { HttpBackend, HttpClient, HttpRequest } from '@angular/common/http';
    import { APP_INITIALIZER, Provider } from '@angular/core';
    import { firstValueFrom } from 'rxjs';
    import { StorageService } from './storage.service';
    import { map, catchError } from 'rxjs';
    
    interface XiConfig {
      apiUrl: string;
    }
    
    const CONFIG_URL = '/assets/config.json';
    
        function appInitializer(http: HttpBackend, storageService: StorageService) {
          return () => {
            return http.handle(new HttpRequest('GET', CONFIG_URL)).pipe(
              map((config: any) => {
                storageService.apiUrl = config.apiUrl;
                console.log('Config loaded successfully');
              }),
              catchError((error: any) => {
                console.error('Error loading configuration:', error);
                throw error;
              })
            );
          };
        }
    
        export function provideAppInitializer(): Provider {
          return {
            provide: APP_INITIALIZER,
            useFactory: appInitializer,
            deps: [HttpBackend, StorageService],
            multi: true,
          };
        }
    

    Stackblitz 演示

  • 感谢您的回答,它在 stackblitz 中有效。但在应用程序中无效。我已将错误的屏幕截图与您的解决方案一起添加。请检查。

  • 您的意思是 config.json 吗?如果是,那么它已经在路径中并正在获取正确的数据。从 angular 18 开始,它位于公共文件夹中。在 angular.json 中它是这样的。\'assets\': [ {\'glob\': \'**/*\', \'input\': \'public\' } ]。在启动和刷新时,请参阅所附的屏幕截图。您将看到问题。提前致谢

  • @skdonthi 尝试执行 console.log 并检查是否收到响应,可能是获取 JSON 的路径不正确

返回
作者最近主题: