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

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

问题描述

我想知道在angular2中设置可配置模块的最佳方法是什么。在angular1中,这通常是通过提供商完成的。随着它们的改变,你会如何将配置参数传递给可重用的ng2模块/指令/组件?



一个 ng1示例

  //配置(第三方)模块
.config(function(angularPromiseButtonsProvider){
angularPromiseButtonsProvider.extendConfig({
spinnerTpl:'< div class =other-class>< / span>',
disableBtn:false
});
});

//设置提供者
.provider('angularPromiseButtons',函数angularPromiseButtonsProvider(){
var config = {
spinnerTpl:'< span class = btn-spinner>< / span>',
优先级:0,
disableBtn:true,
};

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

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

//使用指令中的结果等。
.directive('promiseBtn',function(angularPromiseButtons){
var config = angularPromiseButtons.config;
})

这与这一个但针对angular2 。

解决方案

有几种食谱可以单独使用,也可以一起使用。



配置服务



通常需要提供以密钥/值形式提供必要配置的服务。



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



< h3>强制配置服务

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

AngularJS

  //第三位 - 如果someConfig没有被用户定义
angular.module('some',[]),那么派对模块
//将会失败.stactory('someService',(someConfig)=> {.. 。})

//用户定义的模块
angular.module('app',['some'])。constant('someConfig',{foo:'foo'}) ;

Angular

  //第三方模块
export const SOME_CONFIG = new InjectionToken('someConfig');

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

@NgModule ({providers:[SomeService]})
导出类SomeModule {}

//用户定义模块
@NgModule({
imports:[SomeModule],
提供者:[{提供:SOME_CONFIG,useValue:{foo:'foo'}}]

导出类AppModule {}



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



这是上一个配方的略微变化,唯一不同之处在于,如果用户未定义配置服务,则存在无法使应用程序失败的空缺省值:



AngularJS

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

Angular

  //第三方模块
@N gModule({providers:[...,{provide:SOME_CONFIG,useValue:{}}]})
导出类SomeModule {}
...



可选配置服务



或者,可以使配置服务完全可选以进行注入。



AngularJS

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

Angular

  //第三方模块
export const SOME_CONFIG = new InjectionToken('someConfig );

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

@NgModule({providers:[SomeService]})
导出类SomeModule {}
。 ..



forRoot方法



forRoot 静态模块方法是一个约定,后面是Angular路由器模块和众多第三方模块。 如指南中所述,方法返回一个实现 ModuleWithProviders的对象



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



AngularJS

  //第三方模块
angular.module('some',[])
.constant('someDefaultConfig',{bar:'bar'})
.provider('someService',function(someDefaultConfig){
let someMergedConfig;

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

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

Angular

  //第三方模块
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'}}
]})
导出类SomeModule {
static forRoot(config):ModuleWithProviders {
return {
ngModule:SomeModule,
提供者:[{提供:SOME_CONFIG,useValue:config}]
};
}
}

//用户定义模块
@NgModule({imports:[SomeModule.forRoot({foo:'foo'})]})
导出类AppModule {}



APP_INITIALIZER多提供商



Angular APP_INITIALIZER 多提供程序允许为应用程序提供异步初始化例程。



APP_INITIALIZER 与AngularJS配置阶段共享一些相似之处。 APP_INITIALIZER 例程容易受到竞争条件的影响,类似于 config run AngularJS中的块。例如, Router 可以在根组件中注入,但不能在 APP_INITIALIZER 中,因为循环依赖于另一个 APP_INITIALIZER



同步初始化程序



AngularJS

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

Angular

  ... 
//用户定义的模块
导出函数someAppInitializer(someService:SomeService){
return()=> {
someService.configure({foo:'foo'});
};
}

@NgModule({
进口:[SomeModule],
供应商:[{
提供:APP_INITIALIZER,
multi:true ,
useFactory:someAppInitializer,
deps:[SomeService]
}]
})
导出类AppModule {}



异步初始化例程



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



AngularJS

  ... 
//用户定义模块
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(呃));

Angular

  ... 
//用户定义模块
导出函数someAppInitializer(http:HttpClient,someService:SomeService){
return()=> ; {
返回http.get('data.json')。toPromise()
.then(data => {
someService.configure(data);
});
};
}

@NgModule({
进口:[SomeModule],
供应商:[{
提供:APP_INITIALIZER,
multi:true ,
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天全站免登陆