From bb9c7ae6e76a4462e38a0bd5095c06a1587ec4c0 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 14 Feb 2017 21:03:18 -0800 Subject: [PATCH] feat: RendererV2 integration (#14469) --- .../common/test/directives/ng_class_spec.ts | 2 +- .../src/view_compiler_next/view_compiler.ts | 12 +- .../@angular/core/src/core_private_export.ts | 6 +- modules/@angular/core/src/debug/debug_node.ts | 26 ++- .../@angular/core/src/debug/debug_renderer.ts | 154 ++++++++++++- modules/@angular/core/src/errors.ts | 1 - .../core/src/linker/view_container.ts | 10 +- .../core/src/linker/view_container_ref.ts | 3 +- modules/@angular/core/src/render.ts | 2 +- modules/@angular/core/src/render/api.ts | 59 ++++- modules/@angular/core/src/view/element.ts | 32 ++- modules/@angular/core/src/view/provider.ts | 1 - modules/@angular/core/src/view/refs.ts | 8 +- modules/@angular/core/src/view/renderer.ts | 147 ------------- modules/@angular/core/src/view/services.ts | 47 ++-- modules/@angular/core/src/view/types.ts | 56 +---- modules/@angular/core/src/view/util.ts | 5 + .../change_detection_integration_spec.ts | 38 +++- .../core/test/linker/integration_spec.ts | 27 ++- .../@angular/core/test/view/anchor_spec.ts | 1 - .../core/test/view/component_view_spec.ts | 29 +-- .../@angular/core/test/view/element_spec.ts | 1 - .../core/test/view/embedded_view_spec.ts | 5 +- .../core/test/view/ng_content_spec.ts | 9 +- .../@angular/platform-browser/src/browser.ts | 6 +- .../src/browser/browser_adapter.ts | 8 +- .../src/dom/debug/ng_probe.ts | 30 ++- .../platform-browser/src/dom/dom_adapter.ts | 200 ++++++++--------- .../platform-browser/src/dom/dom_renderer.ts | 142 +++++++++++- .../src/private_import_core.ts | 3 + .../platform-server/src/parse5_adapter.ts | 24 +- .../src/private_import_core.ts | 2 + .../@angular/platform-server/src/server.ts | 19 +- .../platform-server/src/server_renderer.ts | 133 +++++++++-- .../src/web_workers/worker/worker_adapter.ts | 207 +++++++----------- modules/benchmarks/src/tree/ng2_next/index.ts | 2 +- modules/benchmarks/src/tree/ng2_next/tree.ts | 10 +- tools/public_api_guard/core/index.d.ts | 28 +++ 38 files changed, 888 insertions(+), 607 deletions(-) delete mode 100644 modules/@angular/core/src/view/renderer.ts diff --git a/modules/@angular/common/test/directives/ng_class_spec.ts b/modules/@angular/common/test/directives/ng_class_spec.ts index 1d47db0d49..cd5e242c6d 100644 --- a/modules/@angular/common/test/directives/ng_class_spec.ts +++ b/modules/@angular/common/test/directives/ng_class_spec.ts @@ -15,7 +15,7 @@ export function main() { function detectChangesAndExpectClassName(classes: string): void { fixture.detectChanges(); - expect(fixture.debugElement.children[0].nativeElement.className).toEqual(classes); + expect(fixture.debugElement.children[0].nativeElement.className.trim()).toEqual(classes); } function getComponent(): TestComponent { return fixture.debugElement.componentInstance; } diff --git a/modules/@angular/compiler/src/view_compiler_next/view_compiler.ts b/modules/@angular/compiler/src/view_compiler_next/view_compiler.ts index d8ed0a8661..f4751ffab0 100644 --- a/modules/@angular/compiler/src/view_compiler_next/view_compiler.ts +++ b/modules/@angular/compiler/src/view_compiler_next/view_compiler.ts @@ -107,7 +107,8 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter } templateVisitAll(this, astNodes, {elementDepth}); - if (astNodes.length === 0 || (this.parent && hasViewContainer(astNodes[astNodes.length - 1]))) { + if (astNodes.length === 0 || + (this.parent && needsAdditionalRootNode(astNodes[astNodes.length - 1]))) { // if the view is empty, or an embedded view has a view container as last root nde, // create an additional root node. this.nodeDefs.push(o.importExpr(createIdentifier(Identifiers.anchorDef)).callFn([ @@ -752,13 +753,16 @@ function depDef(dep: CompileDiDependencyMetadata): o.Expression { return flags === viewEngine.DepFlags.None ? expr : o.literalArr([o.literal(flags), expr]); } -function hasViewContainer(ast: TemplateAst): boolean { +function needsAdditionalRootNode(ast: TemplateAst): boolean { if (ast instanceof EmbeddedTemplateAst) { return ast.hasViewContainer; - } else if (ast instanceof ElementAst) { + } + + if (ast instanceof ElementAst) { return ast.hasViewContainer; } - return false; + + return ast instanceof NgContentAst; } function calcQueryId(queryId: QueryId): string { diff --git a/modules/@angular/core/src/core_private_export.ts b/modules/@angular/core/src/core_private_export.ts index cfef55fcc3..ffb6c38894 100644 --- a/modules/@angular/core/src/core_private_export.ts +++ b/modules/@angular/core/src/core_private_export.ts @@ -92,6 +92,8 @@ export const __core_private__: { makeDecorator: typeof decorators.makeDecorator, DebugDomRootRenderer: typeof debug.DebugDomRootRenderer, _DebugDomRootRenderer: debug.DebugDomRootRenderer, + DebugDomRendererV2: typeof debug.DebugDomRendererV2, + _DebugDomRendererV2: debug.DebugDomRendererV2, Console: typeof console.Console, _Console: console.Console, reflector: typeof reflection.reflector, @@ -101,9 +103,6 @@ export const __core_private__: { _NoOpAnimationPlayer: NoOpAnimationPlayer_, AnimationPlayer: typeof AnimationPlayer_, _AnimationPlayer: AnimationPlayer_, - - - AnimationSequencePlayer: typeof AnimationSequencePlayer_, _AnimationSequencePlayer: AnimationSequencePlayer_, AnimationGroupPlayer: typeof AnimationGroupPlayer_, @@ -157,6 +156,7 @@ export const __core_private__: { ReflectionCapabilities: reflection_capabilities.ReflectionCapabilities, makeDecorator: decorators.makeDecorator, DebugDomRootRenderer: debug.DebugDomRootRenderer, + DebugDomRendererV2: debug.DebugDomRendererV2, Console: console.Console, reflector: reflection.reflector, Reflector: reflection.Reflector, diff --git a/modules/@angular/core/src/debug/debug_node.ts b/modules/@angular/core/src/debug/debug_node.ts index a2ba3c3b2d..e092c299ce 100644 --- a/modules/@angular/core/src/debug/debug_node.ts +++ b/modules/@angular/core/src/debug/debug_node.ts @@ -85,16 +85,26 @@ export class DebugElement extends DebugNode { insertChildrenAfter(child: DebugNode, newChildren: DebugNode[]) { const siblingIndex = this.childNodes.indexOf(child); if (siblingIndex !== -1) { - const previousChildren = this.childNodes.slice(0, siblingIndex + 1); - const nextChildren = this.childNodes.slice(siblingIndex + 1); - this.childNodes = previousChildren.concat(newChildren, nextChildren); - for (let i = 0; i < newChildren.length; ++i) { - const newChild = newChildren[i]; - if (newChild.parent) { - newChild.parent.removeChild(newChild); + this.childNodes.splice(siblingIndex + 1, 0, ...newChildren); + newChildren.forEach(c => { + if (c.parent) { + c.parent.removeChild(c); } - newChild.parent = this; + c.parent = this; + }); + } + } + + insertBefore(refChild: DebugNode, newChild: DebugNode): void { + const refIndex = this.childNodes.indexOf(refChild); + if (refIndex === -1) { + this.addChild(newChild); + } else { + if (newChild.parent) { + newChild.parent.removeChild(newChild); } + newChild.parent = this; + this.childNodes.splice(refIndex, 0, newChild); } } diff --git a/modules/@angular/core/src/debug/debug_renderer.ts b/modules/@angular/core/src/debug/debug_renderer.ts index 8350747696..3f09a1bc2d 100644 --- a/modules/@angular/core/src/debug/debug_renderer.ts +++ b/modules/@angular/core/src/debug/debug_renderer.ts @@ -10,7 +10,7 @@ import {AnimationKeyframe} from '../animation/animation_keyframe'; import {AnimationPlayer} from '../animation/animation_player'; import {AnimationStyles} from '../animation/animation_styles'; import {isPresent} from '../facade/lang'; -import {RenderComponentType, RenderDebugInfo, Renderer, RootRenderer} from '../render/api'; +import {RenderComponentType, RenderDebugInfo, Renderer, RendererV2, RootRenderer} from '../render/api'; import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from './debug_node'; @@ -22,7 +22,7 @@ export class DebugDomRootRenderer implements RootRenderer { } } -export class DebugDomRenderer { +export class DebugDomRenderer implements Renderer { constructor(private _delegate: Renderer) {} selectRootElement(selectorOrNode: string|any, debugInfo?: RenderDebugInfo): any { @@ -81,7 +81,7 @@ export class DebugDomRenderer { detachView(viewRootNodes: any[]) { viewRootNodes.forEach((node) => { const debugNode = getDebugNode(node); - if (isPresent(debugNode) && isPresent(debugNode.parent)) { + if (debugNode && debugNode.parent) { debugNode.parent.removeChild(debugNode); } }); @@ -156,3 +156,151 @@ export class DebugDomRenderer { element, startingStyles, keyframes, duration, delay, easing, previousPlayers); } } + +export class DebugDomRendererV2 implements RendererV2 { + constructor(private _delegate: RendererV2) {} + + createElement(name: string, namespace?: string, debugInfo?: any): any { + const el = this._delegate.createElement(name, namespace, debugInfo); + const debugEl = new DebugElement(el, null, debugInfo); + debugEl.name = name; + indexDebugNode(debugEl); + return el; + } + + createComment(value: string, debugInfo?: any): any { + const comment = this._delegate.createComment(value, debugInfo); + const debugEl = new DebugNode(comment, null, debugInfo); + indexDebugNode(debugEl); + return comment; + } + + createText(value: string, debugInfo?: any): any { + const text = this._delegate.createText(value, debugInfo); + const debugEl = new DebugNode(text, null, debugInfo); + indexDebugNode(debugEl); + return text; + } + + appendChild(parent: any, newChild: any): void { + const debugEl = getDebugNode(parent); + const debugChildEl = getDebugNode(newChild); + if (debugEl && debugChildEl && debugEl instanceof DebugElement) { + debugEl.addChild(debugChildEl); + } + this._delegate.appendChild(parent, newChild); + } + + insertBefore(parent: any, newChild: any, refChild: any): void { + const debugEl = getDebugNode(parent); + const debugChildEl = getDebugNode(newChild); + const debugRefEl = getDebugNode(refChild); + if (debugEl && debugChildEl && debugEl instanceof DebugElement) { + debugEl.insertBefore(debugRefEl, debugChildEl); + } + + this._delegate.insertBefore(parent, newChild, refChild); + } + + removeChild(parent: any, oldChild: any): void { + const debugEl = getDebugNode(parent); + const debugChildEl = getDebugNode(oldChild); + if (debugEl && debugChildEl && debugEl instanceof DebugElement) { + debugEl.removeChild(debugChildEl); + } + this._delegate.removeChild(parent, oldChild); + } + + selectRootElement(selectorOrNode: string|any, debugInfo?: any): any { + const el = this._delegate.selectRootElement(selectorOrNode, debugInfo); + const debugEl = new DebugElement(el, null, debugInfo); + indexDebugNode(debugEl); + return el; + } + + parentNode(node: any): any { return this._delegate.parentNode(node); } + + nextSibling(node: any): any { return this._delegate.nextSibling(node); } + + setAttribute(el: any, name: string, value: string, namespace?: string): void { + const debugEl = getDebugNode(el); + if (debugEl && debugEl instanceof DebugElement) { + const fullName = namespace ? namespace + ':' + name : name; + debugEl.attributes[fullName] = value; + } + this._delegate.setAttribute(el, name, value, namespace); + } + + removeAttribute(el: any, name: string, namespace?: string): void { + const debugEl = getDebugNode(el); + if (debugEl && debugEl instanceof DebugElement) { + const fullName = namespace ? namespace + ':' + name : name; + debugEl.attributes[fullName] = null; + } + this._delegate.removeAttribute(el, name, namespace); + } + + setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void { + this._delegate.setBindingDebugInfo(el, propertyName, propertyValue); + } + + removeBindingDebugInfo(el: any, propertyName: string): void { + this._delegate.removeBindingDebugInfo(el, propertyName); + } + + addClass(el: any, name: string): void { + const debugEl = getDebugNode(el); + if (debugEl && debugEl instanceof DebugElement) { + debugEl.classes[name] = true; + } + this._delegate.addClass(el, name); + } + + removeClass(el: any, name: string): void { + const debugEl = getDebugNode(el); + if (debugEl && debugEl instanceof DebugElement) { + debugEl.classes[name] = false; + } + this._delegate.removeClass(el, name); + } + + setStyle(el: any, style: string, value: any, hasVendorPrefix: boolean, hasImportant: boolean): + void { + const debugEl = getDebugNode(el); + if (debugEl && debugEl instanceof DebugElement) { + debugEl.styles[style] = value; + } + this._delegate.setStyle(el, style, value, hasVendorPrefix, hasImportant); + } + + removeStyle(el: any, style: string, hasVendorPrefix: boolean): void { + const debugEl = getDebugNode(el); + if (debugEl && debugEl instanceof DebugElement) { + debugEl.styles[style] = null; + } + this._delegate.removeStyle(el, style, hasVendorPrefix); + } + + setProperty(el: any, name: string, value: any): void { + const debugEl = getDebugNode(el); + if (debugEl && debugEl instanceof DebugElement) { + debugEl.properties[name] = value; + } + this._delegate.setProperty(el, name, value); + } + + setText(node: any, value: string): void { this._delegate.setText(node, value); } + + listen( + target: 'document'|'windows'|'body'|any, eventName: string, + callback: (event: any) => boolean): () => void { + if (typeof target !== 'string') { + const debugEl = getDebugNode(target); + if (debugEl) { + debugEl.listeners.push(new EventListener(eventName, callback)); + } + } + + return this._delegate.listen(target, eventName, callback); + } +} diff --git a/modules/@angular/core/src/errors.ts b/modules/@angular/core/src/errors.ts index 9d2fb63a8c..29bde2ca76 100644 --- a/modules/@angular/core/src/errors.ts +++ b/modules/@angular/core/src/errors.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {Type} from './type'; import {DebugContext} from './view'; export const ERROR_TYPE = 'ngType'; diff --git a/modules/@angular/core/src/linker/view_container.ts b/modules/@angular/core/src/linker/view_container.ts index daa60fb1f6..64eadf06fc 100644 --- a/modules/@angular/core/src/linker/view_container.ts +++ b/modules/@angular/core/src/linker/view_container.ts @@ -80,8 +80,8 @@ export class ViewContainer { return result; } - moveView(view: AppView, currentIndex: number) { - const previousIndex = this.nestedViews.indexOf(view); + moveView(view: AppView, toIndex: number) { + const fromIndex = this.nestedViews.indexOf(view); if (view.type === ViewType.COMPONENT) { throw new Error(`Component views can't be moved!`); } @@ -90,9 +90,9 @@ export class ViewContainer { nestedViews = []; this.nestedViews = nestedViews; } - nestedViews.splice(previousIndex, 1); - nestedViews.splice(currentIndex, 0, view); - const prevView = currentIndex > 0 ? nestedViews[currentIndex - 1] : null; + nestedViews.splice(fromIndex, 1); + nestedViews.splice(toIndex, 0, view); + const prevView = toIndex > 0 ? nestedViews[toIndex - 1] : null; view.moveAfter(this, prevView); } diff --git a/modules/@angular/core/src/linker/view_container_ref.ts b/modules/@angular/core/src/linker/view_container_ref.ts index 947f8a1dc1..1674c73f1c 100644 --- a/modules/@angular/core/src/linker/view_container_ref.ts +++ b/modules/@angular/core/src/linker/view_container_ref.ts @@ -7,7 +7,6 @@ */ import {Injector} from '../di/injector'; -import {isPresent} from '../facade/lang'; import {WtfScopeFn, wtfCreateScope, wtfLeave} from '../profile/profile'; import {ComponentFactory, ComponentRef} from './component_factory'; @@ -133,7 +132,7 @@ export class ViewContainerRef_ implements ViewContainerRef { get(index: number): ViewRef { return this._element.nestedViews[index].ref; } get length(): number { const views = this._element.nestedViews; - return isPresent(views) ? views.length : 0; + return views ? views.length : 0; } get element(): ElementRef { return this._element.elementRef; } diff --git a/modules/@angular/core/src/render.ts b/modules/@angular/core/src/render.ts index fef0896f50..fc8e4f1f7f 100644 --- a/modules/@angular/core/src/render.ts +++ b/modules/@angular/core/src/render.ts @@ -7,4 +7,4 @@ */ // Public API for render -export {RenderComponentType, Renderer, RootRenderer} from './render/api'; +export {RENDERER_V2_DIRECT, RenderComponentType, Renderer, RendererV2, RootRenderer} from './render/api'; diff --git a/modules/@angular/core/src/render/api.ts b/modules/@angular/core/src/render/api.ts index e22de06fe3..d237eecdc7 100644 --- a/modules/@angular/core/src/render/api.ts +++ b/modules/@angular/core/src/render/api.ts @@ -9,9 +9,16 @@ import {AnimationKeyframe} from '../../src/animation/animation_keyframe'; import {AnimationPlayer} from '../../src/animation/animation_player'; import {AnimationStyles} from '../../src/animation/animation_styles'; -import {Injector} from '../di/injector'; +import {InjectionToken, Injector} from '../di'; import {ViewEncapsulation} from '../metadata/view'; +/** + * Provide a concrete implementation of {@link RendererV2} + * + * @experimental + */ +export const RENDERER_V2_DIRECT = new InjectionToken('Renderer V2'); + /** * @experimental */ @@ -91,6 +98,56 @@ export abstract class Renderer { previousPlayers?: AnimationPlayer[]): AnimationPlayer; } +/** + * @experimental + */ +export abstract class RendererV2 { + abstract createElement(name: string, namespace?: string, debugInfo?: RenderDebugContext): any; + abstract createComment(value: string, debugInfo?: RenderDebugContext): any; + abstract createText(value: string, debugInfo?: RenderDebugContext): any; + abstract appendChild(parent: any, newChild: any): void; + abstract insertBefore(parent: any, newChild: any, refChild: any): void; + abstract removeChild(parent: any, oldChild: any): void; + abstract selectRootElement(selectorOrNode: string|any, debugInfo?: RenderDebugContext): any; + /** + * Attention: On WebWorkers, this will always return a value, + * as we are asking for a result synchronously. I.e. + * the caller can't rely on checking whether this is null or not. + */ + abstract parentNode(node: any): any; + /** + * Attention: On WebWorkers, this will always return a value, + * as we are asking for a result synchronously. I.e. + * the caller can't rely on checking whether this is null or not. + */ + abstract nextSibling(node: any): any; + abstract setAttribute(el: any, name: string, value: string, namespace?: string): void; + abstract removeAttribute(el: any, name: string, namespace?: string): void; + abstract setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void; + abstract removeBindingDebugInfo(el: any, propertyName: string): void; + abstract addClass(el: any, name: string): void; + abstract removeClass(el: any, name: string): void; + abstract setStyle( + el: any, style: string, value: any, hasVendorPrefix: boolean, hasImportant: boolean): void; + abstract removeStyle(el: any, style: string, hasVendorPrefix: boolean): void; + abstract setProperty(el: any, name: string, value: any): void; + abstract setText(node: any, value: string): void; + abstract listen( + target: 'window'|'document'|'body'|any, eventName: string, + callback: (event: any) => boolean): () => void; +} + +export abstract class RenderDebugContext { + abstract get injector(): Injector; + abstract get component(): any; + abstract get providerTokens(): any[]; + abstract get references(): {[key: string]: any}; + abstract get context(): any; + abstract get source(): string; + abstract get componentRenderElement(): any; + abstract get renderNode(): any; +} + /** * Injectable service that provides a low-level interface for modifying the UI. * diff --git a/modules/@angular/core/src/view/element.ts b/modules/@angular/core/src/view/element.ts index 73d4f864d1..56826c5387 100644 --- a/modules/@angular/core/src/view/element.ts +++ b/modules/@angular/core/src/view/element.ts @@ -137,7 +137,13 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): El if (view.parent || !rootSelectorOrNode) { const parentNode = def.parent != null ? asElementData(view, def.parent).renderElement : renderHost; - el = elDef.name ? renderer.createElement(elDef.name) : renderer.createComment(''); + if (elDef.name) { + // TODO(vicb): move the namespace to the node definition + const nsAndName = splitNamespace(elDef.name); + el = renderer.createElement(nsAndName[1], nsAndName[0]); + } else { + el = renderer.createComment(''); + } if (parentNode) { renderer.appendChild(parentNode, el); } @@ -146,7 +152,9 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): El } if (elDef.attrs) { for (let attrName in elDef.attrs) { - renderer.setAttribute(el, attrName, elDef.attrs[attrName]); + // TODO(vicb): move the namespace to the node definition + const nsAndName = splitNamespace(attrName); + renderer.setAttribute(el, nsAndName[1], elDef.attrs[attrName], nsAndName[0]); } } if (elDef.outputs.length) { @@ -234,10 +242,12 @@ function setElementAttribute( let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value; renderValue = renderValue != null ? renderValue.toString() : null; const renderer = view.root.renderer; + // TODO(vicb): move the namespace to the node definition + const nsAndName = splitNamespace(name); if (value != null) { - renderer.setAttribute(renderNode, name, renderValue); + renderer.setAttribute(renderNode, nsAndName[1], renderValue, nsAndName[0]); } else { - renderer.removeAttribute(renderNode, name); + renderer.removeAttribute(renderNode, nsAndName[1], nsAndName[0]); } } @@ -264,9 +274,9 @@ function setElementStyle( } const renderer = view.root.renderer; if (renderValue != null) { - renderer.setStyle(renderNode, name, renderValue); + renderer.setStyle(renderNode, name, renderValue, false, false); } else { - renderer.removeStyle(renderNode, name); + renderer.removeStyle(renderNode, name, false); } } @@ -276,3 +286,13 @@ function setElementProperty( let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value; view.root.renderer.setProperty(renderNode, name, renderValue); } + +const NS_PREFIX_RE = /^:([^:]+):(.+)$/; + +function splitNamespace(name: string): string[] { + if (name[0] === ':') { + const match = name.match(NS_PREFIX_RE); + return [match[1], match[2]]; + } + return ['', name]; +} diff --git a/modules/@angular/core/src/view/provider.ts b/modules/@angular/core/src/view/provider.ts index 31f4822674..1a99d80b9e 100644 --- a/modules/@angular/core/src/view/provider.ts +++ b/modules/@angular/core/src/view/provider.ts @@ -12,7 +12,6 @@ import {ElementRef} from '../linker/element_ref'; import {TemplateRef} from '../linker/template_ref'; import {ViewContainerRef} from '../linker/view_container_ref'; import * as v1renderer from '../render/api'; -import {Type} from '../type'; import {createChangeDetectorRef, createInjector, createTemplateRef, createViewContainerRef} from './refs'; import {BindingDef, BindingType, DepDef, DepFlags, DirectiveOutputDef, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types'; diff --git a/modules/@angular/core/src/view/refs.ts b/modules/@angular/core/src/view/refs.ts index 9fa8afd54a..bfa5bbe3b4 100644 --- a/modules/@angular/core/src/view/refs.ts +++ b/modules/@angular/core/src/view/refs.ts @@ -6,20 +6,16 @@ * found in the LICENSE file at https://angular.io/license */ -import {isDevMode} from '../application_ref'; import {ChangeDetectorRef} from '../change_detection/change_detection'; -import {Injectable, Injector} from '../di'; +import {Injector} from '../di'; import {ComponentFactory, ComponentRef} from '../linker/component_factory'; import {ElementRef} from '../linker/element_ref'; import {TemplateRef} from '../linker/template_ref'; import {ViewContainerRef} from '../linker/view_container_ref'; import {EmbeddedViewRef, ViewRef} from '../linker/view_ref'; -import * as v1renderer from '../render/api'; -import {Sanitizer, SecurityContext} from '../security'; import {Type} from '../type'; -import {DirectDomRenderer, LegacyRendererAdapter} from './renderer'; -import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types'; +import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types'; import {isComponentView, renderNode, resolveViewDefinition, rootRenderNodes, tokenKey, viewParentElIndex} from './util'; const EMPTY_CONTEXT = new Object(); diff --git a/modules/@angular/core/src/view/renderer.ts b/modules/@angular/core/src/view/renderer.ts deleted file mode 100644 index 77992597ac..0000000000 --- a/modules/@angular/core/src/view/renderer.ts +++ /dev/null @@ -1,147 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {ViewEncapsulation} from '../metadata/view'; -import * as v1 from '../render/api'; - -import {DebugContext, RendererV2} from './types'; - -export class DirectDomRenderer implements RendererV2 { - createElement(name: string): any { return document.createElement(name); } - createComment(value: string): any { return document.createComment(value); } - createText(value: string): any { return document.createTextNode(value); } - appendChild(parent: any, newChild: any): void { parent.appendChild(newChild); } - insertBefore(parent: any, newChild: any, refChild: any): void { - if (parent) { - parent.insertBefore(newChild, refChild); - } - } - removeChild(parent: any, oldChild: any): void { - if (parent) { - parent.removeChild(oldChild); - } - } - selectRootElement(selectorOrNode: string|any, debugInfo?: DebugContext): any { - let el: any; - if (typeof selectorOrNode === 'string') { - el = document.querySelector(selectorOrNode); - } else { - el = selectorOrNode; - } - el.textContent = ''; - return el; - } - parentNode(node: any): any { return node.parentNode; } - nextSibling(node: any): any { return node.nextSiblibng; } - setAttribute(el: any, name: string, value: string): void { return el.setAttribute(name, value); } - removeAttribute(el: any, name: string): void { el.removeAttribute(name); } - setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void {} - removeBindingDebugInfo(el: any, propertyName: string): void {} - addClass(el: any, name: string): void { el.classList.add(name); } - removeClass(el: any, name: string): void { el.classList.remove(name); } - setStyle(el: any, style: string, value: any): void { el.style[style] = value; } - removeStyle(el: any, style: string): void { - // IE requires '' instead of null - // see https://github.com/angular/angular/issues/7916 - (el.style as any)[style] = ''; - } - setProperty(el: any, name: string, value: any): void { el[name] = value; } - setText(node: any, value: string): void { node.nodeValue = value; } - listen(target: any, eventName: string, callback: (event: any) => boolean): () => void { - let renderTarget: any; - switch (target) { - case 'window': - renderTarget = window; - break; - case 'document': - renderTarget = document; - break; - default: - renderTarget = target; - } - const closure = (event: any) => { - if (callback(event) === false) { - event.preventDefault(); - } - }; - renderTarget.addEventListener(eventName, closure); - return () => renderTarget.removeEventListener(eventName, closure); - } -} - -const EMPTY_V1_RENDER_COMPONENT_TYPE = - new v1.RenderComponentType('EMPTY', '', 0, ViewEncapsulation.None, [], {}); - -/** - * A temporal implementation of `Renderer` until we migrated our current renderer - * in all packages to the new API. - * - * Note that this is not complete, e.g. does not support shadow dom, view encapsulation, ...! - */ -export class LegacyRendererAdapter implements RendererV2 { - private _delegate: v1.Renderer; - constructor(rootDelegate: v1.RootRenderer) { - this._delegate = rootDelegate.renderComponent(EMPTY_V1_RENDER_COMPONENT_TYPE); - } - createElement(name: string, debugInfo?: DebugContext): any { - return this._delegate.createElement(null, name, debugInfo); - } - createComment(value: string, debugInfo?: DebugContext): any { - return this._delegate.createTemplateAnchor(null, debugInfo); - } - createText(value: string, debugInfo?: DebugContext): any { - return this._delegate.createText(null, value, debugInfo); - } - appendChild(parent: any, newChild: any): void { this._delegate.projectNodes(parent, [newChild]); } - insertBefore(parent: any, newChild: any, refChild: any): void { - if (refChild) { - this._delegate.attachViewAfter(refChild.previousSibling, [newChild]); - } else { - this.appendChild(parent, newChild); - } - } - removeChild(parent: any, oldChild: any): void { - if (parent) { - this._delegate.detachView([oldChild]); - } - } - selectRootElement(selectorOrNode: any, debugInfo?: DebugContext): any { - return this._delegate.selectRootElement(selectorOrNode, debugInfo); - } - parentNode(node: any): any { return node.parentNode; } - nextSibling(node: any): any { return node.nextSibling; } - setAttribute(el: any, name: string, value: string): void { - this._delegate.setElementAttribute(el, name, value); - } - removeAttribute(el: any, name: string): void { - this._delegate.setElementAttribute(el, name, null); - } - setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void { - this._delegate.setBindingDebugInfo(el, propertyName, propertyValue); - } - removeBindingDebugInfo(el: any, propertyName: string): void { - this._delegate.setBindingDebugInfo(el, propertyName, null); - } - addClass(el: any, name: string): void { this._delegate.setElementClass(el, name, true); } - removeClass(el: any, name: string): void { this._delegate.setElementClass(el, name, false); } - setStyle(el: any, style: string, value: any): void { - this._delegate.setElementStyle(el, style, value); - } - removeStyle(el: any, style: string): void { this._delegate.setElementStyle(el, style, null); } - setProperty(el: any, name: string, value: any): void { - this._delegate.setElementProperty(el, name, value); - } - setText(node: any, value: string): void { this._delegate.setText(node, value); } - listen(target: any, eventName: string, callback: (event: any) => boolean): () => void { - if (typeof target === 'string') { - return this._delegate.listenGlobal(target, eventName, callback); - } else { - return this._delegate.listen(target, eventName, callback); - } - } -} diff --git a/modules/@angular/core/src/view/services.ts b/modules/@angular/core/src/view/services.ts index 903b234bed..2ec65ef86e 100644 --- a/modules/@angular/core/src/view/services.ts +++ b/modules/@angular/core/src/view/services.ts @@ -7,20 +7,16 @@ */ import {isDevMode} from '../application_ref'; -import {Injectable, Injector} from '../di'; -import {looseIdentical} from '../facade/lang'; -import {ElementRef} from '../linker/element_ref'; -import * as v1renderer from '../render/api'; +import {Injector} from '../di'; +import {RendererV2} from '../render/api'; import {Sanitizer, SecurityContext} from '../security'; -import {Type} from '../type'; import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors'; import {resolveDep} from './provider'; import {getQueryValue} from './query'; import {createInjector} from './refs'; -import {DirectDomRenderer, LegacyRendererAdapter} from './renderer'; -import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, ViewUpdateFn, asElementData, asProviderData} from './types'; -import {checkBinding, isComponentView, queryIdIsReference, renderNode, resolveViewDefinition, rootRenderNodes, viewParentElIndex} from './util'; +import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types'; +import {checkBinding, isComponentView, queryIdIsReference, renderNode, viewParentElIndex} from './util'; import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view'; import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach'; @@ -112,10 +108,8 @@ function debugCreateRootView( function createRootData( injector: Injector, projectableNodes: any[][], rootSelectorOrNode: any): RootData { const sanitizer = injector.get(Sanitizer); - // TODO(tbosch): once the new renderer interface is implemented via platform-browser, - // just get it via the injector and drop LegacyRendererAdapter and DirectDomRenderer. - const renderer = isDevMode() ? new LegacyRendererAdapter(injector.get(v1renderer.RootRenderer)) : - new DirectDomRenderer(); + const renderer = injector.get(RendererV2); + const rootElement = rootSelectorOrNode ? renderer.selectRootElement(rootSelectorOrNode) : undefined; return {injector, projectableNodes, selectorOrNode: rootSelectorOrNode, sanitizer, renderer}; @@ -183,7 +177,7 @@ function debugUpdateRenderer(check: NodeCheckFn, view: ViewData) { const result = debugCheckFn(check, view, nodeIndex, argStyle, values); debugSetCurrentNode(view, nextRenderNodeWithBinding(view, nodeIndex)); return result; - }; + } } function debugCheckFn( @@ -249,8 +243,9 @@ function nextRenderNodeWithBinding(view: ViewData, nodeIndex: number): number { class DebugRenderer implements RendererV2 { constructor(private _delegate: RendererV2) {} - createElement(name: string): any { - return this._delegate.createElement(name, getCurrentDebugContext()); + + createElement(name: string, namespace?: string): any { + return this._delegate.createElement(name, namespace, getCurrentDebugContext()); } createComment(value: string): any { return this._delegate.createComment(value, getCurrentDebugContext()); @@ -272,10 +267,12 @@ class DebugRenderer implements RendererV2 { } parentNode(node: any): any { return this._delegate.parentNode(node); } nextSibling(node: any): any { return this._delegate.nextSibling(node); } - setAttribute(el: any, name: string, value: string): void { - return this._delegate.setAttribute(el, name, value); + setAttribute(el: any, name: string, value: string, namespace?: string): void { + return this._delegate.setAttribute(el, name, value, namespace); + } + removeAttribute(el: any, name: string, namespace?: string): void { + return this._delegate.removeAttribute(el, name, namespace); } - removeAttribute(el: any, name: string): void { return this._delegate.removeAttribute(el, name); } setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void { this._delegate.setBindingDebugInfo(el, propertyName, propertyValue); } @@ -284,16 +281,20 @@ class DebugRenderer implements RendererV2 { } addClass(el: any, name: string): void { return this._delegate.addClass(el, name); } removeClass(el: any, name: string): void { return this._delegate.removeClass(el, name); } - setStyle(el: any, style: string, value: any): void { - return this._delegate.setStyle(el, style, value); + setStyle(el: any, style: string, value: any, hasVendorPrefix: boolean, hasImportant: boolean): + void { + return this._delegate.setStyle(el, style, value, hasVendorPrefix, hasImportant); + } + removeStyle(el: any, style: string, hasVendorPrefix: boolean): void { + return this._delegate.removeStyle(el, style, hasVendorPrefix); } - removeStyle(el: any, style: string): void { return this._delegate.removeStyle(el, style); } setProperty(el: any, name: string, value: any): void { return this._delegate.setProperty(el, name, value); } setText(node: any, value: string): void { return this._delegate.setText(node, value); } - listen(target: 'window'|'document'|any, eventName: string, callback: (event: any) => boolean): - () => void { + listen( + target: 'window'|'document'|'body'|any, eventName: string, + callback: (event: any) => boolean): () => void { return this._delegate.listen(target, eventName, callback); } } diff --git a/modules/@angular/core/src/view/types.ts b/modules/@angular/core/src/view/types.ts index 1a15a1d78e..137697bb27 100644 --- a/modules/@angular/core/src/view/types.ts +++ b/modules/@angular/core/src/view/types.ts @@ -14,6 +14,7 @@ import {TemplateRef} from '../linker/template_ref'; import {ViewContainerRef} from '../linker/view_container_ref'; import {ViewRef} from '../linker/view_ref'; import {ViewEncapsulation} from '../metadata/view'; +import {RenderDebugContext, RendererV2} from '../render/api'; import {Sanitizer, SecurityContext} from '../security'; // ------------------------------------- @@ -408,61 +409,6 @@ export interface RootData { sanitizer: Sanitizer; } -/** - * TODO(tbosch): move this interface into @angular/core/src/render/api, - * and implement it in @angular/platform-browser, ... - */ -export interface RendererV2 { - createElement(name: string, debugInfo?: RenderDebugContext): any; - createComment(value: string, debugInfo?: RenderDebugContext): any; - createText(value: string, debugInfo?: RenderDebugContext): any; - appendChild(parent: any, newChild: any): void; - insertBefore(parent: any, newChild: any, refChild: any): void; - removeChild(parent: any, oldChild: any): void; - selectRootElement(selectorOrNode: string|any, debugInfo?: RenderDebugContext): any; - /** - * Attention: On WebWorkers, this will always return a value, - * as we are asking for a result synchronously. I.e. - * the caller can't rely on checking whether this is null or not. - */ - parentNode(node: any): any; - /** - * Attention: On WebWorkers, this will always return a value, - * as we are asking for a result synchronously. I.e. - * the caller can't rely on checking whether this is null or not. - */ - nextSibling(node: any): any; - /** - * Used only in debug mode to serialize property changes to dom nodes as attributes. - */ - setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void; - /** - * Used only in debug mode to serialize property changes to dom nodes as attributes. - */ - removeBindingDebugInfo(el: any, propertyName: string): void; - setAttribute(el: any, name: string, value: string): void; - removeAttribute(el: any, name: string): void; - addClass(el: any, name: string): void; - removeClass(el: any, name: string): void; - setStyle(el: any, style: string, value: any): void; - removeStyle(el: any, style: string): void; - setProperty(el: any, name: string, value: any): void; - setText(node: any, value: string): void; - listen(target: 'window'|'document'|any, eventName: string, callback: (event: any) => boolean): - () => void; -} - -export abstract class RenderDebugContext { - abstract get injector(): Injector; - abstract get component(): any; - abstract get providerTokens(): any[]; - abstract get references(): {[key: string]: any}; - abstract get context(): any; - abstract get source(): string; - abstract get componentRenderElement(): any; - abstract get renderNode(): any; -} - export abstract class DebugContext extends RenderDebugContext { abstract get view(): ViewData; abstract get nodeIndex(): number; diff --git a/modules/@angular/core/src/view/util.ts b/modules/@angular/core/src/view/util.ts index 16ff45725e..3288ab0505 100644 --- a/modules/@angular/core/src/view/util.ts +++ b/modules/@angular/core/src/view/util.ts @@ -183,6 +183,11 @@ export enum RenderNodeAction { export function visitRootRenderNodes( view: ViewData, action: RenderNodeAction, parentNode: any, nextSibling: any, target: any[]) { + // We need to re-compute the parent node in case the nodes have been moved around manually + if (action === RenderNodeAction.RemoveChild) { + parentNode = view.root.renderer.parentNode(renderNode(view, view.def.lastRootNode)); + } + const len = view.def.nodes.length; for (let i = 0; i < len; i++) { const nodeDef = view.def.nodes[i]; diff --git a/modules/@angular/core/test/linker/change_detection_integration_spec.ts b/modules/@angular/core/test/linker/change_detection_integration_spec.ts index 6058f560b9..7690476f0e 100644 --- a/modules/@angular/core/test/linker/change_detection_integration_spec.ts +++ b/modules/@angular/core/test/linker/change_detection_integration_spec.ts @@ -9,8 +9,8 @@ import {USE_VIEW_ENGINE} from '@angular/compiler/src/config'; import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry'; import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings'; -import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RenderComponentType, Renderer, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core'; -import {DebugDomRenderer} from '@angular/core/src/debug/debug_renderer'; +import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RENDERER_V2_DIRECT, RenderComponentType, Renderer, RendererV2, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core'; +import {DebugDomRenderer, DebugDomRendererV2} from '@angular/core/src/debug/debug_renderer'; import {ComponentFixture, TestBed, fakeAsync} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; @@ -24,8 +24,12 @@ export function main() { describe('View Engine compiler', () => { beforeEach(() => { - TestBed.configureCompiler( - {useJit: true, providers: [{provide: USE_VIEW_ENGINE, useValue: true}]}); + TestBed.configureCompiler({ + useJit: true, + providers: [ + {provide: USE_VIEW_ENGINE, useValue: true}, + ] + }); }); createTests({viewEngine: true}); @@ -115,8 +119,16 @@ function createTests({viewEngine}: {viewEngine: boolean}) { IdentityPipe, WrappedPipe, ], - providers: - [RenderLog, DirectiveLog, {provide: RootRenderer, useClass: LoggingRootRenderer}] + providers: [ + RenderLog, + DirectiveLog, + {provide: RootRenderer, useClass: LoggingRootRenderer}, + { + provide: RendererV2, + useFactory: (r: RendererV2, log: RenderLog) => new LoggingRendererV2(r, log), + deps: [[new Inject(RENDERER_V2_DIRECT)], [RenderLog]], + }, + ], }); }); @@ -1303,6 +1315,20 @@ class DirectiveLogEntry { constructor(public directiveName: string, public method: string) {} } +class LoggingRendererV2 extends DebugDomRendererV2 { + constructor(private delegate: RendererV2, private log: RenderLog) { super(delegate); } + + setProperty(el: any, name: string, value: any): void { + this.log.setElementProperty(el, name, value); + super.setProperty(el, name, value); + } + + setText(node: any, value: string): void { + this.log.setText(node, value); + super.setText(node, value); + } +} + @Injectable() class DirectiveLog { entries: DirectiveLogEntry[] = []; diff --git a/modules/@angular/core/test/linker/integration_spec.ts b/modules/@angular/core/test/linker/integration_spec.ts index 916bfe6ba5..52a50419bf 100644 --- a/modules/@angular/core/test/linker/integration_spec.ts +++ b/modules/@angular/core/test/linker/integration_spec.ts @@ -18,7 +18,6 @@ import {TemplateRef, TemplateRef_} from '@angular/core/src/linker/template_ref'; import {ViewContainerRef} from '@angular/core/src/linker/view_container_ref'; import {EmbeddedViewRef} from '@angular/core/src/linker/view_ref'; import {Attribute, Component, ContentChildren, Directive, HostBinding, HostListener, Input, Output, Pipe} from '@angular/core/src/metadata'; -import {Renderer} from '@angular/core/src/render'; import {TestBed, async, fakeAsync, getTestBed, tick} from '@angular/core/testing'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {DOCUMENT} from '@angular/platform-browser/src/dom/dom_tokens'; @@ -26,7 +25,7 @@ import {dispatchEvent, el} from '@angular/platform-browser/testing/browser_util' import {expect} from '@angular/platform-browser/testing/matchers'; import {EventEmitter} from '../../src/facade/async'; -import {isBlank, isPresent, stringify} from '../../src/facade/lang'; +import {stringify} from '../../src/facade/lang'; const ANCHOR_ELEMENT = new InjectionToken('AnchorElement'); @@ -37,18 +36,24 @@ export function main() { describe('view engine', () => { beforeEach(() => { - TestBed.configureCompiler( - {useJit: true, providers: [{provide: USE_VIEW_ENGINE, useValue: true}]}); + TestBed.configureCompiler({ + useJit: true, + providers: [{ + provide: USE_VIEW_ENGINE, + useValue: true, + }], + }); }); declareTests({useJit: true, viewEngine: true}); }); } + function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolean}) { describe('integration tests', function() { - beforeEach(() => { TestBed.configureCompiler({useJit: useJit}); }); + beforeEach(() => { TestBed.configureCompiler({useJit}); }); describe('react to record changes', function() { it('should consume text node changes', () => { @@ -1472,7 +1477,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea fixture.detectChanges(); const el = getDOM().querySelector(fixture.nativeElement, 'span'); - expect(isBlank(el.title) || el.title == '').toBeTruthy(); + expect(el.title).toBeFalsy(); }); it('should work when a directive uses hostProperty to update the DOM element', () => { @@ -2084,8 +2089,7 @@ class ToolbarPart { @Directive({selector: '[toolbarVc]', inputs: ['toolbarVc']}) class ToolbarViewContainer { - vc: ViewContainerRef; - constructor(vc: ViewContainerRef) { this.vc = vc; } + constructor(public vc: ViewContainerRef) {} set toolbarVc(part: ToolbarPart) { this.vc.createEmbeddedView(part.templateRef, new ToolbarContext('From toolbar'), 0); @@ -2098,9 +2102,9 @@ class ToolbarViewContainer { }) class ToolbarComponent { @ContentChildren(ToolbarPart) query: QueryList; - ctxProp: string; + ctxProp: string = 'hello world'; - constructor() { this.ctxProp = 'hello world'; } + constructor() {} } @Directive({selector: '[two-way]', inputs: ['control'], outputs: ['controlChange']}) @@ -2238,10 +2242,11 @@ class SomeImperativeViewport { } set someImpvp(value: boolean) { - if (isPresent(this.view)) { + if (this.view) { this.vc.clear(); this.view = null; } + if (value) { this.view = this.vc.createEmbeddedView(this.templateRef); const nodes = this.view.rootNodes; diff --git a/modules/@angular/core/test/view/anchor_spec.ts b/modules/@angular/core/test/view/anchor_spec.ts index 50c1ca1da9..25ebbf1d8a 100644 --- a/modules/@angular/core/test/view/anchor_spec.ts +++ b/modules/@angular/core/test/view/anchor_spec.ts @@ -8,7 +8,6 @@ import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, getDebugNode} from '@angular/core'; import {DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index'; -import {inject} from '@angular/core/testing'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {createRootView, isBrowser} from './helper'; diff --git a/modules/@angular/core/test/view/component_view_spec.ts b/modules/@angular/core/test/view/component_view_spec.ts index 3730d63cce..1eb8209127 100644 --- a/modules/@angular/core/test/view/component_view_spec.ts +++ b/modules/@angular/core/test/view/component_view_spec.ts @@ -8,7 +8,6 @@ import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core'; import {ArgumentType, BindingType, NodeCheckFn, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, anchorDef, asProviderData, directiveDef, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index'; -import {inject} from '@angular/core/testing'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {createRootView, isBrowser, removeNodes} from './helper'; @@ -182,23 +181,19 @@ export function main() { const update = jasmine.createSpy('updater'); const addListenerSpy = spyOn(HTMLElement.prototype, 'addEventListener').and.callThrough(); - const {view, rootNodes} = - createAndGetRootNodes( - compViewDef( - [ - elementDef(NodeFlags.None, null, null, 1, 'div'), - directiveDef( - NodeFlags.None, null, 0, AComp, [], {a: [0, 'a']}, null, - () => - compViewDef( - [ - elementDef(NodeFlags.None, null, null, 0, 'span', null, null, ['click']), - ], - update, null, null, ViewFlags.OnPush)), - ], - (check, view) => { check(view, 1, ArgumentType.Inline, compInputValue); })); - const compView = asProviderData(view, 1).componentView; + const {view} = createAndGetRootNodes(compViewDef( + [ + elementDef(NodeFlags.None, null, null, 1, 'div'), + directiveDef( + NodeFlags.None, null, 0, AComp, [], {a: [0, 'a']}, null, + () => compViewDef( + [ + elementDef(NodeFlags.None, null, null, 0, 'span', null, null, ['click']), + ], + update, null, null, ViewFlags.OnPush)), + ], + (check, view) => { check(view, 1, ArgumentType.Inline, compInputValue); })); Services.checkAndUpdateView(view); diff --git a/modules/@angular/core/test/view/element_spec.ts b/modules/@angular/core/test/view/element_spec.ts index d13a348c07..66cab0cdf6 100644 --- a/modules/@angular/core/test/view/element_spec.ts +++ b/modules/@angular/core/test/view/element_spec.ts @@ -9,7 +9,6 @@ import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core'; import {getDebugContext} from '@angular/core/src/errors'; import {ArgumentType, BindingType, DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index'; -import {inject} from '@angular/core/testing'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser, removeNodes} from './helper'; diff --git a/modules/@angular/core/test/view/embedded_view_spec.ts b/modules/@angular/core/test/view/embedded_view_spec.ts index 5ced178e13..df802cb038 100644 --- a/modules/@angular/core/test/view/embedded_view_spec.ts +++ b/modules/@angular/core/test/view/embedded_view_spec.ts @@ -36,7 +36,7 @@ export function main() { const parentContext = new Object(); const childContext = new Object(); - const {view: parentView, rootNodes} = createAndGetRootNodes( + const {view: parentView} = createAndGetRootNodes( compViewDef([ elementDef(NodeFlags.None, null, null, 1, 'div'), anchorDef( @@ -146,7 +146,6 @@ export function main() { const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]); - const rootEl = rootNodes[0]; attachEmbeddedView(asElementData(parentView, 1), 0, childView0); Services.checkAndUpdateView(parentView); @@ -171,7 +170,7 @@ export function main() { ngOnDestroy() { log.push('ngOnDestroy'); }; } - const {view: parentView, rootNodes} = createAndGetRootNodes(compViewDef([ + const {view: parentView} = createAndGetRootNodes(compViewDef([ elementDef(NodeFlags.None, null, null, 1, 'div'), anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, embeddedViewDef([ elementDef(NodeFlags.None, null, null, 1, 'span'), diff --git a/modules/@angular/core/test/view/ng_content_spec.ts b/modules/@angular/core/test/view/ng_content_spec.ts index 460117938f..542b1b8f75 100644 --- a/modules/@angular/core/test/view/ng_content_spec.ts +++ b/modules/@angular/core/test/view/ng_content_spec.ts @@ -8,7 +8,6 @@ import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core'; import {DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, ngContentDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index'; -import {inject} from '@angular/core/testing'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {createRootView, isBrowser} from './helper'; @@ -104,14 +103,18 @@ export function main() { it('should include projected nodes when attaching / detaching embedded views', () => { const {view, rootNodes} = createAndGetRootNodes(compViewDef(hostElDef([textDef(0, ['a'])], [ elementDef(NodeFlags.None, null, null, 1, 'div'), - anchorDef(NodeFlags.HasEmbeddedViews, null, 0, 0, embeddedViewDef([ngContentDef(null, 0)])), + anchorDef(NodeFlags.HasEmbeddedViews, null, 0, 0, embeddedViewDef([ + ngContentDef(null, 0), + // The anchor would be added by the compiler after the ngContent + anchorDef(NodeFlags.None, null, null, 0), + ])), ]))); const componentView = asProviderData(view, 1).componentView; const view0 = Services.createEmbeddedView(componentView, componentView.def.nodes[1]); attachEmbeddedView(asElementData(componentView, 1), 0, view0); - expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(2); + expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(3); expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0]))[1]) .toBe(asTextData(view, 2).renderText); diff --git a/modules/@angular/platform-browser/src/browser.ts b/modules/@angular/platform-browser/src/browser.ts index 24eb0b129c..ad7c8c7e35 100644 --- a/modules/@angular/platform-browser/src/browser.ts +++ b/modules/@angular/platform-browser/src/browser.ts @@ -7,7 +7,7 @@ */ import {CommonModule, PlatformLocation} from '@angular/common'; -import {ApplicationModule, ErrorHandler, NgModule, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, RootRenderer, Sanitizer, SkipSelf, Testability, createPlatformFactory, platformCore} from '@angular/core'; +import {ApplicationModule, ErrorHandler, NgModule, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, RENDERER_V2_DIRECT, RendererV2, RootRenderer, Sanitizer, SkipSelf, Testability, createPlatformFactory, platformCore} from '@angular/core'; import {AnimationDriver} from '../src/dom/animation_driver'; import {WebAnimationsDriver} from '../src/dom/web_animations_driver'; @@ -19,7 +19,7 @@ import {BrowserGetTestability} from './browser/testability'; import {Title} from './browser/title'; import {ELEMENT_PROBE_PROVIDERS} from './dom/debug/ng_probe'; import {getDOM} from './dom/dom_adapter'; -import {DomRootRenderer, DomRootRenderer_} from './dom/dom_renderer'; +import {DomRendererV2, DomRootRenderer, DomRootRenderer_} from './dom/dom_renderer'; import {DOCUMENT} from './dom/dom_tokens'; import {DomEventsPlugin} from './dom/events/dom_events'; import {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager'; @@ -86,6 +86,8 @@ export function _resolveDefaultAnimationDriver(): AnimationDriver { {provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig}, {provide: DomRootRenderer, useClass: DomRootRenderer_}, {provide: RootRenderer, useExisting: DomRootRenderer}, + {provide: RENDERER_V2_DIRECT, useClass: DomRendererV2}, + {provide: RendererV2, useExisting: RENDERER_V2_DIRECT}, {provide: SharedStylesHost, useExisting: DomSharedStylesHost}, {provide: AnimationDriver, useFactory: _resolveDefaultAnimationDriver}, DomSharedStylesHost, diff --git a/modules/@angular/platform-browser/src/browser/browser_adapter.ts b/modules/@angular/platform-browser/src/browser/browser_adapter.ts index 2beae22f63..f16a175a4a 100644 --- a/modules/@angular/platform-browser/src/browser/browser_adapter.ts +++ b/modules/@angular/platform-browser/src/browser/browser_adapter.ts @@ -175,11 +175,11 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter { } return node; } - insertBefore(el: Node, node: Node) { el.parentNode.insertBefore(node, el); } - insertAllBefore(el: Node, nodes: Node[]) { - nodes.forEach((n: any) => el.parentNode.insertBefore(n, el)); + insertBefore(parent: Node, ref: Node, node: Node) { parent.insertBefore(node, ref); } + insertAllBefore(parent: Node, ref: Node, nodes: Node[]) { + nodes.forEach((n: any) => parent.insertBefore(n, ref)); } - insertAfter(el: Node, node: any) { el.parentNode.insertBefore(node, el.nextSibling); } + insertAfter(parent: Node, ref: Node, node: any) { parent.insertBefore(node, ref.nextSibling); } setInnerHTML(el: Element, value: string) { el.innerHTML = value; } getText(el: Node): string { return el.textContent; } setText(el: Node, value: string) { el.textContent = value; } diff --git a/modules/@angular/platform-browser/src/dom/debug/ng_probe.ts b/modules/@angular/platform-browser/src/dom/debug/ng_probe.ts index c5c901f3f4..3e5e92419e 100644 --- a/modules/@angular/platform-browser/src/dom/debug/ng_probe.ts +++ b/modules/@angular/platform-browser/src/dom/debug/ng_probe.ts @@ -9,7 +9,7 @@ import * as core from '@angular/core'; import {StringMapWrapper} from '../../facade/collection'; -import {DebugDomRootRenderer} from '../../private_import_core'; +import {DebugDomRendererV2, DebugDomRootRenderer} from '../../private_import_core'; import {getDOM} from '../dom_adapter'; import {DomRootRenderer} from '../dom_renderer'; @@ -58,14 +58,26 @@ function _ngProbeTokensToMap(tokens: NgProbeToken[]): {[name: string]: any} { return tokens.reduce((prev: any, t: any) => (prev[t.name] = t.token, prev), {}); } +export function _createDebugRendererV2(renderer: core.RendererV2): core.RendererV2 { + return core.isDevMode() ? new DebugDomRendererV2(renderer) : renderer; +} + /** * Providers which support debugging Angular applications (e.g. via `ng.probe`). */ -export const ELEMENT_PROBE_PROVIDERS: core.Provider[] = [{ - provide: core.RootRenderer, - useFactory: _createConditionalRootRenderer, - deps: [ - DomRootRenderer, [NgProbeToken, new core.Optional()], - [core.NgProbeToken, new core.Optional()] - ] -}]; \ No newline at end of file +export const ELEMENT_PROBE_PROVIDERS: core.Provider[] = [ + { + provide: core.RootRenderer, + useFactory: _createConditionalRootRenderer, + deps: [ + DomRootRenderer, + [NgProbeToken, new core.Optional()], + [core.NgProbeToken, new core.Optional()], + ], + }, + { + provide: core.RendererV2, + useFactory: _createDebugRendererV2, + deps: [core.RENDERER_V2_DIRECT], + } +]; \ No newline at end of file diff --git a/modules/@angular/platform-browser/src/dom/dom_adapter.ts b/modules/@angular/platform-browser/src/dom/dom_adapter.ts index 84303f8cb1..136b079b44 100644 --- a/modules/@angular/platform-browser/src/dom/dom_adapter.ts +++ b/modules/@angular/platform-browser/src/dom/dom_adapter.ts @@ -33,15 +33,15 @@ export function setRootDomAdapter(adapter: DomAdapter) { */ export abstract class DomAdapter { public resourceLoaderType: Type = null; - abstract hasProperty(element: any /** TODO #9100 */, name: string): boolean; - abstract setProperty(el: Element, name: string, value: any): any /** TODO #9100 */; + abstract hasProperty(element: any, name: string): boolean; + abstract setProperty(el: Element, name: string, value: any): any; abstract getProperty(el: Element, name: string): any; abstract invoke(el: Element, methodName: string, args: any[]): any; - abstract logError(error: any /** TODO #9100 */): any /** TODO #9100 */; - abstract log(error: any /** TODO #9100 */): any /** TODO #9100 */; - abstract logGroup(error: any /** TODO #9100 */): any /** TODO #9100 */; - abstract logGroupEnd(): any /** TODO #9100 */; + abstract logError(error: any): any; + abstract log(error: any): any; + abstract logGroup(error: any): any; + abstract logGroupEnd(): any; /** * Maps attribute names to their corresponding property names for cases @@ -52,114 +52,93 @@ export abstract class DomAdapter { /** @internal */ _attrToPropMap: {[key: string]: string}; - abstract parse(templateHtml: string): any /** TODO #9100 */; - abstract querySelector(el: any /** TODO #9100 */, selector: string): any; - abstract querySelectorAll(el: any /** TODO #9100 */, selector: string): any[]; - abstract on( - el: any /** TODO #9100 */, evt: any /** TODO #9100 */, listener: any /** TODO #9100 */): any - /** TODO #9100 */; - abstract onAndCancel( - el: any /** TODO #9100 */, evt: any /** TODO #9100 */, - listener: any /** TODO #9100 */): Function; - abstract dispatchEvent(el: any /** TODO #9100 */, evt: any /** TODO #9100 */): any - /** TODO #9100 */; - abstract createMouseEvent(eventType: any /** TODO #9100 */): any; + abstract parse(templateHtml: string): any; + abstract querySelector(el: any, selector: string): any; + abstract querySelectorAll(el: any, selector: string): any[]; + abstract on(el: any, evt: any, listener: any): any; + abstract onAndCancel(el: any, evt: any, listener: any): Function; + abstract dispatchEvent(el: any, evt: any): any; + abstract createMouseEvent(eventType: any): any; abstract createEvent(eventType: string): any; - abstract preventDefault(evt: any /** TODO #9100 */): any /** TODO #9100 */; - abstract isPrevented(evt: any /** TODO #9100 */): boolean; - abstract getInnerHTML(el: any /** TODO #9100 */): string; + abstract preventDefault(evt: any): any; + abstract isPrevented(evt: any): boolean; + abstract getInnerHTML(el: any): string; /** Returns content if el is a