我想知道在angular2中设置可配置模块的最佳方法是什么。在angular1中,这通常是通过提供程序完成的。在对它们进行了相当多的更改之后,您将如何将配置参数传递给可重用的ng2模块/指令/组件?
一个NG1例子:
// 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; })
这个问题基本上与此问题相同,但针对的是angular2。
有几种配方,可以单独使用或一起使用。
通常需要提供服务来提供键/值形式的必要配置。
可能有多个配置服务来配置一个应用程序实体,例如someConfig用于通用用户定义的配置以及someDefaultConfig所有可能被更改的默认值。例如,someConfig可能包含始终由用户定义的身份验证凭据,并且someDefaultConfig可能包含默认的钩子回调,身份验证提供程序的深度设置等。实现此目的的最简单方法是使用合并配置对象Object.assign。
someConfig
someDefaultConfig
Object.assign
一个需要用户显式定义配置服务的配方,它基本上使用DI来表示某些模块如果没有适当的配置将无法工作。
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' });
角度的
// 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 {}
这是先前配方的略微变化,唯一的区别是,如果用户未定义配置服务,则默认值为空不会使应用程序失败:
// third-party module angular.module('some', []) .constant('someConfig', {}) ...
// third-party module @NgModule({ providers: [..., { provide: SOME_CONFIG, useValue: {} }] }) export class SomeModule {} ...
或者,可以将配置服务完全设为可选注入。
// third-party module angular.module('some', []).factory('someService', ($injector) => { const someConfig = $injector.has('someConfig') ? $injector.get('someConfig') : {}; ... }) ...
// 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静态模块方法是Angular路由器模块和众多第三方模块遵循的约定。如指南中所述,该方法返回一个实现的对象ModuleWithProviders。
forRoot
ModuleWithProviders
基本上,它提供了一个基于forRoot(...)参数动态定义模块提供程序的机会。这可以被视为AngularJS config和providerAngular中不存在的单元的替代。
forRoot(...)
config
provider
// 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' }); });
// 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 {}
Angular APP_INITIALIZERMulti- provider允许为应用程序提供异步初始化例程。
APP_INITIALIZER
APP_INITIALIZER与AngularJS配置阶段有一些相似之处。APP_INITIALIZER例程易受竞争条件的影响,类似于AngularJS中的config和run块。例如,由于循环依赖于另一个Router组件,因此可用于在根组件中进行注入,但不能在其中进行注入。APP_INITIALIZER``APP_INITIALIZER
run
Router
APP_INITIALIZER``APP_INITIALIZER
... // user-defined module angular.module('app', ['some']).config((someServiceProvider) => { someServiceProvider.configure({ foo: 'foo' }); });
... // 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 {}
初始化可能涉及从远程源获取配置以配置服务。单个AngularJS应用程序无法实现的功能。这需要具有另一个应用程序来初始化和引导主模块。这种情况自然是由处理的APP_INITIALIZER。
... // 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));
... // 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 {}