我应该如何在 Angular2 中制作可配置模块 [英] How should I make configurable modules in Angular2

查看:28
本文介绍了我应该如何在 Angular2 中制作可配置模块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道在 angular2 中设置可配置模块的最佳方法是什么.在 angular1 中,这通常是通过提供者完成的.随着它们发生了相当大的变化,您将如何将配置参数传递给可重用的 ng2 模块/指令/组件?

一个 ng1 示例:

//配置一个(第三方)模块.config(function (angularPromiseButtonsProvider) {angularPromiseButtonsProvider.extendConfig({spinnerTpl: '<div class="other-class"></span>',disableBtn: 假});});//设置提供者.provider('angularPromiseButtons', function angularPromiseButtonsProvider() {变量配置 = {spinnerTpl: '<span class="btn-spinner"></span>',优先级:0,disableBtn:真,};返回 {扩展配置:功能(新配置){config = angular.extend(config, newConfig);},$get:函数(){返回 {配置:配置};}};})//在指令等中使用结果.directive('promiseBtn', function(angularPromiseButtons){var config = angularPromiseButtons.config;})

这与这个问题基本相同但针对 angular2.

解决方案

有几个方案,可以单独使用,也可以一起使用.

配置服务

通常需要提供以键/值形式提供必要配置的服务.

可能有多个配置服务来配置一个应用实体,例如someConfig 用于通用用户定义配置,someDefaultConfig 用于所有可能更改的默认值.例如,someConfig 可能包含始终由用户定义的身份验证凭据,而 someDefaultConfig 可能包含默认挂钩回调、身份验证提供程序的深层设置等.实现这是将配置对象与 Object.assign 合并.

强制配置服务

一个需要用户明确定义配置服务的方法,它基本上使用 DI 来指定某些模块在没有正确配置的情况下将无法工作.

AngularJS

//第三方模块//如果 someConfig 不是由用户定义的,将会失败angular.module('some', []).factory('someService', (someConfig) => { ... })//用户自定义模块angular.module('app', ['some']).constant('someConfig', { foo: 'foo' });

角度

//第三方模块export const SOME_CONFIG = new InjectionToken('someConfig');@Injectable类 SomeService {构造函数(@Inject(SOME_CONFIG) someConfig) { ... }}@NgModule({ 提供者:[SomeService] })导出类 SomeModule {}//用户自定义模块@NgModule({进口:[SomeModule],提供者:[{提供:SOME_CONFIG,useValue:{foo:'foo'}}])导出类 AppModule {}

具有可覆盖空值的可选配置服务

这是对上一个方案的一个细微变化,唯一的区别是有一个空的默认值,如果用户没有定义配置服务,它不会使应用程序失败:

AngularJS

//第三方模块angular.module('some', []).constant('someConfig', {})...

角度

//第三方模块@NgModule({ 提供者: [..., { 提供: SOME_CONFIG, useValue: {} }] })导出类 SomeModule {}...

可选配置服务

或者,配置服务可以完全成为可选的注入.

AngularJS

//第三方模块angular.module('some', []).factory('someService', ($injector) => {const someConfig = $injector.has('someConfig') ?$injector.get('someConfig') : {};...})...

角度

//第三方模块export const SOME_CONFIG = new InjectionToken('someConfig');@Injectable类 SomeService {构造函数(@Inject(SOME_CONFIG)@Optional()someConfig){this.someConfig = someConfig !== null ?一些配置:{};...}}@NgModule({ 提供者:[SomeService] })导出类 SomeModule {}...

forRoot 方法

forRoot 静态模块方法是 一种约定,遵循 Angular 路由器模块和众多第三方模块.如指南中所述,方法返回一个实现 ModuleWithProviders.

基本上,它提供了基于 forRoot(...) 参数动态定义模块提供程序的机会.这可以被认为是 Angular 中不存在的 AngularJS configprovider 单元的替代.

AngularJS

//第三方模块angular.module('some', []).constant('someDefaultConfig', { bar: 'bar' }).provider('someService', function (someDefaultConfig) {让 someMergedConfig;this.configure = (config) =>{someMergedConfig = Object.assign({}, someDefaultConfig, config);};this.$get = ...});//用户自定义模块angular.module('app', ['some']).config((someServiceProvider) => {someServiceProvider.configure({ foo: 'foo' });});

角度

//第三方模块export const SOME_CONFIG = new InjectionToken('someConfig');export const SOME_DEFAULT_CONFIG = new InjectionToken('someDefaultConfig');@Injectable类 SomeService {构造函数(@Inject(SOME_CONFIG) someConfig,@Inject(SOME_DEFAULT_CONFIG) someDefaultConfig){this.someMergedConfig = Object.assign({}, someDefaultConfig, someConfig);...}}@NgModule({ 提供者:[一些服务,{ 提供:SOME_DEFAULT_CONFIG,useValue { bar:'bar' } }] })导出类 SomeModule {静态 forRoot(config): ModuleWithProviders {返回 {ngModule: SomeModule,提供者:[{提供:SOME_CONFIG,useValue:配置}]};}}//用户自定义模块@NgModule({ 导入: [SomeModule.forRoot({ foo: 'foo' })] })导出类 AppModule {}

APP_INITIALIZER 多供应商

Angular APP_INITIALIZER 多提供者允许提供异步初始化例程用于应用程序.

APP_INITIALIZER 与 AngularJS 配置阶段有一些相似之处.APP_INITIALIZER 例程容易受到竞争条件的影响,类似于 AngularJS 中的 configrun 块.例如,Router 可用于在根组件中注入,但不能在 APP_INITIALIZER 中注入,这是由于对另一个 APP_INITIALIZER 的循环依赖.

同步初始化例程

AngularJS

<预><代码>...//用户自定义模块angular.module('app', ['some']).config((someServiceProvider) => {someServiceProvider.configure({ foo: 'foo' });});

角度

<预><代码>...//用户自定义模块导出函数 someAppInitializer(someService: SomeService) {返回 () =>{someService.configure({ foo: 'foo' });};}@NgModule({进口:[SomeModule],提供者:[{提供:APP_INITIALIZER,多:真实,useFactory: someAppInitializer,deps: [SomeService]}]})导出类 AppModule {}

异步初始化例程

初始化可能涉及从远程源获取配置以配置服务;这是单个 AngularJS 应用程序无法实现的.这需要有另一个应用程序来初始化和引导主模块.这个场景自然由APP_INITIALIZER处理.

AngularJS

<预><代码>...//用户自定义模块angular.module('app', ['some']);angular.module('appInitializer', []).factory('初始化程序', ($document, $http) => {返回 $http.get('data.json').then((result) => result.data).then((数据) => {$document.ready(() => {angular.bootstrap($document.find('body'), ['app', (someServiceProvider) => {someServiceProvider.configure(data);}]);});});});angular.injector(['ng', 'appInitializer']).get('初始化程序').catch((err) => console.error(err));

角度

<预><代码>...//用户自定义模块导出函数 someAppInitializer(http: HttpClient, someService: SomeService) {返回 () =>{返回 http.get('data.json').toPromise().then(数据=> {someService.configure(data);});};}@NgModule({进口:[SomeModule],提供者:[{提供:APP_INITIALIZER,多:真实,useFactory: someAppInitializer,deps: [HttpClient, SomeService]}]})导出类 AppModule {}

I'm wondering what would be the best way to set up configurable modules in angular2. In angular1 this was done usually via providers. With them being changed quite a bit, how would you go about passing config parameters to reusable ng2 modules/directives/components?

An ng1 example:

// configuring a (third party) module    
.config(function (angularPromiseButtonsProvider) {
  angularPromiseButtonsProvider.extendConfig({
    spinnerTpl: '<div class="other-class"></span>',
    disableBtn: false
  });
});

 // setting up the provider
.provider('angularPromiseButtons', function angularPromiseButtonsProvider() {
    var config = {
        spinnerTpl: '<span class="btn-spinner"></span>',
        priority: 0,
        disableBtn: true,
    };

    return {
        extendConfig: function(newConfig) {
            config = angular.extend(config, newConfig);
        },

        $get: function() {
            return {
                config: config
            };
        }
    };
})

// using the result in the directive, etc.
.directive('promiseBtn', function(angularPromiseButtons){
    var config = angularPromiseButtons.config;
})

This is basically the same question as this one but directed at angular2.

解决方案

There are several recipes, which can be used separately or together.

Configuration service

Having a service to provide necessary configuration in key/value form is usually desirable.

There may be more than one configuration service to configure one application entity, e.g. someConfig for generic user-defined configuration, and someDefaultConfig for all default values that are supposed to be possibly changed. For example, someConfig may contain auth credentials that are always user-defined, and someDefaultConfig may contain default hook callbacks, deep settings for auth providers, etc. The most simple way to implement this is to merge configuration objects with Object.assign.

Mandatory configuration service

A recipe that requires the user to define configuration service explicitly, it basically uses DI to designate that some module won't work without proper configuration.

AngularJS

// third-party module
// will fail if someConfig wasn't defined by the user
angular.module('some', []).factory('someService', (someConfig) => { ... })

// user-defined module
angular.module('app', ['some']).constant('someConfig', { foo: 'foo' });

Angular

// third-party module
export const SOME_CONFIG = new InjectionToken('someConfig');

@Injectable
class SomeService {
  constructor(@Inject(SOME_CONFIG) someConfig) { ... }
}

@NgModule({ providers: [SomeService] })
export class SomeModule {}

// user-defined module
@NgModule({
  imports: [SomeModule],
  providers: [{ provide: SOME_CONFIG, useValue: { foo: 'foo' } }]
)
export class AppModule {}

Optional configuration service with overridable empty value

This is a slight variation of the previous recipe, the only difference is that there is empty default value that won't make the application to fail if configuration service wasn't defined by a user:

AngularJS

// third-party module
angular.module('some', [])
.constant('someConfig', {})
...

Angular

// third-party module
@NgModule({ providers: [..., { provide: SOME_CONFIG, useValue: {} }] })
export class SomeModule {}
...

Optional configuration service

Alternatively, configuration service can be made totally optional for injection.

AngularJS

// third-party module
angular.module('some', []).factory('someService', ($injector) => {
  const someConfig = $injector.has('someConfig') ? $injector.get('someConfig') : {};
  ...
})
...

Angular

// third-party module
export const SOME_CONFIG = new InjectionToken('someConfig');

@Injectable
class SomeService {
  constructor(@Inject(SOME_CONFIG) @Optional() someConfig) {
    this.someConfig = someConfig !== null ? someConfig : {};
    ...
  }
}

@NgModule({ providers: [SomeService] })
export class SomeModule {}
...

forRoot method

forRoot static module method is a convention that is followed by Angular router module and numerous third-party modules. As explained in the guide, the method returns an object that implements ModuleWithProviders.

Basically it gives an opportunity to define module providers dynamically, based on forRoot(...) arguments. This can be considered an alternative to AngularJS config and provider units that don't exist in Angular.

AngularJS

// third-party module
angular.module('some', [])
.constant('someDefaultConfig', { bar: 'bar' })
.provider('someService', function (someDefaultConfig) {
  let someMergedConfig;

  this.configure = (config) => {
    someMergedConfig = Object.assign({}, someDefaultConfig, config);
  };
  this.$get = ...
});

// user-defined module
angular.module('app', ['some']).config((someServiceProvider) => {
  someServiceProvider.configure({ foo: 'foo' });
});

Angular

// third-party module
export const SOME_CONFIG = new InjectionToken('someConfig');
export const SOME_DEFAULT_CONFIG = new InjectionToken('someDefaultConfig');

@Injectable
class SomeService {
  constructor(
    @Inject(SOME_CONFIG) someConfig,
    @Inject(SOME_DEFAULT_CONFIG) someDefaultConfig
  ) {
    this.someMergedConfig = Object.assign({}, someDefaultConfig, someConfig);
    ...
  }
}

@NgModule({ providers: [
  SomeService,
  { provide: SOME_DEFAULT_CONFIG, useValue { bar: 'bar' } }
] })
export class SomeModule {
  static forRoot(config): ModuleWithProviders {
    return {
      ngModule: SomeModule,
      providers: [{ provide: SOME_CONFIG, useValue: config }]
    };
  }
}

// user-defined module
@NgModule({ imports: [SomeModule.forRoot({ foo: 'foo' })] })
export class AppModule {}

APP_INITIALIZER multi-provider

Angular APP_INITIALIZER multi-provider allows to provide asynchronous initialization routines for the application.

APP_INITIALIZER shares some similarities with AngularJS config phase. APP_INITIALIZER routines are susceptible to race conditions, similarly to config and run blocks in AngularJS. For instance, Router is available for injection in root component but not in APP_INITIALIZER, due to circular dependency on another APP_INITIALIZER.

Synchronous initialization routine

AngularJS

...
// user-defined module
angular.module('app', ['some']).config((someServiceProvider) => {
  someServiceProvider.configure({ foo: 'foo' });
});

Angular

...
// user-defined module
export function someAppInitializer(someService: SomeService) {
  return () => {
    someService.configure({ foo: 'foo' });
  };
}

@NgModule({
  imports: [SomeModule],
  providers: [{
    provide: APP_INITIALIZER,
    multi: true,
    useFactory: someAppInitializer,
    deps: [SomeService]
  }]
})
export class AppModule {}

Asynchronous initialization routine

Initialization may involve fetching configuration from remote source to configure services; something that is not possible with single AngularJS application. This requires to have another application that initializes and bootstraps main module. This scenario is naturally handled by APP_INITIALIZER.

AngularJS

...
// user-defined module
angular.module('app', ['some']);

angular.module('appInitializer', [])
.factory('initializer', ($document, $http) => {
  return $http.get('data.json')
  .then((result) => result.data)
  .then((data) => {
    $document.ready(() => {
      angular.bootstrap($document.find('body'), ['app', (someServiceProvider) => {
        someServiceProvider.configure(data);
      }]);
    });
  });
});

angular.injector(['ng', 'appInitializer'])
.get('initializer')
.catch((err) => console.error(err));

Angular

...
// user-defined module
export function someAppInitializer(http: HttpClient, someService: SomeService) {
  return () => {
    return http.get('data.json').toPromise()
    .then(data => {
      someService.configure(data);
    });
  };
}

@NgModule({
  imports: [SomeModule],
  providers: [{
    provide: APP_INITIALIZER,
    multi: true,
    useFactory: someAppInitializer,
    deps: [HttpClient, SomeService]
  }]
})
export class AppModule {}

这篇关于我应该如何在 Angular2 中制作可配置模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆