diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index 9d14be1e8b..4cd45a5f52 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -3,7 +3,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 140333, + "main-es2015": 140871, "polyfills-es2015": 36964 } } diff --git a/packages/core/src/di/injector_compatibility.ts b/packages/core/src/di/injector_compatibility.ts index 71586aa127..3332f92dfe 100644 --- a/packages/core/src/di/injector_compatibility.ts +++ b/packages/core/src/di/injector_compatibility.ts @@ -11,13 +11,14 @@ import '../util/ng_dev_mode'; import {AbstractType, Type} from '../interface/type'; import {getClosureSafeProperty} from '../util/property'; import {stringify} from '../util/stringify'; + import {resolveForwardRef} from './forward_ref'; import {getInjectImplementation, injectRootLimpMode} from './inject_switch'; import {InjectionToken} from './injection_token'; import {Injector} from './injector'; import {InjectFlags} from './interface/injector'; import {ValueProvider} from './interface/provider'; -import {Inject, Optional, Self, SkipSelf} from './metadata'; +import {Host, Inject, Optional, Self, SkipSelf} from './metadata'; const _THROW_IF_NOT_FOUND = {}; @@ -151,6 +152,8 @@ export function injectArgs(types: (Type|InjectionToken|any[])[]): any[ flags |= InjectFlags.SkipSelf; } else if (meta instanceof Self || meta.ngMetadataName === 'Self' || meta === Self) { flags |= InjectFlags.Self; + } else if (meta instanceof Host || meta.ngMetadataName === 'Host' || meta === Host) { + flags |= InjectFlags.Host; } else if (meta instanceof Inject || meta === Inject) { type = meta.token; } else { diff --git a/packages/core/test/acceptance/di_spec.ts b/packages/core/test/acceptance/di_spec.ts index 70f59007a0..768a5621c1 100644 --- a/packages/core/test/acceptance/di_spec.ts +++ b/packages/core/test/acceptance/di_spec.ts @@ -2803,6 +2803,106 @@ describe('di', () => { }); }); + it('should be able to use Host in `useFactory` dependency config', () => { + // Scenario: + // --------- + // + // + // + @Component({ + selector: 'root', + template: '', + viewProviders: [{ + provide: 'A', + useValue: 'A from Root', + }] + }) + class Root { + } + + @Component({ + selector: 'comp', + template: '{{ token }}', + viewProviders: [{ + provide: 'B', + deps: [[new Inject('A'), new Host()]], + useFactory: (token: string) => `${token} (processed by useFactory)`, + }] + }) + class Comp { + constructor(@Inject('B') readonly token: string) {} + } + + @Component({ + template: ``, + }) + class App { + } + + TestBed.configureTestingModule({declarations: [Root, Comp, App]}); + + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('A from Root (processed by useFactory)'); + }); + + it('should not lookup outside of the host element when Host is used in `useFactory`', () => { + // Scenario: + // --------- + // + // + // + // + // + @Component({ + selector: 'root', + template: '', + viewProviders: [{ + provide: 'A', + useValue: 'A from Root', + }] + }) + class Root { + } + + @Component({ + selector: 'intermediate', + template: '', + }) + class Intermediate { + } + + @Component({ + selector: 'comp', + template: '{{ token }}', + viewProviders: [{ + provide: 'B', + deps: [[new Inject('A'), new Host(), new Optional()]], + useFactory: (token: string) => + token ? `${token} (processed by useFactory)` : 'No token A found', + }] + }) + class Comp { + constructor(@Inject('B') readonly token: string) {} + } + + @Component({ + template: ``, + }) + class App { + } + + TestBed.configureTestingModule({declarations: [Root, Comp, App, Intermediate]}); + + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + // Making sure that the `@Host` takes effect and token `A` becomes unavailable in DI since it's + // defined one level up from the Comp's host view. + expect(fixture.nativeElement.textContent).toBe('No token A found'); + }); + it('should not cause cyclic dependency if same token is requested in deps with @SkipSelf', () => { @Component({ selector: 'my-comp', diff --git a/packages/core/test/bundling/forms/bundle.golden_symbols.json b/packages/core/test/bundling/forms/bundle.golden_symbols.json index 1bf49380ee..822538400b 100644 --- a/packages/core/test/bundling/forms/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms/bundle.golden_symbols.json @@ -236,6 +236,9 @@ { "name": "FormsModule" }, + { + "name": "Host" + }, { "name": "INJECTOR" }, diff --git a/packages/core/test/bundling/injection/bundle.golden_symbols.json b/packages/core/test/bundling/injection/bundle.golden_symbols.json index eee96403a7..45532497ed 100644 --- a/packages/core/test/bundling/injection/bundle.golden_symbols.json +++ b/packages/core/test/bundling/injection/bundle.golden_symbols.json @@ -5,6 +5,9 @@ { "name": "EMPTY_ARRAY" }, + { + "name": "Host" + }, { "name": "INJECTOR" }, diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 0f88828941..fb04c8493f 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -290,6 +290,9 @@ { "name": "HashLocationStrategy" }, + { + "name": "Host" + }, { "name": "INITIAL_VALUE" },