fix(ivy): component ref injector should support change detector ref (#27107)

PR Close #27107
This commit is contained in:
Kara Erickson
2018-11-14 19:25:03 -08:00
committed by Misko Hevery
parent 3ec7c5081d
commit ee12e725c0
8 changed files with 100 additions and 23 deletions

View File

@ -121,8 +121,8 @@ export function renderComponent<T>(
const renderer = rendererFactory.createRenderer(hostRNode, componentDef);
const rootView: LViewData = createLViewData(
renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags);
rootView[INJECTOR] = opts.injector || null;
renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, undefined,
opts.injector || null);
const oldView = enterView(rootView, null);
let component: T;

View File

@ -20,9 +20,10 @@ import {Type} from '../type';
import {assertComponentType, assertDefined} from './assert';
import {LifecycleHooksFeature, createRootComponent, createRootComponentView, createRootContext} from './component';
import {getComponentDef} from './definition';
import {NodeInjector} from './di';
import {createLViewData, createNodeAtIndex, createTView, createViewNode, elementCreate, locateHostElement, refreshDescendantViews} from './instructions';
import {ComponentDef, RenderFlags} from './interfaces/definition';
import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {FLAGS, HEADER_OFFSET, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {enterView, leaveView} from './state';
@ -138,10 +139,12 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
ngModule && !isInternalRootView ? ngModule.injector.get(ROOT_CONTEXT) : createRootContext();
const renderer = rendererFactory.createRenderer(hostRNode, this.componentDef);
const rootViewInjector =
ngModule ? createChainedInjector(injector, ngModule.injector) : injector;
// Create the root view. Uses empty TView and ContentTemplate.
const rootView: LViewData = createLViewData(
renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags);
rootView[INJECTOR] = ngModule ? createChainedInjector(injector, ngModule.injector) : injector;
renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, undefined,
rootViewInjector);
// rootView is the parent when bootstrapping
const oldView = enterView(rootView, null);
@ -198,8 +201,8 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
}
const componentRef = new ComponentRef(
this.componentType, component, rootView, injector,
createElementRef(viewEngine_ElementRef, tElementNode, rootView));
this.componentType, component,
createElementRef(viewEngine_ElementRef, tElementNode, rootView), rootView, tElementNode);
if (isInternalRootView) {
// The host element of the internal root view is attached to the component's host view node
@ -232,23 +235,24 @@ export function injectComponentFactoryResolver(): viewEngine_ComponentFactoryRes
*/
export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
destroyCbs: (() => void)[]|null = [];
injector: Injector;
instance: T;
hostView: ViewRef<T>;
changeDetectorRef: ViewEngine_ChangeDetectorRef;
componentType: Type<T>;
constructor(
componentType: Type<T>, instance: T, rootView: LViewData, injector: Injector,
public location: viewEngine_ElementRef) {
componentType: Type<T>, instance: T, public location: viewEngine_ElementRef,
private _rootView: LViewData,
private _tNode: TElementNode|TContainerNode|TElementContainerNode) {
super();
this.instance = instance;
this.hostView = this.changeDetectorRef = new RootViewRef<T>(rootView);
this.hostView._tViewNode = createViewNode(-1, rootView);
this.injector = injector;
this.hostView = this.changeDetectorRef = new RootViewRef<T>(_rootView);
this.hostView._tViewNode = createViewNode(-1, _rootView);
this.componentType = componentType;
}
get injector(): Injector { return new NodeInjector(this._tNode, this._rootView); }
destroy(): void {
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
this.destroyCbs !.forEach(fn => fn());

View File

@ -9,6 +9,7 @@
import './ng_dev_mode';
import {resolveForwardRef} from '../di/forward_ref';
import {InjectionToken} from '../di/injection_token';
import {Injector} from '../di/injector';
import {InjectFlags} from '../di/injector_compatibility';
import {QueryList} from '../linker';
import {Sanitizer} from '../sanitization/security';
@ -156,13 +157,14 @@ function refreshChildComponents(
export function createLViewData<T>(
renderer: Renderer3, tView: TView, context: T | null, flags: LViewFlags,
sanitizer?: Sanitizer | null): LViewData {
sanitizer?: Sanitizer | null, injector?: Injector | null): LViewData {
const viewData = getViewData();
const instance = tView.blueprint.slice() as LViewData;
instance[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit;
instance[PARENT] = instance[DECLARATION_VIEW] = viewData;
instance[CONTEXT] = context;
instance[INJECTOR] = viewData ? viewData[INJECTOR] : null;
instance[INJECTOR as any] =
injector === undefined ? (viewData ? viewData[INJECTOR] : null) : injector;
instance[RENDERER] = renderer;
instance[SANITIZER] = sanitizer || null;
return instance;
@ -680,7 +682,7 @@ export function createTView(
// that has a host binding, we will update the blueprint with that def's hostVars count.
const initialViewLength = bindingStartIndex + vars;
const blueprint = createViewBlueprint(bindingStartIndex, initialViewLength);
return blueprint[TVIEW] = {
return blueprint[TVIEW as any] = {
id: viewIndex,
blueprint: blueprint,
template: templateFn,

View File

@ -69,7 +69,7 @@ export interface LViewData extends Array<any> {
* node tree in DI and get the TView.data array associated with a node (where the
* directive defs are stored).
*/
[TVIEW]: TView;
readonly[TVIEW]: TView;
/** Flags for this view. See LViewFlags for more info. */
[FLAGS]: LViewFlags;
@ -147,7 +147,7 @@ export interface LViewData extends Array<any> {
[CONTEXT]: {}|RootContext|null;
/** An optional Module Injector to be used as fall back after Element Injectors are consulted. */
[INJECTOR]: Injector|null;
readonly[INJECTOR]: Injector|null;
/** Renderer to be used for this view. */
[RENDERER]: Renderer3;

View File

@ -275,6 +275,8 @@ export class RootViewRef<T> extends ViewRef<T> {
detectChanges(): void { detectChangesInRootView(this._view); }
checkNoChanges(): void { checkNoChangesInRootView(this._view); }
get context(): T { return null !; }
}
function collectNativeNodes(lView: LViewData, parentTNode: TNode, result: any[]): any[] {
@ -289,4 +291,4 @@ function collectNativeNodes(lView: LViewData, parentTNode: TNode, result: any[])
}
return result;
}
}

View File

@ -67,11 +67,14 @@ export class Testability implements PublicTestability {
private _didWork: boolean = false;
private _callbacks: WaitCallback[] = [];
private taskTrackingZone: any;
private taskTrackingZone: {macroTasks: Task[]}|null = null;
constructor(private _ngZone: NgZone) {
this._watchAngularEvents();
_ngZone.run(() => { this.taskTrackingZone = Zone.current.get('TaskTrackingZone'); });
_ngZone.run(() => {
this.taskTrackingZone =
typeof Zone == 'undefined' ? null : Zone.current.get('TaskTrackingZone');
});
}
private _watchAngularEvents(): void {