Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/src/core_render3_private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export {
inject as ɵinject,
injectTemplateRef as ɵinjectTemplateRef,
injectViewContainerRef as ɵinjectViewContainerRef,
injectChangeDetectorRef as ɵinjectChangeDetectorRef,
InjectFlags as ɵInjectFlags,
PublicFeature as ɵPublicFeature,
NgOnChangesFeature as ɵNgOnChangesFeature,
Expand Down
96 changes: 9 additions & 87 deletions packages/core/src/render3/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,14 @@
// correctly implementing its interfaces for backwards compatibility.
import {Injector} from '../di/injector';
import {ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef} from '../linker/view_ref';

import {assertNotNull} from './assert';
import {CLEAN_PROMISE, NG_HOST_SYMBOL, _getComponentHostLElementNode, createError, createLView, createTView, detectChanges, directiveCreate, enterView, getDirectiveInstance, hostElement, leaveView, locateHostElement, scheduleChangeDetection} from './instructions';
import {CLEAN_PROMISE, _getComponentHostLElementNode, createLView, createTView, detectChanges, directiveCreate, enterView, getDirectiveInstance, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement, scheduleChangeDetection} from './instructions';
import {ComponentDef, ComponentType} from './interfaces/definition';
import {LElementNode} from './interfaces/node';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {LViewFlags, RootContext} from './interfaces/view';
import {notImplemented, stringify} from './util';

import {stringify} from './util';
import {createViewRef} from './view_ref';


/** Options that control how the component should be bootstrapped. */
Expand Down Expand Up @@ -69,7 +67,7 @@ export interface CreateComponentOptions {
export function createComponentRef<T>(
componentType: ComponentType<T>, opts: CreateComponentOptions): viewEngine_ComponentRef<T> {
const component = renderComponent(componentType, opts);
const hostView = createViewRef(() => detectChanges(component), component);
const hostView = createViewRef(component);
return {
location: {nativeElement: getHostElement(component)},
injector: opts.injector || NULL_INJECTOR,
Expand All @@ -83,84 +81,6 @@ export function createComponentRef<T>(
};
}

/**
* Creates an EmbeddedViewRef.
*
* @param detectChanges The detectChanges function for this view
* @param context The context for this view
* @returns The EmbeddedViewRef
*/
function createViewRef<T>(detectChanges: () => void, context: T): EmbeddedViewRef<T> {
return addDestroyable(new EmbeddedViewRef(detectChanges), context);
}

class EmbeddedViewRef<T> implements viewEngine_EmbeddedViewRef<T> {
// TODO: rootNodes should be replaced when properly implemented
rootNodes = null !;
context: T;
destroyed: boolean;

constructor(public detectChanges: () => void) {}

// inherited from core/ChangeDetectorRef
markForCheck() {
if (ngDevMode) {
throw notImplemented();
}
}
detach() {
if (ngDevMode) {
throw notImplemented();
}
}

checkNoChanges() {
if (ngDevMode) {
throw notImplemented();
}
}

reattach() {
if (ngDevMode) {
throw notImplemented();
}
}

destroy(): void {}

onDestroy(cb: Function): void {}
}

/** Interface for destroy logic. Implemented by addDestroyable. */
interface DestroyRef<T> {
context: T;
/** Whether or not this object has been destroyed */
destroyed: boolean;
/** Destroy the instance and call all onDestroy callbacks. */
destroy(): void;
/** Register callbacks that should be called onDestroy */
onDestroy(cb: Function): void;
}

/**
* Decorates an object with destroy logic (implementing the DestroyRef interface)
* and returns the enhanced object.
*
* @param obj The object to decorate
* @returns The object with destroy logic
*/
function addDestroyable<T, C>(obj: any, context: C): T&DestroyRef<C> {
let destroyFn: Function[]|null = null;
obj.destroyed = false;
obj.destroy = function() {
destroyFn && destroyFn.forEach((fn) => fn());
this.destroyed = true;
};
obj.onDestroy = (fn: Function) => (destroyFn || (destroyFn = [])).push(fn);
obj.context = context;
return obj;
}


// TODO: A hack to not pull in the NullInjector from @angular/core.
export const NULL_INJECTOR: Injector = {
Expand Down Expand Up @@ -202,10 +122,12 @@ export function renderComponent<T>(
null !);
try {
// Create element node at index 0 in data array
hostElement(hostNode, componentDef);
const elementNode = hostElement(hostNode, componentDef);
// Create directive instance with n() and store at index 1 in data array (el is 0)
const instance = componentDef.n();
component = rootContext.component =
getDirectiveInstance(directiveCreate(1, componentDef.n(), componentDef));
getDirectiveInstance(directiveCreate(1, instance, componentDef));
initChangeDetectorIfExisting(elementNode.nodeInjector, instance);
} finally {
leaveView(oldView);
}
Expand Down
80 changes: 54 additions & 26 deletions packages/core/src/render3/di.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

// We are temporarily importing the existing viewEngine_from core so we can be sure we are
// correctly implementing its interfaces for backwards compatibility.
import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {Injector} from '../di/injector';
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
import {ElementRef as viewEngine_ElementRef} from '../linker/element_ref';
Expand All @@ -24,10 +25,10 @@ import {LInjector} from './interfaces/injector';
import {LContainerNode, LElementNode, LNode, LNodeFlags, LViewNode} from './interfaces/node';
import {QueryReadType} from './interfaces/query';
import {Renderer3} from './interfaces/renderer';
import {LView} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {insertView} from './node_manipulation';
import {notImplemented, stringify} from './util';
import {EmbeddedViewRef, ViewRef, addDestroyable, createViewRef} from './view_ref';



Expand Down Expand Up @@ -124,7 +125,8 @@ export function getOrCreateNodeInjectorForNode(node: LElementNode | LContainerNo
injector: null,
templateRef: null,
viewContainerRef: null,
elementRef: null
elementRef: null,
changeDetectorRef: null
};
}

Expand Down Expand Up @@ -227,6 +229,55 @@ export function injectViewContainerRef(): viewEngine_ViewContainerRef {
return getOrCreateContainerRef(getOrCreateNodeInjector());
}

/** Returns a ChangeDetectorRef (a.k.a. a ViewRef) */
export function injectChangeDetectorRef(): viewEngine_ChangeDetectorRef {
return getOrCreateChangeDetectorRef(getOrCreateNodeInjector(), null);
}

/**
* Creates a ViewRef and stores it on the injector as ChangeDetectorRef (public alias).
* Or, if it already exists, retrieves the existing instance.
*
* @returns The ChangeDetectorRef to use
*/
export function getOrCreateChangeDetectorRef(
di: LInjector, context: any): viewEngine_ChangeDetectorRef {
if (di.changeDetectorRef) return di.changeDetectorRef;

const currentNode = di.node;
if (currentNode.data === null) {
// if data is null, this node is a regular element node (not a component)
return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view.node);
} else if ((currentNode.flags & LNodeFlags.TYPE_MASK) === LNodeFlags.Element) {
// if it's an element node with data, it's a component and context will be set later
return di.changeDetectorRef = createViewRef(context);
}
return null !;
}

/** Gets or creates ChangeDetectorRef for the closest host component */
function getOrCreateHostChangeDetector(currentNode: LViewNode | LElementNode):
viewEngine_ChangeDetectorRef {
const hostNode = getClosestComponentAncestor(currentNode);
const hostInjector = hostNode.nodeInjector;
const existingRef = hostInjector && hostInjector.changeDetectorRef;

return existingRef ? existingRef :
createViewRef(hostNode.view.data[hostNode.flags >> LNodeFlags.INDX_SHIFT]);
}

/**
* If the node is an embedded view, traverses up the view tree to return the closest
* ancestor view that is attached to a component. If it's already a component node,
* returns itself.
*/
function getClosestComponentAncestor(node: LViewNode | LElementNode): LElementNode {
while ((node.flags & LNodeFlags.TYPE_MASK) === LNodeFlags.View) {
node = node.view.node;
}
return node as LElementNode;
}

/**
* Searches for an instance of the given directive type up the injector tree and returns
* that instance if found.
Expand Down Expand Up @@ -527,29 +578,6 @@ class TemplateRef<T> implements viewEngine_TemplateRef<T> {

createEmbeddedView(context: T): viewEngine_EmbeddedViewRef<T> {
let viewNode: LViewNode = renderEmbeddedTemplate(null, this._template, context, this._renderer);
return new EmbeddedViewRef(viewNode, this._template, context);
return addDestroyable(new EmbeddedViewRef(viewNode, this._template, context));
}
}

class EmbeddedViewRef<T> implements viewEngine_EmbeddedViewRef<T> {
context: T;
rootNodes: any[];
/**
* @internal
*/
_lViewNode: LViewNode;

constructor(viewNode: LViewNode, template: ComponentTemplate<T>, context: T) {
this._lViewNode = viewNode;
this.context = context;
}

destroy(): void { notImplemented(); }
destroyed: boolean;
onDestroy(callback: Function) { notImplemented(); }
markForCheck(): void { notImplemented(); }
detach(): void { notImplemented(); }
detectChanges(): void { notImplemented(); }
checkNoChanges(): void { notImplemented(); }
reattach(): void { notImplemented(); }
}
2 changes: 1 addition & 1 deletion packages/core/src/render3/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {NgOnChangesFeature, PublicFeature, defineComponent, defineDirective, def
import {InjectFlags} from './di';
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveType} from './interfaces/definition';

export {InjectFlags, QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, inject, injectElementRef, injectTemplateRef, injectViewContainerRef} from './di';
export {InjectFlags, QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, inject, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from './di';
export {CssSelector} from './interfaces/projection';


Expand Down
19 changes: 16 additions & 3 deletions packages/core/src/render3/instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import './ng_dev_mode';

import {assertEqual, assertLessThan, assertNotEqual, assertNotNull, assertNull, assertSame} from './assert';
import {LContainer, TContainer} from './interfaces/container';
import {LInjector} from './interfaces/injector';
import {CssSelector, LProjection} from './interfaces/projection';
import {LQueries} from './interfaces/query';
import {LView, LViewFlags, LifecycleStage, RootContext, TData, TView} from './interfaces/view';
Expand All @@ -22,6 +23,7 @@ import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveT
import {RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
import {isDifferent, stringify} from './util';
import {executeHooks, executeContentHooks, queueLifecycleHooks, queueInitHooks, executeInitHooks} from './hooks';
import {ViewRef} from './view_ref';

/**
* Directive (D) sets a property on all component instances using this constant as a key and the
Expand Down Expand Up @@ -465,14 +467,23 @@ export function elementStart(
if (hostComponentDef) {
// TODO(mhevery): This assumes that the directives come in correct order, which
// is not guaranteed. Must be refactored to take it into account.
directiveCreate(++index, hostComponentDef.n(), hostComponentDef, queryName);
const instance = hostComponentDef.n();
directiveCreate(++index, instance, hostComponentDef, queryName);
initChangeDetectorIfExisting(node.nodeInjector, instance);
}
hack_declareDirectives(index, directiveTypes, localRefs);
}
}
return native;
}

/** Sets the context for a ChangeDetectorRef to the given instance. */
export function initChangeDetectorIfExisting(injector: LInjector | null, instance: any): void {
if (injector && injector.changeDetectorRef != null) {
(injector.changeDetectorRef as ViewRef<any>)._setComponentContext(instance);
}
}

/**
* This function instantiates a directive with a correct queryName. It is a hack since we should
* compute the query value only once and store it with the template (rather than on each invocation)
Expand Down Expand Up @@ -589,10 +600,12 @@ export function locateHostElement(
*
* @param rNode Render host element.
* @param def ComponentDef
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add @returns documentation

*
* @returns LElementNode created
*/
export function hostElement(rNode: RElement | null, def: ComponentDef<any>) {
export function hostElement(rNode: RElement | null, def: ComponentDef<any>): LElementNode {
resetApplicationState();
createLNode(
return createLNode(
0, LNodeFlags.Element, rNode, createLView(
-1, renderer, getOrCreateTView(def.template), null, null,
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways));
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/render3/interfaces/injector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ChangeDetectorRef} from '../../change_detection/change_detector_ref';
import {Injector} from '../../di/injector';
import {ElementRef} from '../../linker/element_ref';
import {TemplateRef} from '../../linker/template_ref';
Expand Down Expand Up @@ -66,6 +67,12 @@ export interface LInjector {

/** Stores the ElementRef so subsequent injections of the ElementRef get the same instance. */
elementRef: ElementRef|null;

/**
* Stores the ChangeDetectorRef so subsequent injections of the ChangeDetectorRef get the
* same instance.
*/
changeDetectorRef: ChangeDetectorRef|null;
}

// Note: This hack is necessary so we don't erroneously get a circular dependency
Expand Down
Loading