美文网首页
Angular InjectionToken APP_INITI

Angular InjectionToken APP_INITI

作者: _扫地僧_ | 来源:发表于2022-07-11 11:56 被阅读0次

    下图是 SAP 电商云 Spartacus UI 中的一段和 Site Context 处理逻辑相关的代码:

    export const contextServiceProviders: Provider[] = [
      BaseSiteService,
      LanguageService,
      CurrencyService,
      {
        provide: APP_INITIALIZER,
        useFactory: initializeContext,
        deps: [ConfigInitializerService, SiteContextRoutesHandler],
        multi: true,
      },
    ];
    

    注意第 40 行的标志位,multi,默认值为 false,这意味着在代码后注册的 initializer 的实现,会覆盖掉先注册的 initializer provider 实现。换言之,这种情况下,APP_INITIALIZER 的 provider 只能有一个。

    如果 multi: true 被设置,那么新的提供者会被添加到之前注册的提供者中,使得一个令牌的提供者不止一个。当这个令牌被调用时,angular会执行所有这些令牌。

    因此,开发人员可以使用 multi: true 来创建 multi Provider令牌。这意味着我们可以创建多个函数/服务,并在初始化期间调用它。

    做一个测试,分别创建两个 initializer 的实现:

    
    export function initializeApp2() {
      return (): Promise<any> => {
        return new Promise((resolve, reject) => {
          console.log(`initializeApp2 called`);
          setTimeout(() => {
            console.log(`initializeApp2 Finished`);
            resolve();
          }, 2000);
        });
      };
    }
    

    然后使用 multi:true 给同一个 Injection Token APP_INITIALIZER 注册两个不同的 provider:

      providers: [ 
        AppInitService,
        { provide: APP_INITIALIZER,useFactory: initializeApp1, deps: [AppInitService], multi: true},
        { provide: APP_INITIALIZER,useFactory: initializeApp2, multi: true}
      ],
    

    最后运行时,这两个 initializers 会同时得到触发。

    另外,这个例子使用了 useFactory 为 Injection Token 注册 provider.

    一个例子:

    const WINDOW = new InjectionToken<Window>('A reference to the window object', {
      factory: () => window,
    });
    

    上面的例子,使用工厂函数作为提供者来设置 InjectionToken,就好像它是在应用程序的根注入器中显式定义的一样。 现在我们可以在应用程序的任意位置使用它:

    @Component({
      selector: 'my-app'
    })
    export class AppComponent {
      constructor(@Inject(WINDOW) window: Window) {}
    }
    

    我们可以使用 inject 函数,在工厂函数的执行环境里,来获取其他提供者的引用。 让我们看另一个例子:

    import { inject, InjectionToken } from '@angular/core';
    import { ActivatedRoute } from '@angular/router';
    
    export type TimespanProvider = Observable<string>;
    
    export const TIMESPAN = new InjectionToken('Subscribe to timespan query param', {
      factory() {
        const activatedRoute = inject(ActivatedRoute);
    
        return activatedRoute.queryParams.pipe(
          pluck('timespan'),
          filterNil(),
          distinctUntilChanged()
        );
      },
    });
    

    上面的例子,我们注入 ActivatedRoute,使用 inject 获得其 provider 实例,并为时间跨度查询参数返回一个 observable. 将 Observable<String> 导出成新的类型别名:TimespanProvider.

    这个 Injection Token 的注入例子:

    @Component({
      selector: 'app-home'
    })
    export class HomeComponent implements OnInit {
      constructor(@Inject(TIMESPAN) private timespan$: TimespanProvider) {}
    
      ngOnInit() {
        this.timespan$.pipe(untilDestroyed(this)).subscribe(console.log);
      }
    }
    

    另一个例子:我们有一个 ThemeService 实现类,将用户当前的 Theme 值暴露出去:

    @Injectable({ providedIn: 'root' })
    export class ThemeService {
      private theme = new Subject<string>();
      theme$ = this.theme.asObservable();
    
      setTheme(theme: string) {
        this.theme.next(theme);
      }
    }
    

    Component 读取当前 Theme 的值:

    @Component({
      selector: 'app-hello',
      template: `<h1>{{ theme$ | async }}</h1>`
    })
    export class HelloComponent {
      theme$: Observable<string>;
    
      constructor(private themeService: ThemeService) {}
    
      ngOnInit() {
        this.theme$ = this.themeService.theme$;
      } 
    
    }
    

    采取 Injection Token 的实现方式:

    export type ActiveThemeProvider = Observable<string>;
    export const ACTIVE_THEME = new InjectionToken<ActiveThemeProvider>('Active theme', {
      factory() {
        return inject(ThemeService).theme$;
      }
    });
    

    首先定义一个新的 Type,然后使用 factory 方法,返回 Observable<string> 类型的结果。

    相关文章

      网友评论

          本文标题:Angular InjectionToken APP_INITI

          本文链接:https://www.haomeiwen.com/subject/qzmnbrtx.html