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:
@ -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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user