fix(core): view engine - fix perf regressions (#14345)
- Make sure `NodeDef`s don’t fall into dictionary mode. - Use strategy pattern to add debug information / checks, instead of constantly checking for `isDevMode`. - introduce a very light weight `RendererV2` interface to not have duplicate code paths for direct and non direct rendering The strategy pattern is implemented via the new `Services` object. Part of #14013 PR Close #14345
This commit is contained in:
parent
f6b5965a63
commit
24af51a623
|
@ -9,8 +9,8 @@
|
||||||
import {isDevMode} from '../application_ref';
|
import {isDevMode} from '../application_ref';
|
||||||
import {SecurityContext} from '../security';
|
import {SecurityContext} from '../security';
|
||||||
|
|
||||||
import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementOutputDef, EntryAction, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, Refs, ViewData, ViewDefinition, ViewFlags, asElementData} from './types';
|
import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementOutputDef, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags, asElementData} from './types';
|
||||||
import {checkAndUpdateBinding, dispatchEvent, entryAction, setBindingDebugInfo, setCurrentNode, sliceErrorStack, unwrapValue} from './util';
|
import {checkAndUpdateBinding, dispatchEvent, sliceErrorStack, unwrapValue} from './util';
|
||||||
|
|
||||||
export function anchorDef(
|
export function anchorDef(
|
||||||
flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
|
flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
|
||||||
|
@ -65,7 +65,7 @@ export function elementDef(
|
||||||
matchedQueries.forEach(([queryId, valueType]) => { matchedQueryDefs[queryId] = valueType; });
|
matchedQueries.forEach(([queryId, valueType]) => { matchedQueryDefs[queryId] = valueType; });
|
||||||
}
|
}
|
||||||
bindings = bindings || [];
|
bindings = bindings || [];
|
||||||
const bindingDefs = new Array(bindings.length);
|
const bindingDefs: BindingDef[] = new Array(bindings.length);
|
||||||
for (let i = 0; i < bindings.length; i++) {
|
for (let i = 0; i < bindings.length; i++) {
|
||||||
const entry = bindings[i];
|
const entry = bindings[i];
|
||||||
let bindingDef: BindingDef;
|
let bindingDef: BindingDef;
|
||||||
|
@ -82,7 +82,7 @@ export function elementDef(
|
||||||
securityContext = <SecurityContext>entry[2];
|
securityContext = <SecurityContext>entry[2];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
bindingDefs[i] = {type: bindingType, name, nonMinfiedName: name, securityContext, suffix};
|
bindingDefs[i] = {type: bindingType, name, nonMinifiedName: name, securityContext, suffix};
|
||||||
}
|
}
|
||||||
outputs = outputs || [];
|
outputs = outputs || [];
|
||||||
const outputDefs: ElementOutputDef[] = new Array(outputs.length);
|
const outputDefs: ElementOutputDef[] = new Array(outputs.length);
|
||||||
|
@ -130,68 +130,30 @@ export function elementDef(
|
||||||
|
|
||||||
export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData {
|
export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData {
|
||||||
const elDef = def.element;
|
const elDef = def.element;
|
||||||
const rootSelectorOrNode = view.root.selectorOrNode;
|
const rootElement = view.root.element;
|
||||||
|
const renderer = view.root.renderer;
|
||||||
let el: any;
|
let el: any;
|
||||||
if (view.parent || !rootSelectorOrNode) {
|
if (view.parent || !rootElement) {
|
||||||
const parentNode =
|
const parentNode =
|
||||||
def.parent != null ? asElementData(view, def.parent).renderElement : renderHost;
|
def.parent != null ? asElementData(view, def.parent).renderElement : renderHost;
|
||||||
if (view.renderer) {
|
el = elDef.name ? renderer.createElement(elDef.name) : renderer.createComment('');
|
||||||
const debugContext = isDevMode() ? Refs.createDebugContext(view, def.index) : undefined;
|
if (parentNode) {
|
||||||
el = elDef.name ? view.renderer.createElement(parentNode, elDef.name, debugContext) :
|
renderer.appendChild(parentNode, el);
|
||||||
view.renderer.createTemplateAnchor(parentNode, debugContext);
|
|
||||||
} else {
|
|
||||||
el = elDef.name ? document.createElement(elDef.name) : document.createComment('');
|
|
||||||
if (parentNode) {
|
|
||||||
parentNode.appendChild(el);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (view.renderer) {
|
el = rootElement;
|
||||||
const debugContext = isDevMode() ? Refs.createDebugContext(view, def.index) : undefined;
|
|
||||||
el = view.renderer.selectRootElement(rootSelectorOrNode, debugContext);
|
|
||||||
} else {
|
|
||||||
el = typeof rootSelectorOrNode === 'string' ? document.querySelector(rootSelectorOrNode) :
|
|
||||||
rootSelectorOrNode;
|
|
||||||
el.textContent = '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (elDef.attrs) {
|
if (elDef.attrs) {
|
||||||
for (let attrName in elDef.attrs) {
|
for (let attrName in elDef.attrs) {
|
||||||
if (view.renderer) {
|
renderer.setAttribute(el, attrName, elDef.attrs[attrName]);
|
||||||
view.renderer.setElementAttribute(el, attrName, elDef.attrs[attrName]);
|
|
||||||
} else {
|
|
||||||
el.setAttribute(attrName, elDef.attrs[attrName]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (elDef.outputs.length) {
|
if (elDef.outputs.length) {
|
||||||
for (let i = 0; i < elDef.outputs.length; i++) {
|
for (let i = 0; i < elDef.outputs.length; i++) {
|
||||||
const output = elDef.outputs[i];
|
const output = elDef.outputs[i];
|
||||||
let disposable: DisposableFn;
|
const handleEventClosure = renderEventHandlerClosure(view, def.index, output.eventName);
|
||||||
if (view.renderer) {
|
const disposable =
|
||||||
const handleEventClosure = renderEventHandlerClosure(view, def.index, output.eventName);
|
<any>renderer.listen(output.target || el, output.eventName, handleEventClosure);
|
||||||
if (output.target) {
|
|
||||||
disposable =
|
|
||||||
<any>view.renderer.listenGlobal(output.target, output.eventName, handleEventClosure);
|
|
||||||
} else {
|
|
||||||
disposable = <any>view.renderer.listen(el, output.eventName, handleEventClosure);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let target: any;
|
|
||||||
switch (output.target) {
|
|
||||||
case 'window':
|
|
||||||
target = window;
|
|
||||||
break;
|
|
||||||
case 'document':
|
|
||||||
target = document;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
target = el;
|
|
||||||
}
|
|
||||||
const handleEventClosure = directDomEventHandlerClosure(view, def.index, output.eventName);
|
|
||||||
target.addEventListener(output.eventName, handleEventClosure);
|
|
||||||
disposable = target.removeEventListener.bind(target, output.eventName, handleEventClosure);
|
|
||||||
}
|
|
||||||
view.disposables[def.disposableIndex + i] = disposable;
|
view.disposables[def.disposableIndex + i] = disposable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,21 +165,10 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): El
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderEventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
function renderEventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
||||||
return entryAction(
|
return (event: any) => dispatchEvent(view, index, eventName, event);
|
||||||
EntryAction.HandleEvent, (event: any) => dispatchEvent(view, index, eventName, event));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function directDomEventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
|
||||||
return entryAction(EntryAction.HandleEvent, (event: any) => {
|
|
||||||
const result = dispatchEvent(view, index, eventName, event);
|
|
||||||
if (result === false) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function checkAndUpdateElementInline(
|
export function checkAndUpdateElementInline(
|
||||||
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
|
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
|
||||||
v7: any, v8: any, v9: any) {
|
v7: any, v8: any, v9: any) {
|
||||||
|
@ -282,26 +233,20 @@ function setElementAttribute(
|
||||||
const securityContext = binding.securityContext;
|
const securityContext = binding.securityContext;
|
||||||
let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value;
|
let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value;
|
||||||
renderValue = renderValue != null ? renderValue.toString() : null;
|
renderValue = renderValue != null ? renderValue.toString() : null;
|
||||||
if (view.renderer) {
|
const renderer = view.root.renderer;
|
||||||
view.renderer.setElementAttribute(renderNode, name, renderValue);
|
if (value != null) {
|
||||||
|
renderer.setAttribute(renderNode, name, renderValue);
|
||||||
} else {
|
} else {
|
||||||
if (value != null) {
|
renderer.removeAttribute(renderNode, name);
|
||||||
renderNode.setAttribute(name, renderValue);
|
|
||||||
} else {
|
|
||||||
renderNode.removeAttribute(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setElementClass(view: ViewData, renderNode: any, name: string, value: boolean) {
|
function setElementClass(view: ViewData, renderNode: any, name: string, value: boolean) {
|
||||||
if (view.renderer) {
|
const renderer = view.root.renderer;
|
||||||
view.renderer.setElementClass(renderNode, name, value);
|
if (value) {
|
||||||
|
renderer.addClass(renderNode, name);
|
||||||
} else {
|
} else {
|
||||||
if (value) {
|
renderer.removeClass(renderNode, name);
|
||||||
renderNode.classList.add(name);
|
|
||||||
} else {
|
|
||||||
renderNode.classList.remove(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,16 +262,11 @@ function setElementStyle(
|
||||||
} else {
|
} else {
|
||||||
renderValue = null;
|
renderValue = null;
|
||||||
}
|
}
|
||||||
if (view.renderer) {
|
const renderer = view.root.renderer;
|
||||||
view.renderer.setElementStyle(renderNode, name, renderValue);
|
if (renderValue != null) {
|
||||||
|
renderer.setStyle(renderNode, name, renderValue);
|
||||||
} else {
|
} else {
|
||||||
if (renderValue != null) {
|
renderer.removeStyle(renderNode, name);
|
||||||
renderNode.style[name] = renderValue;
|
|
||||||
} else {
|
|
||||||
// IE requires '' instead of null
|
|
||||||
// see https://github.com/angular/angular/issues/7916
|
|
||||||
(renderNode.style as any)[name] = '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,12 +274,5 @@ function setElementProperty(
|
||||||
view: ViewData, binding: BindingDef, renderNode: any, name: string, value: any) {
|
view: ViewData, binding: BindingDef, renderNode: any, name: string, value: any) {
|
||||||
const securityContext = binding.securityContext;
|
const securityContext = binding.securityContext;
|
||||||
let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value;
|
let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value;
|
||||||
if (view.renderer) {
|
view.root.renderer.setProperty(renderNode, name, renderValue);
|
||||||
view.renderer.setElementProperty(renderNode, name, renderValue);
|
|
||||||
if (isDevMode() && (view.def.flags & ViewFlags.DirectDom) === 0) {
|
|
||||||
setBindingDebugInfo(view.renderer, renderNode, name, renderValue);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
renderNode[name] = renderValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ERROR_DEBUG_CONTEXT, ERROR_ORIGINAL_ERROR, getDebugContext} from '../errors';
|
import {ERROR_DEBUG_CONTEXT, ERROR_ORIGINAL_ERROR, getDebugContext} from '../errors';
|
||||||
import {DebugContext, EntryAction, ViewState} from './types';
|
import {DebugContext, ViewState} from './types';
|
||||||
|
|
||||||
export function expressionChangedAfterItHasBeenCheckedError(
|
export function expressionChangedAfterItHasBeenCheckedError(
|
||||||
context: DebugContext, oldValue: any, currValue: any, isFirstCheck: boolean): Error {
|
context: DebugContext, oldValue: any, currValue: any, isFirstCheck: boolean): Error {
|
||||||
let msg =
|
let msg =
|
||||||
`Expression has changed after it was checked. Previous value: '${oldValue}'. Current value: '${currValue}'.`;
|
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: '${oldValue}'. Current value: '${currValue}'.`;
|
||||||
if (isFirstCheck) {
|
if (isFirstCheck) {
|
||||||
msg +=
|
msg +=
|
||||||
` It seems like the view has been created after its parent and its children have been dirty checked.` +
|
` It seems like the view has been created after its parent and its children have been dirty checked.` +
|
||||||
|
@ -39,6 +39,6 @@ export function isViewDebugError(err: Error): boolean {
|
||||||
return !!getDebugContext(err);
|
return !!getDebugContext(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function viewDestroyedError(action: EntryAction): Error {
|
export function viewDestroyedError(action: string): Error {
|
||||||
return new Error(`View has been used after destroy for ${EntryAction[action]}`);
|
return new Error(`ViewDestroyedError: Attempt to use a destroyed view: ${action}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,16 +11,11 @@ export {ngContentDef} from './ng_content';
|
||||||
export {directiveDef, providerDef} from './provider';
|
export {directiveDef, providerDef} from './provider';
|
||||||
export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression';
|
export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression';
|
||||||
export {queryDef} from './query';
|
export {queryDef} from './query';
|
||||||
|
export {createComponentFactory} from './refs';
|
||||||
|
export {initServicesIfNeeded} from './services';
|
||||||
export {textDef} from './text';
|
export {textDef} from './text';
|
||||||
export {rootRenderNodes, setCurrentNode} from './util';
|
export {rootRenderNodes} from './util';
|
||||||
export {checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, destroyView, viewDef} from './view';
|
export {viewDef} from './view';
|
||||||
export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
|
export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
|
||||||
|
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|
||||||
import {createRefs} from './refs';
|
|
||||||
import {Refs} from './types';
|
|
||||||
|
|
||||||
Refs.setInstance(createRefs());
|
|
||||||
|
|
||||||
export const createComponentFactory: typeof Refs.createComponentFactory =
|
|
||||||
Refs.createComponentFactory;
|
|
||||||
|
|
|
@ -46,13 +46,6 @@ export function appendNgContent(view: ViewData, renderHost: any, def: NodeDef) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ngContentIndex = def.ngContent.index;
|
const ngContentIndex = def.ngContent.index;
|
||||||
if (view.renderer) {
|
visitProjectedRenderNodes(
|
||||||
const projectedNodes: any[] = [];
|
view, ngContentIndex, RenderNodeAction.AppendChild, parentEl, undefined, undefined);
|
||||||
visitProjectedRenderNodes(
|
|
||||||
view, ngContentIndex, RenderNodeAction.Collect, undefined, undefined, projectedNodes);
|
|
||||||
view.renderer.projectNodes(parentEl, projectedNodes);
|
|
||||||
} else {
|
|
||||||
visitProjectedRenderNodes(
|
|
||||||
view, ngContentIndex, RenderNodeAction.AppendChild, parentEl, undefined, undefined);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,23 +6,19 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {isDevMode} from '../application_ref';
|
|
||||||
import {ChangeDetectorRef, SimpleChange, SimpleChanges} from '../change_detection/change_detection';
|
import {ChangeDetectorRef, SimpleChange, SimpleChanges} from '../change_detection/change_detection';
|
||||||
import {Injector} from '../di';
|
import {Injector} from '../di';
|
||||||
import {stringify} from '../facade/lang';
|
|
||||||
import {ElementRef} from '../linker/element_ref';
|
import {ElementRef} from '../linker/element_ref';
|
||||||
import {TemplateRef} from '../linker/template_ref';
|
import {TemplateRef} from '../linker/template_ref';
|
||||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||||
import {Renderer} from '../render/api';
|
import * as v1renderer from '../render/api';
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
|
|
||||||
import {queryDef} from './query';
|
import {createChangeDetectorRef, createInjector, createTemplateRef, createViewContainerRef} from './refs';
|
||||||
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, EntryAction, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderOutputDef, ProviderType, QueryBindingType, QueryDef, QueryValueType, Refs, RootData, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
|
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderOutputDef, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
|
||||||
import {checkAndUpdateBinding, dispatchEvent, entryAction, findElementDef, parentDiIndex, setBindingDebugInfo, setCurrentNode, unwrapValue} from './util';
|
import {checkAndUpdateBinding, dispatchEvent, findElementDef, isComponentView, parentDiIndex, tokenKey, unwrapValue} from './util';
|
||||||
|
|
||||||
const _tokenKeyCache = new Map<any, string>();
|
const RendererV1TokenKey = tokenKey(v1renderer.Renderer);
|
||||||
|
|
||||||
const RendererTokenKey = tokenKey(Renderer);
|
|
||||||
const ElementRefTokenKey = tokenKey(ElementRef);
|
const ElementRefTokenKey = tokenKey(ElementRef);
|
||||||
const ViewContainerRefTokenKey = tokenKey(ViewContainerRef);
|
const ViewContainerRefTokenKey = tokenKey(ViewContainerRef);
|
||||||
const TemplateRefTokenKey = tokenKey(TemplateRef);
|
const TemplateRefTokenKey = tokenKey(TemplateRef);
|
||||||
|
@ -119,23 +115,13 @@ export function _providerDef(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function tokenKey(token: any): string {
|
|
||||||
let key = _tokenKeyCache.get(token);
|
|
||||||
if (!key) {
|
|
||||||
key = stringify(token) + '_' + _tokenKeyCache.size;
|
|
||||||
_tokenKeyCache.set(token, key);
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createProviderInstance(view: ViewData, def: NodeDef): any {
|
export function createProviderInstance(view: ViewData, def: NodeDef): any {
|
||||||
const providerDef = def.provider;
|
const providerDef = def.provider;
|
||||||
return def.flags & NodeFlags.LazyProvider ? NOT_CREATED : createInstance(view, def);
|
return def.flags & NodeFlags.LazyProvider ? NOT_CREATED : createInstance(view, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
function eventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
function eventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
||||||
return entryAction(
|
return (event: any) => dispatchEvent(view, index, eventName, event);
|
||||||
EntryAction.HandleEvent, (event: any) => dispatchEvent(view, index, eventName, event));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkAndUpdateProviderInline(
|
export function checkAndUpdateProviderInline(
|
||||||
|
@ -310,18 +296,24 @@ export function resolveDep(
|
||||||
while (view) {
|
while (view) {
|
||||||
const elDef = view.def.nodes[elIndex];
|
const elDef = view.def.nodes[elIndex];
|
||||||
switch (tokenKey) {
|
switch (tokenKey) {
|
||||||
case RendererTokenKey:
|
case RendererV1TokenKey: {
|
||||||
if (view.renderer) {
|
let compView = view;
|
||||||
return view.renderer;
|
while (compView && !isComponentView(compView)) {
|
||||||
} else {
|
compView = compView.parent;
|
||||||
return Injector.NULL.get(depDef.token, notFoundValue);
|
|
||||||
}
|
}
|
||||||
|
const rootRenderer: v1renderer.RootRenderer =
|
||||||
|
view.root.injector.get(v1renderer.RootRenderer);
|
||||||
|
|
||||||
|
// 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, [], {}));
|
||||||
|
}
|
||||||
case ElementRefTokenKey:
|
case ElementRefTokenKey:
|
||||||
return new ElementRef(asElementData(view, elIndex).renderElement);
|
return new ElementRef(asElementData(view, elIndex).renderElement);
|
||||||
case ViewContainerRefTokenKey:
|
case ViewContainerRefTokenKey:
|
||||||
return Refs.createViewContainerRef(view, elIndex);
|
return createViewContainerRef(view, elIndex);
|
||||||
case TemplateRefTokenKey:
|
case TemplateRefTokenKey:
|
||||||
return Refs.createTemplateRef(view, elDef);
|
return createTemplateRef(view, elDef);
|
||||||
case ChangeDetectorRefTokenKey:
|
case ChangeDetectorRefTokenKey:
|
||||||
let cdView = view;
|
let cdView = view;
|
||||||
// If we are still checking dependencies on the initial element...
|
// If we are still checking dependencies on the initial element...
|
||||||
|
@ -331,10 +323,9 @@ export function resolveDep(
|
||||||
cdView = asProviderData(view, requestNodeIndex).componentView;
|
cdView = asProviderData(view, requestNodeIndex).componentView;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// A ViewRef is also a ChangeDetectorRef
|
return createChangeDetectorRef(cdView);
|
||||||
return Refs.createViewRef(cdView);
|
|
||||||
case InjectorRefTokenKey:
|
case InjectorRefTokenKey:
|
||||||
return Refs.createInjector(view, elIndex);
|
return createInjector(view, elIndex);
|
||||||
default:
|
default:
|
||||||
const providerIndex = elDef.element.providerIndices[tokenKey];
|
const providerIndex = elDef.element.providerIndices[tokenKey];
|
||||||
if (providerIndex != null) {
|
if (providerIndex != null) {
|
||||||
|
@ -374,12 +365,6 @@ function checkAndUpdateProp(
|
||||||
// the user passed in the property name as an object has to `providerDef`,
|
// the user passed in the property name as an object has to `providerDef`,
|
||||||
// so Closure Compiler will have renamed the property correctly already.
|
// so Closure Compiler will have renamed the property correctly already.
|
||||||
provider[propName] = value;
|
provider[propName] = value;
|
||||||
|
|
||||||
if (isDevMode() && (view.def.flags & ViewFlags.DirectDom) === 0) {
|
|
||||||
setBindingDebugInfo(
|
|
||||||
view.renderer, asElementData(view, def.parent).renderElement, binding.nonMinifiedName,
|
|
||||||
value);
|
|
||||||
}
|
|
||||||
if (change) {
|
if (change) {
|
||||||
changes = changes || {};
|
changes = changes || {};
|
||||||
changes[binding.nonMinifiedName] = change;
|
changes[binding.nonMinifiedName] = change;
|
||||||
|
@ -399,7 +384,7 @@ export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: Node
|
||||||
const nodeIndex = nodeDef.index;
|
const nodeIndex = nodeDef.index;
|
||||||
if (nodeDef.flags & lifecycles) {
|
if (nodeDef.flags & lifecycles) {
|
||||||
// a leaf
|
// a leaf
|
||||||
setCurrentNode(view, nodeIndex);
|
Services.setCurrentNode(view, nodeIndex);
|
||||||
callProviderLifecycles(asProviderData(view, nodeIndex).instance, nodeDef.flags & lifecycles);
|
callProviderLifecycles(asProviderData(view, nodeIndex).instance, nodeDef.flags & lifecycles);
|
||||||
} else if ((nodeDef.childFlags & lifecycles) === 0) {
|
} else if ((nodeDef.childFlags & lifecycles) === 0) {
|
||||||
// a parent with leafs
|
// a parent with leafs
|
||||||
|
|
|
@ -6,9 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {resolveDep, tokenKey} from './provider';
|
import {BindingDef, BindingType, DepDef, DepFlags, NodeData, NodeDef, NodeType, ProviderData, PureExpressionData, PureExpressionType, Services, ViewData, asPureExpressionData} from './types';
|
||||||
import {BindingDef, BindingType, DepDef, DepFlags, NodeData, NodeDef, NodeType, ProviderData, PureExpressionData, PureExpressionType, ViewData, asPureExpressionData} from './types';
|
import {checkAndUpdateBinding, tokenKey, unwrapValue} from './util';
|
||||||
import {checkAndUpdateBinding, unwrapValue} from './util';
|
|
||||||
|
|
||||||
export function purePipeDef(pipeToken: any, argCount: number): NodeDef {
|
export function purePipeDef(pipeToken: any, argCount: number): NodeDef {
|
||||||
return _pureExpressionDef(
|
return _pureExpressionDef(
|
||||||
|
@ -64,7 +63,7 @@ function _pureExpressionDef(
|
||||||
|
|
||||||
export function createPureExpression(view: ViewData, def: NodeDef): PureExpressionData {
|
export function createPureExpression(view: ViewData, def: NodeDef): PureExpressionData {
|
||||||
const pipe = def.pureExpression.pipeDep ?
|
const pipe = def.pureExpression.pipeDep ?
|
||||||
resolveDep(view, def.index, def.parent, def.pureExpression.pipeDep) :
|
Services.resolveDep(view, def.index, def.parent, def.pureExpression.pipeDep) :
|
||||||
undefined;
|
undefined;
|
||||||
return {value: undefined, pipe};
|
return {value: undefined, pipe};
|
||||||
}
|
}
|
||||||
|
@ -98,6 +97,7 @@ export function checkAndUpdatePureExpressionInline(
|
||||||
if (checkAndUpdateBinding(view, def, 0, v0)) changed = true;
|
if (checkAndUpdateBinding(view, def, 0, v0)) changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data = asPureExpressionData(view, def.index);
|
||||||
if (changed) {
|
if (changed) {
|
||||||
v0 = unwrapValue(v0);
|
v0 = unwrapValue(v0);
|
||||||
v1 = unwrapValue(v1);
|
v1 = unwrapValue(v1);
|
||||||
|
@ -110,7 +110,6 @@ export function checkAndUpdatePureExpressionInline(
|
||||||
v8 = unwrapValue(v8);
|
v8 = unwrapValue(v8);
|
||||||
v9 = unwrapValue(v9);
|
v9 = unwrapValue(v9);
|
||||||
|
|
||||||
const data = asPureExpressionData(view, def.index);
|
|
||||||
let value: any;
|
let value: any;
|
||||||
switch (def.pureExpression.type) {
|
switch (def.pureExpression.type) {
|
||||||
case PureExpressionType.Array:
|
case PureExpressionType.Array:
|
||||||
|
@ -202,6 +201,7 @@ export function checkAndUpdatePureExpressionInline(
|
||||||
}
|
}
|
||||||
data.value = value;
|
data.value = value;
|
||||||
}
|
}
|
||||||
|
return data.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef, values: any[]) {
|
export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef, values: any[]) {
|
||||||
|
@ -214,8 +214,8 @@ export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const data = asPureExpressionData(view, def.index);
|
||||||
if (changed) {
|
if (changed) {
|
||||||
const data = asPureExpressionData(view, def.index);
|
|
||||||
let value: any;
|
let value: any;
|
||||||
switch (def.pureExpression.type) {
|
switch (def.pureExpression.type) {
|
||||||
case PureExpressionType.Array:
|
case PureExpressionType.Array:
|
||||||
|
@ -240,4 +240,5 @@ export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef
|
||||||
}
|
}
|
||||||
data.value = value;
|
data.value = value;
|
||||||
}
|
}
|
||||||
|
return data.value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ import {QueryList} from '../linker/query_list';
|
||||||
import {TemplateRef} from '../linker/template_ref';
|
import {TemplateRef} from '../linker/template_ref';
|
||||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||||
|
|
||||||
import {NodeDef, NodeFlags, NodeType, QueryBindingDef, QueryBindingType, QueryDef, QueryValueType, Refs, ViewData, asElementData, asProviderData, asQueryList} from './types';
|
import {createTemplateRef, createViewContainerRef} from './refs';
|
||||||
|
import {NodeDef, NodeFlags, NodeType, QueryBindingDef, QueryBindingType, QueryDef, QueryValueType, Services, ViewData, asElementData, asProviderData, asQueryList} from './types';
|
||||||
import {declaredViewContainer} from './util';
|
import {declaredViewContainer} from './util';
|
||||||
|
|
||||||
export function queryDef(
|
export function queryDef(
|
||||||
|
@ -158,10 +159,10 @@ export function getQueryValue(view: ViewData, nodeDef: NodeDef, queryId: string)
|
||||||
value = new ElementRef(asElementData(view, nodeDef.index).renderElement);
|
value = new ElementRef(asElementData(view, nodeDef.index).renderElement);
|
||||||
break;
|
break;
|
||||||
case QueryValueType.TemplateRef:
|
case QueryValueType.TemplateRef:
|
||||||
value = Refs.createTemplateRef(view, nodeDef);
|
value = createTemplateRef(view, nodeDef);
|
||||||
break;
|
break;
|
||||||
case QueryValueType.ViewContainerRef:
|
case QueryValueType.ViewContainerRef:
|
||||||
value = Refs.createViewContainerRef(view, nodeDef.index);
|
value = createViewContainerRef(view, nodeDef.index);
|
||||||
break;
|
break;
|
||||||
case QueryValueType.Provider:
|
case QueryValueType.Provider:
|
||||||
value = asProviderData(view, nodeDef.index).instance;
|
value = asProviderData(view, nodeDef.index).instance;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {isDevMode} from '../application_ref';
|
||||||
import {ChangeDetectorRef} from '../change_detection/change_detection';
|
import {ChangeDetectorRef} from '../change_detection/change_detection';
|
||||||
import {Injectable, Injector} from '../di';
|
import {Injectable, Injector} from '../di';
|
||||||
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
|
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
|
||||||
|
@ -13,67 +14,32 @@ import {ElementRef} from '../linker/element_ref';
|
||||||
import {TemplateRef} from '../linker/template_ref';
|
import {TemplateRef} from '../linker/template_ref';
|
||||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||||
import {EmbeddedViewRef, ViewRef} from '../linker/view_ref';
|
import {EmbeddedViewRef, ViewRef} from '../linker/view_ref';
|
||||||
import {RenderComponentType, Renderer, RootRenderer} from '../render/api';
|
import * as v1renderer from '../render/api';
|
||||||
import {Sanitizer, SecurityContext} from '../security';
|
import {Sanitizer, SecurityContext} from '../security';
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
|
|
||||||
import {resolveDep, tokenKey} from './provider';
|
import {DirectDomRenderer, LegacyRendererAdapter} from './renderer';
|
||||||
import {getQueryValue} from './query';
|
import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types';
|
||||||
import {DebugContext, DepFlags, ElementData, NodeData, NodeDef, NodeType, Refs, RootData, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types';
|
import {findElementDef, isComponentView, parentDiIndex, renderNode, resolveViewDefinition, rootRenderNodes, tokenKey} from './util';
|
||||||
import {findElementDef, isComponentView, parentDiIndex, renderNode, resolveViewDefinition, rootRenderNodes} from './util';
|
|
||||||
import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view';
|
|
||||||
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
|
|
||||||
|
|
||||||
const EMPTY_CONTEXT = new Object();
|
const EMPTY_CONTEXT = new Object();
|
||||||
|
|
||||||
export function createRefs() {
|
export function createComponentFactory(
|
||||||
return new Refs_();
|
selector: string, componentType: Type<any>,
|
||||||
}
|
viewDefFactory: ViewDefinitionFactory): ComponentFactory<any> {
|
||||||
|
return new ComponentFactory_(selector, componentType, viewDefFactory);
|
||||||
export class Refs_ implements Refs {
|
|
||||||
createComponentFactory(selector: string, viewDefFactory: ViewDefinitionFactory):
|
|
||||||
ComponentFactory<any> {
|
|
||||||
return new ComponentFactory_(selector, viewDefFactory);
|
|
||||||
}
|
|
||||||
createViewRef(data: ViewData): ViewRef { return new ViewRef_(data); }
|
|
||||||
createViewContainerRef(view: ViewData, elIndex: number): ViewContainerRef {
|
|
||||||
return new ViewContainerRef_(view, elIndex);
|
|
||||||
}
|
|
||||||
createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any> {
|
|
||||||
return new TemplateRef_(parentView, def);
|
|
||||||
}
|
|
||||||
createInjector(view: ViewData, elIndex: number): Injector { return new Injector_(view, elIndex); }
|
|
||||||
createDebugContext(view: ViewData, nodeIndex: number): DebugContext {
|
|
||||||
return new DebugContext_(view, nodeIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComponentFactory_ implements ComponentFactory<any> {
|
class ComponentFactory_ implements ComponentFactory<any> {
|
||||||
/**
|
/**
|
||||||
* Only needed so that we can implement ComponentFactory
|
* We are not renaming this field as the old ComponentFactory is using it.
|
||||||
* @internal */
|
* @internal */
|
||||||
_viewClass: any;
|
_viewClass: any;
|
||||||
|
|
||||||
private _viewDef: ViewDefinition;
|
constructor(
|
||||||
private _componentNodeIndex: number;
|
public selector: string, public componentType: Type<any>,
|
||||||
|
_viewDefFactory: ViewDefinitionFactory) {
|
||||||
constructor(public selector: string, viewDefFactory: ViewDefinitionFactory) {
|
this._viewClass = _viewDefFactory;
|
||||||
const viewDef = this._viewDef = resolveViewDefinition(viewDefFactory);
|
|
||||||
const len = viewDef.nodes.length;
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
const nodeDef = viewDef.nodes[i];
|
|
||||||
if (nodeDef.provider && nodeDef.provider.component) {
|
|
||||||
this._componentNodeIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this._componentNodeIndex == null) {
|
|
||||||
throw new Error(`Illegal State: Could not find a component in the view definition!`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get componentType(): Type<any> {
|
|
||||||
return this._viewDef.nodes[this._componentNodeIndex].provider.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,29 +48,28 @@ class ComponentFactory_ implements ComponentFactory<any> {
|
||||||
create(
|
create(
|
||||||
injector: Injector, projectableNodes: any[][] = null,
|
injector: Injector, projectableNodes: any[][] = null,
|
||||||
rootSelectorOrNode: string|any = null): ComponentRef<any> {
|
rootSelectorOrNode: string|any = null): ComponentRef<any> {
|
||||||
if (!projectableNodes) {
|
const viewDef = resolveViewDefinition(this._viewClass);
|
||||||
projectableNodes = [];
|
let componentNodeIndex: number;
|
||||||
|
const len = viewDef.nodes.length;
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
const nodeDef = viewDef.nodes[i];
|
||||||
|
if (nodeDef.provider && nodeDef.provider.component) {
|
||||||
|
componentNodeIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!rootSelectorOrNode) {
|
if (componentNodeIndex == null) {
|
||||||
rootSelectorOrNode = this.selector;
|
throw new Error(`Illegal State: Could not find a component in the view definition!`);
|
||||||
}
|
}
|
||||||
const renderer = injector.get(RootRenderer);
|
const view = Services.createRootView(
|
||||||
const sanitizer = injector.get(Sanitizer);
|
injector, projectableNodes || [], rootSelectorOrNode, viewDef, EMPTY_CONTEXT);
|
||||||
|
const component = asProviderData(view, componentNodeIndex).instance;
|
||||||
const root: RootData =
|
return new ComponentRef_(view, new ViewRef_(view), component);
|
||||||
{injector, projectableNodes, selectorOrNode: rootSelectorOrNode, sanitizer, renderer};
|
|
||||||
|
|
||||||
const view = createRootView(root, this._viewDef, EMPTY_CONTEXT);
|
|
||||||
const component = asProviderData(view, this._componentNodeIndex).instance;
|
|
||||||
return new ComponentRef_(view, component);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComponentRef_ implements ComponentRef<any> {
|
class ComponentRef_ implements ComponentRef<any> {
|
||||||
private _viewRef: ViewRef_;
|
constructor(private _view: ViewData, private _viewRef: ViewRef, private _component: any) {}
|
||||||
constructor(private _view: ViewData, private _component: any) {
|
|
||||||
this._viewRef = new ViewRef_(_view);
|
|
||||||
}
|
|
||||||
get location(): ElementRef { return new ElementRef(asElementData(this._view, 0).renderElement); }
|
get location(): ElementRef { return new ElementRef(asElementData(this._view, 0).renderElement); }
|
||||||
get injector(): Injector { return new Injector_(this._view, 0); }
|
get injector(): Injector { return new Injector_(this._view, 0); }
|
||||||
get instance(): any { return this._component; };
|
get instance(): any { return this._component; };
|
||||||
|
@ -116,6 +81,10 @@ class ComponentRef_ implements ComponentRef<any> {
|
||||||
onDestroy(callback: Function): void { this._viewRef.onDestroy(callback); }
|
onDestroy(callback: Function): void { this._viewRef.onDestroy(callback); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createViewContainerRef(view: ViewData, elIndex: number): ViewContainerRef {
|
||||||
|
return new ViewContainerRef_(view, elIndex);
|
||||||
|
}
|
||||||
|
|
||||||
class ViewContainerRef_ implements ViewContainerRef {
|
class ViewContainerRef_ implements ViewContainerRef {
|
||||||
private _data: ElementData;
|
private _data: ElementData;
|
||||||
constructor(private _view: ViewData, private _elIndex: number) {
|
constructor(private _view: ViewData, private _elIndex: number) {
|
||||||
|
@ -139,8 +108,8 @@ class ViewContainerRef_ implements ViewContainerRef {
|
||||||
clear(): void {
|
clear(): void {
|
||||||
const len = this._data.embeddedViews.length;
|
const len = this._data.embeddedViews.length;
|
||||||
for (let i = len - 1; i >= 0; i--) {
|
for (let i = len - 1; i >= 0; i--) {
|
||||||
const view = detachEmbeddedView(this._data, i);
|
const view = Services.detachEmbeddedView(this._data, i);
|
||||||
destroyView(view);
|
Services.destroyView(view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,13 +135,13 @@ class ViewContainerRef_ implements ViewContainerRef {
|
||||||
|
|
||||||
insert(viewRef: ViewRef, index?: number): ViewRef {
|
insert(viewRef: ViewRef, index?: number): ViewRef {
|
||||||
const viewData = (<ViewRef_>viewRef)._view;
|
const viewData = (<ViewRef_>viewRef)._view;
|
||||||
attachEmbeddedView(this._data, index, viewData);
|
Services.attachEmbeddedView(this._data, index, viewData);
|
||||||
return viewRef;
|
return viewRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
move(viewRef: ViewRef_, currentIndex: number): ViewRef {
|
move(viewRef: ViewRef_, currentIndex: number): ViewRef {
|
||||||
const previousIndex = this._data.embeddedViews.indexOf(viewRef._view);
|
const previousIndex = this._data.embeddedViews.indexOf(viewRef._view);
|
||||||
moveEmbeddedView(this._data, previousIndex, currentIndex);
|
Services.moveEmbeddedView(this._data, previousIndex, currentIndex);
|
||||||
return viewRef;
|
return viewRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,17 +150,21 @@ class ViewContainerRef_ implements ViewContainerRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(index?: number): void {
|
remove(index?: number): void {
|
||||||
const viewData = detachEmbeddedView(this._data, index);
|
const viewData = Services.detachEmbeddedView(this._data, index);
|
||||||
destroyView(viewData);
|
Services.destroyView(viewData);
|
||||||
}
|
}
|
||||||
|
|
||||||
detach(index?: number): ViewRef {
|
detach(index?: number): ViewRef {
|
||||||
const view = this.get(index);
|
const view = this.get(index);
|
||||||
detachEmbeddedView(this._data, index);
|
Services.detachEmbeddedView(this._data, index);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createChangeDetectorRef(view: ViewData): ChangeDetectorRef {
|
||||||
|
return new ViewRef_(view);
|
||||||
|
}
|
||||||
|
|
||||||
class ViewRef_ implements EmbeddedViewRef<any> {
|
class ViewRef_ implements EmbeddedViewRef<any> {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_view: ViewData;
|
_view: ViewData;
|
||||||
|
@ -206,20 +179,24 @@ class ViewRef_ implements EmbeddedViewRef<any> {
|
||||||
|
|
||||||
markForCheck(): void { this.reattach(); }
|
markForCheck(): void { this.reattach(); }
|
||||||
detach(): void { this._view.state &= ~ViewState.ChecksEnabled; }
|
detach(): void { this._view.state &= ~ViewState.ChecksEnabled; }
|
||||||
detectChanges(): void { checkAndUpdateView(this._view); }
|
detectChanges(): void { Services.checkAndUpdateView(this._view); }
|
||||||
checkNoChanges(): void { checkNoChangesView(this._view); }
|
checkNoChanges(): void { Services.checkNoChangesView(this._view); }
|
||||||
|
|
||||||
reattach(): void { this._view.state |= ViewState.ChecksEnabled; }
|
reattach(): void { this._view.state |= ViewState.ChecksEnabled; }
|
||||||
onDestroy(callback: Function) { this._view.disposables.push(<any>callback); }
|
onDestroy(callback: Function) { this._view.disposables.push(<any>callback); }
|
||||||
|
|
||||||
destroy() { destroyView(this._view); }
|
destroy() { Services.destroyView(this._view); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createTemplateRef(view: ViewData, def: NodeDef): TemplateRef<any> {
|
||||||
|
return new TemplateRef_(view, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
class TemplateRef_ implements TemplateRef<any> {
|
class TemplateRef_ implements TemplateRef<any> {
|
||||||
constructor(private _parentView: ViewData, private _def: NodeDef) {}
|
constructor(private _parentView: ViewData, private _def: NodeDef) {}
|
||||||
|
|
||||||
createEmbeddedView(context: any): EmbeddedViewRef<any> {
|
createEmbeddedView(context: any): EmbeddedViewRef<any> {
|
||||||
return new ViewRef_(createEmbeddedView(this._parentView, this._def, context));
|
return new ViewRef_(Services.createEmbeddedView(this._parentView, this._def, context));
|
||||||
}
|
}
|
||||||
|
|
||||||
get elementRef(): ElementRef {
|
get elementRef(): ElementRef {
|
||||||
|
@ -227,91 +204,15 @@ class TemplateRef_ implements TemplateRef<any> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createInjector(view: ViewData, elIndex: number): Injector {
|
||||||
|
return new Injector_(view, elIndex);
|
||||||
|
}
|
||||||
|
|
||||||
class Injector_ implements Injector {
|
class Injector_ implements Injector {
|
||||||
constructor(private view: ViewData, private elIndex: number) {}
|
constructor(private view: ViewData, private elIndex: number) {}
|
||||||
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
||||||
return resolveDep(
|
return Services.resolveDep(
|
||||||
this.view, undefined, this.elIndex,
|
this.view, undefined, this.elIndex,
|
||||||
{flags: DepFlags.None, token, tokenKey: tokenKey(token)}, notFoundValue);
|
{flags: DepFlags.None, token, tokenKey: tokenKey(token)}, notFoundValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DebugContext_ implements DebugContext {
|
|
||||||
private nodeDef: NodeDef;
|
|
||||||
private elDef: NodeDef;
|
|
||||||
constructor(public view: ViewData, public nodeIndex: number) {
|
|
||||||
if (nodeIndex == null) {
|
|
||||||
this.nodeIndex = nodeIndex = view.parentIndex;
|
|
||||||
this.view = view = view.parent;
|
|
||||||
}
|
|
||||||
this.nodeDef = view.def.nodes[nodeIndex];
|
|
||||||
this.elDef = findElementDef(view, nodeIndex);
|
|
||||||
}
|
|
||||||
get injector(): Injector { return new Injector_(this.view, this.elDef.index); }
|
|
||||||
get component(): any { return this.view.component; }
|
|
||||||
get providerTokens(): any[] {
|
|
||||||
const tokens: any[] = [];
|
|
||||||
if (this.elDef) {
|
|
||||||
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
|
|
||||||
const childDef = this.view.def.nodes[i];
|
|
||||||
if (childDef.type === NodeType.Provider) {
|
|
||||||
tokens.push(childDef.provider.token);
|
|
||||||
} else {
|
|
||||||
i += childDef.childCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
get references(): {[key: string]: any} {
|
|
||||||
const references: {[key: string]: any} = {};
|
|
||||||
if (this.elDef) {
|
|
||||||
collectReferences(this.view, this.elDef, references);
|
|
||||||
|
|
||||||
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
|
|
||||||
const childDef = this.view.def.nodes[i];
|
|
||||||
if (childDef.type === NodeType.Provider) {
|
|
||||||
collectReferences(this.view, childDef, references);
|
|
||||||
} else {
|
|
||||||
i += childDef.childCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return references;
|
|
||||||
}
|
|
||||||
get context(): any { return this.view.context; }
|
|
||||||
get source(): string {
|
|
||||||
if (this.nodeDef.type === NodeType.Text) {
|
|
||||||
return this.nodeDef.text.source;
|
|
||||||
} else {
|
|
||||||
return this.elDef.element.source;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
get componentRenderElement() {
|
|
||||||
const elData = findHostElement(this.view);
|
|
||||||
return elData ? elData.renderElement : undefined;
|
|
||||||
}
|
|
||||||
get renderNode(): any {
|
|
||||||
let nodeDef = this.nodeDef.type === NodeType.Text ? this.nodeDef : this.elDef;
|
|
||||||
return renderNode(this.view, nodeDef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function findHostElement(view: ViewData): ElementData {
|
|
||||||
while (view && !isComponentView(view)) {
|
|
||||||
view = view.parent;
|
|
||||||
}
|
|
||||||
if (view.parent) {
|
|
||||||
const hostData = asElementData(view.parent, view.parentIndex);
|
|
||||||
return hostData;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key: string]: any}) {
|
|
||||||
for (let queryId in nodeDef.matchedQueries) {
|
|
||||||
if (queryId.startsWith('#')) {
|
|
||||||
references[queryId.slice(1)] = getQueryValue(view, nodeDef, queryId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {ViewEncapsulation} from '../metadata/view';
|
||||||
|
import * as v1 from '../render/api';
|
||||||
|
|
||||||
|
import {DebugContext, RendererV2} from './types';
|
||||||
|
|
||||||
|
export class DirectDomRenderer implements RendererV2 {
|
||||||
|
createElement(name: string): any { return document.createElement(name); }
|
||||||
|
createComment(value: string): any { return document.createComment(value); }
|
||||||
|
createText(value: string): any { return document.createTextNode(value); }
|
||||||
|
appendChild(parent: any, newChild: any): void { parent.appendChild(newChild); }
|
||||||
|
insertBefore(parent: any, newChild: any, refChild: any): void {
|
||||||
|
if (parent) {
|
||||||
|
parent.insertBefore(newChild, refChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeChild(parent: any, oldChild: any): void {
|
||||||
|
if (parent) {
|
||||||
|
parent.removeChild(oldChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectRootElement(selectorOrNode: string|any, debugInfo?: DebugContext): any {
|
||||||
|
let el: any;
|
||||||
|
if (typeof selectorOrNode === 'string') {
|
||||||
|
el = document.querySelector(selectorOrNode);
|
||||||
|
} else {
|
||||||
|
el = selectorOrNode;
|
||||||
|
}
|
||||||
|
el.textContent = '';
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
parentNode(node: any): any { return node.parentNode; }
|
||||||
|
nextSibling(node: any): any { return node.nextSiblibng; }
|
||||||
|
setAttribute(el: any, name: string, value: string): void { return el.setAttribute(name, value); }
|
||||||
|
removeAttribute(el: any, name: string): void { el.removeAttribute(name); }
|
||||||
|
addClass(el: any, name: string): void { el.classList.add(name); }
|
||||||
|
removeClass(el: any, name: string): void { el.classList.remove(name); }
|
||||||
|
setStyle(el: any, style: string, value: any): void { el.style[style] = value; }
|
||||||
|
removeStyle(el: any, style: string): void {
|
||||||
|
// IE requires '' instead of null
|
||||||
|
// see https://github.com/angular/angular/issues/7916
|
||||||
|
(el.style as any)[style] = '';
|
||||||
|
}
|
||||||
|
setProperty(el: any, name: string, value: any): void { el[name] = value; }
|
||||||
|
setText(node: any, value: string): void { node.nodeValue = value; }
|
||||||
|
listen(target: any, eventName: string, callback: (event: any) => boolean): () => void {
|
||||||
|
let renderTarget: any;
|
||||||
|
switch (target) {
|
||||||
|
case 'window':
|
||||||
|
renderTarget = window;
|
||||||
|
break;
|
||||||
|
case 'document':
|
||||||
|
renderTarget = document;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
renderTarget = target;
|
||||||
|
}
|
||||||
|
const closure = (event: any) => {
|
||||||
|
if (callback(event) === false) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
renderTarget.addEventListener(eventName, closure);
|
||||||
|
return () => renderTarget.removeEventListener(eventName, closure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const EMPTY_V1_RENDER_COMPONENT_TYPE =
|
||||||
|
new v1.RenderComponentType('EMPTY', '', 0, ViewEncapsulation.None, [], {});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A temporal implementation of `Renderer` until we migrated our current renderer
|
||||||
|
* in all packages to the new API.
|
||||||
|
*
|
||||||
|
* Note that this is not complete, e.g. does not support shadow dom, view encapsulation, ...!
|
||||||
|
*/
|
||||||
|
export class LegacyRendererAdapter implements RendererV2 {
|
||||||
|
private _delegate: v1.Renderer;
|
||||||
|
constructor(rootDelegate: v1.RootRenderer) {
|
||||||
|
this._delegate = rootDelegate.renderComponent(EMPTY_V1_RENDER_COMPONENT_TYPE);
|
||||||
|
}
|
||||||
|
createElement(name: string, debugInfo?: DebugContext): any {
|
||||||
|
return this._delegate.createElement(null, name, debugInfo);
|
||||||
|
}
|
||||||
|
createComment(value: string, debugInfo?: DebugContext): any {
|
||||||
|
return this._delegate.createTemplateAnchor(null, debugInfo);
|
||||||
|
}
|
||||||
|
createText(value: string, debugInfo?: DebugContext): any {
|
||||||
|
return this._delegate.createText(null, value, debugInfo);
|
||||||
|
}
|
||||||
|
appendChild(parent: any, newChild: any): void { this._delegate.projectNodes(parent, [newChild]); }
|
||||||
|
insertBefore(parent: any, newChild: any, refChild: any): void {
|
||||||
|
const beforeSibling = refChild.nextSiblingOf ? refChild.nextSiblingOf : refChild;
|
||||||
|
this._delegate.attachViewAfter(beforeSibling, [newChild]);
|
||||||
|
}
|
||||||
|
removeChild(parent: any, oldChild: any): void {
|
||||||
|
if (parent) {
|
||||||
|
this._delegate.detachView([oldChild]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectRootElement(selectorOrNode: any, debugInfo?: DebugContext): any {
|
||||||
|
return this._delegate.selectRootElement(selectorOrNode, debugInfo);
|
||||||
|
}
|
||||||
|
parentNode(node: any): any { return {parentOf: node}; }
|
||||||
|
nextSibling(node: any): any { return {nextSiblingOf: node}; }
|
||||||
|
setAttribute(el: any, name: string, value: string): void {
|
||||||
|
this._delegate.setElementAttribute(el, name, value);
|
||||||
|
}
|
||||||
|
removeAttribute(el: any, name: string): void {
|
||||||
|
this._delegate.setElementAttribute(el, name, null);
|
||||||
|
}
|
||||||
|
addClass(el: any, name: string): void { this._delegate.setElementClass(el, name, true); }
|
||||||
|
removeClass(el: any, name: string): void { this._delegate.setElementClass(el, name, false); }
|
||||||
|
setStyle(el: any, style: string, value: any): void {
|
||||||
|
this._delegate.setElementStyle(el, style, value);
|
||||||
|
}
|
||||||
|
removeStyle(el: any, style: string): void { this._delegate.setElementStyle(el, style, null); }
|
||||||
|
setProperty(el: any, name: string, value: any): void {
|
||||||
|
this._delegate.setElementProperty(el, name, value);
|
||||||
|
}
|
||||||
|
setText(node: any, value: string): void { this._delegate.setText(node, value); }
|
||||||
|
listen(target: any, eventName: string, callback: (event: any) => boolean): () => void {
|
||||||
|
if (typeof target === 'string') {
|
||||||
|
return <any>this._delegate.listenGlobal(target, eventName, callback);
|
||||||
|
} else {
|
||||||
|
return <any>this._delegate.listen(target, eventName, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,360 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {isDevMode} from '../application_ref';
|
||||||
|
import {Injectable, Injector} from '../di';
|
||||||
|
import {looseIdentical} from '../facade/lang';
|
||||||
|
import {ElementRef} from '../linker/element_ref';
|
||||||
|
import * as v1renderer from '../render/api';
|
||||||
|
import {Sanitizer, SecurityContext} from '../security';
|
||||||
|
import {Type} from '../type';
|
||||||
|
|
||||||
|
import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
|
||||||
|
import {resolveDep} from './provider';
|
||||||
|
import {getQueryValue} from './query';
|
||||||
|
import {createInjector} from './refs';
|
||||||
|
import {DirectDomRenderer, LegacyRendererAdapter} from './renderer';
|
||||||
|
import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types';
|
||||||
|
import {checkBinding, findElementDef, isComponentView, parentDiIndex, renderNode, resolveViewDefinition, rootRenderNodes} from './util';
|
||||||
|
import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view';
|
||||||
|
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
|
||||||
|
|
||||||
|
let initialized = false;
|
||||||
|
|
||||||
|
export function initServicesIfNeeded() {
|
||||||
|
if (initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
const services = isDevMode() ? createDebugServices() : createProdServices();
|
||||||
|
Services.setCurrentNode = services.setCurrentNode;
|
||||||
|
Services.createRootView = services.createRootView;
|
||||||
|
Services.createEmbeddedView = services.createEmbeddedView;
|
||||||
|
Services.checkAndUpdateView = services.checkAndUpdateView;
|
||||||
|
Services.checkNoChangesView = services.checkNoChangesView;
|
||||||
|
Services.destroyView = services.destroyView;
|
||||||
|
Services.attachEmbeddedView = services.attachEmbeddedView,
|
||||||
|
Services.detachEmbeddedView = services.detachEmbeddedView,
|
||||||
|
Services.moveEmbeddedView = services.moveEmbeddedView;
|
||||||
|
Services.resolveDep = services.resolveDep;
|
||||||
|
Services.createDebugContext = services.createDebugContext;
|
||||||
|
Services.handleEvent = services.handleEvent;
|
||||||
|
Services.updateView = services.updateView;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProdServices() {
|
||||||
|
return {
|
||||||
|
setCurrentNode: () => {},
|
||||||
|
createRootView: createProdRootView,
|
||||||
|
createEmbeddedView: createEmbeddedView,
|
||||||
|
checkAndUpdateView: checkAndUpdateView,
|
||||||
|
checkNoChangesView: checkNoChangesView,
|
||||||
|
destroyView: destroyView,
|
||||||
|
attachEmbeddedView: attachEmbeddedView,
|
||||||
|
detachEmbeddedView: detachEmbeddedView,
|
||||||
|
moveEmbeddedView: moveEmbeddedView,
|
||||||
|
resolveDep: resolveDep,
|
||||||
|
createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex),
|
||||||
|
handleEvent: (view: ViewData, nodeIndex: number, eventName: string, event: any) =>
|
||||||
|
view.def.handleEvent(view, nodeIndex, eventName, event),
|
||||||
|
updateView: (check: NodeCheckFn, view: ViewData) => view.def.update(check, view)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDebugServices() {
|
||||||
|
return {
|
||||||
|
setCurrentNode: debugSetCurrentNode,
|
||||||
|
createRootView: debugCreateRootView,
|
||||||
|
createEmbeddedView: debugCreateEmbeddedView,
|
||||||
|
checkAndUpdateView: debugCheckAndUpdateView,
|
||||||
|
checkNoChangesView: debugCheckNoChangesView,
|
||||||
|
destroyView: debugDestroyView,
|
||||||
|
attachEmbeddedView: attachEmbeddedView,
|
||||||
|
detachEmbeddedView: detachEmbeddedView,
|
||||||
|
moveEmbeddedView: moveEmbeddedView,
|
||||||
|
resolveDep: resolveDep,
|
||||||
|
createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex),
|
||||||
|
handleEvent: debugHandleEvent,
|
||||||
|
updateView: debugUpdateView
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProdRootView(
|
||||||
|
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any,
|
||||||
|
def: ViewDefinition, context?: any): ViewData {
|
||||||
|
return createRootView(
|
||||||
|
createRootData(injector, 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,
|
||||||
|
element: root.element,
|
||||||
|
renderer: new DebugRenderer(root.renderer),
|
||||||
|
sanitizer: root.sanitizer
|
||||||
|
};
|
||||||
|
return callWithDebugContext('create', createRootView, null, [debugRoot, def, context]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRootData(
|
||||||
|
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: any): RootData {
|
||||||
|
const sanitizer = injector.get(Sanitizer);
|
||||||
|
// TODO(tbosch): once the new renderer interface is implemented via platform-browser,
|
||||||
|
// just get it via the injector and drop LegacyRendererAdapter and DirectDomRenderer.
|
||||||
|
const renderer = isDevMode() ? new LegacyRendererAdapter(injector.get(v1renderer.RootRenderer)) :
|
||||||
|
new DirectDomRenderer();
|
||||||
|
const rootElement =
|
||||||
|
rootSelectorOrNode ? renderer.selectRootElement(rootSelectorOrNode) : undefined;
|
||||||
|
return {injector, projectableNodes, element: rootElement, sanitizer, renderer};
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugCreateEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
||||||
|
return callWithDebugContext('create', createEmbeddedView, null, [parent, anchorDef, context]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugCheckAndUpdateView(view: ViewData) {
|
||||||
|
return callWithDebugContext('detectChanges', checkAndUpdateView, null, [view]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugCheckNoChangesView(view: ViewData) {
|
||||||
|
return callWithDebugContext('checkNoChanges', checkNoChangesView, null, [view]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugDestroyView(view: ViewData) {
|
||||||
|
return callWithDebugContext('destroyView', destroyView, null, [view]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let _currentAction: string;
|
||||||
|
let _currentView: ViewData;
|
||||||
|
let _currentNodeIndex: number;
|
||||||
|
|
||||||
|
function debugSetCurrentNode(view: ViewData, nodeIndex: number) {
|
||||||
|
_currentView = view;
|
||||||
|
_currentNodeIndex = nodeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugHandleEvent(view: ViewData, nodeIndex: number, eventName: string, event: any) {
|
||||||
|
if (view.state & ViewState.Destroyed) {
|
||||||
|
throw viewDestroyedError(_currentAction);
|
||||||
|
}
|
||||||
|
debugSetCurrentNode(view, nodeIndex);
|
||||||
|
return callWithDebugContext(
|
||||||
|
'handleEvent', view.def.handleEvent, null, [view, nodeIndex, eventName, event]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugUpdateView(check: NodeCheckFn, view: ViewData) {
|
||||||
|
if (view.state & ViewState.Destroyed) {
|
||||||
|
throw viewDestroyedError(_currentAction);
|
||||||
|
}
|
||||||
|
debugSetCurrentNode(view, nextNodeIndexWithBinding(view, 0));
|
||||||
|
return view.def.update(debugCheckFn, view);
|
||||||
|
|
||||||
|
function debugCheckFn(
|
||||||
|
view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any,
|
||||||
|
v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any) {
|
||||||
|
const values = argStyle === ArgumentType.Dynamic ? v0 : [].slice.call(arguments, 3);
|
||||||
|
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.ProviderProperty) &&
|
||||||
|
checkBinding(view, nodeDef, i, value)) {
|
||||||
|
const elIndex = nodeDef.type === NodeType.Provider ? nodeDef.parent : nodeDef.index;
|
||||||
|
setBindingDebugInfo(
|
||||||
|
view.root.renderer, asElementData(view, elIndex).renderElement, binding.nonMinifiedName,
|
||||||
|
value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result = check(view, nodeIndex, <any>argStyle, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||||
|
|
||||||
|
debugSetCurrentNode(view, nextNodeIndexWithBinding(view, nodeIndex));
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBindingDebugInfo(renderer: RendererV2, renderNode: any, propName: string, value: any) {
|
||||||
|
try {
|
||||||
|
renderer.setAttribute(
|
||||||
|
renderNode, `ng-reflect-${camelCaseToDashCase(propName)}`, value ? value.toString() : null);
|
||||||
|
} catch (e) {
|
||||||
|
renderer.setAttribute(
|
||||||
|
renderNode, `ng-reflect-${camelCaseToDashCase(propName)}`,
|
||||||
|
'[ERROR] Exception while trying to serialize the value');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||||
|
|
||||||
|
function camelCaseToDashCase(input: string): string {
|
||||||
|
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextNodeIndexWithBinding(view: ViewData, nodeIndex: number): number {
|
||||||
|
for (let i = nodeIndex; i < view.def.nodes.length; i++) {
|
||||||
|
const nodeDef = view.def.nodes[i];
|
||||||
|
if (nodeDef.bindings && nodeDef.bindings.length) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DebugRenderer implements RendererV2 {
|
||||||
|
constructor(private _delegate: RendererV2) {}
|
||||||
|
createElement(name: string): any {
|
||||||
|
return this._delegate.createElement(name, 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): void {
|
||||||
|
return this._delegate.setAttribute(el, name, value);
|
||||||
|
}
|
||||||
|
removeAttribute(el: any, name: string): void { return this._delegate.removeAttribute(el, name); }
|
||||||
|
addClass(el: any, name: string): void { return this._delegate.addClass(el, name); }
|
||||||
|
removeClass(el: any, name: string): void { return this._delegate.removeClass(el, name); }
|
||||||
|
setStyle(el: any, style: string, value: any): void {
|
||||||
|
return this._delegate.setStyle(el, style, value);
|
||||||
|
}
|
||||||
|
removeStyle(el: any, style: string): void { return this._delegate.removeStyle(el, style); }
|
||||||
|
setProperty(el: any, name: string, value: any): void {
|
||||||
|
return this._delegate.setProperty(el, name, value);
|
||||||
|
}
|
||||||
|
setText(node: any, value: string): void { return this._delegate.setText(node, value); }
|
||||||
|
listen(target: 'window'|'document'|any, eventName: string, callback: (event: any) => boolean):
|
||||||
|
() => void {
|
||||||
|
return this._delegate.listen(target, eventName, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DebugContext_ implements DebugContext {
|
||||||
|
private nodeDef: NodeDef;
|
||||||
|
private elDef: NodeDef;
|
||||||
|
constructor(public view: ViewData, public nodeIndex: number) {
|
||||||
|
if (nodeIndex == null) {
|
||||||
|
this.nodeIndex = nodeIndex = view.parentIndex;
|
||||||
|
this.view = view = view.parent;
|
||||||
|
}
|
||||||
|
this.nodeDef = view.def.nodes[nodeIndex];
|
||||||
|
this.elDef = findElementDef(view, nodeIndex);
|
||||||
|
}
|
||||||
|
get injector(): Injector { return createInjector(this.view, this.elDef.index); }
|
||||||
|
get component(): any { return this.view.component; }
|
||||||
|
get providerTokens(): any[] {
|
||||||
|
const tokens: any[] = [];
|
||||||
|
if (this.elDef) {
|
||||||
|
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
|
||||||
|
const childDef = this.view.def.nodes[i];
|
||||||
|
if (childDef.type === NodeType.Provider) {
|
||||||
|
tokens.push(childDef.provider.token);
|
||||||
|
} else {
|
||||||
|
i += childDef.childCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
get references(): {[key: string]: any} {
|
||||||
|
const references: {[key: string]: any} = {};
|
||||||
|
if (this.elDef) {
|
||||||
|
collectReferences(this.view, this.elDef, references);
|
||||||
|
|
||||||
|
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
|
||||||
|
const childDef = this.view.def.nodes[i];
|
||||||
|
if (childDef.type === NodeType.Provider) {
|
||||||
|
collectReferences(this.view, childDef, references);
|
||||||
|
} else {
|
||||||
|
i += childDef.childCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return references;
|
||||||
|
}
|
||||||
|
get context(): any { return this.view.context; }
|
||||||
|
get source(): string {
|
||||||
|
if (this.nodeDef.type === NodeType.Text) {
|
||||||
|
return this.nodeDef.text.source;
|
||||||
|
} else {
|
||||||
|
return this.elDef.element.source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get componentRenderElement() {
|
||||||
|
const elData = findHostElement(this.view);
|
||||||
|
return elData ? elData.renderElement : undefined;
|
||||||
|
}
|
||||||
|
get renderNode(): any {
|
||||||
|
let nodeDef = this.nodeDef.type === NodeType.Text ? this.nodeDef : this.elDef;
|
||||||
|
return renderNode(this.view, nodeDef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findHostElement(view: ViewData): ElementData {
|
||||||
|
while (view && !isComponentView(view)) {
|
||||||
|
view = view.parent;
|
||||||
|
}
|
||||||
|
if (view.parent) {
|
||||||
|
const hostData = asElementData(view.parent, view.parentIndex);
|
||||||
|
return hostData;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key: string]: any}) {
|
||||||
|
for (let queryId in nodeDef.matchedQueries) {
|
||||||
|
if (queryId.startsWith('#')) {
|
||||||
|
references[queryId.slice(1)] = getQueryValue(view, nodeDef, queryId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callWithDebugContext(action: string, fn: any, self: any, args: any[]) {
|
||||||
|
const oldAction = _currentAction;
|
||||||
|
const oldView = _currentView;
|
||||||
|
const oldNodeIndex = _currentNodeIndex;
|
||||||
|
try {
|
||||||
|
_currentAction = action;
|
||||||
|
const result = fn.apply(self, args);
|
||||||
|
_currentView = oldView;
|
||||||
|
_currentNodeIndex = oldNodeIndex;
|
||||||
|
_currentAction = oldAction;
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
if (isViewDebugError(e) || !_currentView) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
throw viewWrappedDebugError(e, getCurrentDebugContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentDebugContext() {
|
||||||
|
return new DebugContext_(_currentView, _currentNodeIndex);
|
||||||
|
}
|
|
@ -9,7 +9,7 @@
|
||||||
import {isDevMode} from '../application_ref';
|
import {isDevMode} from '../application_ref';
|
||||||
import {looseIdentical} from '../facade/lang';
|
import {looseIdentical} from '../facade/lang';
|
||||||
|
|
||||||
import {BindingDef, BindingType, DebugContext, NodeData, NodeDef, NodeFlags, NodeType, Refs, RootData, TextData, ViewData, ViewFlags, asElementData, asTextData} from './types';
|
import {BindingDef, BindingType, DebugContext, NodeData, NodeDef, NodeFlags, NodeType, RootData, Services, TextData, ViewData, ViewFlags, asElementData, asTextData} from './types';
|
||||||
import {checkAndUpdateBinding, sliceErrorStack, unwrapValue} from './util';
|
import {checkAndUpdateBinding, sliceErrorStack, unwrapValue} from './util';
|
||||||
|
|
||||||
export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
|
export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
|
||||||
|
@ -53,14 +53,10 @@ export function createText(view: ViewData, renderHost: any, def: NodeDef): TextD
|
||||||
const parentNode =
|
const parentNode =
|
||||||
def.parent != null ? asElementData(view, def.parent).renderElement : renderHost;
|
def.parent != null ? asElementData(view, def.parent).renderElement : renderHost;
|
||||||
let renderNode: any;
|
let renderNode: any;
|
||||||
if (view.renderer) {
|
const renderer = view.root.renderer;
|
||||||
const debugContext = isDevMode() ? Refs.createDebugContext(view, def.index) : undefined;
|
renderNode = renderer.createText(def.text.prefix);
|
||||||
renderNode = view.renderer.createText(parentNode, def.text.prefix, debugContext);
|
if (parentNode) {
|
||||||
} else {
|
renderer.appendChild(parentNode, renderNode);
|
||||||
renderNode = document.createTextNode(def.text.prefix);
|
|
||||||
if (parentNode) {
|
|
||||||
parentNode.appendChild(renderNode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {renderText: renderNode};
|
return {renderText: renderNode};
|
||||||
}
|
}
|
||||||
|
@ -121,11 +117,7 @@ export function checkAndUpdateTextInline(
|
||||||
}
|
}
|
||||||
value = def.text.prefix + value;
|
value = def.text.prefix + value;
|
||||||
const renderNode = asTextData(view, def.index).renderText;
|
const renderNode = asTextData(view, def.index).renderText;
|
||||||
if (view.renderer) {
|
view.root.renderer.setText(renderNode, value);
|
||||||
view.renderer.setText(renderNode, value);
|
|
||||||
} else {
|
|
||||||
renderNode.nodeValue = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,11 +138,7 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values:
|
||||||
}
|
}
|
||||||
value = def.text.prefix + value;
|
value = def.text.prefix + value;
|
||||||
const renderNode = asTextData(view, def.index).renderText;
|
const renderNode = asTextData(view, def.index).renderText;
|
||||||
if (view.renderer) {
|
view.root.renderer.setText(renderNode, value);
|
||||||
view.renderer.setText(renderNode, value);
|
|
||||||
} else {
|
|
||||||
renderNode.nodeValue = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
|
|
||||||
import {PipeTransform} from '../change_detection/change_detection';
|
import {PipeTransform} from '../change_detection/change_detection';
|
||||||
import {Injector} from '../di';
|
import {Injector} from '../di';
|
||||||
import {ComponentFactory} from '../linker/component_factory';
|
import {ComponentRef} from '../linker/component_factory';
|
||||||
import {QueryList} from '../linker/query_list';
|
import {QueryList} from '../linker/query_list';
|
||||||
import {TemplateRef} from '../linker/template_ref';
|
import {TemplateRef} from '../linker/template_ref';
|
||||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||||
import {ViewRef} from '../linker/view_ref';
|
import {ViewRef} from '../linker/view_ref';
|
||||||
import {RenderComponentType, RenderDebugInfo, Renderer, RootRenderer} from '../render/api';
|
import {ViewEncapsulation} from '../metadata/view';
|
||||||
import {Sanitizer, SecurityContext} from '../security';
|
import {Sanitizer, SecurityContext} from '../security';
|
||||||
|
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
|
@ -22,7 +22,7 @@ import {Sanitizer, SecurityContext} from '../security';
|
||||||
|
|
||||||
export interface ViewDefinition {
|
export interface ViewDefinition {
|
||||||
flags: ViewFlags;
|
flags: ViewFlags;
|
||||||
componentType: RenderComponentType;
|
component: ComponentDefinition;
|
||||||
update: ViewUpdateFn;
|
update: ViewUpdateFn;
|
||||||
handleEvent: ViewHandleEventFn;
|
handleEvent: ViewHandleEventFn;
|
||||||
/**
|
/**
|
||||||
|
@ -49,10 +49,24 @@ export interface ViewDefinition {
|
||||||
|
|
||||||
export type ViewDefinitionFactory = () => ViewDefinition;
|
export type ViewDefinitionFactory = () => ViewDefinition;
|
||||||
|
|
||||||
export type ViewUpdateFn = (view: ViewData) => void;
|
export type ViewUpdateFn = (check: NodeCheckFn, view: ViewData) => void;
|
||||||
|
|
||||||
export type ViewHandleEventFn =
|
// helper functions to create an overloaded function type.
|
||||||
(view: ViewData, nodeIndex: number, eventName: string, event: any) => boolean;
|
export declare function _nodeCheckFn(
|
||||||
|
view: ViewData, nodeIndex: number, argStyle: ArgumentType.Dynamic, values: any[]): any;
|
||||||
|
export declare function _nodeCheckFn(
|
||||||
|
view: ViewData, nodeIndex: number, argStyle: ArgumentType.Inline, v0?: any, v1?: any,
|
||||||
|
v2?: any, v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any):
|
||||||
|
any;
|
||||||
|
|
||||||
|
export type NodeCheckFn = typeof _nodeCheckFn;
|
||||||
|
|
||||||
|
export type ViewHandleEventFn =
|
||||||
|
(view: ViewData, nodeIndex: number, eventName: string, event: any) => boolean;
|
||||||
|
|
||||||
|
export enum ArgumentType {
|
||||||
|
Inline, Dynamic
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bitmask for ViewDefintion.flags.
|
* Bitmask for ViewDefintion.flags.
|
||||||
|
@ -63,6 +77,12 @@ export enum ViewFlags {
|
||||||
OnPush = 1 << 2
|
OnPush = 1 << 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ComponentDefinition {
|
||||||
|
id: string;
|
||||||
|
encapsulation: ViewEncapsulation;
|
||||||
|
styles: string[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A node definition in the view.
|
* A node definition in the view.
|
||||||
*
|
*
|
||||||
|
@ -265,7 +285,6 @@ export interface NgContentDef {
|
||||||
*/
|
*/
|
||||||
export interface ViewData {
|
export interface ViewData {
|
||||||
def: ViewDefinition;
|
def: ViewDefinition;
|
||||||
renderer: Renderer;
|
|
||||||
root: RootData;
|
root: RootData;
|
||||||
// index of parent element / anchor. Not the index
|
// index of parent element / anchor. Not the index
|
||||||
// of the provider with the component view.
|
// of the provider with the component view.
|
||||||
|
@ -389,59 +408,103 @@ export function asQueryList(view: ViewData, index: number): QueryList<any> {
|
||||||
export interface RootData {
|
export interface RootData {
|
||||||
injector: Injector;
|
injector: Injector;
|
||||||
projectableNodes: any[][];
|
projectableNodes: any[][];
|
||||||
selectorOrNode: string|any;
|
element: any;
|
||||||
renderer: RootRenderer;
|
renderer: RendererV2;
|
||||||
sanitizer: Sanitizer;
|
sanitizer: Sanitizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO(tbosch): move this interface into @angular/core/src/render/api,
|
||||||
|
* and implement it in @angular/platform-browser, ...
|
||||||
|
*/
|
||||||
|
export interface RendererV2 {
|
||||||
|
createElement(name: string, debugInfo?: RenderDebugContext): any;
|
||||||
|
createComment(value: string, debugInfo?: RenderDebugContext): any;
|
||||||
|
createText(value: string, debugInfo?: RenderDebugContext): any;
|
||||||
|
appendChild(parent: any, newChild: any): void;
|
||||||
|
insertBefore(parent: any, newChild: any, refChild: any): void;
|
||||||
|
removeChild(parent: any, oldChild: any): void;
|
||||||
|
selectRootElement(selectorOrNode: string|any, debugInfo?: RenderDebugContext): any;
|
||||||
|
/**
|
||||||
|
* Attention: On WebWorkers, this will always return a value,
|
||||||
|
* as we are asking for a result synchronously. I.e.
|
||||||
|
* the caller can't rely on checking whether this is null or not.
|
||||||
|
*/
|
||||||
|
parentNode(node: any): any;
|
||||||
|
/**
|
||||||
|
* Attention: On WebWorkers, this will always return a value,
|
||||||
|
* as we are asking for a result synchronously. I.e.
|
||||||
|
* the caller can't rely on checking whether this is null or not.
|
||||||
|
*/
|
||||||
|
nextSibling(node: any): any;
|
||||||
|
setAttribute(el: any, name: string, value: string): void;
|
||||||
|
removeAttribute(el: any, name: string): void;
|
||||||
|
addClass(el: any, name: string): void;
|
||||||
|
removeClass(el: any, name: string): void;
|
||||||
|
setStyle(el: any, style: string, value: any): void;
|
||||||
|
removeStyle(el: any, style: string): void;
|
||||||
|
setProperty(el: any, name: string, value: any): void;
|
||||||
|
setText(node: any, value: string): void;
|
||||||
|
listen(target: 'window'|'document'|any, eventName: string, callback: (event: any) => boolean):
|
||||||
|
() => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class RenderDebugContext {
|
||||||
|
abstract get injector(): Injector;
|
||||||
|
abstract get component(): any;
|
||||||
|
abstract get providerTokens(): any[];
|
||||||
|
abstract get references(): {[key: string]: any};
|
||||||
|
abstract get context(): any;
|
||||||
|
abstract get source(): string;
|
||||||
|
abstract get componentRenderElement(): any;
|
||||||
|
abstract get renderNode(): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class DebugContext extends RenderDebugContext {
|
||||||
|
abstract get view(): ViewData;
|
||||||
|
abstract get nodeIndex(): number;
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
// Other
|
// Other
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
export enum EntryAction {
|
|
||||||
CheckAndUpdate,
|
|
||||||
CheckNoChanges,
|
|
||||||
Create,
|
|
||||||
Destroy,
|
|
||||||
HandleEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DebugContext extends RenderDebugInfo {
|
export interface Services {
|
||||||
view: ViewData;
|
setCurrentNode(view: ViewData, nodeIndex: number): void;
|
||||||
nodeIndex: number;
|
createRootView(
|
||||||
componentRenderElement: any;
|
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: string|any,
|
||||||
renderNode: any;
|
def: ViewDefinition, context?: any): ViewData;
|
||||||
|
createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData;
|
||||||
|
checkAndUpdateView(view: ViewData): void;
|
||||||
|
checkNoChangesView(view: ViewData): void;
|
||||||
|
attachEmbeddedView(elementData: ElementData, viewIndex: number, view: ViewData): void;
|
||||||
|
detachEmbeddedView(elementData: ElementData, viewIndex: number): ViewData;
|
||||||
|
moveEmbeddedView(elementData: ElementData, oldViewIndex: number, newViewIndex: number): ViewData;
|
||||||
|
destroyView(view: ViewData): void;
|
||||||
|
resolveDep(
|
||||||
|
view: ViewData, requestNodeIndex: number, elIndex: number, depDef: DepDef,
|
||||||
|
notFoundValue?: any): any;
|
||||||
|
createDebugContext(view: ViewData, nodeIndex: number): DebugContext;
|
||||||
|
handleEvent: ViewHandleEventFn;
|
||||||
|
updateView: ViewUpdateFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is used to prevent cycles in the source files.
|
* This object is used to prevent cycles in the source files and to have a place where
|
||||||
|
* debug mode can hook it. It is lazily filled when `isDevMode` is known.
|
||||||
*/
|
*/
|
||||||
export abstract class Refs {
|
export const Services: Services = {
|
||||||
private static instance: Refs;
|
setCurrentNode: undefined,
|
||||||
|
createRootView: undefined,
|
||||||
static setInstance(instance: Refs) { Refs.instance = instance; }
|
createEmbeddedView: undefined,
|
||||||
static createComponentFactory(selector: string, viewDefFactory: ViewDefinitionFactory):
|
checkAndUpdateView: undefined,
|
||||||
ComponentFactory<any> {
|
checkNoChangesView: undefined,
|
||||||
return Refs.instance.createComponentFactory(selector, viewDefFactory);
|
destroyView: undefined,
|
||||||
}
|
attachEmbeddedView: undefined,
|
||||||
static createViewRef(data: ViewData): ViewRef { return Refs.instance.createViewRef(data); }
|
detachEmbeddedView: undefined,
|
||||||
static createViewContainerRef(view: ViewData, elIndex: number): ViewContainerRef {
|
moveEmbeddedView: undefined,
|
||||||
return Refs.instance.createViewContainerRef(view, elIndex);
|
resolveDep: undefined,
|
||||||
}
|
createDebugContext: undefined,
|
||||||
static createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any> {
|
handleEvent: undefined,
|
||||||
return Refs.instance.createTemplateRef(parentView, def);
|
updateView: undefined,
|
||||||
}
|
};
|
||||||
static createInjector(view: ViewData, elIndex: number): Injector {
|
|
||||||
return Refs.instance.createInjector(view, elIndex);
|
|
||||||
}
|
|
||||||
static createDebugContext(view: ViewData, nodeIndex: number): DebugContext {
|
|
||||||
return Refs.instance.createDebugContext(view, nodeIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract createComponentFactory(selector: string, viewDefFactory: ViewDefinitionFactory):
|
|
||||||
ComponentFactory<any>;
|
|
||||||
abstract createViewRef(data: ViewData): ViewRef;
|
|
||||||
abstract createViewContainerRef(view: ViewData, elIndex: number): ViewContainerRef;
|
|
||||||
abstract createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any>;
|
|
||||||
abstract createInjector(view: ViewData, elIndex: number): Injector;
|
|
||||||
abstract createDebugContext(view: ViewData, nodeIndex: number): DebugContext;
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,31 +10,30 @@ import {isDevMode} from '../application_ref';
|
||||||
import {WrappedValue, devModeEqual} from '../change_detection/change_detection';
|
import {WrappedValue, devModeEqual} from '../change_detection/change_detection';
|
||||||
import {SimpleChange} from '../change_detection/change_detection_util';
|
import {SimpleChange} from '../change_detection/change_detection_util';
|
||||||
import {Injector} from '../di';
|
import {Injector} from '../di';
|
||||||
import {looseIdentical} from '../facade/lang';
|
import {looseIdentical, stringify} from '../facade/lang';
|
||||||
import {TemplateRef} from '../linker/template_ref';
|
import {TemplateRef} from '../linker/template_ref';
|
||||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||||
import {ViewRef} from '../linker/view_ref';
|
import {ViewRef} from '../linker/view_ref';
|
||||||
import {Renderer} from '../render/api';
|
import {Renderer} from '../render/api';
|
||||||
|
|
||||||
import {expressionChangedAfterItHasBeenCheckedError, isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
|
import {expressionChangedAfterItHasBeenCheckedError, isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
|
||||||
import {DebugContext, ElementData, EntryAction, NodeData, NodeDef, NodeFlags, NodeType, Refs, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState, asElementData, asProviderData, asTextData} from './types';
|
import {DebugContext, ElementData, NodeData, NodeDef, NodeFlags, NodeType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState, asElementData, asProviderData, asTextData} from './types';
|
||||||
|
|
||||||
export function setBindingDebugInfo(
|
const _tokenKeyCache = new Map<any, string>();
|
||||||
renderer: Renderer, renderNode: any, propName: string, value: any) {
|
|
||||||
try {
|
export function tokenKey(token: any): string {
|
||||||
renderer.setBindingDebugInfo(
|
let key = _tokenKeyCache.get(token);
|
||||||
renderNode, `ng-reflect-${camelCaseToDashCase(propName)}`, value ? value.toString() : null);
|
if (!key) {
|
||||||
} catch (e) {
|
key = stringify(token) + '_' + _tokenKeyCache.size;
|
||||||
renderer.setBindingDebugInfo(
|
_tokenKeyCache.set(token, key);
|
||||||
renderNode, `ng-reflect-${camelCaseToDashCase(propName)}`,
|
|
||||||
'[ERROR] Exception while trying to serialize the value');
|
|
||||||
}
|
}
|
||||||
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CAMEL_CASE_REGEXP = /([A-Z])/g;
|
export function checkBinding(
|
||||||
|
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
|
||||||
function camelCaseToDashCase(input: string): string {
|
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
|
||||||
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
|
return !!(view.state & ViewState.FirstCheck) || !devModeEqual(oldValue, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkBindingNoChanges(
|
export function checkBindingNoChanges(
|
||||||
|
@ -42,7 +41,7 @@ export function checkBindingNoChanges(
|
||||||
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
|
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
|
||||||
if ((view.state & ViewState.FirstCheck) || !devModeEqual(oldValue, value)) {
|
if ((view.state & ViewState.FirstCheck) || !devModeEqual(oldValue, value)) {
|
||||||
throw expressionChangedAfterItHasBeenCheckedError(
|
throw expressionChangedAfterItHasBeenCheckedError(
|
||||||
Refs.createDebugContext(view, def.index), oldValue, value,
|
Services.createDebugContext(view, def.index), oldValue, value,
|
||||||
(view.state & ViewState.FirstCheck) !== 0);
|
(view.state & ViewState.FirstCheck) !== 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +65,6 @@ export function checkAndUpdateBinding(
|
||||||
|
|
||||||
export function dispatchEvent(
|
export function dispatchEvent(
|
||||||
view: ViewData, nodeIndex: number, eventName: string, event: any): boolean {
|
view: ViewData, nodeIndex: number, eventName: string, event: any): boolean {
|
||||||
setCurrentNode(view, nodeIndex);
|
|
||||||
let currView = view;
|
let currView = view;
|
||||||
while (currView) {
|
while (currView) {
|
||||||
if (currView.def.flags & ViewFlags.OnPush) {
|
if (currView.def.flags & ViewFlags.OnPush) {
|
||||||
|
@ -74,7 +72,7 @@ export function dispatchEvent(
|
||||||
}
|
}
|
||||||
currView = currView.parent;
|
currView = currView.parent;
|
||||||
}
|
}
|
||||||
return view.def.handleEvent(view, nodeIndex, eventName, event);
|
return Services.handleEvent(view, nodeIndex, eventName, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unwrapValue(value: any): any {
|
export function unwrapValue(value: any): any {
|
||||||
|
@ -159,68 +157,6 @@ export function sliceErrorStack(start: number, end: number): string {
|
||||||
return lines.slice(start, end).join('\n');
|
return lines.slice(start, end).join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
let _currentAction: EntryAction;
|
|
||||||
let _currentView: ViewData;
|
|
||||||
let _currentNodeIndex: number;
|
|
||||||
|
|
||||||
export function currentView() {
|
|
||||||
return _currentView;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function currentNodeIndex() {
|
|
||||||
return _currentNodeIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function currentAction() {
|
|
||||||
return _currentAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the node that is currently worked on.
|
|
||||||
* It needs to be called whenever we call user code,
|
|
||||||
* or code of the framework that might throw as a valid use case.
|
|
||||||
*/
|
|
||||||
export function setCurrentNode(view: ViewData, nodeIndex: number) {
|
|
||||||
if (view.state & ViewState.Destroyed) {
|
|
||||||
throw viewDestroyedError(_currentAction);
|
|
||||||
}
|
|
||||||
_currentView = view;
|
|
||||||
_currentNodeIndex = nodeIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a try/catch handler around the given function to wrap all
|
|
||||||
* errors that occur into new errors that contain the current debug info
|
|
||||||
* set via setCurrentNode.
|
|
||||||
*/
|
|
||||||
export function entryAction<A, R>(action: EntryAction, fn: (arg: A) => R): (arg: A) => R {
|
|
||||||
return <any>function(arg: any) {
|
|
||||||
const oldAction = _currentAction;
|
|
||||||
const oldView = _currentView;
|
|
||||||
const oldNodeIndex = _currentNodeIndex;
|
|
||||||
_currentAction = action;
|
|
||||||
// Note: We can't call `isDevMode()` outside of this closure as
|
|
||||||
// it might not have been initialized.
|
|
||||||
const result = isDevMode() ? callWithTryCatch(fn, arg) : fn(arg);
|
|
||||||
_currentAction = oldAction;
|
|
||||||
_currentView = oldView;
|
|
||||||
_currentNodeIndex = oldNodeIndex;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function callWithTryCatch(fn: (a: any) => any, arg: any): any {
|
|
||||||
try {
|
|
||||||
return fn(arg);
|
|
||||||
} catch (e) {
|
|
||||||
if (isViewDebugError(e) || !_currentView) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
const debugContext = Refs.createDebugContext(_currentView, _currentNodeIndex);
|
|
||||||
throw viewWrappedDebugError(e, debugContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function rootRenderNodes(view: ViewData): any[] {
|
export function rootRenderNodes(view: ViewData): any[] {
|
||||||
const renderNodes: any[] = [];
|
const renderNodes: any[] = [];
|
||||||
visitRootRenderNodes(view, RenderNodeAction.Collect, undefined, undefined, renderNodes);
|
visitRootRenderNodes(view, RenderNodeAction.Collect, undefined, undefined, renderNodes);
|
||||||
|
@ -269,7 +205,7 @@ export function visitProjectedRenderNodes(
|
||||||
const projectedNodes = view.root.projectableNodes[ngContentIndex];
|
const projectedNodes = view.root.projectableNodes[ngContentIndex];
|
||||||
if (projectedNodes) {
|
if (projectedNodes) {
|
||||||
for (let i = 0; i < projectedNodes.length; i++) {
|
for (let i = 0; i < projectedNodes.length; i++) {
|
||||||
execRenderNodeAction(projectedNodes[i], action, parentNode, nextSibling, target);
|
execRenderNodeAction(view, projectedNodes[i], action, parentNode, nextSibling, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,7 +219,7 @@ function visitRenderNode(
|
||||||
view, nodeDef.ngContent.index, action, parentNode, nextSibling, target);
|
view, nodeDef.ngContent.index, action, parentNode, nextSibling, target);
|
||||||
} else {
|
} else {
|
||||||
const rn = renderNode(view, nodeDef);
|
const rn = renderNode(view, nodeDef);
|
||||||
execRenderNodeAction(rn, action, parentNode, nextSibling, target);
|
execRenderNodeAction(view, rn, action, parentNode, nextSibling, target);
|
||||||
if (nodeDef.flags & NodeFlags.HasEmbeddedViews) {
|
if (nodeDef.flags & NodeFlags.HasEmbeddedViews) {
|
||||||
const embeddedViews = asElementData(view, nodeDef.index).embeddedViews;
|
const embeddedViews = asElementData(view, nodeDef.index).embeddedViews;
|
||||||
if (embeddedViews) {
|
if (embeddedViews) {
|
||||||
|
@ -296,16 +232,18 @@ function visitRenderNode(
|
||||||
}
|
}
|
||||||
|
|
||||||
function execRenderNodeAction(
|
function execRenderNodeAction(
|
||||||
renderNode: any, action: RenderNodeAction, parentNode: any, nextSibling: any, target: any[]) {
|
view: ViewData, renderNode: any, action: RenderNodeAction, parentNode: any, nextSibling: any,
|
||||||
|
target: any[]) {
|
||||||
|
const renderer = view.root.renderer;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case RenderNodeAction.AppendChild:
|
case RenderNodeAction.AppendChild:
|
||||||
parentNode.appendChild(renderNode);
|
renderer.appendChild(parentNode, renderNode);
|
||||||
break;
|
break;
|
||||||
case RenderNodeAction.InsertBefore:
|
case RenderNodeAction.InsertBefore:
|
||||||
parentNode.insertBefore(renderNode, nextSibling);
|
renderer.insertBefore(parentNode, renderNode, nextSibling);
|
||||||
break;
|
break;
|
||||||
case RenderNodeAction.RemoveChild:
|
case RenderNodeAction.RemoveChild:
|
||||||
parentNode.removeChild(renderNode);
|
renderer.removeChild(parentNode, renderNode);
|
||||||
break;
|
break;
|
||||||
case RenderNodeAction.Collect:
|
case RenderNodeAction.Collect:
|
||||||
target.push(renderNode);
|
target.push(renderNode);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RenderComponentType, Renderer} from '../render/api';
|
import {ViewEncapsulation} from '../metadata/view';
|
||||||
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
|
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
|
||||||
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
||||||
import {appendNgContent} from './ng_content';
|
import {appendNgContent} from './ng_content';
|
||||||
|
@ -14,14 +14,15 @@ import {callLifecycleHooksChildrenFirst, checkAndUpdateProviderDynamic, checkAnd
|
||||||
import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
|
import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
|
||||||
import {checkAndUpdateQuery, createQuery, queryDef} from './query';
|
import {checkAndUpdateQuery, createQuery, queryDef} from './query';
|
||||||
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
||||||
import {ElementDef, EntryAction, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, Refs, RootData, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList} from './types';
|
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 {checkBindingNoChanges, currentAction, currentNodeIndex, currentView, entryAction, isComponentView, resolveViewDefinition, setCurrentNode} from './util';
|
import {checkBindingNoChanges, isComponentView, resolveViewDefinition} from './util';
|
||||||
|
|
||||||
const NOOP = (): any => undefined;
|
const NOOP = (): any => undefined;
|
||||||
|
|
||||||
export function viewDef(
|
export function viewDef(
|
||||||
flags: ViewFlags, nodesWithoutIndices: NodeDef[], update?: ViewUpdateFn,
|
flags: ViewFlags, nodesWithoutIndices: NodeDef[], update?: ViewUpdateFn,
|
||||||
handleEvent?: ViewHandleEventFn, componentType?: RenderComponentType): ViewDefinition {
|
handleEvent?: ViewHandleEventFn, compId?: string, encapsulation?: ViewEncapsulation,
|
||||||
|
styles?: string[]): ViewDefinition {
|
||||||
// clone nodes and set auto calculated values
|
// clone nodes and set auto calculated values
|
||||||
if (nodesWithoutIndices.length === 0) {
|
if (nodesWithoutIndices.length === 0) {
|
||||||
throw new Error(`Illegal State: Views without nodes are not allowed!`);
|
throw new Error(`Illegal State: Views without nodes are not allowed!`);
|
||||||
|
@ -99,13 +100,15 @@ export function viewDef(
|
||||||
}
|
}
|
||||||
currentParent = newParent;
|
currentParent = newParent;
|
||||||
}
|
}
|
||||||
|
const componentDef =
|
||||||
|
compId ? <ComponentDefinition>{id: compId, encapsulation, styles} : undefined;
|
||||||
return {
|
return {
|
||||||
nodeFlags: viewNodeFlags,
|
nodeFlags: viewNodeFlags,
|
||||||
nodeMatchedQueries: viewMatchedQueries, flags,
|
nodeMatchedQueries: viewMatchedQueries, flags,
|
||||||
nodes: nodes, reverseChildNodes,
|
nodes: nodes, reverseChildNodes,
|
||||||
update: update || NOOP,
|
update: update || NOOP,
|
||||||
handleEvent: handleEvent || NOOP, componentType,
|
handleEvent: handleEvent || NOOP,
|
||||||
|
component: componentDef,
|
||||||
bindingCount: viewBindingCount,
|
bindingCount: viewBindingCount,
|
||||||
disposableCount: viewDisposableCount, lastRootNode
|
disposableCount: viewDisposableCount, lastRootNode
|
||||||
};
|
};
|
||||||
|
@ -196,27 +199,44 @@ function cloneAndModifyNode(nodeDef: NodeDef, values: {
|
||||||
bindingIndex: number,
|
bindingIndex: number,
|
||||||
disposableIndex: number,
|
disposableIndex: number,
|
||||||
}): NodeDef {
|
}): NodeDef {
|
||||||
const clonedNode: NodeDef = <any>{};
|
// Attention: don't use copyInto here to prevent v8 from treating this object
|
||||||
copyInto(nodeDef, clonedNode);
|
// as a dictionary!
|
||||||
|
return {
|
||||||
clonedNode.index = values.index;
|
type: nodeDef.type,
|
||||||
clonedNode.bindingIndex = values.bindingIndex;
|
index: values.index,
|
||||||
clonedNode.disposableIndex = values.disposableIndex;
|
reverseChildIndex: values.reverseChildIndex,
|
||||||
clonedNode.parent = values.parent;
|
parent: values.parent,
|
||||||
clonedNode.reverseChildIndex = values.reverseChildIndex;
|
childFlags: 0,
|
||||||
|
childMatchedQueries: {},
|
||||||
// Note: We can't set the value immediately, as we need to walk the children first.
|
bindingIndex: values.bindingIndex,
|
||||||
clonedNode.childFlags = 0;
|
disposableIndex: values.disposableIndex,
|
||||||
clonedNode.childMatchedQueries = {};
|
flags: nodeDef.flags,
|
||||||
return clonedNode;
|
matchedQueries: nodeDef.matchedQueries,
|
||||||
|
ngContentIndex: nodeDef.ngContentIndex,
|
||||||
|
childCount: nodeDef.childCount,
|
||||||
|
bindings: nodeDef.bindings,
|
||||||
|
disposableCount: nodeDef.disposableCount,
|
||||||
|
element: nodeDef.element,
|
||||||
|
provider: nodeDef.provider,
|
||||||
|
text: nodeDef.text,
|
||||||
|
pureExpression: nodeDef.pureExpression,
|
||||||
|
query: nodeDef.query,
|
||||||
|
ngContent: nodeDef.ngContent
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function cloneAndModifyElement(
|
function cloneAndModifyElement(
|
||||||
elementDef: ElementDef, values: {providerIndices: {[tokenKey: string]: number}}): ElementDef {
|
elementDef: ElementDef, values: {providerIndices: {[tokenKey: string]: number}}): ElementDef {
|
||||||
const clonedElement: ElementDef = <any>{};
|
// Attention: don't use copyInto here to prevent v8 from treating this object
|
||||||
copyInto(elementDef, clonedElement);
|
// as a dictionary!
|
||||||
clonedElement.providerIndices = values.providerIndices;
|
return {
|
||||||
return clonedElement;
|
name: elementDef.name,
|
||||||
|
attrs: elementDef.attrs,
|
||||||
|
outputs: elementDef.outputs,
|
||||||
|
template: elementDef.template,
|
||||||
|
providerIndices: values.providerIndices,
|
||||||
|
source: elementDef.source
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
||||||
|
@ -238,13 +258,6 @@ export function createRootView(root: RootData, def: ViewDefinition, context?: an
|
||||||
function createView(
|
function createView(
|
||||||
root: RootData, parent: ViewData, parentIndex: number, def: ViewDefinition): ViewData {
|
root: RootData, parent: ViewData, parentIndex: number, def: ViewDefinition): ViewData {
|
||||||
const nodes: NodeData[] = new Array(def.nodes.length);
|
const nodes: NodeData[] = new Array(def.nodes.length);
|
||||||
let renderer: Renderer;
|
|
||||||
if (def.flags != null && (def.flags & ViewFlags.DirectDom)) {
|
|
||||||
renderer = null;
|
|
||||||
} else {
|
|
||||||
renderer =
|
|
||||||
def.componentType ? root.renderer.renderComponent(def.componentType) : parent.renderer;
|
|
||||||
}
|
|
||||||
const disposables = def.disposableCount ? new Array(def.disposableCount) : undefined;
|
const disposables = def.disposableCount ? new Array(def.disposableCount) : undefined;
|
||||||
const view: ViewData = {
|
const view: ViewData = {
|
||||||
def,
|
def,
|
||||||
|
@ -252,7 +265,7 @@ function createView(
|
||||||
parentIndex,
|
parentIndex,
|
||||||
context: undefined,
|
context: undefined,
|
||||||
component: undefined, nodes,
|
component: undefined, nodes,
|
||||||
state: ViewState.FirstCheck | ViewState.ChecksEnabled, renderer, root,
|
state: ViewState.FirstCheck | ViewState.ChecksEnabled, root,
|
||||||
oldValues: new Array(def.bindingCount), disposables
|
oldValues: new Array(def.bindingCount), disposables
|
||||||
};
|
};
|
||||||
return view;
|
return view;
|
||||||
|
@ -263,25 +276,17 @@ function initView(view: ViewData, component: any, context: any) {
|
||||||
view.context = context;
|
view.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
const createViewNodes: (view: ViewData) => void =
|
function createViewNodes(view: ViewData) {
|
||||||
entryAction(EntryAction.CheckNoChanges, _createViewNodes);
|
|
||||||
|
|
||||||
function _createViewNodes(view: ViewData) {
|
|
||||||
let renderHost: any;
|
let renderHost: any;
|
||||||
if (isComponentView(view)) {
|
if (isComponentView(view)) {
|
||||||
renderHost = asElementData(view.parent, view.parentIndex).renderElement;
|
renderHost = asElementData(view.parent, view.parentIndex).renderElement;
|
||||||
if (view.renderer) {
|
|
||||||
renderHost = view.renderer.createViewRoot(renderHost);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const def = view.def;
|
const def = view.def;
|
||||||
const nodes = view.nodes;
|
const nodes = view.nodes;
|
||||||
for (let i = 0; i < def.nodes.length; i++) {
|
for (let i = 0; i < def.nodes.length; i++) {
|
||||||
const nodeDef = def.nodes[i];
|
const nodeDef = def.nodes[i];
|
||||||
// As the current node is being created, we have to use
|
Services.setCurrentNode(view, i);
|
||||||
// the parent node as the current node for error messages, ...
|
|
||||||
setCurrentNode(view, nodeDef.parent);
|
|
||||||
switch (nodeDef.type) {
|
switch (nodeDef.type) {
|
||||||
case NodeType.Element:
|
case NodeType.Element:
|
||||||
nodes[i] = createElement(view, renderHost, nodeDef) as any;
|
nodes[i] = createElement(view, renderHost, nodeDef) as any;
|
||||||
|
@ -324,22 +329,16 @@ function _createViewNodes(view: ViewData) {
|
||||||
execComponentViewsAction(view, ViewAction.CreateViewNodes);
|
execComponentViewsAction(view, ViewAction.CreateViewNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const checkNoChangesView: (view: ViewData) => void =
|
export function checkNoChangesView(view: ViewData) {
|
||||||
entryAction(EntryAction.CheckNoChanges, _checkNoChangesView);
|
Services.updateView(checkNoChangesNode, view);
|
||||||
|
|
||||||
function _checkNoChangesView(view: ViewData) {
|
|
||||||
view.def.update(view);
|
|
||||||
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
|
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
|
||||||
execQueriesAction(view, NodeFlags.HasContentQuery, QueryAction.CheckNoChanges);
|
execQueriesAction(view, NodeFlags.HasContentQuery, QueryAction.CheckNoChanges);
|
||||||
execComponentViewsAction(view, ViewAction.CheckNoChanges);
|
execComponentViewsAction(view, ViewAction.CheckNoChanges);
|
||||||
execQueriesAction(view, NodeFlags.HasViewQuery, QueryAction.CheckNoChanges);
|
execQueriesAction(view, NodeFlags.HasViewQuery, QueryAction.CheckNoChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const checkAndUpdateView: (view: ViewData) => void =
|
export function checkAndUpdateView(view: ViewData) {
|
||||||
entryAction(EntryAction.CheckAndUpdate, _checkAndUpdateView);
|
Services.updateView(checkAndUpdateNode, view);
|
||||||
|
|
||||||
function _checkAndUpdateView(view: ViewData) {
|
|
||||||
view.def.update(view);
|
|
||||||
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
|
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
|
||||||
execQueriesAction(view, NodeFlags.HasContentQuery, QueryAction.CheckAndUpdate);
|
execQueriesAction(view, NodeFlags.HasContentQuery, QueryAction.CheckAndUpdate);
|
||||||
|
|
||||||
|
@ -359,73 +358,58 @@ function _checkAndUpdateView(view: ViewData) {
|
||||||
view.state &= ~ViewState.FirstCheck;
|
view.state &= ~ViewState.FirstCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkNodeInline(
|
function checkAndUpdateNode(
|
||||||
v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any,
|
view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any,
|
||||||
v9?: any): any {
|
v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any {
|
||||||
const action = currentAction();
|
if (argStyle === ArgumentType.Inline) {
|
||||||
const view = currentView();
|
return checkAndUpdateNodeInline(view, nodeIndex, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||||
const nodeIndex = currentNodeIndex();
|
} else {
|
||||||
const nodeDef = view.def.nodes[nodeIndex];
|
return checkAndUpdateNodeDynamic(view, nodeIndex, v0);
|
||||||
switch (action) {
|
|
||||||
case EntryAction.CheckNoChanges:
|
|
||||||
checkNodeNoChangesInline(view, nodeIndex, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
|
||||||
break;
|
|
||||||
case EntryAction.CheckAndUpdate:
|
|
||||||
switch (nodeDef.type) {
|
|
||||||
case NodeType.Element:
|
|
||||||
checkAndUpdateElementInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
|
||||||
break;
|
|
||||||
case NodeType.Text:
|
|
||||||
checkAndUpdateTextInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
|
||||||
break;
|
|
||||||
case NodeType.Provider:
|
|
||||||
checkAndUpdateProviderInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
|
||||||
break;
|
|
||||||
case NodeType.PureExpression:
|
|
||||||
checkAndUpdatePureExpressionInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(`Illegal State: In action ${EntryAction[action]}`);
|
|
||||||
}
|
}
|
||||||
return nodeDef.type === NodeType.PureExpression ? asPureExpressionData(view, nodeIndex).value :
|
|
||||||
undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkNodeDynamic(values: any[]): any {
|
function checkAndUpdateNodeInline(
|
||||||
const action = currentAction();
|
view: ViewData, nodeIndex: number, v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any,
|
||||||
const view = currentView();
|
v6?: any, v7?: any, v8?: any, v9?: any): any {
|
||||||
const nodeIndex = currentNodeIndex();
|
|
||||||
const nodeDef = view.def.nodes[nodeIndex];
|
const nodeDef = view.def.nodes[nodeIndex];
|
||||||
switch (action) {
|
switch (nodeDef.type) {
|
||||||
case EntryAction.CheckNoChanges:
|
case NodeType.Element:
|
||||||
checkNodeNoChangesDynamic(view, nodeIndex, values);
|
return checkAndUpdateElementInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||||
break;
|
case NodeType.Text:
|
||||||
case EntryAction.CheckAndUpdate:
|
return checkAndUpdateTextInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||||
switch (nodeDef.type) {
|
case NodeType.Provider:
|
||||||
case NodeType.Element:
|
return checkAndUpdateProviderInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||||
checkAndUpdateElementDynamic(view, nodeDef, values);
|
case NodeType.PureExpression:
|
||||||
break;
|
return checkAndUpdatePureExpressionInline(
|
||||||
case NodeType.Text:
|
view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||||
checkAndUpdateTextDynamic(view, nodeDef, values);
|
|
||||||
break;
|
|
||||||
case NodeType.Provider:
|
|
||||||
checkAndUpdateProviderDynamic(view, nodeDef, values);
|
|
||||||
break;
|
|
||||||
case NodeType.PureExpression:
|
|
||||||
checkAndUpdatePureExpressionDynamic(view, nodeDef, values);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(`Illegal State: In action ${EntryAction[action]}`);
|
|
||||||
}
|
}
|
||||||
return nodeDef.type === NodeType.PureExpression ? asPureExpressionData(view, nodeIndex).value :
|
|
||||||
undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkNodeNoChangesInline(
|
function checkAndUpdateNodeDynamic(view: ViewData, nodeIndex: number, values: any[]): any {
|
||||||
|
const nodeDef = view.def.nodes[nodeIndex];
|
||||||
|
switch (nodeDef.type) {
|
||||||
|
case NodeType.Element:
|
||||||
|
return checkAndUpdateElementDynamic(view, nodeDef, values);
|
||||||
|
case NodeType.Text:
|
||||||
|
return checkAndUpdateTextDynamic(view, nodeDef, values);
|
||||||
|
case NodeType.Provider:
|
||||||
|
return checkAndUpdateProviderDynamic(view, nodeDef, values);
|
||||||
|
case NodeType.PureExpression:
|
||||||
|
return checkAndUpdatePureExpressionDynamic(view, nodeDef, values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkNoChangesNode(
|
||||||
|
view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any,
|
||||||
|
v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any): any {
|
||||||
|
if (argStyle === ArgumentType.Inline) {
|
||||||
|
return checkNoChangesNodeInline(view, nodeIndex, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||||
|
} else {
|
||||||
|
return checkNoChangesNodeDynamic(view, nodeIndex, v0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkNoChangesNodeInline(
|
||||||
view: ViewData, nodeIndex: number, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any,
|
view: ViewData, nodeIndex: number, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any,
|
||||||
v6: any, v7: any, v8: any, v9: any): void {
|
v6: any, v7: any, v8: any, v9: any): void {
|
||||||
const nodeDef = view.def.nodes[nodeIndex];
|
const nodeDef = view.def.nodes[nodeIndex];
|
||||||
|
@ -452,28 +436,29 @@ function checkNodeNoChangesInline(
|
||||||
case 1:
|
case 1:
|
||||||
checkBindingNoChanges(view, nodeDef, 0, v0);
|
checkBindingNoChanges(view, nodeDef, 0, v0);
|
||||||
}
|
}
|
||||||
return undefined;
|
return nodeDef.type === NodeType.PureExpression ? asPureExpressionData(view, nodeIndex).value :
|
||||||
|
undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkNodeNoChangesDynamic(view: ViewData, nodeIndex: number, values: any[]): void {
|
function checkNoChangesNodeDynamic(view: ViewData, nodeIndex: number, values: any[]): void {
|
||||||
const nodeDef = view.def.nodes[nodeIndex];
|
const nodeDef = view.def.nodes[nodeIndex];
|
||||||
for (let i = 0; i < values.length; i++) {
|
for (let i = 0; i < values.length; i++) {
|
||||||
checkBindingNoChanges(view, nodeDef, i, values[i]);
|
checkBindingNoChanges(view, nodeDef, i, values[i]);
|
||||||
}
|
}
|
||||||
|
return nodeDef.type === NodeType.PureExpression ? asPureExpressionData(view, nodeIndex).value :
|
||||||
|
undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkNoChangesQuery(view: ViewData, nodeDef: NodeDef) {
|
function checkNoChangesQuery(view: ViewData, nodeDef: NodeDef) {
|
||||||
const queryList = asQueryList(view, nodeDef.index);
|
const queryList = asQueryList(view, nodeDef.index);
|
||||||
if (queryList.dirty) {
|
if (queryList.dirty) {
|
||||||
throw expressionChangedAfterItHasBeenCheckedError(
|
throw expressionChangedAfterItHasBeenCheckedError(
|
||||||
Refs.createDebugContext(view, nodeDef.index), `Query ${nodeDef.query.id} not dirty`,
|
Services.createDebugContext(view, nodeDef.index), `Query ${nodeDef.query.id} not dirty`,
|
||||||
`Query ${nodeDef.query.id} dirty`, (view.state & ViewState.FirstCheck) !== 0);
|
`Query ${nodeDef.query.id} dirty`, (view.state & ViewState.FirstCheck) !== 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const destroyView: (view: ViewData) => void = entryAction(EntryAction.Destroy, _destroyView);
|
export function destroyView(view: ViewData) {
|
||||||
|
|
||||||
function _destroyView(view: ViewData) {
|
|
||||||
callLifecycleHooksChildrenFirst(view, NodeFlags.OnDestroy);
|
callLifecycleHooksChildrenFirst(view, NodeFlags.OnDestroy);
|
||||||
if (view.disposables) {
|
if (view.disposables) {
|
||||||
for (let i = 0; i < view.disposables.length; i++) {
|
for (let i = 0; i < view.disposables.length; i++) {
|
||||||
|
@ -542,20 +527,20 @@ function callViewAction(view: ViewData, action: ViewAction) {
|
||||||
case ViewAction.CheckNoChanges:
|
case ViewAction.CheckNoChanges:
|
||||||
if ((viewState & ViewState.ChecksEnabled) &&
|
if ((viewState & ViewState.ChecksEnabled) &&
|
||||||
(viewState & (ViewState.Errored | ViewState.Destroyed)) === 0) {
|
(viewState & (ViewState.Errored | ViewState.Destroyed)) === 0) {
|
||||||
_checkNoChangesView(view);
|
checkNoChangesView(view);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ViewAction.CheckAndUpdate:
|
case ViewAction.CheckAndUpdate:
|
||||||
if ((viewState & ViewState.ChecksEnabled) &&
|
if ((viewState & ViewState.ChecksEnabled) &&
|
||||||
(viewState & (ViewState.Errored | ViewState.Destroyed)) === 0) {
|
(viewState & (ViewState.Errored | ViewState.Destroyed)) === 0) {
|
||||||
_checkAndUpdateView(view);
|
checkAndUpdateView(view);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ViewAction.Destroy:
|
case ViewAction.Destroy:
|
||||||
_destroyView(view);
|
destroyView(view);
|
||||||
break;
|
break;
|
||||||
case ViewAction.CreateViewNodes:
|
case ViewAction.CreateViewNodes:
|
||||||
_createViewNodes(view);
|
createViewNodes(view);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -573,7 +558,7 @@ function execQueriesAction(view: ViewData, queryFlags: NodeFlags, action: QueryA
|
||||||
for (let i = 0; i < nodeCount; i++) {
|
for (let i = 0; i < nodeCount; i++) {
|
||||||
const nodeDef = view.def.nodes[i];
|
const nodeDef = view.def.nodes[i];
|
||||||
if (nodeDef.flags & queryFlags) {
|
if (nodeDef.flags & queryFlags) {
|
||||||
setCurrentNode(view, nodeDef.index);
|
Services.setCurrentNode(view, nodeDef.index);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case QueryAction.CheckAndUpdate:
|
case QueryAction.CheckAndUpdate:
|
||||||
checkAndUpdateQuery(view, nodeDef);
|
checkAndUpdateQuery(view, nodeDef);
|
||||||
|
|
|
@ -83,27 +83,16 @@ export function moveEmbeddedView(
|
||||||
function renderAttachEmbeddedView(elementData: ElementData, prevView: ViewData, view: ViewData) {
|
function renderAttachEmbeddedView(elementData: ElementData, prevView: ViewData, view: ViewData) {
|
||||||
const prevRenderNode =
|
const prevRenderNode =
|
||||||
prevView ? renderNode(prevView, prevView.def.lastRootNode) : elementData.renderElement;
|
prevView ? renderNode(prevView, prevView.def.lastRootNode) : elementData.renderElement;
|
||||||
if (view.renderer) {
|
const parentNode = view.root.renderer.parentNode(prevRenderNode);
|
||||||
view.renderer.attachViewAfter(prevRenderNode, rootRenderNodes(view));
|
const nextSibling = view.root.renderer.nextSibling(prevRenderNode);
|
||||||
} else {
|
// Note: We can't check if `nextSibling` is present, as on WebWorkers it will always be!
|
||||||
const parentNode = prevRenderNode.parentNode;
|
// However, browsers automatically do `appendChild` when there is no `nextSibling`.
|
||||||
const nextSibling = prevRenderNode.nextSibling;
|
visitRootRenderNodes(view, RenderNodeAction.InsertBefore, parentNode, nextSibling, undefined);
|
||||||
if (parentNode) {
|
|
||||||
const action = nextSibling ? RenderNodeAction.InsertBefore : RenderNodeAction.AppendChild;
|
|
||||||
visitRootRenderNodes(view, action, parentNode, nextSibling, undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDetachEmbeddedView(elementData: ElementData, view: ViewData) {
|
function renderDetachEmbeddedView(elementData: ElementData, view: ViewData) {
|
||||||
if (view.renderer) {
|
const parentNode = view.root.renderer.parentNode(elementData.renderElement);
|
||||||
view.renderer.detachView(rootRenderNodes(view));
|
visitRootRenderNodes(view, RenderNodeAction.RemoveChild, parentNode, null, undefined);
|
||||||
} else {
|
|
||||||
const parentNode = elementData.renderElement.parentNode;
|
|
||||||
if (parentNode) {
|
|
||||||
visitRootRenderNodes(view, RenderNodeAction.RemoveChild, parentNode, null, undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addToArray(arr: any[], index: number, value: any) {
|
function addToArray(arr: any[], index: number, value: any) {
|
||||||
|
|
|
@ -7,40 +7,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, getDebugNode} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, getDebugNode} from '@angular/core';
|
||||||
import {DebugContext, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
import {createRootView, isBrowser} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
describe(`View Anchor`, () => {
|
||||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
|
||||||
}
|
|
||||||
defineTests({directDom: false, viewFlags: 0});
|
|
||||||
}
|
|
||||||
|
|
||||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|
||||||
describe(`View Anchor, directDom: ${config.directDom}`, () => {
|
|
||||||
setupAndCheckRenderer(config);
|
|
||||||
|
|
||||||
let rootData: RootData;
|
|
||||||
let renderComponentType: RenderComponentType;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rootData = createRootData();
|
|
||||||
renderComponentType =
|
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
|
||||||
});
|
|
||||||
|
|
||||||
function compViewDef(
|
function compViewDef(
|
||||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||||
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
return viewDef(ViewFlags.None, nodes, update, handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(
|
function createAndGetRootNodes(
|
||||||
viewDef: ViewDefinition, ctx?: any): {rootNodes: any[], view: ViewData} {
|
viewDef: ViewDefinition, ctx?: any): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(rootData, viewDef, ctx);
|
const view = createRootView(viewDef, ctx);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -69,14 +51,12 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
expect(getDOM().childNodes(rootNodes[0]).length).toBe(1);
|
expect(getDOM().childNodes(rootNodes[0]).length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!config.directDom) {
|
it('should add debug information to the renderer', () => {
|
||||||
it('should add debug information to the renderer', () => {
|
const someContext = new Object();
|
||||||
const someContext = new Object();
|
const {view, rootNodes} = createAndGetRootNodes(
|
||||||
const {view, rootNodes} = createAndGetRootNodes(
|
compViewDef([anchorDef(NodeFlags.None, null, null, 0)]), someContext);
|
||||||
compViewDef([anchorDef(NodeFlags.None, null, null, 0)]), someContext);
|
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asElementData(view, 0).renderElement);
|
||||||
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asElementData(view, 0).renderElement);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,40 +7,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||||
import {BindingType, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, anchorDef, asProviderData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, destroyView, directiveDef, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {ArgumentType, BindingType, NodeCheckFn, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, anchorDef, asProviderData, directiveDef, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {createRootData, isBrowser, removeNodes, setupAndCheckRenderer} from './helper';
|
import {createRootView, isBrowser, removeNodes} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
describe(`Component Views`, () => {
|
||||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
|
||||||
}
|
|
||||||
defineTests({directDom: false, viewFlags: 0});
|
|
||||||
}
|
|
||||||
|
|
||||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|
||||||
describe(`Component Views, directDom: ${config.directDom}`, () => {
|
|
||||||
setupAndCheckRenderer(config);
|
|
||||||
|
|
||||||
let rootData: RootData;
|
|
||||||
let renderComponentType: RenderComponentType;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rootData = createRootData();
|
|
||||||
renderComponentType =
|
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
|
||||||
});
|
|
||||||
|
|
||||||
function compViewDef(
|
function compViewDef(
|
||||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||||
flags?: ViewFlags): ViewDefinition {
|
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||||
return viewDef(config.viewFlags | flags, nodes, update, handleEvent, renderComponentType);
|
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(rootData, viewDef);
|
const view = createRootView(viewDef);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -79,39 +61,41 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should select root elements based on a selector', () => {
|
it('should select root elements based on a selector', () => {
|
||||||
rootData.selectorOrNode = 'root';
|
const view = createRootView(
|
||||||
const view = createRootView(rootData, compViewDef([
|
compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||||
]));
|
]),
|
||||||
|
{}, [], 'root');
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
expect(rootNodes).toEqual([rootNode]);
|
expect(rootNodes).toEqual([rootNode]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should select root elements based on a node', () => {
|
it('should select root elements based on a node', () => {
|
||||||
rootData.selectorOrNode = rootNode;
|
const view = createRootView(
|
||||||
const view = createRootView(rootData, compViewDef([
|
compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||||
]));
|
]),
|
||||||
|
{}, [], rootNode);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
expect(rootNodes).toEqual([rootNode]);
|
expect(rootNodes).toEqual([rootNode]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set attributes on the root node', () => {
|
it('should set attributes on the root node', () => {
|
||||||
rootData.selectorOrNode = rootNode;
|
const view = createRootView(
|
||||||
const view =
|
compViewDef([
|
||||||
createRootView(rootData, compViewDef([
|
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}),
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}),
|
]),
|
||||||
]));
|
{}, [], rootNode);
|
||||||
expect(rootNode.getAttribute('a')).toBe('b');
|
expect(rootNode.getAttribute('a')).toBe('b');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear the content of the root node', () => {
|
it('should clear the content of the root node', () => {
|
||||||
rootData.selectorOrNode = rootNode;
|
|
||||||
rootNode.appendChild(document.createElement('div'));
|
rootNode.appendChild(document.createElement('div'));
|
||||||
const view =
|
const view = createRootView(
|
||||||
createRootView(rootData, compViewDef([
|
compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}),
|
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}),
|
||||||
]));
|
]),
|
||||||
|
{}, [], rootNode);
|
||||||
expect(rootNode.childNodes.length).toBe(0);
|
expect(rootNode.childNodes.length).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -124,10 +108,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
a: any;
|
a: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const update = jasmine.createSpy('updater').and.callFake((view: ViewData) => {
|
const update =
|
||||||
setCurrentNode(view, 0);
|
jasmine.createSpy('updater').and.callFake((check: NodeCheckFn, view: ViewData) => {
|
||||||
checkNodeInline(value);
|
check(view, 0, ArgumentType.Inline, value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(
|
const {view, rootNodes} = createAndGetRootNodes(
|
||||||
compViewDef([
|
compViewDef([
|
||||||
|
@ -141,19 +125,19 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asProviderData(view, 1).componentView;
|
||||||
|
|
||||||
value = 'v1';
|
value = 'v1';
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
expect(update).toHaveBeenCalledWith(compView);
|
expect(update.calls.mostRecent().args[1]).toBe(compView);
|
||||||
|
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
checkNoChangesView(view);
|
Services.checkNoChangesView(view);
|
||||||
|
|
||||||
expect(update).toHaveBeenCalledWith(compView);
|
expect(update.calls.mostRecent().args[1]).toBe(compView);
|
||||||
|
|
||||||
value = 'v2';
|
value = 'v2';
|
||||||
expect(() => checkNoChangesView(view))
|
expect(() => Services.checkNoChangesView(view))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Expression has changed after it was checked. Previous value: 'v1'. Current value: 'v2'.`);
|
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'v1'. Current value: 'v2'.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support detaching and attaching component views for dirty checking', () => {
|
it('should support detaching and attaching component views for dirty checking', () => {
|
||||||
|
@ -176,15 +160,15 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asProviderData(view, 1).componentView;
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
|
|
||||||
compView.state &= ~ViewState.ChecksEnabled;
|
compView.state &= ~ViewState.ChecksEnabled;
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).not.toHaveBeenCalled();
|
expect(update).not.toHaveBeenCalled();
|
||||||
|
|
||||||
compView.state |= ViewState.ChecksEnabled;
|
compView.state |= ViewState.ChecksEnabled;
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).toHaveBeenCalled();
|
expect(update).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -212,40 +196,37 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
],
|
],
|
||||||
update, null, ViewFlags.OnPush)),
|
update, null, ViewFlags.OnPush)),
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => { check(view, 1, ArgumentType.Inline, compInputValue); }));
|
||||||
setCurrentNode(view, 1);
|
|
||||||
checkNodeInline(compInputValue);
|
|
||||||
}));
|
|
||||||
|
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asProviderData(view, 1).componentView;
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
// auto detach
|
// auto detach
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).not.toHaveBeenCalled();
|
expect(update).not.toHaveBeenCalled();
|
||||||
|
|
||||||
// auto attach on input changes
|
// auto attach on input changes
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
compInputValue = 'v1';
|
compInputValue = 'v1';
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).toHaveBeenCalled();
|
expect(update).toHaveBeenCalled();
|
||||||
|
|
||||||
// auto detach
|
// auto detach
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).not.toHaveBeenCalled();
|
expect(update).not.toHaveBeenCalled();
|
||||||
|
|
||||||
// auto attach on events
|
// auto attach on events
|
||||||
addListenerSpy.calls.mostRecent().args[1]('SomeEvent');
|
addListenerSpy.calls.mostRecent().args[1]('SomeEvent');
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).toHaveBeenCalled();
|
expect(update).toHaveBeenCalled();
|
||||||
|
|
||||||
// auto detach
|
// auto detach
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).not.toHaveBeenCalled();
|
expect(update).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -263,22 +244,19 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
NodeFlags.None, null, 0, AComp, [], null, null,
|
NodeFlags.None, null, 0, AComp, [], null, null,
|
||||||
() => compViewDef(
|
() => compViewDef(
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'span'),
|
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
|
||||||
],
|
],
|
||||||
update)),
|
update)),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asProviderData(view, 1).componentView;
|
||||||
|
|
||||||
update.and.callFake((view: ViewData) => {
|
update.and.callFake((check: NodeCheckFn, view: ViewData) => { throw new Error('Test'); });
|
||||||
setCurrentNode(view, 0);
|
expect(() => Services.checkAndUpdateView(view)).toThrowError('Test');
|
||||||
throw new Error('Test');
|
|
||||||
});
|
|
||||||
expect(() => checkAndUpdateView(view)).toThrow();
|
|
||||||
expect(update).toHaveBeenCalled();
|
expect(update).toHaveBeenCalled();
|
||||||
|
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).not.toHaveBeenCalled();
|
expect(update).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -304,7 +282,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
])),
|
])),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
destroyView(view);
|
Services.destroyView(view);
|
||||||
|
|
||||||
expect(log).toEqual(['ngOnDestroy']);
|
expect(log).toEqual(['ngOnDestroy']);
|
||||||
});
|
});
|
||||||
|
@ -314,12 +292,12 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'div'),
|
elementDef(NodeFlags.None, null, null, 0, 'div'),
|
||||||
],
|
],
|
||||||
(view) => { setCurrentNode(view, 0); }));
|
(view) => {}));
|
||||||
|
|
||||||
destroyView(view);
|
Services.destroyView(view);
|
||||||
|
|
||||||
expect(() => checkAndUpdateView(view))
|
expect(() => Services.checkAndUpdateView(view))
|
||||||
.toThrowError('View has been used after destroy for CheckAndUpdate');
|
.toThrowError('ViewDestroyedError: Attempt to use a destroyed view: detectChanges');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,40 +8,23 @@
|
||||||
|
|
||||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||||
import {getDebugContext} from '@angular/core/src/errors';
|
import {getDebugContext} from '@angular/core/src/errors';
|
||||||
import {BindingType, DebugContext, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, destroyView, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {ArgumentType, BindingType, DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic, createRootData, isBrowser, removeNodes, setupAndCheckRenderer} from './helper';
|
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser, removeNodes} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
describe(`View Elements`, () => {
|
||||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
|
||||||
}
|
|
||||||
defineTests({directDom: false, viewFlags: 0});
|
|
||||||
}
|
|
||||||
|
|
||||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|
||||||
describe(`View Elements, directDom: ${config.directDom}`, () => {
|
|
||||||
setupAndCheckRenderer(config);
|
|
||||||
|
|
||||||
let rootData: RootData;
|
|
||||||
let renderComponentType: RenderComponentType;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rootData = createRootData();
|
|
||||||
renderComponentType =
|
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
|
||||||
});
|
|
||||||
|
|
||||||
function compViewDef(
|
function compViewDef(
|
||||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||||
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||||
|
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(
|
function createAndGetRootNodes(
|
||||||
viewDef: ViewDefinition, context?: any): {rootNodes: any[], view: ViewData} {
|
viewDef: ViewDefinition, context?: any): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(rootData, viewDef, context);
|
const view = createRootView(viewDef, context);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -81,19 +64,17 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
expect(getDOM().getAttribute(rootNodes[0], 'title')).toBe('a');
|
expect(getDOM().getAttribute(rootNodes[0], 'title')).toBe('a');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!config.directDom) {
|
it('should add debug information to the renderer', () => {
|
||||||
it('should add debug information to the renderer', () => {
|
const someContext = new Object();
|
||||||
const someContext = new Object();
|
const {view, rootNodes} = createAndGetRootNodes(
|
||||||
const {view, rootNodes} = createAndGetRootNodes(
|
compViewDef([elementDef(NodeFlags.None, null, null, 0, 'div')]), someContext);
|
||||||
compViewDef([elementDef(NodeFlags.None, null, null, 0, 'div')]), someContext);
|
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asElementData(view, 0).renderElement);
|
||||||
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asElementData(view, 0).renderElement);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('change properties', () => {
|
describe('change properties', () => {
|
||||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||||
it(`should update ${InlineDynamic[inlineDynamic]}`, () => {
|
it(`should update ${ArgumentType[inlineDynamic]}`, () => {
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
[
|
[
|
||||||
|
@ -104,27 +85,24 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
[BindingType.ElementProperty, 'value', SecurityContext.NONE]
|
[BindingType.ElementProperty, 'value', SecurityContext.NONE]
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 0);
|
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['v1', 'v2']);
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, ['v1', 'v2']);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const el = rootNodes[0];
|
const el = rootNodes[0];
|
||||||
expect(getDOM().getProperty(el, 'title')).toBe('v1');
|
expect(getDOM().getProperty(el, 'title')).toBe('v1');
|
||||||
expect(getDOM().getProperty(el, 'value')).toBe('v2');
|
expect(getDOM().getProperty(el, 'value')).toBe('v2');
|
||||||
|
|
||||||
if (!config.directDom) {
|
expect(getDOM().getAttribute(el, 'ng-reflect-title')).toBe('v1');
|
||||||
expect(getDOM().getAttribute(el, 'ng-reflect-title')).toBe('v1');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('change attributes', () => {
|
describe('change attributes', () => {
|
||||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||||
it(`should update ${InlineDynamic[inlineDynamic]}`, () => {
|
it(`should update ${ArgumentType[inlineDynamic]}`, () => {
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
[
|
[
|
||||||
elementDef(
|
elementDef(
|
||||||
|
@ -134,12 +112,11 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
[BindingType.ElementAttribute, 'a2', SecurityContext.NONE]
|
[BindingType.ElementAttribute, 'a2', SecurityContext.NONE]
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 0);
|
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['v1', 'v2']);
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, ['v1', 'v2']);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const el = rootNodes[0];
|
const el = rootNodes[0];
|
||||||
expect(getDOM().getAttribute(el, 'a1')).toBe('v1');
|
expect(getDOM().getAttribute(el, 'a1')).toBe('v1');
|
||||||
|
@ -149,20 +126,19 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('change classes', () => {
|
describe('change classes', () => {
|
||||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||||
it(`should update ${InlineDynamic[inlineDynamic]}`, () => {
|
it(`should update ${ArgumentType[inlineDynamic]}`, () => {
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
[
|
[
|
||||||
elementDef(
|
elementDef(
|
||||||
NodeFlags.None, null, null, 0, 'div', null,
|
NodeFlags.None, null, null, 0, 'div', null,
|
||||||
[[BindingType.ElementClass, 'c1'], [BindingType.ElementClass, 'c2']]),
|
[[BindingType.ElementClass, 'c1'], [BindingType.ElementClass, 'c2']]),
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 0);
|
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [true, true]);
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, [true, true]);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const el = rootNodes[0];
|
const el = rootNodes[0];
|
||||||
expect(getDOM().hasClass(el, 'c1')).toBeTruthy();
|
expect(getDOM().hasClass(el, 'c1')).toBeTruthy();
|
||||||
|
@ -172,8 +148,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('change styles', () => {
|
describe('change styles', () => {
|
||||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||||
it(`should update ${InlineDynamic[inlineDynamic]}`, () => {
|
it(`should update ${ArgumentType[inlineDynamic]}`, () => {
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
[
|
[
|
||||||
elementDef(
|
elementDef(
|
||||||
|
@ -183,12 +159,11 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
[BindingType.ElementStyle, 'color', null]
|
[BindingType.ElementStyle, 'color', null]
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 0);
|
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [10, 'red']);
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, [10, 'red']);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const el = rootNodes[0];
|
const el = rootNodes[0];
|
||||||
expect(getDOM().getStyle(el, 'width')).toBe('10px');
|
expect(getDOM().getStyle(el, 'width')).toBe('10px');
|
||||||
|
@ -198,8 +173,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('general binding behavior', () => {
|
describe('general binding behavior', () => {
|
||||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||||
it(`should unwrap values with ${InlineDynamic[inlineDynamic]}`, () => {
|
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||||
let bindingValue: any;
|
let bindingValue: any;
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
|
@ -210,25 +185,24 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
[BindingType.ElementProperty, 'someProp', SecurityContext.NONE],
|
[BindingType.ElementProperty, 'someProp', SecurityContext.NONE],
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 0);
|
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [bindingValue]);
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, [bindingValue]);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const setterSpy = jasmine.createSpy('set');
|
const setterSpy = jasmine.createSpy('set');
|
||||||
Object.defineProperty(rootNodes[0], 'someProp', {set: setterSpy});
|
Object.defineProperty(rootNodes[0], 'someProp', {set: setterSpy});
|
||||||
|
|
||||||
bindingValue = 'v1';
|
bindingValue = 'v1';
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||||
|
|
||||||
setterSpy.calls.reset();
|
setterSpy.calls.reset();
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(setterSpy).not.toHaveBeenCalled();
|
expect(setterSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
setterSpy.calls.reset();
|
setterSpy.calls.reset();
|
||||||
bindingValue = WrappedValue.wrap('v1');
|
bindingValue = WrappedValue.wrap('v1');
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -265,7 +239,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
expect(handleEventArgs[2]).toBe('click');
|
expect(handleEventArgs[2]).toBe('click');
|
||||||
expect(handleEventArgs[3]).toBeTruthy();
|
expect(handleEventArgs[3]).toBeTruthy();
|
||||||
|
|
||||||
destroyView(view);
|
Services.destroyView(view);
|
||||||
|
|
||||||
expect(removeListenerSpy).toHaveBeenCalled();
|
expect(removeListenerSpy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
@ -291,7 +265,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
expect(handleEventArgs[2]).toBe('windowClick');
|
expect(handleEventArgs[2]).toBe('windowClick');
|
||||||
expect(handleEventArgs[3]).toBeTruthy();
|
expect(handleEventArgs[3]).toBeTruthy();
|
||||||
|
|
||||||
destroyView(view);
|
Services.destroyView(view);
|
||||||
|
|
||||||
expect(removeListenerSpy).toHaveBeenCalled();
|
expect(removeListenerSpy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
@ -317,7 +291,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
expect(handleEventArgs[2]).toBe('documentClick');
|
expect(handleEventArgs[2]).toBe('documentClick');
|
||||||
expect(handleEventArgs[3]).toBeTruthy();
|
expect(handleEventArgs[3]).toBeTruthy();
|
||||||
|
|
||||||
destroyView(view);
|
Services.destroyView(view);
|
||||||
|
|
||||||
expect(removeListenerSpy).toHaveBeenCalled();
|
expect(removeListenerSpy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,44 +7,27 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||||
import {BindingType, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, directiveDef, elementDef, moveEmbeddedView, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {ArgumentType, BindingType, NodeCheckFn, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, moveEmbeddedView, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
import {createRootView, isBrowser} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
describe(`Embedded Views`, () => {
|
||||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
|
||||||
}
|
|
||||||
defineTests({directDom: false, viewFlags: 0});
|
|
||||||
}
|
|
||||||
|
|
||||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|
||||||
describe(`Embedded Views, directDom: ${config.directDom}`, () => {
|
|
||||||
setupAndCheckRenderer(config);
|
|
||||||
|
|
||||||
let rootData: RootData;
|
|
||||||
let renderComponentType: RenderComponentType;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rootData = createRootData();
|
|
||||||
renderComponentType =
|
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
|
||||||
});
|
|
||||||
|
|
||||||
function compViewDef(
|
function compViewDef(
|
||||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||||
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||||
|
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
||||||
return viewDef(config.viewFlags, nodes, update);
|
return viewDef(ViewFlags.None, nodes, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(
|
function createAndGetRootNodes(
|
||||||
viewDef: ViewDefinition, context: any = null): {rootNodes: any[], view: ViewData} {
|
viewDef: ViewDefinition, context: any = null): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(rootData, viewDef, context);
|
const view = createRootView(viewDef, context);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -62,7 +45,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
]),
|
]),
|
||||||
parentContext);
|
parentContext);
|
||||||
|
|
||||||
const childView = createEmbeddedView(parentView, parentView.def.nodes[1], childContext);
|
const childView =
|
||||||
|
Services.createEmbeddedView(parentView, parentView.def.nodes[1], childContext);
|
||||||
expect(childView.component).toBe(parentContext);
|
expect(childView.component).toBe(parentContext);
|
||||||
expect(childView.context).toBe(childContext);
|
expect(childView.context).toBe(childContext);
|
||||||
});
|
});
|
||||||
|
@ -79,8 +63,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
]));
|
]));
|
||||||
const viewContainerData = asElementData(parentView, 1);
|
const viewContainerData = asElementData(parentView, 1);
|
||||||
|
|
||||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||||
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
|
const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||||
|
|
||||||
attachEmbeddedView(viewContainerData, 0, childView0);
|
attachEmbeddedView(viewContainerData, 0, childView0);
|
||||||
attachEmbeddedView(viewContainerData, 1, childView1);
|
attachEmbeddedView(viewContainerData, 1, childView1);
|
||||||
|
@ -109,8 +93,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
]));
|
]));
|
||||||
const viewContainerData = asElementData(parentView, 1);
|
const viewContainerData = asElementData(parentView, 1);
|
||||||
|
|
||||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||||
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
|
const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||||
|
|
||||||
attachEmbeddedView(viewContainerData, 0, childView0);
|
attachEmbeddedView(viewContainerData, 0, childView0);
|
||||||
attachEmbeddedView(viewContainerData, 1, childView1);
|
attachEmbeddedView(viewContainerData, 1, childView1);
|
||||||
|
@ -133,7 +117,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'span', {'name': 'after'})
|
elementDef(NodeFlags.None, null, null, 0, 'span', {'name': 'after'})
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[0]);
|
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[0]);
|
||||||
attachEmbeddedView(asElementData(parentView, 0), 0, childView0);
|
attachEmbeddedView(asElementData(parentView, 0), 0, childView0);
|
||||||
|
|
||||||
const rootNodes = rootRenderNodes(parentView);
|
const rootNodes = rootRenderNodes(parentView);
|
||||||
|
@ -144,10 +128,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
it('should dirty check embedded views', () => {
|
it('should dirty check embedded views', () => {
|
||||||
let childValue = 'v1';
|
let childValue = 'v1';
|
||||||
const update = jasmine.createSpy('updater').and.callFake((view: ViewData) => {
|
const update =
|
||||||
setCurrentNode(view, 0);
|
jasmine.createSpy('updater').and.callFake((check: NodeCheckFn, view: ViewData) => {
|
||||||
checkNodeInline(childValue);
|
check(view, 0, ArgumentType.Inline, childValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
const {view: parentView, rootNodes} = createAndGetRootNodes(compViewDef([
|
const {view: parentView, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||||
|
@ -160,24 +144,24 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
update))
|
update))
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||||
|
|
||||||
const rootEl = rootNodes[0];
|
const rootEl = rootNodes[0];
|
||||||
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
|
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
|
||||||
|
|
||||||
checkAndUpdateView(parentView);
|
Services.checkAndUpdateView(parentView);
|
||||||
|
|
||||||
expect(update).toHaveBeenCalledWith(childView0);
|
expect(update.calls.mostRecent().args[1]).toBe(childView0);
|
||||||
|
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
checkNoChangesView(parentView);
|
Services.checkNoChangesView(parentView);
|
||||||
|
|
||||||
expect(update).toHaveBeenCalledWith(childView0);
|
expect(update.calls.mostRecent().args[1]).toBe(childView0);
|
||||||
|
|
||||||
childValue = 'v2';
|
childValue = 'v2';
|
||||||
expect(() => checkNoChangesView(parentView))
|
expect(() => Services.checkNoChangesView(parentView))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Expression has changed after it was checked. Previous value: 'v1'. Current value: 'v2'.`);
|
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'v1'. Current value: 'v2'.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should destroy embedded views', () => {
|
it('should destroy embedded views', () => {
|
||||||
|
@ -195,10 +179,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
]))
|
]))
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||||
|
|
||||||
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
|
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
|
||||||
destroyView(parentView);
|
Services.destroyView(parentView);
|
||||||
|
|
||||||
expect(log).toEqual(['ngOnDestroy']);
|
expect(log).toEqual(['ngOnDestroy']);
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, RootRenderer, Sanitizer} from '@angular/core';
|
import {Injector, RootRenderer, Sanitizer} from '@angular/core';
|
||||||
import {RootData, checkNodeDynamic, checkNodeInline} from '@angular/core/src/view/index';
|
import {ArgumentType, NodeCheckFn, RootData, Services, ViewData, ViewDefinition, initServicesIfNeeded} from '@angular/core/src/view/index';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
@ -15,53 +15,25 @@ export function isBrowser() {
|
||||||
return getDOM().supportsDOMEvents();
|
return getDOM().supportsDOMEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setupAndCheckRenderer(config: {directDom: boolean}) {
|
export const ARG_TYPE_VALUES = [ArgumentType.Inline, ArgumentType.Dynamic];
|
||||||
let rootRenderer: any;
|
|
||||||
if (config.directDom) {
|
export function checkNodeInlineOrDynamic(
|
||||||
beforeEach(() => {
|
check: NodeCheckFn, view: ViewData, nodeIndex: number, argType: ArgumentType,
|
||||||
rootRenderer = <any>{
|
values: any[]): any {
|
||||||
renderComponent: jasmine.createSpy('renderComponent')
|
switch (argType) {
|
||||||
.and.throwError('Renderer should not have been called!')
|
case ArgumentType.Inline:
|
||||||
};
|
return (<any>check)(view, nodeIndex, argType, ...values);
|
||||||
TestBed.configureTestingModule(
|
case ArgumentType.Dynamic:
|
||||||
{providers: [{provide: RootRenderer, useValue: rootRenderer}]});
|
return check(view, nodeIndex, argType, values);
|
||||||
});
|
|
||||||
afterEach(() => { expect(rootRenderer.renderComponent).not.toHaveBeenCalled(); });
|
|
||||||
} else {
|
|
||||||
beforeEach(() => {
|
|
||||||
rootRenderer = TestBed.get(RootRenderer);
|
|
||||||
spyOn(rootRenderer, 'renderComponent').and.callThrough();
|
|
||||||
});
|
|
||||||
afterEach(() => { expect(rootRenderer.renderComponent).toHaveBeenCalled(); });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum InlineDynamic {
|
export function createRootView(
|
||||||
Inline,
|
def: ViewDefinition, context?: any, projectableNodes?: any[][],
|
||||||
Dynamic
|
rootSelectorOrNode?: any): ViewData {
|
||||||
}
|
initServicesIfNeeded();
|
||||||
|
return Services.createRootView(
|
||||||
export const INLINE_DYNAMIC_VALUES = [InlineDynamic.Inline, InlineDynamic.Dynamic];
|
TestBed.get(Injector), projectableNodes || [], rootSelectorOrNode, def, context);
|
||||||
|
|
||||||
export function checkNodeInlineOrDynamic(inlineDynamic: InlineDynamic, values: any[]): any {
|
|
||||||
switch (inlineDynamic) {
|
|
||||||
case InlineDynamic.Inline:
|
|
||||||
return (<any>checkNodeInline)(...values);
|
|
||||||
case InlineDynamic.Dynamic:
|
|
||||||
return checkNodeDynamic(values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createRootData(projectableNodes?: any[][], rootSelectorOrNode?: any): RootData {
|
|
||||||
const injector = TestBed.get(Injector);
|
|
||||||
const renderer = injector.get(RootRenderer);
|
|
||||||
const sanitizer = injector.get(Sanitizer);
|
|
||||||
projectableNodes = projectableNodes || [];
|
|
||||||
return <RootData>{
|
|
||||||
injector,
|
|
||||||
projectableNodes,
|
|
||||||
selectorOrNode: rootSelectorOrNode, sanitizer, renderer
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export let removeNodes: Node[];
|
export let removeNodes: Node[];
|
||||||
|
|
|
@ -7,39 +7,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core';
|
||||||
import {DebugContext, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, detachEmbeddedView, directiveDef, elementDef, ngContentDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, ngContentDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
import {createRootView, isBrowser} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
describe(`View NgContent`, () => {
|
||||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
|
||||||
}
|
|
||||||
defineTests({directDom: false, viewFlags: 0});
|
|
||||||
}
|
|
||||||
|
|
||||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|
||||||
describe(`View NgContent, directDom: ${config.directDom}`, () => {
|
|
||||||
setupAndCheckRenderer(config);
|
|
||||||
|
|
||||||
let rootData: RootData;
|
|
||||||
let renderComponentType: RenderComponentType;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rootData = createRootData();
|
|
||||||
renderComponentType =
|
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
|
||||||
});
|
|
||||||
|
|
||||||
function compViewDef(
|
function compViewDef(
|
||||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||||
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||||
|
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
||||||
return viewDef(config.viewFlags, nodes, update);
|
return viewDef(ViewFlags.None, nodes, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hostElDef(contentNodes: NodeDef[], viewNodes: NodeDef[]): NodeDef[] {
|
function hostElDef(contentNodes: NodeDef[], viewNodes: NodeDef[]): NodeDef[] {
|
||||||
|
@ -56,7 +39,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
function createAndGetRootNodes(
|
function createAndGetRootNodes(
|
||||||
viewDef: ViewDefinition, ctx?: any): {rootNodes: any[], view: ViewData} {
|
viewDef: ViewDefinition, ctx?: any): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(rootData, viewDef, ctx || {});
|
const view = createRootView(viewDef, ctx || {});
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -125,7 +108,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
])));
|
])));
|
||||||
|
|
||||||
const componentView = asProviderData(view, 1).componentView;
|
const componentView = asProviderData(view, 1).componentView;
|
||||||
const view0 = createEmbeddedView(componentView, componentView.def.nodes[1]);
|
const view0 = Services.createEmbeddedView(componentView, componentView.def.nodes[1]);
|
||||||
|
|
||||||
attachEmbeddedView(asElementData(componentView, 1), 0, view0);
|
attachEmbeddedView(asElementData(componentView, 1), 0, view0);
|
||||||
expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(2);
|
expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(2);
|
||||||
|
@ -138,14 +121,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
it('should use root projectable nodes', () => {
|
it('should use root projectable nodes', () => {
|
||||||
rootData.projectableNodes =
|
const projectableNodes = [[document.createTextNode('a')], [document.createTextNode('b')]];
|
||||||
[[document.createTextNode('a')], [document.createTextNode('b')]];
|
const view = createRootView(
|
||||||
|
compViewDef(hostElDef([], [ngContentDef(null, 0), ngContentDef(null, 1)])), {},
|
||||||
|
projectableNodes);
|
||||||
|
const rootNodes = rootRenderNodes(view);
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(
|
expect(getDOM().childNodes(rootNodes[0])[0]).toBe(projectableNodes[0][0]);
|
||||||
compViewDef(hostElDef([], [ngContentDef(null, 0), ngContentDef(null, 1)])));
|
expect(getDOM().childNodes(rootNodes[0])[1]).toBe(projectableNodes[1][0]);
|
||||||
|
|
||||||
expect(getDOM().childNodes(rootNodes[0])[0]).toBe(rootData.projectableNodes[0][0]);
|
|
||||||
expect(getDOM().childNodes(rootNodes[0])[1]).toBe(rootData.projectableNodes[1][0]);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,43 +8,27 @@
|
||||||
|
|
||||||
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, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||||
import {getDebugContext} from '@angular/core/src/errors';
|
import {getDebugContext} from '@angular/core/src/errors';
|
||||||
import {BindingType, DebugContext, DepFlags, NodeDef, NodeFlags, ProviderType, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, destroyView, directiveDef, elementDef, providerDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {ArgumentType, BindingType, DebugContext, DepFlags, NodeDef, NodeFlags, ProviderType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, directiveDef, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {TestBed, inject, withModule} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic, createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
describe(`View Providers`, () => {
|
||||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
|
||||||
}
|
|
||||||
defineTests({directDom: false, viewFlags: 0});
|
|
||||||
}
|
|
||||||
|
|
||||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|
||||||
describe(`View Providers, directDom: ${config.directDom}`, () => {
|
|
||||||
setupAndCheckRenderer(config);
|
|
||||||
|
|
||||||
let rootData: RootData;
|
|
||||||
let renderComponentType: RenderComponentType;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rootData = createRootData();
|
|
||||||
renderComponentType =
|
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
|
||||||
});
|
|
||||||
|
|
||||||
function compViewDef(
|
function compViewDef(
|
||||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||||
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||||
|
return viewDef(
|
||||||
|
viewFlags, nodes, update, handleEvent, 'someCompId', ViewEncapsulation.None, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
||||||
return viewDef(config.viewFlags, nodes, update);
|
return viewDef(ViewFlags.None, nodes, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(rootData, viewDef);
|
const view = createRootView(viewDef, {});
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -127,10 +111,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
let err: any;
|
let err: any;
|
||||||
try {
|
try {
|
||||||
createAndGetRootNodes(compViewDef([
|
createRootView(
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
compViewDef([
|
||||||
directiveDef(NodeFlags.None, null, 0, SomeService, [])
|
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||||
]));
|
directiveDef(
|
||||||
|
NodeFlags.None, null, 0, SomeService, [], null, null,
|
||||||
|
() => compViewDef([textDef(null, ['a'])]))
|
||||||
|
]),
|
||||||
|
TestBed.get(Injector), [], getDOM().createElement('div'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err = e;
|
err = e;
|
||||||
}
|
}
|
||||||
|
@ -138,8 +126,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
expect(err.message).toBe('Test');
|
expect(err.message).toBe('Test');
|
||||||
const debugCtx = getDebugContext(err);
|
const debugCtx = getDebugContext(err);
|
||||||
expect(debugCtx.view).toBeTruthy();
|
expect(debugCtx.view).toBeTruthy();
|
||||||
// errors should point to the already existing element
|
expect(debugCtx.nodeIndex).toBe(1);
|
||||||
expect(debugCtx.nodeIndex).toBe(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('deps', () => {
|
describe('deps', () => {
|
||||||
|
@ -229,17 +216,15 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
expect(instance.dep).toBe('someParentValue');
|
expect(instance.dep).toBe('someParentValue');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ask the root injector', () => {
|
it('should ask the root injector',
|
||||||
const getSpy = spyOn(rootData.injector, 'get');
|
withModule({providers: [{provide: 'rootDep', useValue: 'rootValue'}]}, () => {
|
||||||
getSpy.and.returnValue('rootValue');
|
createAndGetRootNodes(compViewDef([
|
||||||
createAndGetRootNodes(compViewDef([
|
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
directiveDef(NodeFlags.None, null, 0, SomeService, ['rootDep'])
|
||||||
directiveDef(NodeFlags.None, null, 0, SomeService, ['rootDep'])
|
]));
|
||||||
]));
|
|
||||||
|
|
||||||
expect(instance.dep).toBe('rootValue');
|
expect(instance.dep).toBe('rootValue');
|
||||||
expect(getSpy).toHaveBeenCalledWith('rootDep', Injector.THROW_IF_NOT_FOUND);
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
describe('builtin tokens', () => {
|
describe('builtin tokens', () => {
|
||||||
it('should inject ViewContainerRef', () => {
|
it('should inject ViewContainerRef', () => {
|
||||||
|
@ -302,24 +287,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
expect(instance.dep._view).toBe(compView);
|
expect(instance.dep._view).toBe(compView);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (config.directDom) {
|
it('should inject RendererV1', () => {
|
||||||
it('should not inject Renderer when using directDom', () => {
|
createAndGetRootNodes(compViewDef([
|
||||||
expect(() => createAndGetRootNodes(compViewDef([
|
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
directiveDef(NodeFlags.None, null, 0, SomeService, [Renderer])
|
||||||
directiveDef(NodeFlags.None, null, 0, SomeService, [Renderer])
|
]));
|
||||||
])))
|
|
||||||
.toThrowError('No provider for Renderer!');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
it('should inject Renderer when not using directDom', () => {
|
|
||||||
createAndGetRootNodes(compViewDef([
|
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
|
||||||
directiveDef(NodeFlags.None, null, 0, SomeService, [Renderer])
|
|
||||||
]));
|
|
||||||
|
|
||||||
expect(instance.dep.createElement).toBeTruthy();
|
expect(instance.dep.createElement).toBeTruthy();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -327,8 +302,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
describe('data binding', () => {
|
describe('data binding', () => {
|
||||||
|
|
||||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||||
it(`should update ${InlineDynamic[inlineDynamic]}`, () => {
|
it(`should update ${ArgumentType[inlineDynamic]}`, () => {
|
||||||
let instance: SomeService;
|
let instance: SomeService;
|
||||||
|
|
||||||
class SomeService {
|
class SomeService {
|
||||||
|
@ -342,23 +317,20 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||||
directiveDef(NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a'], b: [1, 'b']})
|
directiveDef(NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a'], b: [1, 'b']})
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 1);
|
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, ['v1', 'v2']);
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, ['v1', 'v2']);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
expect(instance.a).toBe('v1');
|
expect(instance.a).toBe('v1');
|
||||||
expect(instance.b).toBe('v2');
|
expect(instance.b).toBe('v2');
|
||||||
|
|
||||||
if (!config.directDom) {
|
const el = rootNodes[0];
|
||||||
const el = rootNodes[0];
|
expect(getDOM().getAttribute(el, 'ng-reflect-a')).toBe('v1');
|
||||||
expect(getDOM().getAttribute(el, 'ng-reflect-a')).toBe('v1');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should unwrap values with ${InlineDynamic[inlineDynamic]}`, () => {
|
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||||
let bindingValue: any;
|
let bindingValue: any;
|
||||||
let setterSpy = jasmine.createSpy('set');
|
let setterSpy = jasmine.createSpy('set');
|
||||||
|
|
||||||
|
@ -371,22 +343,21 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||||
directiveDef(NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a']})
|
directiveDef(NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a']})
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 1);
|
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, [bindingValue]);
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, [bindingValue]);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
bindingValue = 'v1';
|
bindingValue = 'v1';
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||||
|
|
||||||
setterSpy.calls.reset();
|
setterSpy.calls.reset();
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(setterSpy).not.toHaveBeenCalled();
|
expect(setterSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
setterSpy.calls.reset();
|
setterSpy.calls.reset();
|
||||||
bindingValue = WrappedValue.wrap('v1');
|
bindingValue = WrappedValue.wrap('v1');
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -422,7 +393,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
emitter.emit('someEventInstance');
|
emitter.emit('someEventInstance');
|
||||||
expect(handleEvent).toHaveBeenCalledWith(view, 0, 'someEventName', 'someEventInstance');
|
expect(handleEvent).toHaveBeenCalledWith(view, 0, 'someEventName', 'someEventInstance');
|
||||||
|
|
||||||
destroyView(view);
|
Services.destroyView(view);
|
||||||
expect(unsubscribeSpy).toHaveBeenCalled();
|
expect(unsubscribeSpy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -485,14 +456,12 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||||
directiveDef(allFlags, null, 0, SomeService, [], {a: [0, 'a']})
|
directiveDef(allFlags, null, 0, SomeService, [], {a: [0, 'a']})
|
||||||
],
|
],
|
||||||
(updater) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 1);
|
check(view, 1, ArgumentType.Inline, 'someValue');
|
||||||
checkNodeInline('someValue');
|
check(view, 3, ArgumentType.Inline, 'someValue');
|
||||||
setCurrentNode(view, 3);
|
|
||||||
checkNodeInline('someValue');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
// Note: After... hooks are called bottom up.
|
// Note: After... hooks are called bottom up.
|
||||||
expect(log).toEqual([
|
expect(log).toEqual([
|
||||||
|
@ -513,7 +482,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
log = [];
|
log = [];
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
// Note: After... hooks are called bottom up.
|
// Note: After... hooks are called bottom up.
|
||||||
expect(log).toEqual([
|
expect(log).toEqual([
|
||||||
|
@ -522,7 +491,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
log = [];
|
log = [];
|
||||||
destroyView(view);
|
Services.destroyView(view);
|
||||||
|
|
||||||
// Note: ngOnDestroy ist called bottom up.
|
// Note: ngOnDestroy ist called bottom up.
|
||||||
expect(log).toEqual(['1_ngOnDestroy', '0_ngOnDestroy']);
|
expect(log).toEqual(['1_ngOnDestroy', '0_ngOnDestroy']);
|
||||||
|
@ -545,17 +514,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
directiveDef(
|
directiveDef(
|
||||||
NodeFlags.OnChanges, null, 0, SomeService, [], {a: [0, 'nonMinifiedA']})
|
NodeFlags.OnChanges, null, 0, SomeService, [], {a: [0, 'nonMinifiedA']})
|
||||||
],
|
],
|
||||||
(updater) => {
|
(check, view) => { check(view, 1, ArgumentType.Inline, currValue); }));
|
||||||
setCurrentNode(view, 1);
|
|
||||||
checkNodeInline(currValue);
|
|
||||||
}));
|
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(changesLog).toEqual([new SimpleChange(undefined, 'v1', true)]);
|
expect(changesLog).toEqual([new SimpleChange(undefined, 'v1', true)]);
|
||||||
|
|
||||||
currValue = 'v2';
|
currValue = 'v2';
|
||||||
changesLog = [];
|
changesLog = [];
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(changesLog).toEqual([new SimpleChange('v1', 'v2', false)]);
|
expect(changesLog).toEqual([new SimpleChange('v1', 'v2', false)]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -571,7 +537,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
let err: any;
|
let err: any;
|
||||||
try {
|
try {
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err = e;
|
err = e;
|
||||||
}
|
}
|
||||||
|
@ -582,7 +548,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
expect(debugCtx.nodeIndex).toBe(1);
|
expect(debugCtx.nodeIndex).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add a DebugContext to errors in destroyView', () => {
|
it('should add a DebugContext to errors inServices.destroyView', () => {
|
||||||
class SomeService implements OnDestroy {
|
class SomeService implements OnDestroy {
|
||||||
ngOnDestroy() { throw new Error('Test'); }
|
ngOnDestroy() { throw new Error('Test'); }
|
||||||
}
|
}
|
||||||
|
@ -594,7 +560,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
let err: any;
|
let err: any;
|
||||||
try {
|
try {
|
||||||
destroyView(view);
|
Services.destroyView(view);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err = e;
|
err = e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,29 +7,21 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, PipeTransform, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue} from '@angular/core';
|
import {Injector, PipeTransform, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue} from '@angular/core';
|
||||||
import {NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, asPureExpressionData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, directiveDef, elementDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {ArgumentType, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, asPureExpressionData, directiveDef, elementDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
|
|
||||||
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic, createRootData} from './helper';
|
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe(`View Pure Expressions`, () => {
|
describe(`View Pure Expressions`, () => {
|
||||||
let rootData: RootData;
|
|
||||||
let renderComponentType: RenderComponentType;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rootData = createRootData();
|
|
||||||
renderComponentType =
|
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
|
||||||
});
|
|
||||||
|
|
||||||
function compViewDef(
|
function compViewDef(
|
||||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||||
return viewDef(ViewFlags.None, nodes, update, handleEvent, renderComponentType);
|
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||||
|
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(rootData, viewDef);
|
const view = createRootView(viewDef);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -40,8 +32,8 @@ export function main() {
|
||||||
|
|
||||||
describe('pure arrays', () => {
|
describe('pure arrays', () => {
|
||||||
|
|
||||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||||
it(`should support ${InlineDynamic[inlineDynamic]} bindings`, () => {
|
it(`should support ${ArgumentType[inlineDynamic]} bindings`, () => {
|
||||||
let values: any[];
|
let values: any[];
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
|
@ -49,55 +41,52 @@ export function main() {
|
||||||
elementDef(NodeFlags.None, null, null, 2, 'span'), pureArrayDef(2),
|
elementDef(NodeFlags.None, null, null, 2, 'span'), pureArrayDef(2),
|
||||||
directiveDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
directiveDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 1);
|
const pureValue = checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, values);
|
||||||
const pureValue = checkNodeInlineOrDynamic(inlineDynamic, values);
|
checkNodeInlineOrDynamic(check, view, 2, inlineDynamic, [pureValue]);
|
||||||
setCurrentNode(view, 2);
|
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, [pureValue]);
|
|
||||||
}));
|
}));
|
||||||
const service = asProviderData(view, 2).instance;
|
const service = asProviderData(view, 2).instance;
|
||||||
|
|
||||||
values = [1, 2];
|
values = [1, 2];
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
const arr0 = service.data;
|
const arr0 = service.data;
|
||||||
expect(arr0).toEqual([1, 2]);
|
expect(arr0).toEqual([1, 2]);
|
||||||
|
|
||||||
// instance should not change
|
// instance should not change
|
||||||
// if the values don't change
|
// if the values don't change
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(service.data).toBe(arr0);
|
expect(service.data).toBe(arr0);
|
||||||
|
|
||||||
values = [3, 2];
|
values = [3, 2];
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
const arr1 = service.data;
|
const arr1 = service.data;
|
||||||
expect(arr1).not.toBe(arr0);
|
expect(arr1).not.toBe(arr0);
|
||||||
expect(arr1).toEqual([3, 2]);
|
expect(arr1).toEqual([3, 2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should unwrap values with ${InlineDynamic[inlineDynamic]}`, () => {
|
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||||
let bindingValue: any;
|
let bindingValue: any;
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||||
pureArrayDef(1),
|
pureArrayDef(1),
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 1);
|
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, [bindingValue]);
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, [bindingValue]);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const exprData = asPureExpressionData(view, 1);
|
const exprData = asPureExpressionData(view, 1);
|
||||||
|
|
||||||
bindingValue = 'v1';
|
bindingValue = 'v1';
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
const v1Arr = exprData.value;
|
const v1Arr = exprData.value;
|
||||||
expect(v1Arr).toEqual(['v1']);
|
expect(v1Arr).toEqual(['v1']);
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(exprData.value).toBe(v1Arr);
|
expect(exprData.value).toBe(v1Arr);
|
||||||
|
|
||||||
bindingValue = WrappedValue.wrap('v1');
|
bindingValue = WrappedValue.wrap('v1');
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(exprData.value).not.toBe(v1Arr);
|
expect(exprData.value).not.toBe(v1Arr);
|
||||||
expect(exprData.value).toEqual(['v1']);
|
expect(exprData.value).toEqual(['v1']);
|
||||||
});
|
});
|
||||||
|
@ -106,8 +95,8 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('pure objects', () => {
|
describe('pure objects', () => {
|
||||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||||
it(`should support ${InlineDynamic[inlineDynamic]} bindings`, () => {
|
it(`should support ${ArgumentType[inlineDynamic]} bindings`, () => {
|
||||||
let values: any[];
|
let values: any[];
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
|
@ -115,55 +104,52 @@ export function main() {
|
||||||
elementDef(NodeFlags.None, null, null, 2, 'span'), pureObjectDef(['a', 'b']),
|
elementDef(NodeFlags.None, null, null, 2, 'span'), pureObjectDef(['a', 'b']),
|
||||||
directiveDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
directiveDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 1);
|
const pureValue = checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, values);
|
||||||
const pureValue = checkNodeInlineOrDynamic(inlineDynamic, values);
|
checkNodeInlineOrDynamic(check, view, 2, inlineDynamic, [pureValue]);
|
||||||
setCurrentNode(view, 2);
|
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, [pureValue]);
|
|
||||||
}));
|
}));
|
||||||
const service = asProviderData(view, 2).instance;
|
const service = asProviderData(view, 2).instance;
|
||||||
|
|
||||||
values = [1, 2];
|
values = [1, 2];
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
const obj0 = service.data;
|
const obj0 = service.data;
|
||||||
expect(obj0).toEqual({a: 1, b: 2});
|
expect(obj0).toEqual({a: 1, b: 2});
|
||||||
|
|
||||||
// instance should not change
|
// instance should not change
|
||||||
// if the values don't change
|
// if the values don't change
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(service.data).toBe(obj0);
|
expect(service.data).toBe(obj0);
|
||||||
|
|
||||||
values = [3, 2];
|
values = [3, 2];
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
const obj1 = service.data;
|
const obj1 = service.data;
|
||||||
expect(obj1).not.toBe(obj0);
|
expect(obj1).not.toBe(obj0);
|
||||||
expect(obj1).toEqual({a: 3, b: 2});
|
expect(obj1).toEqual({a: 3, b: 2});
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should unwrap values with ${InlineDynamic[inlineDynamic]}`, () => {
|
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||||
let bindingValue: any;
|
let bindingValue: any;
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||||
pureObjectDef(['a']),
|
pureObjectDef(['a']),
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 1);
|
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, [bindingValue]);
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, [bindingValue]);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const exprData = asPureExpressionData(view, 1);
|
const exprData = asPureExpressionData(view, 1);
|
||||||
|
|
||||||
bindingValue = 'v1';
|
bindingValue = 'v1';
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
const v1Obj = exprData.value;
|
const v1Obj = exprData.value;
|
||||||
expect(v1Obj).toEqual({'a': 'v1'});
|
expect(v1Obj).toEqual({'a': 'v1'});
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(exprData.value).toBe(v1Obj);
|
expect(exprData.value).toBe(v1Obj);
|
||||||
|
|
||||||
bindingValue = WrappedValue.wrap('v1');
|
bindingValue = WrappedValue.wrap('v1');
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(exprData.value).not.toBe(v1Obj);
|
expect(exprData.value).not.toBe(v1Obj);
|
||||||
expect(exprData.value).toEqual({'a': 'v1'});
|
expect(exprData.value).toEqual({'a': 'v1'});
|
||||||
});
|
});
|
||||||
|
@ -171,8 +157,8 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('pure pipes', () => {
|
describe('pure pipes', () => {
|
||||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||||
it(`should support ${InlineDynamic[inlineDynamic]} bindings`, () => {
|
it(`should support ${ArgumentType[inlineDynamic]} bindings`, () => {
|
||||||
class SomePipe implements PipeTransform {
|
class SomePipe implements PipeTransform {
|
||||||
transform(v1: any, v2: any) { return [v1 + 10, v2 + 20]; }
|
transform(v1: any, v2: any) { return [v1 + 10, v2 + 20]; }
|
||||||
}
|
}
|
||||||
|
@ -185,32 +171,30 @@ export function main() {
|
||||||
directiveDef(NodeFlags.None, null, 0, SomePipe, []), purePipeDef(SomePipe, 2),
|
directiveDef(NodeFlags.None, null, 0, SomePipe, []), purePipeDef(SomePipe, 2),
|
||||||
directiveDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
directiveDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 2);
|
const pureValue = checkNodeInlineOrDynamic(check, view, 2, inlineDynamic, values);
|
||||||
const pureValue = checkNodeInlineOrDynamic(inlineDynamic, values);
|
checkNodeInlineOrDynamic(check, view, 3, inlineDynamic, [pureValue]);
|
||||||
setCurrentNode(view, 3);
|
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, [pureValue]);
|
|
||||||
}));
|
}));
|
||||||
const service = asProviderData(view, 3).instance;
|
const service = asProviderData(view, 3).instance;
|
||||||
|
|
||||||
values = [1, 2];
|
values = [1, 2];
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
const obj0 = service.data;
|
const obj0 = service.data;
|
||||||
expect(obj0).toEqual([11, 22]);
|
expect(obj0).toEqual([11, 22]);
|
||||||
|
|
||||||
// instance should not change
|
// instance should not change
|
||||||
// if the values don't change
|
// if the values don't change
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(service.data).toBe(obj0);
|
expect(service.data).toBe(obj0);
|
||||||
|
|
||||||
values = [3, 2];
|
values = [3, 2];
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
const obj1 = service.data;
|
const obj1 = service.data;
|
||||||
expect(obj1).not.toBe(obj0);
|
expect(obj1).not.toBe(obj0);
|
||||||
expect(obj1).toEqual([13, 22]);
|
expect(obj1).toEqual([13, 22]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should unwrap values with ${InlineDynamic[inlineDynamic]}`, () => {
|
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||||
let bindingValue: any;
|
let bindingValue: any;
|
||||||
let transformSpy = jasmine.createSpy('transform');
|
let transformSpy = jasmine.createSpy('transform');
|
||||||
|
|
||||||
|
@ -224,21 +208,20 @@ export function main() {
|
||||||
directiveDef(NodeFlags.None, null, 0, SomePipe, []),
|
directiveDef(NodeFlags.None, null, 0, SomePipe, []),
|
||||||
purePipeDef(SomePipe, 1),
|
purePipeDef(SomePipe, 1),
|
||||||
],
|
],
|
||||||
(view) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 2);
|
checkNodeInlineOrDynamic(check, view, 2, inlineDynamic, [bindingValue]);
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, [bindingValue]);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
bindingValue = 'v1';
|
bindingValue = 'v1';
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(transformSpy).toHaveBeenCalledWith('v1');
|
expect(transformSpy).toHaveBeenCalledWith('v1');
|
||||||
|
|
||||||
transformSpy.calls.reset();
|
transformSpy.calls.reset();
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(transformSpy).not.toHaveBeenCalled();
|
expect(transformSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
bindingValue = WrappedValue.wrap('v1');
|
bindingValue = WrappedValue.wrap('v1');
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(transformSpy).toHaveBeenCalledWith('v1');
|
expect(transformSpy).toHaveBeenCalledWith('v1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,26 +8,18 @@
|
||||||
|
|
||||||
import {ElementRef, Injector, QueryList, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core';
|
import {ElementRef, Injector, QueryList, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core';
|
||||||
import {getDebugContext} from '@angular/core/src/errors';
|
import {getDebugContext} from '@angular/core/src/errors';
|
||||||
import {BindingType, DebugContext, NodeDef, NodeFlags, QueryBindingType, QueryValueType, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, directiveDef, elementDef, queryDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {BindingType, DebugContext, NodeDef, NodeFlags, QueryBindingType, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, queryDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {createRootData} from './helper';
|
import {createRootView} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe(`Query Views`, () => {
|
describe(`Query Views`, () => {
|
||||||
let rootData: RootData;
|
|
||||||
let renderComponentType: RenderComponentType;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rootData = createRootData();
|
|
||||||
renderComponentType =
|
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
|
||||||
});
|
|
||||||
|
|
||||||
function compViewDef(
|
function compViewDef(
|
||||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||||
return viewDef(ViewFlags.None, nodes, update, handleEvent, renderComponentType);
|
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||||
|
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
||||||
|
@ -36,7 +28,7 @@ export function main() {
|
||||||
|
|
||||||
function createAndGetRootNodes(
|
function createAndGetRootNodes(
|
||||||
viewDef: ViewDefinition, context: any = null): {rootNodes: any[], view: ViewData} {
|
viewDef: ViewDefinition, context: any = null): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(rootData, viewDef, context);
|
const view = createRootView(viewDef, context);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -79,7 +71,7 @@ export function main() {
|
||||||
const qs: QueryService = asProviderData(view, 1).instance;
|
const qs: QueryService = asProviderData(view, 1).instance;
|
||||||
expect(qs.a).toBeUndefined();
|
expect(qs.a).toBeUndefined();
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const as = qs.a.toArray();
|
const as = qs.a.toArray();
|
||||||
expect(as.length).toBe(2);
|
expect(as.length).toBe(2);
|
||||||
|
@ -97,7 +89,7 @@ export function main() {
|
||||||
aServiceProvider(),
|
aServiceProvider(),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const qs: QueryService = asProviderData(view, 3).instance;
|
const qs: QueryService = asProviderData(view, 3).instance;
|
||||||
expect(qs.a.length).toBe(0);
|
expect(qs.a.length).toBe(0);
|
||||||
|
@ -114,7 +106,7 @@ export function main() {
|
||||||
])),
|
])),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const comp: QueryService = asProviderData(view, 1).instance;
|
const comp: QueryService = asProviderData(view, 1).instance;
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asProviderData(view, 1).componentView;
|
||||||
|
@ -131,7 +123,7 @@ export function main() {
|
||||||
aServiceProvider(),
|
aServiceProvider(),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
const comp: QueryService = asProviderData(view, 1).instance;
|
const comp: QueryService = asProviderData(view, 1).instance;
|
||||||
expect(comp.a.length).toBe(0);
|
expect(comp.a.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
@ -153,9 +145,9 @@ export function main() {
|
||||||
...contentQueryProviders(),
|
...contentQueryProviders(),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
||||||
attachEmbeddedView(asElementData(view, 3), 0, childView);
|
attachEmbeddedView(asElementData(view, 3), 0, childView);
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
// queries on parent elements of anchors
|
// queries on parent elements of anchors
|
||||||
const qs1: QueryService = asProviderData(view, 1).instance;
|
const qs1: QueryService = asProviderData(view, 1).instance;
|
||||||
|
@ -185,11 +177,11 @@ export function main() {
|
||||||
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0),
|
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
||||||
// attach at a different place than the one where the template was defined
|
// attach at a different place than the one where the template was defined
|
||||||
attachEmbeddedView(asElementData(view, 7), 0, childView);
|
attachEmbeddedView(asElementData(view, 7), 0, childView);
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
// query on the declaration place
|
// query on the declaration place
|
||||||
const qs1: QueryService = asProviderData(view, 1).instance;
|
const qs1: QueryService = asProviderData(view, 1).instance;
|
||||||
|
@ -215,19 +207,19 @@ export function main() {
|
||||||
])),
|
])),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const qs: QueryService = asProviderData(view, 1).instance;
|
const qs: QueryService = asProviderData(view, 1).instance;
|
||||||
expect(qs.a.length).toBe(0);
|
expect(qs.a.length).toBe(0);
|
||||||
|
|
||||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
||||||
attachEmbeddedView(asElementData(view, 3), 0, childView);
|
attachEmbeddedView(asElementData(view, 3), 0, childView);
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
expect(qs.a.length).toBe(1);
|
expect(qs.a.length).toBe(1);
|
||||||
|
|
||||||
detachEmbeddedView(asElementData(view, 3), 0);
|
detachEmbeddedView(asElementData(view, 3), 0);
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
expect(qs.a.length).toBe(0);
|
expect(qs.a.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
@ -247,20 +239,20 @@ export function main() {
|
||||||
])),
|
])),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const comp: QueryService = asProviderData(view, 1).instance;
|
const comp: QueryService = asProviderData(view, 1).instance;
|
||||||
expect(comp.a.length).toBe(0);
|
expect(comp.a.length).toBe(0);
|
||||||
|
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asProviderData(view, 1).componentView;
|
||||||
const childView = createEmbeddedView(compView, compView.def.nodes[0]);
|
const childView = Services.createEmbeddedView(compView, compView.def.nodes[0]);
|
||||||
attachEmbeddedView(asElementData(compView, 0), 0, childView);
|
attachEmbeddedView(asElementData(compView, 0), 0, childView);
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
expect(comp.a.length).toBe(1);
|
expect(comp.a.length).toBe(1);
|
||||||
|
|
||||||
detachEmbeddedView(asElementData(compView, 0), 0);
|
detachEmbeddedView(asElementData(compView, 0), 0);
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
expect(comp.a.length).toBe(0);
|
expect(comp.a.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
@ -280,7 +272,7 @@ export function main() {
|
||||||
aServiceProvider(),
|
aServiceProvider(),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const qs: QueryService = asProviderData(view, 1).instance;
|
const qs: QueryService = asProviderData(view, 1).instance;
|
||||||
expect(qs.a instanceof QueryList).toBeTruthy();
|
expect(qs.a instanceof QueryList).toBeTruthy();
|
||||||
|
@ -303,7 +295,7 @@ export function main() {
|
||||||
aServiceProvider(),
|
aServiceProvider(),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const qs: QueryService = asProviderData(view, 1).instance;
|
const qs: QueryService = asProviderData(view, 1).instance;
|
||||||
expect(qs.a).toBe(asProviderData(view, 3).instance);
|
expect(qs.a).toBe(asProviderData(view, 3).instance);
|
||||||
|
@ -322,7 +314,7 @@ export function main() {
|
||||||
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const qs: QueryService = asProviderData(view, 1).instance;
|
const qs: QueryService = asProviderData(view, 1).instance;
|
||||||
expect(qs.a.nativeElement).toBe(asElementData(view, 0).renderElement);
|
expect(qs.a.nativeElement).toBe(asElementData(view, 0).renderElement);
|
||||||
|
@ -341,7 +333,7 @@ export function main() {
|
||||||
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const qs: QueryService = asProviderData(view, 1).instance;
|
const qs: QueryService = asProviderData(view, 1).instance;
|
||||||
expect(qs.a.createEmbeddedView).toBeTruthy();
|
expect(qs.a.createEmbeddedView).toBeTruthy();
|
||||||
|
@ -358,7 +350,7 @@ export function main() {
|
||||||
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const qs: QueryService = asProviderData(view, 1).instance;
|
const qs: QueryService = asProviderData(view, 1).instance;
|
||||||
expect(qs.a.createEmbeddedView).toBeTruthy();
|
expect(qs.a.createEmbeddedView).toBeTruthy();
|
||||||
|
@ -380,22 +372,22 @@ export function main() {
|
||||||
])),
|
])),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
checkNoChangesView(view);
|
Services.checkNoChangesView(view);
|
||||||
|
|
||||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
||||||
attachEmbeddedView(asElementData(view, 3), 0, childView);
|
attachEmbeddedView(asElementData(view, 3), 0, childView);
|
||||||
|
|
||||||
let err: any;
|
let err: any;
|
||||||
try {
|
try {
|
||||||
checkNoChangesView(view);
|
Services.checkNoChangesView(view);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err = e;
|
err = e;
|
||||||
}
|
}
|
||||||
expect(err).toBeTruthy();
|
expect(err).toBeTruthy();
|
||||||
expect(err.message)
|
expect(err.message)
|
||||||
.toBe(
|
.toBe(
|
||||||
`Expression has changed after it was checked. Previous value: 'Query query1 not dirty'. Current value: 'Query query1 dirty'.`);
|
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'Query query1 not dirty'. Current value: 'Query query1 dirty'.`);
|
||||||
const debugCtx = getDebugContext(err);
|
const debugCtx = getDebugContext(err);
|
||||||
expect(debugCtx.view).toBe(view);
|
expect(debugCtx.view).toBe(view);
|
||||||
expect(debugCtx.nodeIndex).toBe(2);
|
expect(debugCtx.nodeIndex).toBe(2);
|
||||||
|
@ -416,7 +408,7 @@ export function main() {
|
||||||
|
|
||||||
let err: any;
|
let err: any;
|
||||||
try {
|
try {
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err = e;
|
err = e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,31 +7,23 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, getDebugNode} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, getDebugNode} from '@angular/core';
|
||||||
import {DebugContext, NodeDef, NodeFlags, QueryValueType, Refs, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, directiveDef, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {DebugContext, NodeDef, NodeFlags, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, directiveDef, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
import {createRootView, isBrowser} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('View References', () => {
|
describe('View Services', () => {
|
||||||
let rootData: RootData;
|
|
||||||
let renderComponentType: RenderComponentType;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rootData = createRootData();
|
|
||||||
renderComponentType =
|
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
|
||||||
});
|
|
||||||
|
|
||||||
function compViewDef(
|
function compViewDef(
|
||||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||||
return viewDef(ViewFlags.None, nodes, update, handleEvent, renderComponentType);
|
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||||
|
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(
|
function createAndGetRootNodes(
|
||||||
viewDef: ViewDefinition, context: any = null): {rootNodes: any[], view: ViewData} {
|
viewDef: ViewDefinition, context: any = null): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(rootData, viewDef, context);
|
const view = createRootView(viewDef, context);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -58,7 +50,7 @@ export function main() {
|
||||||
const view = createViewWithData();
|
const view = createViewWithData();
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asProviderData(view, 1).componentView;
|
||||||
|
|
||||||
const debugCtx = Refs.createDebugContext(compView, 0);
|
const debugCtx = Services.createDebugContext(compView, 0);
|
||||||
|
|
||||||
expect(debugCtx.componentRenderElement).toBe(asElementData(view, 0).renderElement);
|
expect(debugCtx.componentRenderElement).toBe(asElementData(view, 0).renderElement);
|
||||||
expect(debugCtx.renderNode).toBe(asElementData(compView, 0).renderElement);
|
expect(debugCtx.renderNode).toBe(asElementData(compView, 0).renderElement);
|
||||||
|
@ -75,7 +67,7 @@ export function main() {
|
||||||
const view = createViewWithData();
|
const view = createViewWithData();
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asProviderData(view, 1).componentView;
|
||||||
|
|
||||||
const debugCtx = Refs.createDebugContext(compView, 2);
|
const debugCtx = Services.createDebugContext(compView, 2);
|
||||||
|
|
||||||
expect(debugCtx.componentRenderElement).toBe(asElementData(view, 0).renderElement);
|
expect(debugCtx.componentRenderElement).toBe(asElementData(view, 0).renderElement);
|
||||||
expect(debugCtx.renderNode).toBe(asTextData(compView, 2).renderText);
|
expect(debugCtx.renderNode).toBe(asTextData(compView, 2).renderText);
|
||||||
|
@ -89,7 +81,7 @@ export function main() {
|
||||||
const view = createViewWithData();
|
const view = createViewWithData();
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asProviderData(view, 1).componentView;
|
||||||
|
|
||||||
const debugCtx = Refs.createDebugContext(compView, 1);
|
const debugCtx = Services.createDebugContext(compView, 1);
|
||||||
|
|
||||||
expect(debugCtx.renderNode).toBe(asElementData(compView, 0).renderElement);
|
expect(debugCtx.renderNode).toBe(asElementData(compView, 0).renderElement);
|
||||||
});
|
});
|
|
@ -7,40 +7,23 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||||
import {DebugContext, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asTextData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {ArgumentType, DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asTextData, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic, createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
describe(`View Text`, () => {
|
||||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
|
||||||
}
|
|
||||||
defineTests({directDom: false, viewFlags: 0});
|
|
||||||
}
|
|
||||||
|
|
||||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
|
||||||
describe(`View Text, directDom: ${config.directDom}`, () => {
|
|
||||||
setupAndCheckRenderer(config);
|
|
||||||
|
|
||||||
let rootData: RootData;
|
|
||||||
let renderComponentType: RenderComponentType;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rootData = createRootData();
|
|
||||||
renderComponentType =
|
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
|
||||||
});
|
|
||||||
|
|
||||||
function compViewDef(
|
function compViewDef(
|
||||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||||
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||||
|
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(
|
function createAndGetRootNodes(
|
||||||
viewDef: ViewDefinition, context?: any): {rootNodes: any[], view: ViewData} {
|
viewDef: ViewDefinition, context?: any): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(rootData, viewDef, context);
|
const view = createRootView(viewDef, context);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -69,36 +52,33 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
expect(getDOM().getText(textNode)).toBe('a');
|
expect(getDOM().getText(textNode)).toBe('a');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!config.directDom) {
|
it('should add debug information to the renderer', () => {
|
||||||
it('should add debug information to the renderer', () => {
|
const someContext = new Object();
|
||||||
const someContext = new Object();
|
const {view, rootNodes} =
|
||||||
const {view, rootNodes} =
|
createAndGetRootNodes(compViewDef([textDef(null, ['a'])]), someContext);
|
||||||
createAndGetRootNodes(compViewDef([textDef(null, ['a'])]), someContext);
|
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asTextData(view, 0).renderText);
|
||||||
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asTextData(view, 0).renderText);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('change text', () => {
|
describe('change text', () => {
|
||||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||||
it(`should update ${InlineDynamic[inlineDynamic]}`, () => {
|
it(`should update ${ArgumentType[inlineDynamic]}`, () => {
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
[
|
[
|
||||||
textDef(null, ['0', '1', '2']),
|
textDef(null, ['0', '1', '2']),
|
||||||
],
|
],
|
||||||
(view: ViewData) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 0);
|
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['a', 'b']);
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, ['a', 'b']);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const node = rootNodes[0];
|
const node = rootNodes[0];
|
||||||
expect(getDOM().getText(rootNodes[0])).toBe('0a1b2');
|
expect(getDOM().getText(rootNodes[0])).toBe('0a1b2');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
it(`should unwrap values with ${InlineDynamic[inlineDynamic]}`, () => {
|
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||||
let bindingValue: any;
|
let bindingValue: any;
|
||||||
const setterSpy = jasmine.createSpy('set');
|
const setterSpy = jasmine.createSpy('set');
|
||||||
|
|
||||||
|
@ -112,24 +92,23 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
[
|
[
|
||||||
textDef(null, ['', '']),
|
textDef(null, ['', '']),
|
||||||
],
|
],
|
||||||
(view: ViewData) => {
|
(check, view) => {
|
||||||
setCurrentNode(view, 0);
|
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [bindingValue]);
|
||||||
checkNodeInlineOrDynamic(inlineDynamic, [bindingValue]);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Object.defineProperty(rootNodes[0], 'nodeValue', {set: setterSpy});
|
Object.defineProperty(rootNodes[0], 'nodeValue', {set: setterSpy});
|
||||||
|
|
||||||
bindingValue = 'v1';
|
bindingValue = 'v1';
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||||
|
|
||||||
setterSpy.calls.reset();
|
setterSpy.calls.reset();
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(setterSpy).not.toHaveBeenCalled();
|
expect(setterSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
setterSpy.calls.reset();
|
setterSpy.calls.reset();
|
||||||
bindingValue = WrappedValue.wrap('v1');
|
bindingValue = WrappedValue.wrap('v1');
|
||||||
checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {NodeFlags, QueryValueType, ViewData, ViewDefinition, ViewFlags, anchorDef, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, directiveDef, elementDef, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {NodeFlags, QueryValueType, ViewData, ViewDefinition, ViewFlags, anchorDef, directiveDef, elementDef, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('viewDef', () => {
|
describe('viewDef', () => {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import {NgIf} from '@angular/common';
|
import {NgIf} from '@angular/common';
|
||||||
import {Component, ComponentFactory, ComponentRef, Injector, NgModule, RootRenderer, Sanitizer, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
import {Component, ComponentFactory, ComponentRef, Injector, NgModule, RootRenderer, Sanitizer, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
||||||
import {BindingType, NodeFlags, ViewData, ViewDefinition, ViewFlags, anchorDef, checkNodeInline, createComponentFactory, directiveDef, elementDef, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {ArgumentType, BindingType, NodeFlags, ViewData, ViewDefinition, ViewFlags, anchorDef, createComponentFactory, directiveDef, elementDef, initServicesIfNeeded, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {DomSanitizerImpl, SafeStyle} from '@angular/platform-browser/src/security/dom_sanitization_service';
|
import {DomSanitizerImpl, SafeStyle} from '@angular/platform-browser/src/security/dom_sanitization_service';
|
||||||
|
|
||||||
import {TreeNode, emptyTree} from '../util';
|
import {TreeNode, emptyTree} from '../util';
|
||||||
|
@ -21,7 +21,7 @@ export class TreeComponent {
|
||||||
get bgColor() { return this.data.depth % 2 ? trustedEmptyColor : trustedGreyColor; }
|
get bgColor() { return this.data.depth % 2 ? trustedEmptyColor : trustedGreyColor; }
|
||||||
}
|
}
|
||||||
|
|
||||||
let viewFlags = ViewFlags.DirectDom;
|
let viewFlags = ViewFlags.None;
|
||||||
|
|
||||||
function TreeComponent_Host(): ViewDefinition {
|
function TreeComponent_Host(): ViewDefinition {
|
||||||
return viewDef(viewFlags, [
|
return viewDef(viewFlags, [
|
||||||
|
@ -38,10 +38,9 @@ function TreeComponent_0(): ViewDefinition {
|
||||||
directiveDef(
|
directiveDef(
|
||||||
NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null, TreeComponent_0),
|
NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null, TreeComponent_0),
|
||||||
],
|
],
|
||||||
(view: ViewData) => {
|
(check, view) => {
|
||||||
const cmp = view.component;
|
const cmp = view.component;
|
||||||
setCurrentNode(view, 1);
|
check(view, 1, ArgumentType.Inline, cmp.data.left);
|
||||||
checkNodeInline(cmp.data.left);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const TreeComponent_2: ViewDefinition = viewDef(
|
const TreeComponent_2: ViewDefinition = viewDef(
|
||||||
|
@ -51,10 +50,9 @@ function TreeComponent_0(): ViewDefinition {
|
||||||
directiveDef(
|
directiveDef(
|
||||||
NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null, TreeComponent_0),
|
NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null, TreeComponent_0),
|
||||||
],
|
],
|
||||||
(view: ViewData) => {
|
(check, view) => {
|
||||||
const cmp = view.component;
|
const cmp = view.component;
|
||||||
setCurrentNode(view, 1);
|
check(view, 1, ArgumentType.Inline, cmp.data.left);
|
||||||
checkNodeInline(cmp.data.right);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return viewDef(
|
return viewDef(
|
||||||
|
@ -71,16 +69,12 @@ function TreeComponent_0(): ViewDefinition {
|
||||||
directiveDef(
|
directiveDef(
|
||||||
NodeFlags.None, null, 0, NgIf, [ViewContainerRef, TemplateRef], {ngIf: [0, 'ngIf']}),
|
NodeFlags.None, null, 0, NgIf, [ViewContainerRef, TemplateRef], {ngIf: [0, 'ngIf']}),
|
||||||
],
|
],
|
||||||
(view: ViewData) => {
|
(check, view) => {
|
||||||
const cmp = view.component;
|
const cmp = view.component;
|
||||||
setCurrentNode(view, 0);
|
check(view, 0, ArgumentType.Inline, cmp.bgColor);
|
||||||
checkNodeInline(cmp.bgColor);
|
check(view, 1, ArgumentType.Inline, cmp.data.value);
|
||||||
setCurrentNode(view, 1);
|
check(view, 3, ArgumentType.Inline, cmp.data.left != null);
|
||||||
checkNodeInline(cmp.data.value);
|
check(view, 5, ArgumentType.Inline, cmp.data.right != null);
|
||||||
setCurrentNode(view, 3);
|
|
||||||
checkNodeInline(cmp.data.left != null);
|
|
||||||
setCurrentNode(view, 5);
|
|
||||||
checkNodeInline(cmp.data.right != null);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +84,11 @@ export class AppModule implements Injector {
|
||||||
componentRef: ComponentRef<TreeComponent>;
|
componentRef: ComponentRef<TreeComponent>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
initServicesIfNeeded();
|
||||||
this.sanitizer = new DomSanitizerImpl();
|
this.sanitizer = new DomSanitizerImpl();
|
||||||
trustedEmptyColor = this.sanitizer.bypassSecurityTrustStyle('');
|
trustedEmptyColor = this.sanitizer.bypassSecurityTrustStyle('');
|
||||||
trustedGreyColor = this.sanitizer.bypassSecurityTrustStyle('grey');
|
trustedGreyColor = this.sanitizer.bypassSecurityTrustStyle('grey');
|
||||||
this.componentFactory = createComponentFactory('#root', TreeComponent_Host);
|
this.componentFactory = createComponentFactory('#root', TreeComponent, TreeComponent_Host);
|
||||||
}
|
}
|
||||||
|
|
||||||
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
||||||
|
@ -106,6 +101,8 @@ export class AppModule implements Injector {
|
||||||
return Injector.NULL.get(token, notFoundValue);
|
return Injector.NULL.get(token, notFoundValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() { this.componentRef = this.componentFactory.create(this); }
|
bootstrap() {
|
||||||
|
this.componentRef = this.componentFactory.create(this, [], this.componentFactory.selector);
|
||||||
|
}
|
||||||
tick() { this.componentRef.changeDetectorRef.detectChanges(); }
|
tick() { this.componentRef.changeDetectorRef.detectChanges(); }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue