diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 99ec239277..a4e21f3bd6 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -6,7 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {ComponentRef, EmbeddedViewRef, Injector} from '../core'; +// We are temporarily importing the existing viewEngine from core so we can be sure we are +// correctly implementing its interfaces for backwards compatibility. +import * as viewEngine from '../core'; import {assertNotNull} from './assert'; import {NG_HOST_SYMBOL, createError, createViewState, directive, enterView, hostElement, leaveView, locateHostElement, renderComponentOrTemplate} from './instructions'; @@ -17,44 +19,41 @@ import {notImplemented, stringify} from './util'; -/** - * Options which control how the component should be bootstrapped. - */ -export interface CreateComponentOptionArgs { - /** - * Which renderer factory to use. - */ +/** Options that control how the component should be bootstrapped. */ +export interface CreateComponentOptions { + /** Which renderer factory to use. */ rendererFactory?: RendererFactory3; /** - * Which host element should the component be bootstrapped on. If not specified + * Host element on which the component will be bootstrapped. If not specified, * the component definition's `tag` is used to query the existing DOM for the * element to bootstrap. */ host?: RElement|string; - /** - * Optional Injector which is the Module Injector for the component. - */ - injector?: Injector; + /** Module injector for the component. If unspecified, the injector will be NULL_INJECTOR. */ + injector?: viewEngine.Injector; /** - * a set of features which should be applied to this component. + * List of features to be applied to the created component. Features are simply + * functions that decorate a component with a certain behavior. + * + * Example: PublicFeature is a function that makes the component public to the DI system. */ features?: ((component: T, componentDef: ComponentDef) => void)[]; } /** - * Bootstrap a Component into an existing host element and return `ComponentRef`. + * Bootstraps a component, then creates and returns a `ComponentRef` for that component. * * @param componentType Component to bootstrap * @param options Optional parameters which control bootstrapping */ export function createComponentRef( - componentType: ComponentType, opts: CreateComponentOptionArgs): ComponentRef { + componentType: ComponentType, opts: CreateComponentOptions): viewEngine.ComponentRef { const component = renderComponent(componentType, opts); - const hostView = createViewRef(detectChanges.bind(component), component); + const hostView = createViewRef(() => detectChanges(component), component); return { location: {nativeElement: getHostElement(component)}, injector: opts.injector || NULL_INJECTOR, @@ -62,49 +61,78 @@ export function createComponentRef( hostView: hostView, changeDetectorRef: hostView, componentType: componentType, - destroy: function() {}, - onDestroy: function(cb: Function): void {} + // TODO: implement destroy and onDestroy + destroy: () => {}, + onDestroy: (cb: Function) => {} }; } +/** + * Creates an EmbeddedViewRef. + * + * @param detectChanges The detectChanges function for this view + * @param context The context for this view + * @returns The EmbeddedViewRef + */ function createViewRef(detectChanges: () => void, context: T): EmbeddedViewRef { - return addDestroyable( - { - // TODO: rootNodes should be replaced when properly implemented - rootNodes: null !, - // inherited from core/ChangeDetectorRef - markForCheck: () => { - if (ngDevMode) { - throw notImplemented(); - } - }, - detach: () => { - if (ngDevMode) { - throw notImplemented(); - } - }, - detectChanges: detectChanges, - checkNoChanges: () => { - if (ngDevMode) { - throw notImplemented(); - } - }, - reattach: () => { - if (ngDevMode) { - throw notImplemented(); - } - }, - }, - context); + return addDestroyable(new EmbeddedViewRef(detectChanges), context); } -interface DestroyRef { +class EmbeddedViewRef implements viewEngine.EmbeddedViewRef { + // 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 { + 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(obj: any, context: C): T&DestroyRef { let destroyFn: Function[]|null = null; obj.destroyed = false; @@ -113,29 +141,28 @@ function addDestroyable(obj: any, context: C): T&DestroyRef { 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 = { - get: function(token: any, notFoundValue?: any) { +export const NULL_INJECTOR: viewEngine.Injector = { + get: (token: any, notFoundValue?: any) => { throw new Error('NullInjector: Not found: ' + stringify(token)); } }; /** - * Bootstrap a Component into an existing host element and return `NgComponent`. - * - * NgComponent is a light weight Custom Elements inspired API for bootstrapping and - * interacting with bootstrapped component. + * Bootstraps a Component into an existing host element and returns an instance + * of the component. * * @param componentType Component to bootstrap * @param options Optional parameters which control bootstrapping */ export function renderComponent( - componentType: ComponentType, opts: CreateComponentOptionArgs = {}): T { + componentType: ComponentType, opts: CreateComponentOptions = {}): T { const rendererFactory = opts.rendererFactory || domRendererFactory3; const componentDef = componentType.ngComponentDef; let component: T; @@ -174,7 +201,7 @@ export function markDirty( ngDevMode && assertNotNull(component, 'component'); if (!isDirty) { isDirty = true; - scheduler(detectChanges.bind(null, component)); + scheduler(() => detectChanges(component)); } } diff --git a/packages/core/src/render3/interfaces.ts b/packages/core/src/render3/interfaces.ts index d085be48e9..16f0b80878 100644 --- a/packages/core/src/render3/interfaces.ts +++ b/packages/core/src/render3/interfaces.ts @@ -13,10 +13,6 @@ import {LNodeStatic} from './l_node_static'; import {ComponentTemplate, DirectiveDef} from './public_interfaces'; import {Renderer3} from './renderer'; -declare global { - const ngDevMode: boolean; -} - /** * `ViewState` stores all of the information needed to process the instructions as * they are invoked from the template. Each embedded view and component view has its diff --git a/packages/core/src/render3/l_node_static.ts b/packages/core/src/render3/l_node_static.ts index b8bcf1f788..186881d698 100644 --- a/packages/core/src/render3/l_node_static.ts +++ b/packages/core/src/render3/l_node_static.ts @@ -12,13 +12,15 @@ import {DirectiveDef} from './public_interfaces'; export type NgStaticData = (LNodeStatic | DirectiveDef| null)[]; /** - * LNode binding data (flywiehgt) for a particular node that is shared between all templates + * LNode binding data (flyweight) for a particular node that is shared between all templates * of a specific type. * * If a property is: - * - Minification Data: that property's data was generated and this is it + * - PropertyAliases: that property's data was generated and this is it * - Null: that property's data was already generated and nothing was found. * - Undefined: that property's data has not yet been generated + * + * see: https://en.wikipedia.org/wiki/Flyweight_pattern for more on the Flyweight pattern */ export interface LNodeStatic { /** The tag name associated with this node. */ diff --git a/packages/core/src/render3/ng_dev_mode.ts b/packages/core/src/render3/ng_dev_mode.ts index b10ea4209c..89a5627e4a 100644 --- a/packages/core/src/render3/ng_dev_mode.ts +++ b/packages/core/src/render3/ng_dev_mode.ts @@ -6,6 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ + +declare global { + const ngDevMode: boolean; +} + if (typeof ngDevMode == 'undefined') { if (typeof window != 'undefined') (window as any).ngDevMode = true; if (typeof self != 'undefined') (self as any).ngDevMode = true; diff --git a/packages/core/src/render3/query.ts b/packages/core/src/render3/query.ts index 5035f32482..c0a1432860 100644 --- a/packages/core/src/render3/query.ts +++ b/packages/core/src/render3/query.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +// We are temporarily importing the existing viewEngine from core so we can be sure we are +// correctly implementing its interfaces for backwards compatibility. import {Observable} from 'rxjs/Observable'; import * as viewEngine from '../core'; diff --git a/packages/core/src/render3/renderer.ts b/packages/core/src/render3/renderer.ts index aafafa0e4f..afa9ad185d 100644 --- a/packages/core/src/render3/renderer.ts +++ b/packages/core/src/render3/renderer.ts @@ -26,7 +26,13 @@ export enum RendererStyleFlags3 { export type Renderer3 = ObjectOrientedRenderer3 | ProceduralRenderer3; -/** Object Oriented style of API needed to create elements and text nodes. */ +/** + * Object Oriented style of API needed to create elements and text nodes. + * + * This is the native browser API style, e.g. operations are methods on individual objects + * like HTMLElement. With this style, no additional code is needed as a facade + * (reducing payload size). + * */ export interface ObjectOrientedRenderer3 { createComment(data: string): RComment; createElement(tagName: string): RElement; @@ -35,7 +41,13 @@ export interface ObjectOrientedRenderer3 { querySelector(selectors: string): RElement|null; } -/** Procedural style of API needed to create elements and text nodes. */ +/** + * Procedural style of API needed to create elements and text nodes. + * + * In non-native browser environments (e.g. platforms such as web-workers), this is the + * facade that enables element manipulation. This also facilitates backwards compatibility + * with Renderer2. + */ export interface ProceduralRenderer3 { destroy(): void; createElement(name: string, namespace?: string|null): RElement; @@ -102,8 +114,8 @@ export interface RNode { * listeners on Element. */ export interface RElement extends RNode { - style: RCSSStyleDeclaration; - classList: RDOMTokenList; + style: RCssStyleDeclaration; + classList: RDomTokenList; setAttribute(name: string, value: string): void; removeAttribute(name: string): void; setAttributeNS(namespaceURI: string, qualifiedName: string, value: string): void; @@ -113,12 +125,12 @@ export interface RElement extends RNode { setProperty?(name: string, value: any): void; } -export interface RCSSStyleDeclaration { +export interface RCssStyleDeclaration { removeProperty(propertyName: string): string; setProperty(propertyName: string, value: string|null, priority?: string): void; } -export interface RDOMTokenList { +export interface RDomTokenList { add(token: string): void; remove(token: string): void; } diff --git a/packages/core/test/render3/component_spec.ts b/packages/core/test/render3/component_spec.ts index 0089db882d..158b059e09 100644 --- a/packages/core/test/render3/component_spec.ts +++ b/packages/core/test/render3/component_spec.ts @@ -34,11 +34,6 @@ describe('component', () => { }); } - beforeEach( - () => { - - }); - describe('renderComponent', () => { it('should render on initial call', () => { renderComponent(CounterComponent);