Skip to content

Commit

Permalink
fix(ivy): DI should work when no element injector on starting node (a…
Browse files Browse the repository at this point in the history
  • Loading branch information
marclaval authored and mhevery committed Nov 16, 2018
1 parent 65943b4 commit 848f414
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 2 deletions.
5 changes: 3 additions & 2 deletions packages/core/src/render3/di.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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];

Expand Down
69 changes: 69 additions & 0 deletions packages/core/test/render3/providers_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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[];
Expand Down

0 comments on commit 848f414

Please sign in to comment.