fix(ivy): DI should work when no element injector on starting node (#27133)
PR Close #27133
This commit is contained in:
parent
65943b458f
commit
848f4148c0
|
@ -321,8 +321,9 @@ export function getOrCreateInjectable<T>(
|
||||||
let injectorIndex = getInjectorIndex(tNode, lViewData);
|
let injectorIndex = getInjectorIndex(tNode, lViewData);
|
||||||
let parentLocation: RelativeInjectorLocation = NO_PARENT_INJECTOR;
|
let parentLocation: RelativeInjectorLocation = NO_PARENT_INJECTOR;
|
||||||
|
|
||||||
// If we should skip this injector, start by searching the parent injector.
|
// If we should skip this injector, or if there is no injector on this node, start by searching
|
||||||
if (flags & InjectFlags.SkipSelf) {
|
// the parent injector.
|
||||||
|
if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
|
||||||
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lViewData) :
|
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lViewData) :
|
||||||
lViewData[injectorIndex + PARENT_INJECTOR];
|
lViewData[injectorIndex + PARENT_INJECTOR];
|
||||||
|
|
||||||
|
|
|
@ -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 {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 {forwardRef} from '../../src/di/forward_ref';
|
||||||
|
import {getInjector} from '../../src/render3/discovery_utils';
|
||||||
import {ProvidersFeature, defineComponent, defineDirective, directiveInject, injectComponentFactoryResolver} from '../../src/render3/index';
|
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 {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, text, textBinding} from '../../src/render3/instructions';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
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 {
|
interface ComponentTest {
|
||||||
providers?: Provider[];
|
providers?: Provider[];
|
||||||
|
|
Loading…
Reference in New Issue