fix(ivy): directiveInject should fall back to inject (#29948)

If a component has its definition set by defineComponent (as opposed to
JIT getter), then it will generate a factory that uses directiveInject()
to retrieve its dependencies. This can be problematic in test code because
tests could use the injection utility before bootstrapping the component,
and directiveInject() relies on the view having been created.

This commit tweaks directiveInject() to fall back to inject() if the view
has not been created. This will allow injection to work in tests even if
it is called before the component is bootstrapped.

PR Close #29948
This commit is contained in:
Kara Erickson 2019-04-16 19:03:40 -07:00 committed by Alex Rickabaugh
parent ca2462cff7
commit d9c39dcab0
3 changed files with 29 additions and 3 deletions

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectFlags, InjectionToken, resolveForwardRef} from '../../di';
import {ɵɵinject} from '../../di/injector_compatibility';
import {Type} from '../../interface/type';
import {getOrCreateInjectable, injectAttributeImpl} from '../di';
import {TContainerNode, TElementContainerNode, TElementNode} from '../interfaces/node';
@ -40,9 +41,14 @@ export function ɵɵdirectiveInject<T>(token: Type<T>| InjectionToken<T>, flags:
export function ɵɵdirectiveInject<T>(
token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
token = resolveForwardRef(token);
const lView = getLView();
// Fall back to inject() if view hasn't been created. This situation can happen in tests
// if inject utilities are used before bootstrapping.
if (lView == null) return ɵɵinject(token, flags);
return getOrCreateInjectable<T>(
getPreviousOrParentTNode() as TElementNode | TContainerNode | TElementContainerNode,
getLView(), token, flags);
getPreviousOrParentTNode() as TElementNode | TContainerNode | TElementContainerNode, lView,
token, flags);
}
/**

View File

@ -7,7 +7,7 @@
*/
import {Component, Directive, Inject, Injectable, InjectionToken} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {TestBed, async, inject} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {onlyInIvy} from '@angular/private/testing';
@ -271,5 +271,16 @@ describe('providers', () => {
const myCompInstance = fixture.debugElement.query(By.css('div')).injector.get(MyDir);
expect(myCompInstance.svc.value).toEqual('some value');
});
describe('injection without bootstrapping', () => {
beforeEach(() => {
TestBed.configureTestingModule({declarations: [MyComp], providers: [MyComp, MyService]});
});
it('should support injecting without bootstrapping',
async(inject([MyComp, MyService], (comp: MyComp, service: MyService) => {
expect(comp.svc.value).toEqual('some value');
})));
});
});
});

View File

@ -359,6 +359,9 @@
{
"name": "_c9"
},
{
"name": "_currentInjector"
},
{
"name": "_currentNamespace"
},
@ -902,6 +905,9 @@
{
"name": "injectElementRef"
},
{
"name": "injectInjectorOnly"
},
{
"name": "injectRootLimpMode"
},
@ -1355,6 +1361,9 @@
{
"name": "ɵɵgetCurrentView"
},
{
"name": "ɵɵinject"
},
{
"name": "ɵɵinterpolation1"
},