feat(compiler): implement style encapsulation for new view engine (#14518)
Included refactoring: - splits the `RendererV2` into a `RendererFactoryV2` and a `RendererV2` - makes the `DebugRendererV2` a private class in `@angular/core` - remove `setBindingDebugInfo` from `RendererV2`, but rename `RendererV2.setText` to `RendererV2.setValue` and allow it on comments and text nodes. Part of #14013
This commit is contained in:
@ -93,8 +93,6 @@ 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,
|
||||
@ -158,7 +156,6 @@ 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,
|
||||
|
@ -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, RendererV2, RootRenderer} from '../render/api';
|
||||
import {RenderComponentType, RenderDebugInfo, Renderer, RootRenderer} from '../render/api';
|
||||
|
||||
import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from './debug_node';
|
||||
|
||||
@ -156,151 +156,3 @@ export class DebugDomRenderer implements Renderer {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -7,4 +7,4 @@
|
||||
*/
|
||||
|
||||
// Public API for render
|
||||
export {RENDERER_V2_DIRECT, RenderComponentType, Renderer, RendererV2, RootRenderer} from './render/api';
|
||||
export {ComponentRenderTypeV2, RenderComponentType, Renderer, RendererFactoryV2, RendererV2, RootRenderer} from './render/api';
|
||||
|
@ -12,13 +12,6 @@ import {AnimationStyles} from '../../src/animation/animation_styles';
|
||||
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<RendererV2>('Renderer V2');
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
@ -98,56 +91,6 @@ 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.
|
||||
*
|
||||
@ -164,3 +107,64 @@ export abstract class RenderDebugContext {
|
||||
export abstract class RootRenderer {
|
||||
abstract renderComponent(componentType: RenderComponentType): Renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export interface ComponentRenderTypeV2 {
|
||||
id: string;
|
||||
encapsulation: ViewEncapsulation;
|
||||
styles: (string|any[])[];
|
||||
data: {[kind: string]: any[]};
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export abstract class RendererFactoryV2 {
|
||||
abstract createRenderer(hostElement: any, type: ComponentRenderTypeV2): RendererV2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export abstract class RendererV2 {
|
||||
abstract destroy(): void;
|
||||
abstract createElement(name: string, namespace?: string): any;
|
||||
abstract createComment(value: string): any;
|
||||
abstract createText(value: string): any;
|
||||
/**
|
||||
* This property is allowed to be null / undefined,
|
||||
* in which case the view engine won't call it.
|
||||
* This is used as a performance optimization for production mode.
|
||||
*/
|
||||
destroyNode: (node: any) => void | null;
|
||||
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): 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 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 setValue(node: any, value: string): void;
|
||||
abstract listen(
|
||||
target: 'window'|'document'|'body'|any, eventName: string,
|
||||
callback: (event: any) => boolean): () => void;
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ export function elementDef(
|
||||
export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData {
|
||||
const elDef = def.element;
|
||||
const rootSelectorOrNode = view.root.selectorOrNode;
|
||||
const renderer = view.root.renderer;
|
||||
const renderer = view.renderer;
|
||||
let el: any;
|
||||
if (view.parent || !rootSelectorOrNode) {
|
||||
if (elDef.name) {
|
||||
@ -240,7 +240,7 @@ function setElementAttribute(
|
||||
const securityContext = binding.securityContext;
|
||||
let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value;
|
||||
renderValue = renderValue != null ? renderValue.toString() : null;
|
||||
const renderer = view.root.renderer;
|
||||
const renderer = view.renderer;
|
||||
// TODO(vicb): move the namespace to the node definition
|
||||
const nsAndName = splitNamespace(name);
|
||||
if (value != null) {
|
||||
@ -251,7 +251,7 @@ function setElementAttribute(
|
||||
}
|
||||
|
||||
function setElementClass(view: ViewData, renderNode: any, name: string, value: boolean) {
|
||||
const renderer = view.root.renderer;
|
||||
const renderer = view.renderer;
|
||||
if (value) {
|
||||
renderer.addClass(renderNode, name);
|
||||
} else {
|
||||
@ -271,7 +271,7 @@ function setElementStyle(
|
||||
} else {
|
||||
renderValue = null;
|
||||
}
|
||||
const renderer = view.root.renderer;
|
||||
const renderer = view.renderer;
|
||||
if (renderValue != null) {
|
||||
renderer.setStyle(renderNode, name, renderValue, false, false);
|
||||
} else {
|
||||
@ -283,7 +283,7 @@ function setElementProperty(
|
||||
view: ViewData, binding: BindingDef, renderNode: any, name: string, value: any) {
|
||||
const securityContext = binding.securityContext;
|
||||
let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value;
|
||||
view.root.renderer.setProperty(renderNode, name, renderValue);
|
||||
view.renderer.setProperty(renderNode, name, renderValue);
|
||||
}
|
||||
|
||||
const NS_PREFIX_RE = /^:([^:]+):(.+)$/;
|
||||
|
@ -14,7 +14,7 @@ export {queryDef} from './query';
|
||||
export {createComponentFactory} from './refs';
|
||||
export {initServicesIfNeeded} from './services';
|
||||
export {textDef} from './text';
|
||||
export {elementEventFullName, nodeValue, rootRenderNodes, unwrapValue} from './util';
|
||||
export {createComponentRenderTypeV2, elementEventFullName, nodeValue, rootRenderNodes, unwrapValue} from './util';
|
||||
export {viewDef} from './view';
|
||||
export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
|
||||
|
||||
|
@ -11,13 +11,15 @@ import {Injector} from '../di';
|
||||
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 {ViewEncapsulation} from '../metadata/view';
|
||||
import {ComponentRenderTypeV2, RenderComponentType as RenderComponentTypeV1, Renderer as RendererV1, RendererFactoryV2, RendererV2, RootRenderer as RootRendererV1} from '../render/api';
|
||||
|
||||
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';
|
||||
import {checkAndUpdateBinding, dispatchEvent, filterQueryId, isComponentView, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';
|
||||
|
||||
const RendererV1TokenKey = tokenKey(v1renderer.Renderer);
|
||||
const RendererV1TokenKey = tokenKey(RendererV1);
|
||||
const RendererV2TokenKey = tokenKey(RendererV2);
|
||||
const ElementRefTokenKey = tokenKey(ElementRef);
|
||||
const ViewContainerRefTokenKey = tokenKey(ViewContainerRef);
|
||||
const TemplateRefTokenKey = tokenKey(TemplateRef);
|
||||
@ -29,7 +31,8 @@ const NOT_CREATED = new Object();
|
||||
export function directiveDef(
|
||||
flags: NodeFlags, matchedQueries: [string | number, QueryValueType][], childCount: number,
|
||||
ctor: any, deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
|
||||
outputs?: {[name: string]: string}, component?: () => ViewDefinition): NodeDef {
|
||||
outputs?: {[name: string]: string}, component?: () => ViewDefinition,
|
||||
componentRenderType?: ComponentRenderTypeV2): NodeDef {
|
||||
const bindings: BindingDef[] = [];
|
||||
if (props) {
|
||||
for (let prop in props) {
|
||||
@ -50,7 +53,7 @@ export function directiveDef(
|
||||
}
|
||||
return _def(
|
||||
NodeType.Directive, flags, matchedQueries, childCount, ProviderType.Class, ctor, ctor, deps,
|
||||
bindings, outputDefs, component);
|
||||
bindings, outputDefs, component, componentRenderType);
|
||||
}
|
||||
|
||||
export function pipeDef(flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[]): NodeDef {
|
||||
@ -67,8 +70,13 @@ export function _def(
|
||||
type: NodeType, flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
|
||||
childCount: number, providerType: ProviderType, token: any, value: any,
|
||||
deps: ([DepFlags, any] | any)[], bindings?: BindingDef[], outputs?: DirectiveOutputDef[],
|
||||
component?: () => ViewDefinition): NodeDef {
|
||||
component?: () => ViewDefinition, componentRenderType?: ComponentRenderTypeV2): NodeDef {
|
||||
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
|
||||
// This is needed as the jit compiler always uses an empty hash as default ComponentRenderTypeV2,
|
||||
// which is not filled for host views.
|
||||
if (componentRenderType && componentRenderType.encapsulation == null) {
|
||||
componentRenderType = null;
|
||||
}
|
||||
if (!outputs) {
|
||||
outputs = [];
|
||||
}
|
||||
@ -111,7 +119,7 @@ export function _def(
|
||||
type: providerType,
|
||||
token,
|
||||
tokenKey: tokenKey(token), value,
|
||||
deps: depDefs, outputs, component
|
||||
deps: depDefs, outputs, component, componentRenderType
|
||||
},
|
||||
text: undefined,
|
||||
pureExpression: undefined,
|
||||
@ -328,16 +336,19 @@ export function resolveDep(
|
||||
if (elDef) {
|
||||
switch (tokenKey) {
|
||||
case RendererV1TokenKey: {
|
||||
let compView = view;
|
||||
while (compView && !isComponentView(compView)) {
|
||||
compView = compView.parent;
|
||||
}
|
||||
const rootRenderer: v1renderer.RootRenderer =
|
||||
view.root.injector.get(v1renderer.RootRenderer);
|
||||
const compView = findCompView(view, elDef, allowPrivateServices);
|
||||
const compDef = compView.parentNodeDef;
|
||||
const rootRendererV1: RootRendererV1 = view.root.injector.get(RootRendererV1);
|
||||
|
||||
// Note: Don't fill in the styles as they have been installed already!
|
||||
return rootRenderer.renderComponent(new v1renderer.RenderComponentType(
|
||||
view.def.component.id, '', 0, view.def.component.encapsulation, [], {}));
|
||||
// Note: Don't fill in the styles as they have been installed already via the RendererV2!
|
||||
const compRenderType = compDef.provider.componentRenderType;
|
||||
return rootRendererV1.renderComponent(new RenderComponentTypeV1(
|
||||
compRenderType ? compRenderType.id : '0', '', 0,
|
||||
compRenderType ? compRenderType.encapsulation : ViewEncapsulation.None, [], {}));
|
||||
}
|
||||
case RendererV2TokenKey: {
|
||||
const compView = findCompView(view, elDef, allowPrivateServices);
|
||||
return compView.renderer;
|
||||
}
|
||||
case ElementRefTokenKey:
|
||||
return new ElementRef(asElementData(view, elDef.index).renderElement);
|
||||
@ -350,15 +361,7 @@ export function resolveDep(
|
||||
break;
|
||||
}
|
||||
case ChangeDetectorRefTokenKey: {
|
||||
let cdView: ViewData;
|
||||
if (allowPrivateServices) {
|
||||
cdView = asProviderData(view, elDef.element.component.index).componentView;
|
||||
} else {
|
||||
cdView = view;
|
||||
while (cdView.parent && !isComponentView(cdView)) {
|
||||
cdView = cdView.parent;
|
||||
}
|
||||
}
|
||||
let cdView = findCompView(view, elDef, allowPrivateServices);
|
||||
return createChangeDetectorRef(cdView);
|
||||
}
|
||||
case InjectorRefTokenKey:
|
||||
@ -383,6 +386,19 @@ export function resolveDep(
|
||||
return startView.root.injector.get(depDef.token, notFoundValue);
|
||||
}
|
||||
|
||||
function findCompView(view: ViewData, elDef: NodeDef, allowPrivateServices: boolean) {
|
||||
let compView: ViewData;
|
||||
if (allowPrivateServices) {
|
||||
compView = asProviderData(view, elDef.element.component.index).componentView;
|
||||
} else {
|
||||
compView = view;
|
||||
while (compView.parent && !isComponentView(compView)) {
|
||||
compView = compView.parent;
|
||||
}
|
||||
}
|
||||
return compView;
|
||||
}
|
||||
|
||||
function checkAndUpdateProp(
|
||||
view: ViewData, providerData: ProviderData, def: NodeDef, bindingIdx: number, value: any,
|
||||
changes: SimpleChanges): SimpleChanges {
|
||||
|
@ -201,8 +201,9 @@ export function createInjector(view: ViewData, elDef: NodeDef): Injector {
|
||||
class Injector_ implements Injector {
|
||||
constructor(private view: ViewData, private elDef: NodeDef) {}
|
||||
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
||||
const allowPrivateServices = !!this.elDef.element.component;
|
||||
return Services.resolveDep(
|
||||
this.view, this.elDef, true, {flags: DepFlags.None, token, tokenKey: tokenKey(token)},
|
||||
notFoundValue);
|
||||
this.view, this.elDef, allowPrivateServices,
|
||||
{flags: DepFlags.None, token, tokenKey: tokenKey(token)}, notFoundValue);
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,9 @@
|
||||
*/
|
||||
|
||||
import {isDevMode} from '../application_ref';
|
||||
import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node';
|
||||
import {Injector} from '../di';
|
||||
import {RendererV2} from '../render/api';
|
||||
import {ComponentRenderTypeV2, RendererFactoryV2, RendererV2} from '../render/api';
|
||||
import {Sanitizer, SecurityContext} from '../security';
|
||||
|
||||
import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
|
||||
@ -87,52 +88,59 @@ function createDebugServices() {
|
||||
function createProdRootView(
|
||||
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any,
|
||||
def: ViewDefinition, context?: any): ViewData {
|
||||
const rendererFactory: RendererFactoryV2 = injector.get(RendererFactoryV2);
|
||||
return createRootView(
|
||||
createRootData(injector, projectableNodes, rootSelectorOrNode), def, context);
|
||||
createRootData(injector, rendererFactory, projectableNodes, rootSelectorOrNode), def,
|
||||
context);
|
||||
}
|
||||
|
||||
function debugCreateRootView(
|
||||
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any,
|
||||
def: ViewDefinition, context?: any): ViewData {
|
||||
const root = createRootData(injector, projectableNodes, rootSelectorOrNode);
|
||||
const debugRoot: RootData = {
|
||||
injector: root.injector,
|
||||
projectableNodes: root.projectableNodes,
|
||||
selectorOrNode: root.selectorOrNode,
|
||||
renderer: new DebugRenderer(root.renderer),
|
||||
sanitizer: root.sanitizer
|
||||
};
|
||||
return callWithDebugContext('create', createRootView, null, [debugRoot, def, context]);
|
||||
const rendererFactory: RendererFactoryV2 = injector.get(RendererFactoryV2);
|
||||
const root = createRootData(
|
||||
injector, new DebugRendererFactoryV2(rendererFactory), projectableNodes, rootSelectorOrNode);
|
||||
return callWithDebugContext(DebugAction.create, createRootView, null, [root, def, context]);
|
||||
}
|
||||
|
||||
function createRootData(
|
||||
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: any): RootData {
|
||||
injector: Injector, rendererFactory: RendererFactoryV2, projectableNodes: any[][],
|
||||
rootSelectorOrNode: any): RootData {
|
||||
const sanitizer = injector.get(Sanitizer);
|
||||
const renderer = injector.get(RendererV2);
|
||||
|
||||
const rootElement =
|
||||
rootSelectorOrNode ? renderer.selectRootElement(rootSelectorOrNode) : undefined;
|
||||
return {injector, projectableNodes, selectorOrNode: rootSelectorOrNode, sanitizer, renderer};
|
||||
const renderer = rendererFactory.createRenderer(null, null);
|
||||
return {
|
||||
injector,
|
||||
projectableNodes,
|
||||
selectorOrNode: rootSelectorOrNode, sanitizer, rendererFactory, renderer
|
||||
};
|
||||
}
|
||||
|
||||
function debugCreateEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
||||
return callWithDebugContext('create', createEmbeddedView, null, [parent, anchorDef, context]);
|
||||
return callWithDebugContext(
|
||||
DebugAction.create, createEmbeddedView, null, [parent, anchorDef, context]);
|
||||
}
|
||||
|
||||
function debugCheckAndUpdateView(view: ViewData) {
|
||||
return callWithDebugContext('detectChanges', checkAndUpdateView, null, [view]);
|
||||
return callWithDebugContext(DebugAction.detectChanges, checkAndUpdateView, null, [view]);
|
||||
}
|
||||
|
||||
function debugCheckNoChangesView(view: ViewData) {
|
||||
return callWithDebugContext('checkNoChanges', checkNoChangesView, null, [view]);
|
||||
return callWithDebugContext(DebugAction.checkNoChanges, checkNoChangesView, null, [view]);
|
||||
}
|
||||
|
||||
function debugDestroyView(view: ViewData) {
|
||||
return callWithDebugContext('destroyView', destroyView, null, [view]);
|
||||
return callWithDebugContext(DebugAction.destroy, destroyView, null, [view]);
|
||||
}
|
||||
|
||||
enum DebugAction {
|
||||
create,
|
||||
detectChanges,
|
||||
checkNoChanges,
|
||||
destroy,
|
||||
handleEvent
|
||||
}
|
||||
|
||||
let _currentAction: string;
|
||||
let _currentAction: DebugAction;
|
||||
let _currentView: ViewData;
|
||||
let _currentNodeIndex: number;
|
||||
|
||||
@ -143,16 +151,16 @@ function debugSetCurrentNode(view: ViewData, nodeIndex: number) {
|
||||
|
||||
function debugHandleEvent(view: ViewData, nodeIndex: number, eventName: string, event: any) {
|
||||
if (view.state & ViewState.Destroyed) {
|
||||
throw viewDestroyedError(_currentAction);
|
||||
throw viewDestroyedError(DebugAction[_currentAction]);
|
||||
}
|
||||
debugSetCurrentNode(view, nodeIndex);
|
||||
return callWithDebugContext(
|
||||
'handleEvent', view.def.handleEvent, null, [view, nodeIndex, eventName, event]);
|
||||
DebugAction.handleEvent, view.def.handleEvent, null, [view, nodeIndex, eventName, event]);
|
||||
}
|
||||
|
||||
function debugUpdateDirectives(check: NodeCheckFn, view: ViewData) {
|
||||
if (view.state & ViewState.Destroyed) {
|
||||
throw viewDestroyedError(_currentAction);
|
||||
throw viewDestroyedError(DebugAction[_currentAction]);
|
||||
}
|
||||
debugSetCurrentNode(view, nextDirectiveWithBinding(view, 0));
|
||||
return view.def.updateDirectives(debugCheckDirectivesFn, view);
|
||||
@ -167,7 +175,7 @@ function debugUpdateDirectives(check: NodeCheckFn, view: ViewData) {
|
||||
|
||||
function debugUpdateRenderer(check: NodeCheckFn, view: ViewData) {
|
||||
if (view.state & ViewState.Destroyed) {
|
||||
throw viewDestroyedError(_currentAction);
|
||||
throw viewDestroyedError(DebugAction[_currentAction]);
|
||||
}
|
||||
debugSetCurrentNode(view, nextRenderNodeWithBinding(view, 0));
|
||||
return view.def.updateRenderer(debugCheckRenderNodeFn, view);
|
||||
@ -183,35 +191,41 @@ function debugUpdateRenderer(check: NodeCheckFn, view: ViewData) {
|
||||
function debugCheckFn(
|
||||
delegate: NodeCheckFn, view: ViewData, nodeIndex: number, argStyle: ArgumentType,
|
||||
givenValues: any[]) {
|
||||
const values = argStyle === ArgumentType.Dynamic ? givenValues[0] : givenValues;
|
||||
const nodeDef = view.def.nodes[nodeIndex];
|
||||
for (let i = 0; i < nodeDef.bindings.length; i++) {
|
||||
const binding = nodeDef.bindings[i];
|
||||
const value = values[i];
|
||||
if ((binding.type === BindingType.ElementProperty ||
|
||||
binding.type === BindingType.DirectiveProperty) &&
|
||||
checkBinding(view, nodeDef, i, value)) {
|
||||
if (_currentAction === DebugAction.detectChanges) {
|
||||
const values = argStyle === ArgumentType.Dynamic ? givenValues[0] : givenValues;
|
||||
const nodeDef = view.def.nodes[nodeIndex];
|
||||
if (nodeDef.type === NodeType.Directive || nodeDef.type === NodeType.Element) {
|
||||
const bindingValues: {[key: string]: string} = {};
|
||||
for (let i = 0; i < nodeDef.bindings.length; i++) {
|
||||
const binding = nodeDef.bindings[i];
|
||||
const value = values[i];
|
||||
if ((binding.type === BindingType.ElementProperty ||
|
||||
binding.type === BindingType.DirectiveProperty) &&
|
||||
checkBinding(view, nodeDef, i, value)) {
|
||||
bindingValues[normalizeDebugBindingName(binding.nonMinifiedName)] =
|
||||
normalizeDebugBindingValue(value);
|
||||
}
|
||||
}
|
||||
const elDef = nodeDef.type === NodeType.Directive ? nodeDef.parent : nodeDef;
|
||||
setBindingDebugInfo(
|
||||
view.root.renderer, asElementData(view, elDef.index).renderElement,
|
||||
binding.nonMinifiedName, value);
|
||||
const el = asElementData(view, elDef.index).renderElement;
|
||||
if (!elDef.element.name) {
|
||||
// a comment.
|
||||
view.renderer.setValue(el, `bindings=${JSON.stringify(bindingValues, null, 2)}`);
|
||||
} else {
|
||||
// a regular element.
|
||||
for (let attr in bindingValues) {
|
||||
view.renderer.setAttribute(el, attr, bindingValues[attr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (<any>delegate)(view, nodeIndex, argStyle, ...givenValues);
|
||||
};
|
||||
|
||||
function setBindingDebugInfo(renderer: RendererV2, renderNode: any, propName: string, value: any) {
|
||||
const renderName = `ng-reflect-${camelCaseToDashCase(propName)}`;
|
||||
if (value) {
|
||||
try {
|
||||
renderer.setBindingDebugInfo(renderNode, renderName, value.toString());
|
||||
} catch (e) {
|
||||
renderer.setBindingDebugInfo(
|
||||
renderNode, renderName, '[ERROR] Exception while trying to serialize the value');
|
||||
}
|
||||
} else {
|
||||
renderer.removeBindingDebugInfo(renderNode, renderName);
|
||||
}
|
||||
function normalizeDebugBindingName(name: string) {
|
||||
// Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
|
||||
name = camelCaseToDashCase(name.replace(/\$/g, '_'));
|
||||
return `ng-reflect-${name}`;
|
||||
}
|
||||
|
||||
const CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||
@ -220,6 +234,15 @@ function camelCaseToDashCase(input: string): string {
|
||||
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
|
||||
}
|
||||
|
||||
function normalizeDebugBindingValue(value: any): string {
|
||||
try {
|
||||
// Limit the size of the value as otherwise the DOM just gets polluted.
|
||||
return value ? value.toString().slice(0, 20) : value;
|
||||
} catch (e) {
|
||||
return '[ERROR] Exception while trying to serialize the value';
|
||||
}
|
||||
}
|
||||
|
||||
function nextDirectiveWithBinding(view: ViewData, nodeIndex: number): number {
|
||||
for (let i = nodeIndex; i < view.def.nodes.length; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
@ -241,64 +264,6 @@ function nextRenderNodeWithBinding(view: ViewData, nodeIndex: number): number {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
class DebugRenderer implements RendererV2 {
|
||||
constructor(private _delegate: RendererV2) {}
|
||||
|
||||
createElement(name: string, namespace?: string): any {
|
||||
return this._delegate.createElement(name, namespace, getCurrentDebugContext());
|
||||
}
|
||||
createComment(value: string): any {
|
||||
return this._delegate.createComment(value, getCurrentDebugContext());
|
||||
}
|
||||
createText(value: string): any {
|
||||
return this._delegate.createText(value, getCurrentDebugContext());
|
||||
}
|
||||
appendChild(parent: any, newChild: any): void {
|
||||
return this._delegate.appendChild(parent, newChild);
|
||||
}
|
||||
insertBefore(parent: any, newChild: any, refChild: any): void {
|
||||
return this._delegate.insertBefore(parent, newChild, refChild);
|
||||
}
|
||||
removeChild(parent: any, oldChild: any): void {
|
||||
return this._delegate.removeChild(parent, oldChild);
|
||||
}
|
||||
selectRootElement(selectorOrNode: string|any): any {
|
||||
return this._delegate.selectRootElement(selectorOrNode, getCurrentDebugContext());
|
||||
}
|
||||
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 {
|
||||
return this._delegate.setAttribute(el, name, value, namespace);
|
||||
}
|
||||
removeAttribute(el: any, name: string, namespace?: string): void {
|
||||
return 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 { 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, 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);
|
||||
}
|
||||
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'|'body'|any, eventName: string,
|
||||
callback: (event: any) => boolean): () => void {
|
||||
return this._delegate.listen(target, eventName, callback);
|
||||
}
|
||||
}
|
||||
|
||||
class DebugContext_ implements DebugContext {
|
||||
private nodeDef: NodeDef;
|
||||
private elView: ViewData;
|
||||
@ -401,7 +366,7 @@ function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key:
|
||||
}
|
||||
}
|
||||
|
||||
function callWithDebugContext(action: string, fn: any, self: any, args: any[]) {
|
||||
function callWithDebugContext(action: DebugAction, fn: any, self: any, args: any[]) {
|
||||
const oldAction = _currentAction;
|
||||
const oldView = _currentView;
|
||||
const oldNodeIndex = _currentNodeIndex;
|
||||
@ -421,6 +386,163 @@ function callWithDebugContext(action: string, fn: any, self: any, args: any[]) {
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentDebugContext() {
|
||||
export function getCurrentDebugContext(): DebugContext {
|
||||
return new DebugContext_(_currentView, _currentNodeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DebugRendererFactoryV2 implements RendererFactoryV2 {
|
||||
constructor(private delegate: RendererFactoryV2) {}
|
||||
|
||||
createRenderer(element: any, renderData: ComponentRenderTypeV2): RendererV2 {
|
||||
return new DebugRendererV2(this.delegate.createRenderer(element, renderData));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DebugRendererV2 implements RendererV2 {
|
||||
constructor(private delegate: RendererV2) {}
|
||||
|
||||
destroyNode(node: any) {
|
||||
removeDebugNodeFromIndex(getDebugNode(node));
|
||||
if (this.delegate.destroyNode) {
|
||||
this.delegate.destroyNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
destroy() { this.delegate.destroy(); }
|
||||
|
||||
createElement(name: string, namespace?: string): any {
|
||||
const el = this.delegate.createElement(name, namespace);
|
||||
const debugEl = new DebugElement(el, null, getCurrentDebugContext());
|
||||
debugEl.name = name;
|
||||
indexDebugNode(debugEl);
|
||||
return el;
|
||||
}
|
||||
|
||||
createComment(value: string): any {
|
||||
const comment = this.delegate.createComment(value);
|
||||
const debugEl = new DebugNode(comment, null, getCurrentDebugContext());
|
||||
indexDebugNode(debugEl);
|
||||
return comment;
|
||||
}
|
||||
|
||||
createText(value: string): any {
|
||||
const text = this.delegate.createText(value);
|
||||
const debugEl = new DebugNode(text, null, getCurrentDebugContext());
|
||||
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): any {
|
||||
const el = this.delegate.selectRootElement(selectorOrNode);
|
||||
const debugEl = new DebugElement(el, null, getCurrentDebugContext());
|
||||
indexDebugNode(debugEl);
|
||||
return el;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
parentNode(node: any): any { return this.delegate.parentNode(node); }
|
||||
nextSibling(node: any): any { return this.delegate.nextSibling(node); }
|
||||
setValue(node: any, value: string): void { return this.delegate.setValue(node, value); }
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
|
||||
|
||||
export function createText(view: ViewData, renderHost: any, def: NodeDef): TextData {
|
||||
let renderNode: any;
|
||||
const renderer = view.root.renderer;
|
||||
const renderer = view.renderer;
|
||||
renderNode = renderer.createText(def.text.prefix);
|
||||
const parentEl = getParentRenderElement(view, renderHost, def);
|
||||
if (parentEl) {
|
||||
@ -119,7 +119,7 @@ export function checkAndUpdateTextInline(
|
||||
}
|
||||
value = def.text.prefix + value;
|
||||
const renderNode = asTextData(view, def.index).renderText;
|
||||
view.root.renderer.setText(renderNode, value);
|
||||
view.renderer.setValue(renderNode, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values:
|
||||
}
|
||||
value = def.text.prefix + value;
|
||||
const renderNode = asTextData(view, def.index).renderText;
|
||||
view.root.renderer.setText(renderNode, value);
|
||||
view.renderer.setValue(renderNode, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +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 {ComponentRenderTypeV2, RendererFactoryV2, RendererV2} from '../render/api';
|
||||
import {Sanitizer, SecurityContext} from '../security';
|
||||
|
||||
// -------------------------------------
|
||||
@ -23,7 +23,6 @@ import {Sanitizer, SecurityContext} from '../security';
|
||||
|
||||
export interface ViewDefinition {
|
||||
flags: ViewFlags;
|
||||
component: ComponentDefinition;
|
||||
updateDirectives: ViewUpdateFn;
|
||||
updateRenderer: ViewUpdateFn;
|
||||
handleEvent: ViewHandleEventFn;
|
||||
@ -75,13 +74,7 @@ export enum ArgumentType {
|
||||
*/
|
||||
export enum ViewFlags {
|
||||
None = 0,
|
||||
OnPush = 1 << 1
|
||||
}
|
||||
|
||||
export interface ComponentDefinition {
|
||||
id: string;
|
||||
encapsulation: ViewEncapsulation;
|
||||
styles: string[];
|
||||
OnPush = 1 << 1,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -223,6 +216,7 @@ export interface ProviderDef {
|
||||
value: any;
|
||||
deps: DepDef[];
|
||||
outputs: DirectiveOutputDef[];
|
||||
componentRenderType: ComponentRenderTypeV2;
|
||||
// closure to allow recursive components
|
||||
component: ViewDefinitionFactory;
|
||||
}
|
||||
@ -306,6 +300,7 @@ export interface NgContentDef {
|
||||
export interface ViewData {
|
||||
def: ViewDefinition;
|
||||
root: RootData;
|
||||
renderer: RendererV2;
|
||||
// index of component provider / anchor.
|
||||
parentNodeDef: NodeDef;
|
||||
parent: ViewData;
|
||||
@ -426,12 +421,21 @@ export interface RootData {
|
||||
projectableNodes: any[][];
|
||||
selectorOrNode: any;
|
||||
renderer: RendererV2;
|
||||
rendererFactory: RendererFactoryV2;
|
||||
sanitizer: Sanitizer;
|
||||
}
|
||||
|
||||
export abstract class DebugContext extends RenderDebugContext {
|
||||
export abstract class DebugContext {
|
||||
abstract get view(): ViewData;
|
||||
abstract get nodeIndex(): number;
|
||||
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;
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
|
@ -14,7 +14,8 @@ import {looseIdentical, stringify} from '../facade/lang';
|
||||
import {TemplateRef} from '../linker/template_ref';
|
||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||
import {ViewRef} from '../linker/view_ref';
|
||||
import {Renderer} from '../render/api';
|
||||
import {ViewEncapsulation} from '../metadata/view';
|
||||
import {ComponentRenderTypeV2, Renderer} from '../render/api';
|
||||
|
||||
import {expressionChangedAfterItHasBeenCheckedError, isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
|
||||
import {DebugContext, ElementData, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState, asElementData, asProviderData, asTextData} from './types';
|
||||
@ -40,6 +41,23 @@ export function unwrapValue(value: any): any {
|
||||
return value;
|
||||
}
|
||||
|
||||
let _renderCompCount = 0;
|
||||
|
||||
export function createComponentRenderTypeV2(values: {
|
||||
styles: (string | any[])[],
|
||||
encapsulation: ViewEncapsulation,
|
||||
data: {[kind: string]: any[]}
|
||||
}): ComponentRenderTypeV2 {
|
||||
const isFilled = values && (values.encapsulation !== ViewEncapsulation.None ||
|
||||
values.styles.length || Object.keys(values.data).length);
|
||||
if (isFilled) {
|
||||
const id = `c${_renderCompCount++}`;
|
||||
return {id: id, styles: values.styles, encapsulation: values.encapsulation, data: values.data};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function checkBinding(
|
||||
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
|
||||
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
|
||||
@ -220,7 +238,7 @@ 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));
|
||||
parentNode = view.renderer.parentNode(renderNode(view, view.def.lastRootNode));
|
||||
}
|
||||
visitSiblingRenderNodes(
|
||||
view, action, 0, view.def.nodes.length - 1, parentNode, nextSibling, target);
|
||||
@ -298,7 +316,7 @@ function visitRenderNode(
|
||||
function execRenderNodeAction(
|
||||
view: ViewData, renderNode: any, action: RenderNodeAction, parentNode: any, nextSibling: any,
|
||||
target: any[]) {
|
||||
const renderer = view.root.renderer;
|
||||
const renderer = view.renderer;
|
||||
switch (action) {
|
||||
case RenderNodeAction.AppendChild:
|
||||
renderer.appendChild(parentNode, renderNode);
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import {ViewEncapsulation} from '../metadata/view';
|
||||
import {ComponentRenderTypeV2, RendererV2} from '../render/api';
|
||||
|
||||
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
|
||||
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
||||
@ -15,15 +16,14 @@ import {callLifecycleHooksChildrenFirst, checkAndUpdateDirectiveDynamic, checkAn
|
||||
import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
|
||||
import {checkAndUpdateQuery, createQuery, queryDef} from './query';
|
||||
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
||||
import {ArgumentType, ComponentDefinition, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList} from './types';
|
||||
import {ArgumentType, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList, asTextData} from './types';
|
||||
import {checkBindingNoChanges, isComponentView, resolveViewDefinition, viewParentEl} from './util';
|
||||
|
||||
const NOOP = (): any => undefined;
|
||||
|
||||
export function viewDef(
|
||||
flags: ViewFlags, nodes: NodeDef[], updateDirectives?: ViewUpdateFn,
|
||||
updateRenderer?: ViewUpdateFn, handleEvent?: ViewHandleEventFn, compId?: string,
|
||||
encapsulation?: ViewEncapsulation, styles?: string[]): ViewDefinition {
|
||||
updateRenderer?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
// clone nodes and set auto calculated values
|
||||
if (nodes.length === 0) {
|
||||
throw new Error(`Illegal State: Views without nodes are not allowed!`);
|
||||
@ -57,8 +57,11 @@ export function viewDef(
|
||||
|
||||
let currentRenderParent: NodeDef;
|
||||
if (currentParent &&
|
||||
!(currentParent.type === NodeType.Element && currentParent.element.component)) {
|
||||
// children of components should never be attached!
|
||||
(currentParent.type !== NodeType.Element || !currentParent.element.component ||
|
||||
(currentParent.element.component.provider.componentRenderType &&
|
||||
currentParent.element.component.provider.componentRenderType.encapsulation ===
|
||||
ViewEncapsulation.Native))) {
|
||||
// children of components that don't use native encapsulation should never be attached!
|
||||
if (currentParent && currentParent.type === NodeType.Element && !currentParent.element.name) {
|
||||
currentRenderParent = currentParent.renderParent;
|
||||
} else {
|
||||
@ -134,8 +137,6 @@ export function viewDef(
|
||||
}
|
||||
currentParent = newParent;
|
||||
}
|
||||
const componentDef =
|
||||
compId ? <ComponentDefinition>{id: compId, encapsulation, styles} : undefined;
|
||||
return {
|
||||
nodeFlags: viewNodeFlags,
|
||||
nodeMatchedQueries: viewMatchedQueries, flags,
|
||||
@ -143,7 +144,6 @@ export function viewDef(
|
||||
updateDirectives: updateDirectives || NOOP,
|
||||
updateRenderer: updateRenderer || NOOP,
|
||||
handleEvent: handleEvent || NOOP,
|
||||
component: componentDef,
|
||||
bindingCount: viewBindingCount,
|
||||
disposableCount: viewDisposableCount, lastRootNode
|
||||
};
|
||||
@ -222,21 +222,23 @@ function validateNode(parent: NodeDef, node: NodeDef, nodeCount: number) {
|
||||
export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
||||
// embedded views are seen as siblings to the anchor, so we need
|
||||
// to get the parent of the anchor and use it as parentIndex.
|
||||
const view = createView(parent.root, parent, anchorDef, anchorDef.element.template);
|
||||
const view =
|
||||
createView(parent.root, parent.renderer, parent, anchorDef, anchorDef.element.template);
|
||||
initView(view, parent.component, context);
|
||||
createViewNodes(view);
|
||||
return view;
|
||||
}
|
||||
|
||||
export function createRootView(root: RootData, def: ViewDefinition, context?: any): ViewData {
|
||||
const view = createView(root, null, null, def);
|
||||
const view = createView(root, root.renderer, null, null, def);
|
||||
initView(view, context, context);
|
||||
createViewNodes(view);
|
||||
return view;
|
||||
}
|
||||
|
||||
function createView(
|
||||
root: RootData, parent: ViewData, parentNodeDef: NodeDef, def: ViewDefinition): ViewData {
|
||||
root: RootData, renderer: RendererV2, parent: ViewData, parentNodeDef: NodeDef,
|
||||
def: ViewDefinition): ViewData {
|
||||
const nodes: NodeData[] = new Array(def.nodes.length);
|
||||
const disposables = def.disposableCount ? new Array(def.disposableCount) : undefined;
|
||||
const view: ViewData = {
|
||||
@ -245,7 +247,7 @@ function createView(
|
||||
parentNodeDef,
|
||||
context: undefined,
|
||||
component: undefined, nodes,
|
||||
state: ViewState.FirstCheck | ViewState.ChecksEnabled, root,
|
||||
state: ViewState.FirstCheck | ViewState.ChecksEnabled, root, renderer,
|
||||
oldValues: new Array(def.bindingCount), disposables
|
||||
};
|
||||
return view;
|
||||
@ -259,9 +261,9 @@ function initView(view: ViewData, component: any, context: any) {
|
||||
function createViewNodes(view: ViewData) {
|
||||
let renderHost: any;
|
||||
if (isComponentView(view)) {
|
||||
renderHost = asElementData(view.parent, viewParentEl(view).index).renderElement;
|
||||
const hostDef = view.parentNodeDef;
|
||||
renderHost = asElementData(view.parent, hostDef.parent.index).renderElement;
|
||||
}
|
||||
|
||||
const def = view.def;
|
||||
const nodes = view.nodes;
|
||||
for (let i = 0; i < def.nodes.length; i++) {
|
||||
@ -291,8 +293,16 @@ function createViewNodes(view: ViewData) {
|
||||
// Components can inject a ChangeDetectorRef that needs a references to
|
||||
// the component view. Therefore, we create the component view first
|
||||
// and set the ProviderData in ViewData, and then instantiate the provider.
|
||||
const componentView = createView(
|
||||
view.root, view, nodeDef, resolveViewDefinition(nodeDef.provider.component));
|
||||
const compViewDef = resolveViewDefinition(nodeDef.provider.component);
|
||||
const compRenderType = nodeDef.provider.componentRenderType;
|
||||
let compRenderer: RendererV2;
|
||||
if (!compRenderType) {
|
||||
compRenderer = view.root.renderer;
|
||||
} else {
|
||||
const hostEl = asElementData(view, nodeDef.parent.index).renderElement;
|
||||
compRenderer = view.root.rendererFactory.createRenderer(hostEl, compRenderType);
|
||||
}
|
||||
const componentView = createView(view.root, compRenderer, view, nodeDef, compViewDef);
|
||||
const providerData = <ProviderData>{componentView, instance: undefined};
|
||||
nodes[i] = providerData as any;
|
||||
const instance = providerData.instance = createDirectiveInstance(view, nodeDef);
|
||||
@ -473,9 +483,27 @@ export function destroyView(view: ViewData) {
|
||||
view.disposables[i]();
|
||||
}
|
||||
}
|
||||
if (view.renderer.destroyNode) {
|
||||
destroyViewNodes(view);
|
||||
}
|
||||
if (view.parentNodeDef && view.parentNodeDef.flags & NodeFlags.HasComponent) {
|
||||
view.renderer.destroy();
|
||||
}
|
||||
view.state |= ViewState.Destroyed;
|
||||
}
|
||||
|
||||
function destroyViewNodes(view: ViewData) {
|
||||
const len = view.def.nodes.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const def = view.def.nodes[i];
|
||||
if (def.type === NodeType.Element) {
|
||||
view.renderer.destroyNode(asElementData(view, i).renderElement);
|
||||
} else if (def.type === NodeType.Text) {
|
||||
view.renderer.destroyNode(asTextData(view, i).renderText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ViewAction {
|
||||
CreateViewNodes,
|
||||
CheckNoChanges,
|
||||
|
@ -77,15 +77,15 @@ export function moveEmbeddedView(
|
||||
function renderAttachEmbeddedView(elementData: ElementData, prevView: ViewData, view: ViewData) {
|
||||
const prevRenderNode =
|
||||
prevView ? renderNode(prevView, prevView.def.lastRootNode) : elementData.renderElement;
|
||||
const parentNode = view.root.renderer.parentNode(prevRenderNode);
|
||||
const nextSibling = view.root.renderer.nextSibling(prevRenderNode);
|
||||
const parentNode = view.renderer.parentNode(prevRenderNode);
|
||||
const nextSibling = view.renderer.nextSibling(prevRenderNode);
|
||||
// Note: We can't check if `nextSibling` is present, as on WebWorkers it will always be!
|
||||
// However, browsers automatically do `appendChild` when there is no `nextSibling`.
|
||||
visitRootRenderNodes(view, RenderNodeAction.InsertBefore, parentNode, nextSibling, undefined);
|
||||
}
|
||||
|
||||
function renderDetachEmbeddedView(elementData: ElementData, view: ViewData) {
|
||||
const parentNode = view.root.renderer.parentNode(elementData.renderElement);
|
||||
const parentNode = view.renderer.parentNode(elementData.renderElement);
|
||||
visitRootRenderNodes(view, RenderNodeAction.RemoveChild, parentNode, null, undefined);
|
||||
}
|
||||
|
||||
|
@ -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, 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 {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RenderComponentType, Renderer, RendererFactoryV2, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
|
||||
import {DebugDomRenderer} 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';
|
||||
@ -57,6 +57,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
renderLog = TestBed.get(RenderLog);
|
||||
directiveLog = TestBed.get(DirectiveLog);
|
||||
elSchema.existingProperties['someProp'] = true;
|
||||
patchLoggingRendererV2(TestBed.get(RendererFactoryV2), renderLog);
|
||||
}
|
||||
|
||||
function queryDirs(el: DebugElement, dirType: Type<any>): any {
|
||||
@ -123,11 +124,6 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
RenderLog,
|
||||
DirectiveLog,
|
||||
{provide: RootRenderer, useClass: LoggingRootRenderer},
|
||||
{
|
||||
provide: RendererV2,
|
||||
useFactory: (r: RendererV2, log: RenderLog) => new LoggingRendererV2(r, log),
|
||||
deps: [[new Inject(RENDERER_V2_DIRECT)], [RenderLog]],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
@ -1244,28 +1240,26 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
expect(renderLog.loggedValues).toEqual(['Tom']);
|
||||
});
|
||||
|
||||
// TODO(tbosch): ViewQueries don't work yet with the view engine...
|
||||
viewEngine ||
|
||||
it('should recurse into nested view containers even if there are no bindings in the component view',
|
||||
() => {
|
||||
@Component({template: '<template #vc>{{name}}</template>'})
|
||||
class Comp {
|
||||
name = 'Tom';
|
||||
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
|
||||
@ViewChild(TemplateRef) template: TemplateRef<any>;
|
||||
}
|
||||
it('should recurse into nested view containers even if there are no bindings in the component view',
|
||||
() => {
|
||||
@Component({template: '<template #vc>{{name}}</template>'})
|
||||
class Comp {
|
||||
name = 'Tom';
|
||||
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
|
||||
@ViewChild(TemplateRef) template: TemplateRef<any>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Comp]});
|
||||
initHelpers();
|
||||
TestBed.configureTestingModule({declarations: [Comp]});
|
||||
initHelpers();
|
||||
|
||||
const ctx = TestBed.createComponent(Comp);
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual([]);
|
||||
const ctx = TestBed.createComponent(Comp);
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual([]);
|
||||
|
||||
ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template);
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual(['Tom']);
|
||||
});
|
||||
ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template);
|
||||
ctx.detectChanges();
|
||||
expect(renderLog.loggedValues).toEqual(['Tom']);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1315,18 +1309,32 @@ 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);
|
||||
function patchLoggingRendererV2(rendererFactory: RendererFactoryV2, log: RenderLog) {
|
||||
if ((<any>rendererFactory).__patchedForLogging) {
|
||||
return;
|
||||
}
|
||||
(<any>rendererFactory).__patchedForLogging = true;
|
||||
const origCreateRenderer = rendererFactory.createRenderer;
|
||||
rendererFactory.createRenderer = function() {
|
||||
const renderer = origCreateRenderer.apply(this, arguments);
|
||||
if ((<any>renderer).__patchedForLogging) {
|
||||
return renderer;
|
||||
}
|
||||
(<any>renderer).__patchedForLogging = true;
|
||||
const origSetProperty = renderer.setProperty;
|
||||
const origSetValue = renderer.setValue;
|
||||
renderer.setProperty = function(el: any, name: string, value: any): void {
|
||||
log.setElementProperty(el, name, value);
|
||||
origSetProperty.call(this, el, name, value);
|
||||
};
|
||||
renderer.setValue = function(node: any, value: string): void {
|
||||
if (getDOM().isTextNode(node)) {
|
||||
log.setText(node, value);
|
||||
}
|
||||
origSetValue.call(this, node, value);
|
||||
};
|
||||
return renderer;
|
||||
};
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
|
@ -1515,7 +1515,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
const fixture = TestBed.createComponent(ParentCmp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('ng-reflect-test$="hello"');
|
||||
expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('ng-reflect-test_="hello"');
|
||||
});
|
||||
|
||||
it('should reflect property values on template comments', () => {
|
||||
|
@ -378,7 +378,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
expect(main.nativeElement).toHaveText('TREE(0:TREE2(1:TREE(2:)))');
|
||||
});
|
||||
|
||||
if (!viewEngine && getDOM().supportsNativeShadowDOM()) {
|
||||
if (getDOM().supportsNativeShadowDOM()) {
|
||||
it('should support native content projection and isolate styles per component', () => {
|
||||
TestBed.configureTestingModule({declarations: [SimpleNative1, SimpleNative2]});
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
@ -396,7 +396,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
});
|
||||
}
|
||||
|
||||
if (!viewEngine && getDOM().supportsDOMEvents()) {
|
||||
if (getDOM().supportsDOMEvents()) {
|
||||
it('should support non emulated styles', () => {
|
||||
TestBed.configureTestingModule({declarations: [OtherComp]});
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, RendererV2, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||
import {getDebugContext} from '@angular/core/src/errors';
|
||||
import {ArgumentType, BindingType, DebugContext, DepFlags, NodeDef, NodeFlags, ProviderType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, directiveDef, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {TestBed, inject, withModule} from '@angular/core/testing';
|
||||
@ -19,9 +19,7 @@ export function main() {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
||||
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(
|
||||
viewFlags, nodes, updateDirectives, updateRenderer, handleEvent, 'someCompId',
|
||||
ViewEncapsulation.None, []);
|
||||
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
|
||||
}
|
||||
|
||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
|
||||
@ -292,11 +290,25 @@ export function main() {
|
||||
it('should inject RendererV1', () => {
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(NodeFlags.None, null, 0, SomeService, [Renderer])
|
||||
directiveDef(
|
||||
NodeFlags.None, null, 0, SomeService, [Renderer], null, null,
|
||||
() => compViewDef([anchorDef(NodeFlags.None, null, null, 0)]))
|
||||
]));
|
||||
|
||||
expect(instance.dep.createElement).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should inject RendererV2', () => {
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(
|
||||
NodeFlags.None, null, 0, SomeService, [RendererV2], null, null,
|
||||
() => compViewDef([anchorDef(NodeFlags.None, null, null, 0)]))
|
||||
]));
|
||||
|
||||
expect(instance.dep.createElement).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
Reference in New Issue
Block a user