From 8943870c191e3f0e0d2f8806ee66e896ff1a2a8a Mon Sep 17 00:00:00 2001 From: Livio Date: Sat, 2 Feb 2019 22:59:10 +0100 Subject: [PATCH] feature(core): Add multi option for custom useValue-providers --- .../interfaces/modules/provider.interface.ts | 15 ++++++++ packages/core/injector/instance-wrapper.ts | 1 + packages/core/injector/module.ts | 35 +++++++++++++++++-- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/packages/common/interfaces/modules/provider.interface.ts b/packages/common/interfaces/modules/provider.interface.ts index 24fdf2a9d35..0efb1b16566 100644 --- a/packages/common/interfaces/modules/provider.interface.ts +++ b/packages/common/interfaces/modules/provider.interface.ts @@ -11,11 +11,21 @@ export interface ClassProvider { provide: any; useClass: Type; scope?: Scope; + /** + * If true, then injector returns an array of instances. This is useful to allow multiple + * providers spread across many files to provide configuration information to a common token. + */ + multi?: boolean | undefined; } export interface ValueProvider { provide: any; useValue: any; + /** + * If true, then injector returns an array of instances. This is useful to allow multiple + * providers spread across many files to provide configuration information to a common token. + */ + multi?: boolean | undefined; } export interface FactoryProvider { @@ -23,4 +33,9 @@ export interface FactoryProvider { useFactory: (...args: any[]) => any; inject?: Array | string | any>; scope?: Scope; + /** + * If true, then injector returns an array of instances. This is useful to allow multiple + * providers spread across many files to provide configuration information to a common token. + */ + multi?: boolean | undefined; } diff --git a/packages/core/injector/instance-wrapper.ts b/packages/core/injector/instance-wrapper.ts index aec18cd1b6a..e1f636814d4 100644 --- a/packages/core/injector/instance-wrapper.ts +++ b/packages/core/injector/instance-wrapper.ts @@ -34,6 +34,7 @@ export class InstanceWrapper { public readonly inject?: (string | symbol | Function | Type)[]; public readonly async?: boolean; public readonly host?: Module; + public readonly multi?: boolean | undefined; public readonly scope?: Scope = Scope.DEFAULT; public forwardRef?: boolean; diff --git a/packages/core/injector/module.ts b/packages/core/injector/module.ts index 521ed314f25..81b2514ff22 100644 --- a/packages/core/injector/module.ts +++ b/packages/core/injector/module.ts @@ -25,6 +25,11 @@ import { ModuleRef } from './module-ref'; export interface CustomProvider { provide: any; name: string; + /** + * If true, then injector returns an array of instances. This is useful to allow multiple + * providers spread across many files to provide configuration information to a common token. + */ + multi?: boolean; } export type OpaqueToken = string | symbol | Type; export type CustomClass = CustomProvider & { @@ -244,7 +249,7 @@ export class Module { provider: CustomClass, collection: Map, ) { - const { name, useClass, scope } = provider; + const { name, useClass, scope, multi } = provider; collection.set( name, new InstanceWrapper({ @@ -253,6 +258,7 @@ export class Module { instance: null, isResolved: false, scope, + multi, host: this, }), ); @@ -262,7 +268,28 @@ export class Module { provider: CustomValue, collection: Map, ) { - const { name, useValue: value } = provider; + const { name, multi } = provider; + let value = provider.useValue; + + if (multi === true) { + // If the provider indicates that it's a multi-provider, process it specially. + // First check whether it's been defined already. + const multiItem = collection.get(name); + if (multiItem) { + // It has. Throw a nice error if + if (multiItem.multi === undefined) { + throw new Error(`Mixed multi-provider for ${name}.`); + } else { + // A provider already exists. Append new value + value = [...multiItem.instance, value]; + } + } else { + // First multi provider + value = [value]; + } + + } + collection.set( name, new InstanceWrapper({ @@ -272,6 +299,7 @@ export class Module { isResolved: true, async: value instanceof Promise, host: this, + multi, }), ); } @@ -280,7 +308,7 @@ export class Module { provider: CustomFactory, collection: Map, ) { - const { name, useFactory: factory, inject, scope } = provider; + const { name, useFactory: factory, inject, scope, multi } = provider; collection.set( name, new InstanceWrapper({ @@ -290,6 +318,7 @@ export class Module { isResolved: false, inject: inject || [], scope, + multi, host: this, }), );