feat(core): view engine - integrate with `ComponentFactory` (#14237)
`ComponentFactory`s can now be created from a `ViewDefinitionFactory` via `RefFactory.createComponentFactory`. This commit also: - splits `Services` into `Refs` and `RootData` - changes `ViewState` into a bitmask - implements `ViewContainerRef.move` Part of #14013 PR Close #14237
This commit is contained in:
parent
388afa414e
commit
14d7844b2b
|
@ -95,12 +95,10 @@ export class ComponentFactory<C> {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_viewClass: Type<AppView<any>>;
|
_viewClass: Type<AppView<any>>;
|
||||||
constructor(
|
constructor(
|
||||||
public selector: string, _viewClass: Type<AppView<any>>, private _componentType: Type<any>) {
|
public selector: string, _viewClass: Type<AppView<any>>, public componentType: Type<any>) {
|
||||||
this._viewClass = _viewClass;
|
this._viewClass = _viewClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
get componentType(): Type<any> { return this._componentType; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new component.
|
* Creates a new component.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
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, ViewData, ViewDefinition, ViewFlags, asElementData} from './types';
|
import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementOutputDef, EntryAction, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, Refs, ViewData, ViewDefinition, ViewFlags, asElementData} from './types';
|
||||||
import {checkAndUpdateBinding, dispatchEvent, entryAction, setBindingDebugInfo, setCurrentNode, sliceErrorStack, unwrapValue} from './util';
|
import {checkAndUpdateBinding, dispatchEvent, entryAction, setBindingDebugInfo, setCurrentNode, sliceErrorStack, unwrapValue} from './util';
|
||||||
|
|
||||||
export function anchorDef(
|
export function anchorDef(
|
||||||
|
@ -129,19 +129,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 parentNode =
|
|
||||||
def.parent != null ? asElementData(view, def.parent).renderElement : renderHost;
|
|
||||||
const elDef = def.element;
|
const elDef = def.element;
|
||||||
|
const rootSelectorOrNode = view.root.selectorOrNode;
|
||||||
let el: any;
|
let el: any;
|
||||||
if (view.renderer) {
|
if (view.parent || !rootSelectorOrNode) {
|
||||||
const debugContext =
|
const parentNode =
|
||||||
isDevMode() ? view.services.createDebugContext(view, def.index) : undefined;
|
def.parent != null ? asElementData(view, def.parent).renderElement : renderHost;
|
||||||
el = elDef.name ? view.renderer.createElement(parentNode, elDef.name, debugContext) :
|
if (view.renderer) {
|
||||||
view.renderer.createTemplateAnchor(parentNode, debugContext);
|
const debugContext = isDevMode() ? Refs.createDebugContext(view, def.index) : undefined;
|
||||||
|
el = elDef.name ? view.renderer.createElement(parentNode, elDef.name, debugContext) :
|
||||||
|
view.renderer.createTemplateAnchor(parentNode, debugContext);
|
||||||
|
} else {
|
||||||
|
el = elDef.name ? document.createElement(elDef.name) : document.createComment('');
|
||||||
|
if (parentNode) {
|
||||||
|
parentNode.appendChild(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
el = elDef.name ? document.createElement(elDef.name) : document.createComment('');
|
if (view.renderer) {
|
||||||
if (parentNode) {
|
const debugContext = isDevMode() ? Refs.createDebugContext(view, def.index) : undefined;
|
||||||
parentNode.appendChild(el);
|
el = view.renderer.selectRootElement(rootSelectorOrNode, debugContext);
|
||||||
|
} else {
|
||||||
|
el = typeof rootSelectorOrNode === 'string' ? document.querySelector(rootSelectorOrNode) :
|
||||||
|
rootSelectorOrNode;
|
||||||
|
el.textContent = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (elDef.attrs) {
|
if (elDef.attrs) {
|
||||||
|
@ -269,7 +280,7 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
|
||||||
function setElementAttribute(
|
function setElementAttribute(
|
||||||
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.services.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) {
|
if (view.renderer) {
|
||||||
view.renderer.setElementAttribute(renderNode, name, renderValue);
|
view.renderer.setElementAttribute(renderNode, name, renderValue);
|
||||||
|
@ -296,7 +307,7 @@ function setElementClass(view: ViewData, renderNode: any, name: string, value: b
|
||||||
|
|
||||||
function setElementStyle(
|
function setElementStyle(
|
||||||
view: ViewData, binding: BindingDef, renderNode: any, name: string, value: any) {
|
view: ViewData, binding: BindingDef, renderNode: any, name: string, value: any) {
|
||||||
let renderValue = view.services.sanitize(SecurityContext.STYLE, value);
|
let renderValue = view.root.sanitizer.sanitize(SecurityContext.STYLE, value);
|
||||||
if (renderValue != null) {
|
if (renderValue != null) {
|
||||||
renderValue = renderValue.toString();
|
renderValue = renderValue.toString();
|
||||||
const unit = binding.suffix;
|
const unit = binding.suffix;
|
||||||
|
@ -322,7 +333,7 @@ function setElementStyle(
|
||||||
function setElementProperty(
|
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.services.sanitize(securityContext, value) : value;
|
let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value;
|
||||||
if (view.renderer) {
|
if (view.renderer) {
|
||||||
view.renderer.setElementProperty(renderNode, name, renderValue);
|
view.renderer.setElementProperty(renderNode, name, renderValue);
|
||||||
if (isDevMode() && (view.def.flags & ViewFlags.DirectDom) === 0) {
|
if (isDevMode() && (view.def.flags & ViewFlags.DirectDom) === 0) {
|
||||||
|
|
|
@ -35,7 +35,7 @@ export function viewDebugError(msg: string, context: DebugContext): ViewDebugErr
|
||||||
const err = new Error(msg) as any;
|
const err = new Error(msg) as any;
|
||||||
err.context = context;
|
err.context = context;
|
||||||
err.stack = context.source;
|
err.stack = context.source;
|
||||||
context.view.state = ViewState.Errored;
|
context.view.state |= ViewState.Errored;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,13 @@ export {queryDef} from './query';
|
||||||
export {textDef} from './text';
|
export {textDef} from './text';
|
||||||
export {rootRenderNodes, setCurrentNode} from './util';
|
export {rootRenderNodes, setCurrentNode} from './util';
|
||||||
export {checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, destroyView, viewDef} from './view';
|
export {checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, destroyView, viewDef} from './view';
|
||||||
export {attachEmbeddedView, detachEmbeddedView} from './view_attach';
|
export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
|
||||||
|
|
||||||
export * from './types';
|
export * from './types';
|
||||||
export {DefaultServices} from './services';
|
|
||||||
|
import {createRefs} from './refs';
|
||||||
|
import {Refs} from './types';
|
||||||
|
|
||||||
|
Refs.setInstance(createRefs());
|
||||||
|
|
||||||
|
export const createComponentFactory: typeof Refs.createComponentFactory =
|
||||||
|
Refs.createComponentFactory;
|
||||||
|
|
|
@ -17,8 +17,8 @@ import {Renderer} from '../render/api';
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
|
|
||||||
import {queryDef} from './query';
|
import {queryDef} from './query';
|
||||||
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, EntryAction, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderOutputDef, ProviderType, QueryBindingType, QueryDef, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
|
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 {checkAndUpdateBinding, dispatchEvent, entryAction, findElementDef, setBindingDebugInfo, setCurrentNode, unwrapValue} from './util';
|
import {checkAndUpdateBinding, dispatchEvent, entryAction, findElementDef, parentDiIndex, setBindingDebugInfo, setCurrentNode, unwrapValue} from './util';
|
||||||
|
|
||||||
const _tokenKeyCache = new Map<any, string>();
|
const _tokenKeyCache = new Map<any, string>();
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ export function checkAndUpdateProviderInline(
|
||||||
if (changes) {
|
if (changes) {
|
||||||
provider.ngOnChanges(changes);
|
provider.ngOnChanges(changes);
|
||||||
}
|
}
|
||||||
if (view.state === ViewState.FirstCheck && (def.flags & NodeFlags.OnInit)) {
|
if ((view.state & ViewState.FirstCheck) && (def.flags & NodeFlags.OnInit)) {
|
||||||
provider.ngOnInit();
|
provider.ngOnInit();
|
||||||
}
|
}
|
||||||
if (def.flags & NodeFlags.DoCheck) {
|
if (def.flags & NodeFlags.DoCheck) {
|
||||||
|
@ -186,7 +186,7 @@ export function checkAndUpdateProviderDynamic(view: ViewData, def: NodeDef, valu
|
||||||
if (changes) {
|
if (changes) {
|
||||||
provider.ngOnChanges(changes);
|
provider.ngOnChanges(changes);
|
||||||
}
|
}
|
||||||
if (view.state === ViewState.FirstCheck && (def.flags & NodeFlags.OnInit)) {
|
if ((view.state & ViewState.FirstCheck) && (def.flags & NodeFlags.OnInit)) {
|
||||||
provider.ngOnInit();
|
provider.ngOnInit();
|
||||||
}
|
}
|
||||||
if (def.flags & NodeFlags.DoCheck) {
|
if (def.flags & NodeFlags.DoCheck) {
|
||||||
|
@ -290,16 +290,18 @@ function callFactory(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveDep(
|
export function resolveDep(
|
||||||
view: ViewData, requestNodeIndex: number, elIndex: number, depDef: DepDef): any {
|
view: ViewData, requestNodeIndex: number, elIndex: number, depDef: DepDef,
|
||||||
const notFoundValue = depDef.flags & DepFlags.Optional ? null : Injector.THROW_IF_NOT_FOUND;
|
notFoundValue = Injector.THROW_IF_NOT_FOUND): any {
|
||||||
|
const startView = view;
|
||||||
|
if (depDef.flags & DepFlags.Optional) {
|
||||||
|
notFoundValue = null;
|
||||||
|
}
|
||||||
const tokenKey = depDef.tokenKey;
|
const tokenKey = depDef.tokenKey;
|
||||||
|
|
||||||
if (depDef.flags & DepFlags.SkipSelf) {
|
if (depDef.flags & DepFlags.SkipSelf) {
|
||||||
requestNodeIndex = null;
|
requestNodeIndex = null;
|
||||||
const elDef = view.def.nodes[elIndex];
|
elIndex = view.def.nodes[elIndex].parent;
|
||||||
if (elDef.parent != null) {
|
while (elIndex == null && view) {
|
||||||
elIndex = elDef.parent;
|
|
||||||
} else {
|
|
||||||
elIndex = parentDiIndex(view);
|
elIndex = parentDiIndex(view);
|
||||||
view = view.parent;
|
view = view.parent;
|
||||||
}
|
}
|
||||||
|
@ -317,9 +319,9 @@ export function resolveDep(
|
||||||
case ElementRefTokenKey:
|
case ElementRefTokenKey:
|
||||||
return new ElementRef(asElementData(view, elIndex).renderElement);
|
return new ElementRef(asElementData(view, elIndex).renderElement);
|
||||||
case ViewContainerRefTokenKey:
|
case ViewContainerRefTokenKey:
|
||||||
return view.services.createViewContainerRef(asElementData(view, elIndex));
|
return Refs.createViewContainerRef(view, elIndex);
|
||||||
case TemplateRefTokenKey:
|
case TemplateRefTokenKey:
|
||||||
return view.services.createTemplateRef(view, elDef);
|
return Refs.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...
|
||||||
|
@ -330,9 +332,9 @@ export function resolveDep(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// A ViewRef is also a ChangeDetectorRef
|
// A ViewRef is also a ChangeDetectorRef
|
||||||
return view.services.createViewRef(cdView);
|
return Refs.createViewRef(cdView);
|
||||||
case InjectorRefTokenKey:
|
case InjectorRefTokenKey:
|
||||||
return createInjector(view, elIndex);
|
return Refs.createInjector(view, elIndex);
|
||||||
default:
|
default:
|
||||||
const providerIndex = elDef.element.providerIndices[tokenKey];
|
const providerIndex = elDef.element.providerIndices[tokenKey];
|
||||||
if (providerIndex != null) {
|
if (providerIndex != null) {
|
||||||
|
@ -347,34 +349,7 @@ export function resolveDep(
|
||||||
elIndex = parentDiIndex(view);
|
elIndex = parentDiIndex(view);
|
||||||
view = view.parent;
|
view = view.parent;
|
||||||
}
|
}
|
||||||
return Injector.NULL.get(depDef.token, notFoundValue);
|
return startView.root.injector.get(depDef.token, notFoundValue);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* for component views, this is the same as parentIndex.
|
|
||||||
* for embedded views, this is the index of the parent node
|
|
||||||
* that contains the view container.
|
|
||||||
*/
|
|
||||||
function parentDiIndex(view: ViewData): number {
|
|
||||||
if (view.parent) {
|
|
||||||
const parentNodeDef = view.def.nodes[view.parentIndex];
|
|
||||||
return parentNodeDef.element && parentNodeDef.element.template ? parentNodeDef.parent :
|
|
||||||
parentNodeDef.index;
|
|
||||||
}
|
|
||||||
return view.parentIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createInjector(view: ViewData, elIndex: number): Injector {
|
|
||||||
return new Injector_(view, elIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
class Injector_ implements Injector {
|
|
||||||
constructor(private view: ViewData, private elIndex: number) {}
|
|
||||||
get(token: any, notFoundValue?: any): any {
|
|
||||||
return resolveDep(
|
|
||||||
this.view, undefined, this.elIndex,
|
|
||||||
{flags: DepFlags.None, token, tokenKey: tokenKey(token)});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkAndUpdateProp(
|
function checkAndUpdateProp(
|
||||||
|
@ -385,8 +360,9 @@ function checkAndUpdateProp(
|
||||||
if (def.flags & NodeFlags.OnChanges) {
|
if (def.flags & NodeFlags.OnChanges) {
|
||||||
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
|
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
|
||||||
changed = checkAndUpdateBinding(view, def, bindingIdx, value);
|
changed = checkAndUpdateBinding(view, def, bindingIdx, value);
|
||||||
change =
|
change = changed ?
|
||||||
changed ? new SimpleChange(oldValue, value, view.state === ViewState.FirstCheck) : null;
|
new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0) :
|
||||||
|
null;
|
||||||
} else {
|
} else {
|
||||||
changed = checkAndUpdateBinding(view, def, bindingIdx, value);
|
changed = checkAndUpdateBinding(view, def, bindingIdx, value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ 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, ViewData, asElementData, asProviderData, asQueryList} from './types';
|
import {NodeDef, NodeFlags, NodeType, QueryBindingDef, QueryBindingType, QueryDef, QueryValueType, Refs, ViewData, asElementData, asProviderData, asQueryList} from './types';
|
||||||
import {declaredViewContainer} from './util';
|
import {declaredViewContainer} from './util';
|
||||||
|
|
||||||
export function queryDef(
|
export function queryDef(
|
||||||
|
@ -158,10 +158,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 = view.services.createTemplateRef(view, nodeDef);
|
value = Refs.createTemplateRef(view, nodeDef);
|
||||||
break;
|
break;
|
||||||
case QueryValueType.ViewContainerRef:
|
case QueryValueType.ViewContainerRef:
|
||||||
value = view.services.createViewContainerRef(asElementData(view, nodeDef.index));
|
value = Refs.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,8 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {ChangeDetectorRef} from '../change_detection/change_detection';
|
||||||
import {Injectable, Injector} from '../di';
|
import {Injectable, Injector} from '../di';
|
||||||
import {unimplemented} from '../facade/errors';
|
|
||||||
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
|
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
|
||||||
import {ElementRef} from '../linker/element_ref';
|
import {ElementRef} from '../linker/element_ref';
|
||||||
import {TemplateRef} from '../linker/template_ref';
|
import {TemplateRef} from '../linker/template_ref';
|
||||||
|
@ -15,44 +15,126 @@ 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 {RenderComponentType, Renderer, RootRenderer} from '../render/api';
|
||||||
import {Sanitizer, SecurityContext} from '../security';
|
import {Sanitizer, SecurityContext} from '../security';
|
||||||
|
import {Type} from '../type';
|
||||||
|
|
||||||
import {createInjector} from './provider';
|
import {resolveDep, tokenKey} from './provider';
|
||||||
import {getQueryValue} from './query';
|
import {getQueryValue} from './query';
|
||||||
import {DebugContext, ElementData, NodeData, NodeDef, NodeType, Services, ViewData, ViewDefinition, ViewState, asElementData} from './types';
|
import {DebugContext, DepFlags, ElementData, NodeData, NodeDef, NodeType, Refs, RootData, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types';
|
||||||
import {findElementDef, isComponentView, renderNode, rootRenderNodes} from './util';
|
import {findElementDef, isComponentView, parentDiIndex, renderNode, resolveViewDefinition, rootRenderNodes} from './util';
|
||||||
import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, destroyView} from './view';
|
import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view';
|
||||||
import {attachEmbeddedView, detachEmbeddedView} from './view_attach';
|
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
|
||||||
|
|
||||||
@Injectable()
|
const EMPTY_CONTEXT = new Object();
|
||||||
export class DefaultServices implements Services {
|
|
||||||
constructor(private _rootRenderer: RootRenderer, private _sanitizer: Sanitizer) {}
|
|
||||||
|
|
||||||
renderComponent(rcp: RenderComponentType): Renderer {
|
export function createRefs() {
|
||||||
return this._rootRenderer.renderComponent(rcp);
|
return new Refs_();
|
||||||
}
|
}
|
||||||
sanitize(context: SecurityContext, value: string): string {
|
|
||||||
return this._sanitizer.sanitize(context, value);
|
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); }
|
createViewRef(data: ViewData): ViewRef { return new ViewRef_(data); }
|
||||||
createViewContainerRef(data: ElementData): ViewContainerRef {
|
createViewContainerRef(view: ViewData, elIndex: number): ViewContainerRef {
|
||||||
return new ViewContainerRef_(data);
|
return new ViewContainerRef_(view, elIndex);
|
||||||
}
|
}
|
||||||
createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any> {
|
createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any> {
|
||||||
return new TemplateRef_(parentView, def);
|
return new TemplateRef_(parentView, def);
|
||||||
}
|
}
|
||||||
|
createInjector(view: ViewData, elIndex: number): Injector { return new Injector_(view, elIndex); }
|
||||||
createDebugContext(view: ViewData, nodeIndex: number): DebugContext {
|
createDebugContext(view: ViewData, nodeIndex: number): DebugContext {
|
||||||
return new DebugContext_(view, nodeIndex);
|
return new DebugContext_(view, nodeIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ComponentFactory_ implements ComponentFactory<any> {
|
||||||
|
/**
|
||||||
|
* Only needed so that we can implement ComponentFactory
|
||||||
|
* @internal */
|
||||||
|
_viewClass: any;
|
||||||
|
|
||||||
|
private _viewDef: ViewDefinition;
|
||||||
|
private _componentNodeIndex: number;
|
||||||
|
|
||||||
|
constructor(public selector: string, viewDefFactory: ViewDefinitionFactory) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new component.
|
||||||
|
*/
|
||||||
|
create(
|
||||||
|
injector: Injector, projectableNodes: any[][] = null,
|
||||||
|
rootSelectorOrNode: string|any = null): ComponentRef<any> {
|
||||||
|
if (!projectableNodes) {
|
||||||
|
projectableNodes = [];
|
||||||
|
}
|
||||||
|
if (!rootSelectorOrNode) {
|
||||||
|
rootSelectorOrNode = this.selector;
|
||||||
|
}
|
||||||
|
const renderer = injector.get(RootRenderer);
|
||||||
|
const sanitizer = injector.get(Sanitizer);
|
||||||
|
|
||||||
|
const root: RootData =
|
||||||
|
{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> {
|
||||||
|
private _viewRef: ViewRef_;
|
||||||
|
constructor(private _view: ViewData, private _component: any) {
|
||||||
|
this._viewRef = new ViewRef_(_view);
|
||||||
|
}
|
||||||
|
get location(): ElementRef { return new ElementRef(asElementData(this._view, 0).renderElement); }
|
||||||
|
get injector(): Injector { return new Injector_(this._view, 0); }
|
||||||
|
get instance(): any { return this._component; };
|
||||||
|
get hostView(): ViewRef { return this._viewRef; };
|
||||||
|
get changeDetectorRef(): ChangeDetectorRef { return this._viewRef; };
|
||||||
|
get componentType(): Type<any> { return <any>this._component.constructor; }
|
||||||
|
|
||||||
|
destroy(): void { this._viewRef.destroy(); }
|
||||||
|
onDestroy(callback: Function): void { this._viewRef.onDestroy(callback); }
|
||||||
|
}
|
||||||
|
|
||||||
class ViewContainerRef_ implements ViewContainerRef {
|
class ViewContainerRef_ implements ViewContainerRef {
|
||||||
constructor(private _data: ElementData) {}
|
private _data: ElementData;
|
||||||
|
constructor(private _view: ViewData, private _elIndex: number) {
|
||||||
|
this._data = asElementData(_view, _elIndex);
|
||||||
|
}
|
||||||
|
|
||||||
get element(): ElementRef { return <ElementRef>unimplemented(); }
|
get element(): ElementRef { return new ElementRef(this._data.renderElement); }
|
||||||
|
|
||||||
get injector(): Injector { return <Injector>unimplemented(); }
|
get injector(): Injector { return new Injector_(this._view, this._elIndex); }
|
||||||
|
|
||||||
get parentInjector(): Injector { return <Injector>unimplemented(); }
|
get parentInjector(): Injector {
|
||||||
|
let view = this._view;
|
||||||
|
let elIndex = view.def.nodes[this._elIndex].parent;
|
||||||
|
while (elIndex == null && view) {
|
||||||
|
elIndex = parentDiIndex(view);
|
||||||
|
view = view.parent;
|
||||||
|
}
|
||||||
|
return view ? new Injector_(view, elIndex) : this._view.root.injector;
|
||||||
|
}
|
||||||
|
|
||||||
clear(): void {
|
clear(): void {
|
||||||
const len = this._data.embeddedViews.length;
|
const len = this._data.embeddedViews.length;
|
||||||
|
@ -76,7 +158,10 @@ class ViewContainerRef_ implements ViewContainerRef {
|
||||||
createComponent<C>(
|
createComponent<C>(
|
||||||
componentFactory: ComponentFactory<C>, index?: number, injector?: Injector,
|
componentFactory: ComponentFactory<C>, index?: number, injector?: Injector,
|
||||||
projectableNodes?: any[][]): ComponentRef<C> {
|
projectableNodes?: any[][]): ComponentRef<C> {
|
||||||
return unimplemented();
|
const contextInjector = injector || this.parentInjector;
|
||||||
|
const componentRef = componentFactory.create(contextInjector, projectableNodes);
|
||||||
|
this.insert(componentRef.hostView, index);
|
||||||
|
return componentRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
insert(viewRef: ViewRef, index?: number): ViewRef {
|
insert(viewRef: ViewRef, index?: number): ViewRef {
|
||||||
|
@ -85,7 +170,11 @@ class ViewContainerRef_ implements ViewContainerRef {
|
||||||
return viewRef;
|
return viewRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
move(viewRef: ViewRef, currentIndex: number): ViewRef { return unimplemented(); }
|
move(viewRef: ViewRef_, currentIndex: number): ViewRef {
|
||||||
|
const previousIndex = this._data.embeddedViews.indexOf(viewRef._view);
|
||||||
|
moveEmbeddedView(this._data, previousIndex, currentIndex);
|
||||||
|
return viewRef;
|
||||||
|
}
|
||||||
|
|
||||||
indexOf(viewRef: ViewRef): number {
|
indexOf(viewRef: ViewRef): number {
|
||||||
return this._data.embeddedViews.indexOf((<ViewRef_>viewRef)._view);
|
return this._data.embeddedViews.indexOf((<ViewRef_>viewRef)._view);
|
||||||
|
@ -113,33 +202,17 @@ class ViewRef_ implements EmbeddedViewRef<any> {
|
||||||
|
|
||||||
get context() { return this._view.context; }
|
get context() { return this._view.context; }
|
||||||
|
|
||||||
get destroyed(): boolean { return this._view.state === ViewState.Destroyed; }
|
get destroyed(): boolean { return (this._view.state & ViewState.Destroyed) !== 0; }
|
||||||
|
|
||||||
markForCheck(): void { this.reattach(); }
|
markForCheck(): void { this.reattach(); }
|
||||||
detach(): void {
|
detach(): void { this._view.state &= ~ViewState.ChecksEnabled; }
|
||||||
if (this._view.state === ViewState.ChecksEnabled) {
|
detectChanges(): void { checkAndUpdateView(this._view); }
|
||||||
this._view.state = ViewState.ChecksDisabled;
|
checkNoChanges(): void { checkNoChangesView(this._view); }
|
||||||
}
|
|
||||||
}
|
|
||||||
detectChanges(): void {
|
|
||||||
if (this._view.state !== ViewState.FirstCheck) {
|
|
||||||
checkAndUpdateView(this._view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkNoChanges(): void {
|
|
||||||
if (this._view.state !== ViewState.FirstCheck) {
|
|
||||||
checkNoChangesView(this._view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reattach(): void {
|
reattach(): void { this._view.state |= ViewState.ChecksEnabled; }
|
||||||
if (this._view.state === ViewState.ChecksDisabled) {
|
onDestroy(callback: Function) { this._view.disposables.push(<any>callback); }
|
||||||
this._view.state = ViewState.ChecksEnabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onDestroy(callback: Function) { unimplemented(); }
|
|
||||||
|
|
||||||
destroy() { unimplemented(); }
|
destroy() { destroyView(this._view); }
|
||||||
}
|
}
|
||||||
|
|
||||||
class TemplateRef_ implements TemplateRef<any> {
|
class TemplateRef_ implements TemplateRef<any> {
|
||||||
|
@ -154,6 +227,15 @@ class TemplateRef_ implements TemplateRef<any> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Injector_ implements Injector {
|
||||||
|
constructor(private view: ViewData, private elIndex: number) {}
|
||||||
|
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
||||||
|
return resolveDep(
|
||||||
|
this.view, undefined, this.elIndex,
|
||||||
|
{flags: DepFlags.None, token, tokenKey: tokenKey(token)}, notFoundValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class DebugContext_ implements DebugContext {
|
class DebugContext_ implements DebugContext {
|
||||||
private nodeDef: NodeDef;
|
private nodeDef: NodeDef;
|
||||||
private elDef: NodeDef;
|
private elDef: NodeDef;
|
||||||
|
@ -165,7 +247,7 @@ class DebugContext_ implements DebugContext {
|
||||||
this.nodeDef = view.def.nodes[nodeIndex];
|
this.nodeDef = view.def.nodes[nodeIndex];
|
||||||
this.elDef = findElementDef(view, nodeIndex);
|
this.elDef = findElementDef(view, nodeIndex);
|
||||||
}
|
}
|
||||||
get injector(): Injector { return createInjector(this.view, this.elDef.index); }
|
get injector(): Injector { return new Injector_(this.view, this.elDef.index); }
|
||||||
get component(): any { return this.view.component; }
|
get component(): any { return this.view.component; }
|
||||||
get providerTokens(): any[] {
|
get providerTokens(): any[] {
|
||||||
const tokens: any[] = [];
|
const tokens: any[] = [];
|
|
@ -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, Services, TextData, ViewData, ViewFlags, asElementData, asTextData} from './types';
|
import {BindingDef, BindingType, DebugContext, NodeData, NodeDef, NodeFlags, NodeType, Refs, RootData, 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 {
|
||||||
|
@ -54,8 +54,7 @@ export function createText(view: ViewData, renderHost: any, def: NodeDef): TextD
|
||||||
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) {
|
if (view.renderer) {
|
||||||
const debugContext =
|
const debugContext = isDevMode() ? Refs.createDebugContext(view, def.index) : undefined;
|
||||||
isDevMode() ? view.services.createDebugContext(view, def.index) : undefined;
|
|
||||||
renderNode = view.renderer.createText(parentNode, def.text.prefix, debugContext);
|
renderNode = view.renderer.createText(parentNode, def.text.prefix, debugContext);
|
||||||
} else {
|
} else {
|
||||||
renderNode = document.createTextNode(def.text.prefix);
|
renderNode = document.createTextNode(def.text.prefix);
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {PipeTransform} from '../change_detection/change_detection';
|
import {PipeTransform} from '../change_detection/change_detection';
|
||||||
|
import {Injector} from '../di';
|
||||||
|
import {ComponentFactory} 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';
|
||||||
|
@ -264,7 +266,7 @@ export interface NgContentDef {
|
||||||
export interface ViewData {
|
export interface ViewData {
|
||||||
def: ViewDefinition;
|
def: ViewDefinition;
|
||||||
renderer: Renderer;
|
renderer: Renderer;
|
||||||
services: Services;
|
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.
|
||||||
parentIndex: number;
|
parentIndex: number;
|
||||||
|
@ -282,12 +284,14 @@ export interface ViewData {
|
||||||
disposables: DisposableFn[];
|
disposables: DisposableFn[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitmask of states
|
||||||
|
*/
|
||||||
export enum ViewState {
|
export enum ViewState {
|
||||||
FirstCheck,
|
FirstCheck = 1 << 0,
|
||||||
ChecksEnabled,
|
ChecksEnabled = 1 << 1,
|
||||||
ChecksDisabled,
|
Errored = 1 << 2,
|
||||||
Errored,
|
Destroyed = 1 << 3
|
||||||
Destroyed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DisposableFn = () => void;
|
export type DisposableFn = () => void;
|
||||||
|
@ -382,17 +386,12 @@ export function asQueryList(view: ViewData, index: number): QueryList<any> {
|
||||||
return <any>view.nodes[index];
|
return <any>view.nodes[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Services {
|
export interface RootData {
|
||||||
renderComponent(rcp: RenderComponentType): Renderer;
|
injector: Injector;
|
||||||
sanitize(context: SecurityContext, value: string): string;
|
projectableNodes: any[][];
|
||||||
// Note: This needs to be here to prevent a cycle in source files.
|
selectorOrNode: string|any;
|
||||||
createViewRef(data: ViewData): ViewRef;
|
renderer: RootRenderer;
|
||||||
// Note: This needs to be here to prevent a cycle in source files.
|
sanitizer: Sanitizer;
|
||||||
createViewContainerRef(data: ElementData): ViewContainerRef;
|
|
||||||
// Note: This needs to be here to prevent a cycle in source files.
|
|
||||||
createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any>;
|
|
||||||
// Note: This needs to be here to prevent a cycle in source files.
|
|
||||||
createDebugContext(view: ViewData, nodeIndex: number): DebugContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
|
@ -412,3 +411,37 @@ export interface DebugContext extends RenderDebugInfo {
|
||||||
componentRenderElement: any;
|
componentRenderElement: any;
|
||||||
renderNode: any;
|
renderNode: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to prevent cycles in the source files.
|
||||||
|
*/
|
||||||
|
export abstract class Refs {
|
||||||
|
private static instance: Refs;
|
||||||
|
|
||||||
|
static setInstance(instance: Refs) { Refs.instance = instance; }
|
||||||
|
static createComponentFactory(selector: string, viewDefFactory: ViewDefinitionFactory):
|
||||||
|
ComponentFactory<any> {
|
||||||
|
return Refs.instance.createComponentFactory(selector, viewDefFactory);
|
||||||
|
}
|
||||||
|
static createViewRef(data: ViewData): ViewRef { return Refs.instance.createViewRef(data); }
|
||||||
|
static createViewContainerRef(view: ViewData, elIndex: number): ViewContainerRef {
|
||||||
|
return Refs.instance.createViewContainerRef(view, elIndex);
|
||||||
|
}
|
||||||
|
static createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any> {
|
||||||
|
return Refs.instance.createTemplateRef(parentView, def);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -9,11 +9,15 @@
|
||||||
import {isDevMode} from '../application_ref';
|
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 {looseIdentical} from '../facade/lang';
|
import {looseIdentical} from '../facade/lang';
|
||||||
|
import {TemplateRef} from '../linker/template_ref';
|
||||||
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||||
|
import {ViewRef} from '../linker/view_ref';
|
||||||
import {Renderer} from '../render/api';
|
import {Renderer} from '../render/api';
|
||||||
|
|
||||||
import {expressionChangedAfterItHasBeenCheckedError, isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
|
import {expressionChangedAfterItHasBeenCheckedError, isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
|
||||||
import {ElementData, EntryAction, NodeData, NodeDef, NodeFlags, NodeType, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState, asElementData, asProviderData, asTextData} from './types';
|
import {DebugContext, ElementData, EntryAction, NodeData, NodeDef, NodeFlags, NodeType, Refs, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState, asElementData, asProviderData, asTextData} from './types';
|
||||||
|
|
||||||
export function setBindingDebugInfo(
|
export function setBindingDebugInfo(
|
||||||
renderer: Renderer, renderNode: any, propName: string, value: any) {
|
renderer: Renderer, renderNode: any, propName: string, value: any) {
|
||||||
|
@ -36,23 +40,23 @@ function camelCaseToDashCase(input: string): string {
|
||||||
export function checkBindingNoChanges(
|
export function checkBindingNoChanges(
|
||||||
view: ViewData, def: NodeDef, bindingIdx: number, value: any) {
|
view: ViewData, def: NodeDef, bindingIdx: number, value: any) {
|
||||||
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(
|
||||||
view.services.createDebugContext(view, def.index), oldValue, value,
|
Refs.createDebugContext(view, def.index), oldValue, value,
|
||||||
view.state === ViewState.FirstCheck);
|
(view.state & ViewState.FirstCheck) !== 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkAndUpdateBinding(
|
export function checkAndUpdateBinding(
|
||||||
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
|
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
|
||||||
const oldValues = view.oldValues;
|
const oldValues = view.oldValues;
|
||||||
if (view.state === ViewState.FirstCheck ||
|
if ((view.state & ViewState.FirstCheck) ||
|
||||||
!looseIdentical(oldValues[def.bindingIndex + bindingIdx], value)) {
|
!looseIdentical(oldValues[def.bindingIndex + bindingIdx], value)) {
|
||||||
oldValues[def.bindingIndex + bindingIdx] = value;
|
oldValues[def.bindingIndex + bindingIdx] = value;
|
||||||
if (def.flags & NodeFlags.HasComponent) {
|
if (def.flags & NodeFlags.HasComponent) {
|
||||||
const compView = asProviderData(view, def.index).componentView;
|
const compView = asProviderData(view, def.index).componentView;
|
||||||
if (compView.state === ViewState.ChecksDisabled && compView.def.flags & ViewFlags.OnPush) {
|
if (compView.def.flags & ViewFlags.OnPush) {
|
||||||
compView.state = ViewState.ChecksEnabled;
|
compView.state |= ViewState.ChecksEnabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -65,8 +69,8 @@ export function dispatchEvent(
|
||||||
setCurrentNode(view, nodeIndex);
|
setCurrentNode(view, nodeIndex);
|
||||||
let currView = view;
|
let currView = view;
|
||||||
while (currView) {
|
while (currView) {
|
||||||
if (currView.state === ViewState.ChecksDisabled && currView.def.flags & ViewFlags.OnPush) {
|
if (currView.def.flags & ViewFlags.OnPush) {
|
||||||
currView.state = ViewState.ChecksEnabled;
|
currView.state |= ViewState.ChecksEnabled;
|
||||||
}
|
}
|
||||||
currView = currView.parent;
|
currView = currView.parent;
|
||||||
}
|
}
|
||||||
|
@ -88,6 +92,20 @@ export function declaredViewContainer(view: ViewData): ElementData {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for component views, this is the same as parentIndex.
|
||||||
|
* for embedded views, this is the index of the parent node
|
||||||
|
* that contains the view container.
|
||||||
|
*/
|
||||||
|
export function parentDiIndex(view: ViewData): number {
|
||||||
|
if (view.parent) {
|
||||||
|
const parentNodeDef = view.def.nodes[view.parentIndex];
|
||||||
|
return parentNodeDef.element && parentNodeDef.element.template ? parentNodeDef.parent :
|
||||||
|
parentNodeDef.index;
|
||||||
|
}
|
||||||
|
return view.parentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
export function findElementDef(view: ViewData, nodeIndex: number): NodeDef {
|
export function findElementDef(view: ViewData, nodeIndex: number): NodeDef {
|
||||||
const viewDef = view.def;
|
const viewDef = view.def;
|
||||||
let nodeDef = viewDef.nodes[nodeIndex];
|
let nodeDef = viewDef.nodes[nodeIndex];
|
||||||
|
@ -163,7 +181,7 @@ export function currentAction() {
|
||||||
* or code of the framework that might throw as a valid use case.
|
* or code of the framework that might throw as a valid use case.
|
||||||
*/
|
*/
|
||||||
export function setCurrentNode(view: ViewData, nodeIndex: number) {
|
export function setCurrentNode(view: ViewData, nodeIndex: number) {
|
||||||
if (view.state === ViewState.Destroyed) {
|
if (view.state & ViewState.Destroyed) {
|
||||||
throw viewDestroyedError(_currentAction);
|
throw viewDestroyedError(_currentAction);
|
||||||
}
|
}
|
||||||
_currentView = view;
|
_currentView = view;
|
||||||
|
@ -198,7 +216,7 @@ function callWithTryCatch(fn: (a: any) => any, arg: any): any {
|
||||||
if (isViewDebugError(e) || !_currentView) {
|
if (isViewDebugError(e) || !_currentView) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
const debugContext = _currentView.services.createDebugContext(_currentView, _currentNodeIndex);
|
const debugContext = Refs.createDebugContext(_currentView, _currentNodeIndex);
|
||||||
throw viewWrappedDebugError(e, debugContext);
|
throw viewWrappedDebugError(e, debugContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -231,7 +249,7 @@ export function visitProjectedRenderNodes(
|
||||||
view: ViewData, ngContentIndex: number, action: RenderNodeAction, parentNode: any,
|
view: ViewData, ngContentIndex: number, action: RenderNodeAction, parentNode: any,
|
||||||
nextSibling: any, target: any[]) {
|
nextSibling: any, target: any[]) {
|
||||||
let compView = view;
|
let compView = view;
|
||||||
while (!isComponentView(compView)) {
|
while (compView && !isComponentView(compView)) {
|
||||||
compView = compView.parent;
|
compView = compView.parent;
|
||||||
}
|
}
|
||||||
const hostView = compView.parent;
|
const hostView = compView.parent;
|
||||||
|
@ -246,6 +264,15 @@ export function visitProjectedRenderNodes(
|
||||||
// jump to next sibling
|
// jump to next sibling
|
||||||
i += nodeDef.childCount;
|
i += nodeDef.childCount;
|
||||||
}
|
}
|
||||||
|
if (!hostView.parent) {
|
||||||
|
// a root view
|
||||||
|
const projectedNodes = view.root.projectableNodes[ngContentIndex];
|
||||||
|
if (projectedNodes) {
|
||||||
|
for (let i = 0; i < projectedNodes.length; i++) {
|
||||||
|
execRenderNodeAction(projectedNodes[i], action, parentNode, nextSibling, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function visitRenderNode(
|
function visitRenderNode(
|
||||||
|
@ -256,20 +283,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);
|
||||||
switch (action) {
|
execRenderNodeAction(rn, action, parentNode, nextSibling, target);
|
||||||
case RenderNodeAction.AppendChild:
|
|
||||||
parentNode.appendChild(rn);
|
|
||||||
break;
|
|
||||||
case RenderNodeAction.InsertBefore:
|
|
||||||
parentNode.insertBefore(rn, nextSibling);
|
|
||||||
break;
|
|
||||||
case RenderNodeAction.RemoveChild:
|
|
||||||
parentNode.removeChild(rn);
|
|
||||||
break;
|
|
||||||
case RenderNodeAction.Collect:
|
|
||||||
target.push(rn);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
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) {
|
||||||
|
@ -280,3 +294,21 @@ function visitRenderNode(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function execRenderNodeAction(
|
||||||
|
renderNode: any, action: RenderNodeAction, parentNode: any, nextSibling: any, target: any[]) {
|
||||||
|
switch (action) {
|
||||||
|
case RenderNodeAction.AppendChild:
|
||||||
|
parentNode.appendChild(renderNode);
|
||||||
|
break;
|
||||||
|
case RenderNodeAction.InsertBefore:
|
||||||
|
parentNode.insertBefore(renderNode, nextSibling);
|
||||||
|
break;
|
||||||
|
case RenderNodeAction.RemoveChild:
|
||||||
|
parentNode.removeChild(renderNode);
|
||||||
|
break;
|
||||||
|
case RenderNodeAction.Collect:
|
||||||
|
target.push(renderNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ 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, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList} from './types';
|
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 {checkBindingNoChanges, currentAction, currentNodeIndex, currentView, entryAction, isComponentView, resolveViewDefinition, setCurrentNode} from './util';
|
import {checkBindingNoChanges, currentAction, currentNodeIndex, currentView, entryAction, isComponentView, resolveViewDefinition, setCurrentNode} from './util';
|
||||||
|
|
||||||
const NOOP = (): any => undefined;
|
const NOOP = (): any => undefined;
|
||||||
|
@ -224,32 +224,28 @@ function cloneAndModifyElement(
|
||||||
export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
||||||
// embedded views are seen as siblings to the anchor, so we need
|
// embedded views are seen as siblings to the anchor, so we need
|
||||||
// to get the parent of the anchor and use it as parentIndex.
|
// to get the parent of the anchor and use it as parentIndex.
|
||||||
const view = createView(parent.services, parent, anchorDef.index, anchorDef.element.template);
|
const view = createView(parent.root, parent, anchorDef.index, anchorDef.element.template);
|
||||||
initView(view, parent.component, context);
|
initView(view, parent.component, context);
|
||||||
createViewNodes(view);
|
createViewNodes(view);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function createRootView(root: RootData, def: ViewDefinition, context?: any): ViewData {
|
||||||
* We take in a ViewDefinitionFactory, so that we can initialize the debug/prod mode first,
|
const view = createView(root, null, null, def);
|
||||||
* and then know whether to capture error stacks in ElementDefs.
|
|
||||||
*/
|
|
||||||
export function createRootView(
|
|
||||||
services: Services, defFactory: ViewDefinitionFactory, context?: any): ViewData {
|
|
||||||
const view = createView(services, null, null, resolveViewDefinition(defFactory));
|
|
||||||
initView(view, context, context);
|
initView(view, context, context);
|
||||||
createViewNodes(view);
|
createViewNodes(view);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createView(
|
function createView(
|
||||||
services: Services, 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;
|
let renderer: Renderer;
|
||||||
if (def.flags != null && (def.flags & ViewFlags.DirectDom)) {
|
if (def.flags != null && (def.flags & ViewFlags.DirectDom)) {
|
||||||
renderer = null;
|
renderer = null;
|
||||||
} else {
|
} else {
|
||||||
renderer = def.componentType ? services.renderComponent(def.componentType) : parent.renderer;
|
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 = {
|
||||||
|
@ -258,7 +254,7 @@ function createView(
|
||||||
parentIndex,
|
parentIndex,
|
||||||
context: undefined,
|
context: undefined,
|
||||||
component: undefined, nodes,
|
component: undefined, nodes,
|
||||||
state: ViewState.FirstCheck, renderer, services,
|
state: ViewState.FirstCheck | ViewState.ChecksEnabled, renderer, root,
|
||||||
oldValues: new Array(def.bindingCount), disposables
|
oldValues: new Array(def.bindingCount), disposables
|
||||||
};
|
};
|
||||||
return view;
|
return view;
|
||||||
|
@ -301,8 +297,7 @@ function _createViewNodes(view: ViewData) {
|
||||||
// the component view. Therefore, we create the component view first
|
// the component view. Therefore, we create the component view first
|
||||||
// and set the ProviderData in ViewData, and then instantiate the provider.
|
// and set the ProviderData in ViewData, and then instantiate the provider.
|
||||||
const componentView = createView(
|
const componentView = createView(
|
||||||
view.services, view, nodeDef.parent,
|
view.root, view, nodeDef.parent, resolveViewDefinition(nodeDef.provider.component));
|
||||||
resolveViewDefinition(nodeDef.provider.component));
|
|
||||||
const providerData = <ProviderData>{componentView, instance: undefined};
|
const providerData = <ProviderData>{componentView, instance: undefined};
|
||||||
nodes[i] = providerData as any;
|
nodes[i] = providerData as any;
|
||||||
const instance = providerData.instance = createProviderInstance(view, nodeDef);
|
const instance = providerData.instance = createProviderInstance(view, nodeDef);
|
||||||
|
@ -352,21 +347,18 @@ function _checkAndUpdateView(view: ViewData) {
|
||||||
|
|
||||||
callLifecycleHooksChildrenFirst(
|
callLifecycleHooksChildrenFirst(
|
||||||
view, NodeFlags.AfterContentChecked |
|
view, NodeFlags.AfterContentChecked |
|
||||||
(view.state === ViewState.FirstCheck ? NodeFlags.AfterContentInit : 0));
|
(view.state & ViewState.FirstCheck ? NodeFlags.AfterContentInit : 0));
|
||||||
execComponentViewsAction(view, ViewAction.CheckAndUpdate);
|
execComponentViewsAction(view, ViewAction.CheckAndUpdate);
|
||||||
execQueriesAction(view, NodeFlags.HasViewQuery, QueryAction.CheckAndUpdate);
|
execQueriesAction(view, NodeFlags.HasViewQuery, QueryAction.CheckAndUpdate);
|
||||||
|
|
||||||
callLifecycleHooksChildrenFirst(
|
callLifecycleHooksChildrenFirst(
|
||||||
view, NodeFlags.AfterViewChecked |
|
view, NodeFlags.AfterViewChecked |
|
||||||
(view.state === ViewState.FirstCheck ? NodeFlags.AfterViewInit : 0));
|
(view.state & ViewState.FirstCheck ? NodeFlags.AfterViewInit : 0));
|
||||||
|
|
||||||
if (view.state === ViewState.FirstCheck || view.state === ViewState.ChecksEnabled) {
|
if (view.def.flags & ViewFlags.OnPush) {
|
||||||
if (view.def.flags & ViewFlags.OnPush) {
|
view.state &= ~ViewState.ChecksEnabled;
|
||||||
view.state = ViewState.ChecksDisabled;
|
|
||||||
} else {
|
|
||||||
view.state = ViewState.ChecksEnabled;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
view.state &= ~ViewState.FirstCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkNodeInline(
|
export function checkNodeInline(
|
||||||
|
@ -476,9 +468,8 @@ 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(
|
||||||
view.services.createDebugContext(view, nodeDef.index),
|
Refs.createDebugContext(view, nodeDef.index), `Query ${nodeDef.query.id} not dirty`,
|
||||||
`Query ${nodeDef.query.id} not dirty`, `Query ${nodeDef.query.id} dirty`,
|
`Query ${nodeDef.query.id} dirty`, (view.state & ViewState.FirstCheck) !== 0);
|
||||||
view.state === ViewState.FirstCheck);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,7 +484,7 @@ function _destroyView(view: ViewData) {
|
||||||
}
|
}
|
||||||
execComponentViewsAction(view, ViewAction.Destroy);
|
execComponentViewsAction(view, ViewAction.Destroy);
|
||||||
execEmbeddedViewsAction(view, ViewAction.Destroy);
|
execEmbeddedViewsAction(view, ViewAction.Destroy);
|
||||||
view.state = ViewState.Destroyed;
|
view.state |= ViewState.Destroyed;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ViewAction {
|
enum ViewAction {
|
||||||
|
@ -548,14 +539,17 @@ function execEmbeddedViewsAction(view: ViewData, action: ViewAction) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function callViewAction(view: ViewData, action: ViewAction) {
|
function callViewAction(view: ViewData, action: ViewAction) {
|
||||||
|
const viewState = view.state;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case ViewAction.CheckNoChanges:
|
case ViewAction.CheckNoChanges:
|
||||||
if (view.state === ViewState.ChecksEnabled || view.state === ViewState.FirstCheck) {
|
if ((viewState & ViewState.ChecksEnabled) &&
|
||||||
|
(viewState & (ViewState.Errored | ViewState.Destroyed)) === 0) {
|
||||||
_checkNoChangesView(view);
|
_checkNoChangesView(view);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ViewAction.CheckAndUpdate:
|
case ViewAction.CheckAndUpdate:
|
||||||
if (view.state === ViewState.ChecksEnabled || view.state === ViewState.FirstCheck) {
|
if ((viewState & ViewState.ChecksEnabled) &&
|
||||||
|
(viewState & (ViewState.Errored | ViewState.Destroyed)) === 0) {
|
||||||
_checkAndUpdateView(view);
|
_checkAndUpdateView(view);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -29,20 +29,8 @@ export function attachEmbeddedView(elementData: ElementData, viewIndex: number,
|
||||||
dirtyParentQuery(queryId, view);
|
dirtyParentQuery(queryId, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update rendering
|
|
||||||
const prevView = viewIndex > 0 ? embeddedViews[viewIndex - 1] : null;
|
const prevView = viewIndex > 0 ? embeddedViews[viewIndex - 1] : null;
|
||||||
const prevRenderNode =
|
renderAttachEmbeddedView(elementData, prevView, view);
|
||||||
prevView ? renderNode(prevView, prevView.def.lastRootNode) : elementData.renderElement;
|
|
||||||
if (view.renderer) {
|
|
||||||
view.renderer.attachViewAfter(prevRenderNode, rootRenderNodes(view));
|
|
||||||
} else {
|
|
||||||
const parentNode = prevRenderNode.parentNode;
|
|
||||||
const nextSibling = prevRenderNode.nextSibling;
|
|
||||||
if (parentNode) {
|
|
||||||
const action = nextSibling ? RenderNodeAction.InsertBefore : RenderNodeAction.AppendChild;
|
|
||||||
visitRootRenderNodes(view, action, parentNode, nextSibling, undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function detachEmbeddedView(elementData: ElementData, viewIndex: number): ViewData {
|
export function detachEmbeddedView(elementData: ElementData, viewIndex: number): ViewData {
|
||||||
|
@ -63,7 +51,51 @@ export function detachEmbeddedView(elementData: ElementData, viewIndex: number):
|
||||||
dirtyParentQuery(queryId, view);
|
dirtyParentQuery(queryId, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update rendering
|
renderDetachEmbeddedView(elementData, view);
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function moveEmbeddedView(
|
||||||
|
elementData: ElementData, oldViewIndex: number, newViewIndex: number): ViewData {
|
||||||
|
const embeddedViews = elementData.embeddedViews;
|
||||||
|
const view = embeddedViews[oldViewIndex];
|
||||||
|
removeFromArray(embeddedViews, oldViewIndex);
|
||||||
|
if (newViewIndex == null) {
|
||||||
|
newViewIndex = embeddedViews.length;
|
||||||
|
}
|
||||||
|
addToArray(embeddedViews, newViewIndex, view);
|
||||||
|
|
||||||
|
// Note: Don't need to change projectedViews as the order in there
|
||||||
|
// as always invalid...
|
||||||
|
|
||||||
|
for (let queryId in view.def.nodeMatchedQueries) {
|
||||||
|
dirtyParentQuery(queryId, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDetachEmbeddedView(elementData, view);
|
||||||
|
const prevView = newViewIndex > 0 ? embeddedViews[newViewIndex - 1] : null;
|
||||||
|
renderAttachEmbeddedView(elementData, prevView, view);
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAttachEmbeddedView(elementData: ElementData, prevView: ViewData, view: ViewData) {
|
||||||
|
const prevRenderNode =
|
||||||
|
prevView ? renderNode(prevView, prevView.def.lastRootNode) : elementData.renderElement;
|
||||||
|
if (view.renderer) {
|
||||||
|
view.renderer.attachViewAfter(prevRenderNode, rootRenderNodes(view));
|
||||||
|
} else {
|
||||||
|
const parentNode = prevRenderNode.parentNode;
|
||||||
|
const nextSibling = prevRenderNode.nextSibling;
|
||||||
|
if (parentNode) {
|
||||||
|
const action = nextSibling ? RenderNodeAction.InsertBefore : RenderNodeAction.AppendChild;
|
||||||
|
visitRootRenderNodes(view, action, parentNode, nextSibling, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDetachEmbeddedView(elementData: ElementData, view: ViewData) {
|
||||||
if (view.renderer) {
|
if (view.renderer) {
|
||||||
view.renderer.detachView(rootRenderNodes(view));
|
view.renderer.detachView(rootRenderNodes(view));
|
||||||
} else {
|
} else {
|
||||||
|
@ -72,7 +104,6 @@ export function detachEmbeddedView(elementData: ElementData, viewIndex: number):
|
||||||
visitRootRenderNodes(view, RenderNodeAction.RemoveChild, parentNode, null, undefined);
|
visitRootRenderNodes(view, RenderNodeAction.RemoveChild, parentNode, null, undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return view;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addToArray(arr: any[], index: number, value: any) {
|
function addToArray(arr: any[], index: number, value: any) {
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, getDebugNode} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, getDebugNode} from '@angular/core';
|
||||||
import {DebugContext, DefaultServices, NodeDef, NodeFlags, Services, 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, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, elementDef, rootRenderNodes, setCurrentNode, 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 {isBrowser, setupAndCheckRenderer} from './helper';
|
import {createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
|
@ -24,15 +24,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
describe(`View Anchor, directDom: ${config.directDom}`, () => {
|
describe(`View Anchor, directDom: ${config.directDom}`, () => {
|
||||||
setupAndCheckRenderer(config);
|
setupAndCheckRenderer(config);
|
||||||
|
|
||||||
let services: Services;
|
let rootData: RootData;
|
||||||
let renderComponentType: RenderComponentType;
|
let renderComponentType: RenderComponentType;
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(() => {
|
||||||
inject([RootRenderer, Sanitizer], (rootRenderer: RootRenderer, sanitizer: Sanitizer) => {
|
rootData = createRootData();
|
||||||
services = new DefaultServices(rootRenderer, sanitizer);
|
renderComponentType =
|
||||||
renderComponentType =
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
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 {
|
||||||
|
@ -41,7 +40,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(services, () => viewDef, ctx);
|
const view = createRootView(rootData, viewDef, ctx);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||||
import {BindingType, DefaultServices, NodeDef, NodeFlags, Services, 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 {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 {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 {isBrowser, setupAndCheckRenderer} from './helper';
|
import {createRootData, isBrowser, removeNodes, setupAndCheckRenderer} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
|
@ -24,15 +24,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
describe(`Component Views, directDom: ${config.directDom}`, () => {
|
describe(`Component Views, directDom: ${config.directDom}`, () => {
|
||||||
setupAndCheckRenderer(config);
|
setupAndCheckRenderer(config);
|
||||||
|
|
||||||
let services: Services;
|
let rootData: RootData;
|
||||||
let renderComponentType: RenderComponentType;
|
let renderComponentType: RenderComponentType;
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(() => {
|
||||||
inject([RootRenderer, Sanitizer], (rootRenderer: RootRenderer, sanitizer: Sanitizer) => {
|
rootData = createRootData();
|
||||||
services = new DefaultServices(rootRenderer, sanitizer);
|
renderComponentType =
|
||||||
renderComponentType =
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
function compViewDef(
|
function compViewDef(
|
||||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||||
|
@ -41,7 +40,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(services, () => viewDef);
|
const view = createRootView(rootData, viewDef);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -70,6 +69,54 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
expect(getDOM().nodeName(compRootEl).toLowerCase()).toBe('span');
|
expect(getDOM().nodeName(compRootEl).toLowerCase()).toBe('span');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isBrowser()) {
|
||||||
|
describe('root views', () => {
|
||||||
|
let rootNode: HTMLElement;
|
||||||
|
beforeEach(() => {
|
||||||
|
rootNode = document.createElement('root');
|
||||||
|
document.body.appendChild(rootNode);
|
||||||
|
removeNodes.push(rootNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should select root elements based on a selector', () => {
|
||||||
|
rootData.selectorOrNode = 'root';
|
||||||
|
const view = createRootView(rootData, compViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||||
|
]));
|
||||||
|
const rootNodes = rootRenderNodes(view);
|
||||||
|
expect(rootNodes).toEqual([rootNode]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should select root elements based on a node', () => {
|
||||||
|
rootData.selectorOrNode = rootNode;
|
||||||
|
const view = createRootView(rootData, compViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||||
|
]));
|
||||||
|
const rootNodes = rootRenderNodes(view);
|
||||||
|
expect(rootNodes).toEqual([rootNode]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set attributes on the root node', () => {
|
||||||
|
rootData.selectorOrNode = rootNode;
|
||||||
|
const view =
|
||||||
|
createRootView(rootData, compViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}),
|
||||||
|
]));
|
||||||
|
expect(rootNode.getAttribute('a')).toBe('b');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear the content of the root node', () => {
|
||||||
|
rootData.selectorOrNode = rootNode;
|
||||||
|
rootNode.appendChild(document.createElement('div'));
|
||||||
|
const view =
|
||||||
|
createRootView(rootData, compViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}),
|
||||||
|
]));
|
||||||
|
expect(rootNode.childNodes.length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
describe('data binding', () => {
|
describe('data binding', () => {
|
||||||
it('should dirty check component views', () => {
|
it('should dirty check component views', () => {
|
||||||
let value: any;
|
let value: any;
|
||||||
|
@ -132,11 +179,11 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
checkAndUpdateView(view);
|
checkAndUpdateView(view);
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
|
|
||||||
compView.state = ViewState.ChecksDisabled;
|
compView.state &= ~ViewState.ChecksEnabled;
|
||||||
checkAndUpdateView(view);
|
checkAndUpdateView(view);
|
||||||
expect(update).not.toHaveBeenCalled();
|
expect(update).not.toHaveBeenCalled();
|
||||||
|
|
||||||
compView.state = ViewState.ChecksEnabled;
|
compView.state |= ViewState.ChecksEnabled;
|
||||||
checkAndUpdateView(view);
|
checkAndUpdateView(view);
|
||||||
expect(update).toHaveBeenCalled();
|
expect(update).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||||
import {BindingType, DebugContext, DefaultServices, NodeDef, NodeFlags, Services, 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 {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 {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, isBrowser, setupAndCheckRenderer} from './helper';
|
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic, createRootData, isBrowser, removeNodes, setupAndCheckRenderer} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
|
@ -24,15 +24,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
describe(`View Elements, directDom: ${config.directDom}`, () => {
|
describe(`View Elements, directDom: ${config.directDom}`, () => {
|
||||||
setupAndCheckRenderer(config);
|
setupAndCheckRenderer(config);
|
||||||
|
|
||||||
let services: Services;
|
let rootData: RootData;
|
||||||
let renderComponentType: RenderComponentType;
|
let renderComponentType: RenderComponentType;
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(() => {
|
||||||
inject([RootRenderer, Sanitizer], (rootRenderer: RootRenderer, sanitizer: Sanitizer) => {
|
rootData = createRootData();
|
||||||
services = new DefaultServices(rootRenderer, sanitizer);
|
renderComponentType =
|
||||||
renderComponentType =
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
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 {
|
||||||
|
@ -41,7 +40,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
function createAndGetRootNodes(
|
function createAndGetRootNodes(
|
||||||
viewDef: ViewDefinition, context?: any): {rootNodes: any[], view: ViewData} {
|
viewDef: ViewDefinition, context?: any): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(services, () => viewDef, context);
|
const view = createRootView(rootData, viewDef, context);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -236,16 +235,6 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
describe('listen to DOM events', () => {
|
describe('listen to DOM events', () => {
|
||||||
let removeNodes: Node[];
|
|
||||||
beforeEach(() => { removeNodes = []; });
|
|
||||||
afterEach(() => {
|
|
||||||
removeNodes.forEach((node) => {
|
|
||||||
if (node.parentNode) {
|
|
||||||
node.parentNode.removeChild(node);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function createAndAttachAndGetRootNodes(viewDef: ViewDefinition):
|
function createAndAttachAndGetRootNodes(viewDef: ViewDefinition):
|
||||||
{rootNodes: any[], view: ViewData} {
|
{rootNodes: any[], view: ViewData} {
|
||||||
const result = createAndGetRootNodes(viewDef);
|
const result = createAndGetRootNodes(viewDef);
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||||
import {BindingType, DefaultServices, NodeDef, NodeFlags, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, directiveDef, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
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 {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 {isBrowser, setupAndCheckRenderer} from './helper';
|
import {createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
|
@ -24,15 +24,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
describe(`Embedded Views, directDom: ${config.directDom}`, () => {
|
describe(`Embedded Views, directDom: ${config.directDom}`, () => {
|
||||||
setupAndCheckRenderer(config);
|
setupAndCheckRenderer(config);
|
||||||
|
|
||||||
let services: Services;
|
let rootData: RootData;
|
||||||
let renderComponentType: RenderComponentType;
|
let renderComponentType: RenderComponentType;
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(() => {
|
||||||
inject([RootRenderer, Sanitizer], (rootRenderer: RootRenderer, sanitizer: Sanitizer) => {
|
rootData = createRootData();
|
||||||
services = new DefaultServices(rootRenderer, sanitizer);
|
renderComponentType =
|
||||||
renderComponentType =
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
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 {
|
||||||
|
@ -45,7 +44,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
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(services, () => viewDef, context);
|
const view = createRootView(rootData, viewDef, context);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -78,26 +77,54 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'span', {'name': 'child1'})
|
elementDef(NodeFlags.None, null, null, 0, 'span', {'name': 'child1'})
|
||||||
]))
|
]))
|
||||||
]));
|
]));
|
||||||
|
const viewContainerData = asElementData(parentView, 1);
|
||||||
|
|
||||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||||
|
|
||||||
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
|
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||||
|
|
||||||
const rootChildren = getDOM().childNodes(rootNodes[0]);
|
attachEmbeddedView(viewContainerData, 0, childView0);
|
||||||
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
|
attachEmbeddedView(viewContainerData, 1, childView1);
|
||||||
attachEmbeddedView(asElementData(parentView, 1), 1, childView1);
|
|
||||||
|
|
||||||
// 2 anchors + 2 elements
|
// 2 anchors + 2 elements
|
||||||
|
const rootChildren = getDOM().childNodes(rootNodes[0]);
|
||||||
expect(rootChildren.length).toBe(4);
|
expect(rootChildren.length).toBe(4);
|
||||||
expect(getDOM().getAttribute(rootChildren[1], 'name')).toBe('child0');
|
expect(getDOM().getAttribute(rootChildren[1], 'name')).toBe('child0');
|
||||||
expect(getDOM().getAttribute(rootChildren[2], 'name')).toBe('child1');
|
expect(getDOM().getAttribute(rootChildren[2], 'name')).toBe('child1');
|
||||||
|
|
||||||
detachEmbeddedView(asElementData(parentView, 1), 1);
|
detachEmbeddedView(viewContainerData, 1);
|
||||||
detachEmbeddedView(asElementData(parentView, 1), 0);
|
detachEmbeddedView(viewContainerData, 0);
|
||||||
|
|
||||||
expect(getDOM().childNodes(rootNodes[0]).length).toBe(2);
|
expect(getDOM().childNodes(rootNodes[0]).length).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should move embedded views', () => {
|
||||||
|
const {view: parentView, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 2, 'div'),
|
||||||
|
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, embeddedViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 0, 'span', {'name': 'child0'})
|
||||||
|
])),
|
||||||
|
anchorDef(NodeFlags.None, null, null, 0, embeddedViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 0, 'span', {'name': 'child1'})
|
||||||
|
]))
|
||||||
|
]));
|
||||||
|
const viewContainerData = asElementData(parentView, 1);
|
||||||
|
|
||||||
|
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||||
|
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||||
|
|
||||||
|
attachEmbeddedView(viewContainerData, 0, childView0);
|
||||||
|
attachEmbeddedView(viewContainerData, 1, childView1);
|
||||||
|
|
||||||
|
moveEmbeddedView(viewContainerData, 0, 1);
|
||||||
|
|
||||||
|
expect(viewContainerData.embeddedViews).toEqual([childView1, childView0]);
|
||||||
|
// 2 anchors + 2 elements
|
||||||
|
const rootChildren = getDOM().childNodes(rootNodes[0]);
|
||||||
|
expect(rootChildren.length).toBe(4);
|
||||||
|
expect(getDOM().getAttribute(rootChildren[1], 'name')).toBe('child1');
|
||||||
|
expect(getDOM().getAttribute(rootChildren[2], 'name')).toBe('child0');
|
||||||
|
});
|
||||||
|
|
||||||
it('should include embedded views in root nodes', () => {
|
it('should include embedded views in root nodes', () => {
|
||||||
const {view: parentView} = createAndGetRootNodes(compViewDef([
|
const {view: parentView} = createAndGetRootNodes(compViewDef([
|
||||||
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, embeddedViewDef([
|
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, embeddedViewDef([
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RootRenderer} from '@angular/core';
|
import {Injector, RootRenderer, Sanitizer} from '@angular/core';
|
||||||
import {checkNodeDynamic, checkNodeInline} from '@angular/core/src/view/index';
|
import {RootData, checkNodeDynamic, checkNodeInline} 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';
|
||||||
|
|
||||||
|
@ -51,3 +51,19 @@ export function checkNodeInlineOrDynamic(inlineDynamic: InlineDynamic, values: a
|
||||||
return checkNodeDynamic(values);
|
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[];
|
||||||
|
beforeEach(() => { removeNodes = []; });
|
||||||
|
afterEach(() => { removeNodes.forEach((node) => getDOM().remove(node)); });
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {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, DefaultServices, NodeDef, NodeFlags, Services, 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, 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 {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 {isBrowser, setupAndCheckRenderer} from './helper';
|
import {createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
|
@ -24,15 +24,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
describe(`View NgContent, directDom: ${config.directDom}`, () => {
|
describe(`View NgContent, directDom: ${config.directDom}`, () => {
|
||||||
setupAndCheckRenderer(config);
|
setupAndCheckRenderer(config);
|
||||||
|
|
||||||
let services: Services;
|
let rootData: RootData;
|
||||||
let renderComponentType: RenderComponentType;
|
let renderComponentType: RenderComponentType;
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(() => {
|
||||||
inject([RootRenderer, Sanitizer], (rootRenderer: RootRenderer, sanitizer: Sanitizer) => {
|
rootData = createRootData();
|
||||||
services = new DefaultServices(rootRenderer, sanitizer);
|
renderComponentType =
|
||||||
renderComponentType =
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
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 {
|
||||||
|
@ -57,7 +56,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(services, () => viewDef, ctx || {});
|
const view = createRootView(rootData, viewDef, ctx || {});
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -136,5 +135,18 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
detachEmbeddedView(asElementData(componentView, 1), 0);
|
detachEmbeddedView(asElementData(componentView, 1), 0);
|
||||||
expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(1);
|
expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isBrowser()) {
|
||||||
|
it('should use root projectable nodes', () => {
|
||||||
|
rootData.projectableNodes =
|
||||||
|
[[document.createTextNode('a')], [document.createTextNode('b')]];
|
||||||
|
|
||||||
|
const {view, rootNodes} = createAndGetRootNodes(
|
||||||
|
compViewDef(hostElDef([], [ngContentDef(null, 0), ngContentDef(null, 1)])));
|
||||||
|
|
||||||
|
expect(getDOM().childNodes(rootNodes[0])[0]).toBe(rootData.projectableNodes[0][0]);
|
||||||
|
expect(getDOM().childNodes(rootNodes[0])[1]).toBe(rootData.projectableNodes[1][0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
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 {BindingType, DebugContext, DefaultServices, DepFlags, NodeDef, NodeFlags, ProviderType, Services, 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 {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 {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, isBrowser, setupAndCheckRenderer} from './helper';
|
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic, createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
|
@ -24,15 +24,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
describe(`View Providers, directDom: ${config.directDom}`, () => {
|
describe(`View Providers, directDom: ${config.directDom}`, () => {
|
||||||
setupAndCheckRenderer(config);
|
setupAndCheckRenderer(config);
|
||||||
|
|
||||||
let services: Services;
|
let rootData: RootData;
|
||||||
let renderComponentType: RenderComponentType;
|
let renderComponentType: RenderComponentType;
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(() => {
|
||||||
inject([RootRenderer, Sanitizer], (rootRenderer: RootRenderer, sanitizer: Sanitizer) => {
|
rootData = createRootData();
|
||||||
services = new DefaultServices(rootRenderer, sanitizer);
|
renderComponentType =
|
||||||
renderComponentType =
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
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 {
|
||||||
|
@ -44,7 +43,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(services, () => viewDef);
|
const view = createRootView(rootData, viewDef);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -229,6 +228,18 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
expect(instance.dep).toBe('someParentValue');
|
expect(instance.dep).toBe('someParentValue');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should ask the root injector', () => {
|
||||||
|
const getSpy = spyOn(rootData.injector, 'get');
|
||||||
|
getSpy.and.returnValue('rootValue');
|
||||||
|
createAndGetRootNodes(compViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||||
|
directiveDef(NodeFlags.None, null, 0, SomeService, ['rootDep'])
|
||||||
|
]));
|
||||||
|
|
||||||
|
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', () => {
|
||||||
createAndGetRootNodes(compViewDef([
|
createAndGetRootNodes(compViewDef([
|
||||||
|
|
|
@ -6,23 +6,22 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {PipeTransform, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue} from '@angular/core';
|
import {Injector, PipeTransform, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue} from '@angular/core';
|
||||||
import {DefaultServices, NodeDef, NodeFlags, Services, 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 {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 {inject} from '@angular/core/testing';
|
import {inject} from '@angular/core/testing';
|
||||||
|
|
||||||
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic} from './helper';
|
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic, createRootData} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe(`View Pure Expressions`, () => {
|
describe(`View Pure Expressions`, () => {
|
||||||
let services: Services;
|
let rootData: RootData;
|
||||||
let renderComponentType: RenderComponentType;
|
let renderComponentType: RenderComponentType;
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(() => {
|
||||||
inject([RootRenderer, Sanitizer], (rootRenderer: RootRenderer, sanitizer: Sanitizer) => {
|
rootData = createRootData();
|
||||||
services = new DefaultServices(rootRenderer, sanitizer);
|
renderComponentType =
|
||||||
renderComponentType =
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
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 {
|
||||||
|
@ -30,7 +29,7 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(services, () => viewDef);
|
const view = createRootView(rootData, viewDef);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,22 +6,23 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ElementRef, 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 {BindingType, DebugContext, DefaultServices, NodeDef, NodeFlags, QueryBindingType, QueryValueType, Services, 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, 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 {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';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe(`Query Views`, () => {
|
describe(`Query Views`, () => {
|
||||||
let services: Services;
|
let rootData: RootData;
|
||||||
let renderComponentType: RenderComponentType;
|
let renderComponentType: RenderComponentType;
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(() => {
|
||||||
inject([RootRenderer, Sanitizer], (rootRenderer: RootRenderer, sanitizer: Sanitizer) => {
|
rootData = createRootData();
|
||||||
services = new DefaultServices(rootRenderer, sanitizer);
|
renderComponentType =
|
||||||
renderComponentType =
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
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 {
|
||||||
|
@ -34,7 +35,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(services, () => viewDef, context);
|
const view = createRootView(rootData, viewDef, context);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,24 +6,23 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, getDebugNode} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, getDebugNode} from '@angular/core';
|
||||||
import {DebugContext, DefaultServices, NodeDef, NodeFlags, QueryValueType, Services, 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, 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 {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 {isBrowser, setupAndCheckRenderer} from './helper';
|
import {createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('View Services', () => {
|
describe('View References', () => {
|
||||||
let services: Services;
|
let rootData: RootData;
|
||||||
let renderComponentType: RenderComponentType;
|
let renderComponentType: RenderComponentType;
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(() => {
|
||||||
inject([RootRenderer, Sanitizer], (rootRenderer: RootRenderer, sanitizer: Sanitizer) => {
|
rootData = createRootData();
|
||||||
services = new DefaultServices(rootRenderer, sanitizer);
|
renderComponentType =
|
||||||
renderComponentType =
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
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 {
|
||||||
|
@ -32,7 +31,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(services, () => viewDef, context);
|
const view = createRootView(rootData, viewDef, context);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
@ -59,7 +58,7 @@ export function main() {
|
||||||
const view = createViewWithData();
|
const view = createViewWithData();
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asProviderData(view, 1).componentView;
|
||||||
|
|
||||||
const debugCtx = view.services.createDebugContext(compView, 0);
|
const debugCtx = Refs.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);
|
||||||
|
@ -76,7 +75,7 @@ export function main() {
|
||||||
const view = createViewWithData();
|
const view = createViewWithData();
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asProviderData(view, 1).componentView;
|
||||||
|
|
||||||
const debugCtx = view.services.createDebugContext(compView, 2);
|
const debugCtx = Refs.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);
|
||||||
|
@ -90,7 +89,7 @@ export function main() {
|
||||||
const view = createViewWithData();
|
const view = createViewWithData();
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asProviderData(view, 1).componentView;
|
||||||
|
|
||||||
const debugCtx = view.services.createDebugContext(compView, 1);
|
const debugCtx = Refs.createDebugContext(compView, 1);
|
||||||
|
|
||||||
expect(debugCtx.renderNode).toBe(asElementData(compView, 0).renderElement);
|
expect(debugCtx.renderNode).toBe(asElementData(compView, 0).renderElement);
|
||||||
});
|
});
|
|
@ -6,12 +6,12 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||||
import {DebugContext, DefaultServices, NodeDef, NodeFlags, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asTextData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
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 {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, isBrowser, setupAndCheckRenderer} from './helper';
|
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic, createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
|
@ -24,15 +24,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
describe(`View Text, directDom: ${config.directDom}`, () => {
|
describe(`View Text, directDom: ${config.directDom}`, () => {
|
||||||
setupAndCheckRenderer(config);
|
setupAndCheckRenderer(config);
|
||||||
|
|
||||||
let services: Services;
|
let rootData: RootData;
|
||||||
let renderComponentType: RenderComponentType;
|
let renderComponentType: RenderComponentType;
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(() => {
|
||||||
inject([RootRenderer, Sanitizer], (rootRenderer: RootRenderer, sanitizer: Sanitizer) => {
|
rootData = createRootData();
|
||||||
services = new DefaultServices(rootRenderer, sanitizer);
|
renderComponentType =
|
||||||
renderComponentType =
|
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||||
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 {
|
||||||
|
@ -41,7 +40,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||||
|
|
||||||
function createAndGetRootNodes(
|
function createAndGetRootNodes(
|
||||||
viewDef: ViewDefinition, context?: any): {rootNodes: any[], view: ViewData} {
|
viewDef: ViewDefinition, context?: any): {rootNodes: any[], view: ViewData} {
|
||||||
const view = createRootView(services, () => viewDef, context);
|
const view = createRootView(rootData, viewDef, context);
|
||||||
const rootNodes = rootRenderNodes(view);
|
const rootNodes = rootRenderNodes(view);
|
||||||
return {rootNodes, view};
|
return {rootNodes, view};
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,10 +43,7 @@ export function main() {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
appMod = new AppModule();
|
appMod = new AppModule();
|
||||||
appMod.bootstrap();
|
appMod.bootstrap();
|
||||||
tree = appMod.rootComp;
|
tree = appMod.componentRef.instance;
|
||||||
const rootEl = document.querySelector('#root');
|
|
||||||
rootEl.textContent = '';
|
|
||||||
rootEl.appendChild(appMod.rootEl);
|
|
||||||
|
|
||||||
bindAction('#destroyDom', destroyDom);
|
bindAction('#destroyDom', destroyDom);
|
||||||
bindAction('#createDom', createDom);
|
bindAction('#createDom', createDom);
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {NgIf} from '@angular/common';
|
import {NgIf} from '@angular/common';
|
||||||
import {Component, NgModule, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
import {Component, ComponentFactory, ComponentRef, Injector, NgModule, RootRenderer, Sanitizer, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
||||||
import {BindingType, DefaultServices, NodeFlags, ViewData, ViewDefinition, ViewFlags, anchorDef, asElementData, asProviderData, checkAndUpdateView, checkNodeInline, createRootView, directiveDef, elementDef, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
import {BindingType, NodeFlags, ViewData, ViewDefinition, ViewFlags, anchorDef, checkNodeInline, createComponentFactory, directiveDef, elementDef, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {DomSanitizer, 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';
|
||||||
|
|
||||||
|
@ -84,21 +84,28 @@ function TreeComponent_0(): ViewDefinition {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AppModule {
|
export class AppModule implements Injector {
|
||||||
public rootComp: TreeComponent;
|
private sanitizer: DomSanitizerImpl;
|
||||||
public rootEl: any;
|
private componentFactory: ComponentFactory<TreeComponent>;
|
||||||
private rootView: ViewData;
|
componentRef: ComponentRef<TreeComponent>;
|
||||||
private sanitizer: DomSanitizer;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
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);
|
||||||
}
|
}
|
||||||
bootstrap() {
|
|
||||||
this.rootView = createRootView(new DefaultServices(null, this.sanitizer), TreeComponent_Host);
|
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
||||||
this.rootComp = asProviderData(this.rootView, 1).instance;
|
switch (token) {
|
||||||
this.rootEl = asElementData(this.rootView, 0).renderElement;
|
case Sanitizer:
|
||||||
|
return this.sanitizer;
|
||||||
|
case RootRenderer:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Injector.NULL.get(token, notFoundValue);
|
||||||
}
|
}
|
||||||
tick() { checkAndUpdateView(this.rootView); }
|
|
||||||
|
bootstrap() { this.componentRef = this.componentFactory.create(this); }
|
||||||
|
tick() { this.componentRef.changeDetectorRef.detectChanges(); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,7 +273,7 @@ export interface ComponentDecorator {
|
||||||
export declare class ComponentFactory<C> {
|
export declare class ComponentFactory<C> {
|
||||||
componentType: Type<any>;
|
componentType: Type<any>;
|
||||||
selector: string;
|
selector: string;
|
||||||
constructor(selector: string, _viewClass: Type<AppView<any>>, _componentType: Type<any>);
|
constructor(selector: string, _viewClass: Type<AppView<any>>, componentType: Type<any>);
|
||||||
create(injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string | any): ComponentRef<C>;
|
create(injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string | any): ComponentRef<C>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue