我应该如何在 Angular2 中制作可配置模块 [英] How should I make configurable modules in 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 config
和 provider
单元的替代.
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 中的 config
和 run
块.例如,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屋!