fix(ivy): DI should work when no element injector on starting node (#27133)

PR Close #27133
This commit is contained in:
Marc Laval 2018-11-16 15:25:38 +01:00 committed by Miško Hevery
parent 65943b458f
commit 848f4148c0
2 changed files with 72 additions and 2 deletions

View File

@ -321,8 +321,9 @@ export function getOrCreateInjectable<T>(
let injectorIndex = getInjectorIndex(tNode, lViewData);
let parentLocation: RelativeInjectorLocation = NO_PARENT_INJECTOR;
// If we should skip this injector, start by searching the parent injector.
if (flags & InjectFlags.SkipSelf) {
// If we should skip this injector, or if there is no injector on this node, start by searching
// the parent injector.
if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lViewData) :
lViewData[injectorIndex + PARENT_INJECTOR];

View File

@ -8,6 +8,7 @@
import {Component as _Component, ComponentFactoryResolver, ElementRef, InjectFlags, Injectable as _Injectable, InjectionToken, InjectorType, Provider, RendererFactory2, ViewContainerRef, createInjector, defineInjectable, defineInjector, inject, ɵNgModuleDef as NgModuleDef} from '../../src/core';
import {forwardRef} from '../../src/di/forward_ref';
import {getInjector} from '../../src/render3/discovery_utils';
import {ProvidersFeature, defineComponent, defineDirective, directiveInject, injectComponentFactoryResolver} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, text, textBinding} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition';
@ -1178,6 +1179,74 @@ describe('providers', () => {
});
});
describe('from a node without injector', () => {
abstract class Some { abstract location: String; }
class SomeInj implements Some {
constructor(public location: String) {}
static ngInjectableDef = defineInjectable({factory: () => new SomeInj(inject(String))});
}
@Component({template: `<p></p>`, providers: [{provide: String, useValue: 'From my component'}]})
class MyComponent {
constructor() {}
static ngComponentDef = defineComponent({
type: MyComponent,
selectors: [['my-cmp']],
factory: () => new MyComponent(),
consts: 1,
vars: 0,
template: (rf: RenderFlags, cmp: MyComponent) => {
if (rf & RenderFlags.Create) {
element(0, 'p');
}
},
features: [
ProvidersFeature([{provide: String, useValue: 'From my component'}]),
],
});
}
@Component({
template: `<my-cmp></my-cmp>`,
providers:
[{provide: String, useValue: 'From app component'}, {provide: Some, useClass: SomeInj}]
})
class AppComponent {
constructor() {}
static ngComponentDef = defineComponent({
type: AppComponent,
selectors: [['app-cmp']],
factory: () => new AppComponent(),
consts: 1,
vars: 0,
template: (rf: RenderFlags, cmp: AppComponent) => {
if (rf & RenderFlags.Create) {
element(0, 'my-cmp');
}
},
features: [
ProvidersFeature([
{provide: String, useValue: 'From app component'}, {provide: Some, useClass: SomeInj}
]),
],
directives: [MyComponent]
});
}
it('should work', () => {
const fixture = new ComponentFixture(AppComponent);
expect(fixture.html).toEqual('<my-cmp><p></p></my-cmp>');
const p = fixture.hostElement.querySelector('p');
const injector = getInjector(p as any);
expect(injector.get(String)).toEqual('From my component');
expect(injector.get(Some).location).toEqual('From app component');
});
});
});
interface ComponentTest {
providers?: Provider[];