feat: RendererV2 integration (#14469)

This commit is contained in:
Victor Berchet
2017-02-14 21:03:18 -08:00
committed by Igor Minar
parent b4d444a0a7
commit bb9c7ae6e7
38 changed files with 888 additions and 607 deletions

View File

@ -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];
}

View File

@ -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';

View File

@ -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();

View File

@ -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 <any>this._delegate.listenGlobal(target, eventName, callback);
} else {
return <any>this._delegate.listen(target, eventName, callback);
}
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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];