2017-01-20 13:10:57 -08:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
|
|
|
import {Injectable, Injector} from '../di';
|
|
|
|
import {unimplemented} from '../facade/errors';
|
|
|
|
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
|
|
|
|
import {ElementRef} from '../linker/element_ref';
|
|
|
|
import {TemplateRef} from '../linker/template_ref';
|
|
|
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
|
|
|
import {EmbeddedViewRef, ViewRef} from '../linker/view_ref';
|
|
|
|
import {RenderComponentType, Renderer, RootRenderer} from '../render/api';
|
|
|
|
import {Sanitizer, SecurityContext} from '../security';
|
|
|
|
|
2017-01-26 17:07:37 -08:00
|
|
|
import {createInjector} from './provider';
|
|
|
|
import {getQueryValue} from './query';
|
|
|
|
import {DebugContext, ElementData, NodeData, NodeDef, NodeType, Services, ViewData, ViewDefinition, asElementData} from './types';
|
2017-01-31 08:51:42 -08:00
|
|
|
import {isComponentView, renderNode, rootRenderNodes} from './util';
|
2017-01-20 13:10:57 -08:00
|
|
|
import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, destroyView} from './view';
|
2017-01-31 08:51:42 -08:00
|
|
|
import {attachEmbeddedView, detachEmbeddedView} from './view_attach';
|
2017-01-20 13:10:57 -08:00
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class DefaultServices implements Services {
|
|
|
|
constructor(private _rootRenderer: RootRenderer, private _sanitizer: Sanitizer) {}
|
|
|
|
|
|
|
|
renderComponent(rcp: RenderComponentType): Renderer {
|
|
|
|
return this._rootRenderer.renderComponent(rcp);
|
|
|
|
}
|
|
|
|
sanitize(context: SecurityContext, value: string): string {
|
|
|
|
return this._sanitizer.sanitize(context, value);
|
|
|
|
}
|
2017-01-25 13:45:07 -08:00
|
|
|
createViewContainerRef(data: ElementData): ViewContainerRef {
|
|
|
|
return new ViewContainerRef_(data);
|
|
|
|
}
|
2017-01-20 13:10:57 -08:00
|
|
|
createTemplateRef(parentView: ViewData, def: NodeDef): TemplateRef<any> {
|
|
|
|
return new TemplateRef_(parentView, def);
|
|
|
|
}
|
2017-01-26 17:07:37 -08:00
|
|
|
createDebugContext(view: ViewData, nodeIndex: number): DebugContext {
|
|
|
|
return new DebugContext_(view, nodeIndex);
|
|
|
|
}
|
2017-01-20 13:10:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
class ViewContainerRef_ implements ViewContainerRef {
|
2017-01-25 13:45:07 -08:00
|
|
|
constructor(private _data: ElementData) {}
|
2017-01-20 13:10:57 -08:00
|
|
|
|
|
|
|
get element(): ElementRef { return <ElementRef>unimplemented(); }
|
|
|
|
|
|
|
|
get injector(): Injector { return <Injector>unimplemented(); }
|
|
|
|
|
|
|
|
get parentInjector(): Injector { return <Injector>unimplemented(); }
|
|
|
|
|
|
|
|
clear(): void {
|
2017-01-25 13:45:07 -08:00
|
|
|
const len = this._data.embeddedViews.length;
|
2017-01-20 13:10:57 -08:00
|
|
|
for (let i = len - 1; i >= 0; i--) {
|
|
|
|
const view = detachEmbeddedView(this._data, i);
|
|
|
|
destroyView(view);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-25 13:45:07 -08:00
|
|
|
get(index: number): ViewRef { return new ViewRef_(this._data.embeddedViews[index]); }
|
2017-01-20 13:10:57 -08:00
|
|
|
|
2017-01-25 13:45:07 -08:00
|
|
|
get length(): number { return this._data.embeddedViews.length; };
|
2017-01-20 13:10:57 -08:00
|
|
|
|
|
|
|
createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number):
|
|
|
|
EmbeddedViewRef<C> {
|
|
|
|
const viewRef = templateRef.createEmbeddedView(context);
|
|
|
|
this.insert(viewRef, index);
|
|
|
|
return viewRef;
|
|
|
|
}
|
|
|
|
|
|
|
|
createComponent<C>(
|
|
|
|
componentFactory: ComponentFactory<C>, index?: number, injector?: Injector,
|
|
|
|
projectableNodes?: any[][]): ComponentRef<C> {
|
|
|
|
return unimplemented();
|
|
|
|
}
|
|
|
|
|
|
|
|
insert(viewRef: ViewRef, index?: number): ViewRef {
|
|
|
|
const viewData = (<ViewRef_>viewRef)._view;
|
|
|
|
attachEmbeddedView(this._data, index, viewData);
|
|
|
|
return viewRef;
|
|
|
|
}
|
|
|
|
|
|
|
|
move(viewRef: ViewRef, currentIndex: number): ViewRef { return unimplemented(); }
|
|
|
|
|
|
|
|
indexOf(viewRef: ViewRef): number {
|
2017-01-25 13:45:07 -08:00
|
|
|
return this._data.embeddedViews.indexOf((<ViewRef_>viewRef)._view);
|
2017-01-20 13:10:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
remove(index?: number): void {
|
|
|
|
const viewData = detachEmbeddedView(this._data, index);
|
|
|
|
destroyView(viewData);
|
|
|
|
}
|
|
|
|
|
|
|
|
detach(index?: number): ViewRef {
|
|
|
|
const view = this.get(index);
|
|
|
|
detachEmbeddedView(this._data, index);
|
|
|
|
return view;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ViewRef_ implements EmbeddedViewRef<any> {
|
|
|
|
/** @internal */
|
|
|
|
_view: ViewData;
|
|
|
|
|
|
|
|
constructor(_view: ViewData) { this._view = _view; }
|
|
|
|
|
|
|
|
get rootNodes(): any[] { return rootRenderNodes(this._view); }
|
|
|
|
|
|
|
|
get context() { return this._view.context; }
|
|
|
|
|
|
|
|
get destroyed(): boolean { return unimplemented(); }
|
|
|
|
|
|
|
|
markForCheck(): void { unimplemented(); }
|
|
|
|
detach(): void { unimplemented(); }
|
|
|
|
detectChanges(): void { checkAndUpdateView(this._view); }
|
|
|
|
checkNoChanges(): void { checkNoChangesView(this._view); }
|
|
|
|
reattach(): void { unimplemented(); }
|
|
|
|
onDestroy(callback: Function) { unimplemented(); }
|
|
|
|
|
|
|
|
destroy() { unimplemented(); }
|
|
|
|
}
|
|
|
|
|
|
|
|
class TemplateRef_ implements TemplateRef<any> {
|
|
|
|
constructor(private _parentView: ViewData, private _def: NodeDef) {}
|
|
|
|
|
|
|
|
createEmbeddedView(context: any): EmbeddedViewRef<any> {
|
|
|
|
return new ViewRef_(createEmbeddedView(this._parentView, this._def, context));
|
|
|
|
}
|
|
|
|
|
|
|
|
get elementRef(): ElementRef {
|
2017-01-25 13:45:07 -08:00
|
|
|
return new ElementRef(asElementData(this._parentView, this._def.index).renderElement);
|
2017-01-20 13:10:57 -08:00
|
|
|
}
|
|
|
|
}
|
2017-01-26 17:07:37 -08:00
|
|
|
|
|
|
|
class DebugContext_ implements DebugContext {
|
|
|
|
private nodeDef: NodeDef;
|
|
|
|
private elDef: NodeDef;
|
|
|
|
constructor(public view: ViewData, public nodeIndex: number) {
|
2017-01-31 08:51:42 -08:00
|
|
|
if (nodeIndex == null) {
|
|
|
|
this.nodeIndex = nodeIndex = view.parentIndex;
|
|
|
|
this.view = view = view.parent;
|
|
|
|
}
|
2017-01-26 17:07:37 -08:00
|
|
|
this.nodeDef = view.def.nodes[nodeIndex];
|
|
|
|
this.elDef = findElementDef(view, nodeIndex);
|
|
|
|
}
|
|
|
|
get injector(): Injector { return createInjector(this.view, this.elDef.index); }
|
|
|
|
get component(): any { return this.view.component; }
|
|
|
|
get providerTokens(): any[] {
|
|
|
|
const tokens: any[] = [];
|
|
|
|
if (this.elDef) {
|
|
|
|
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
|
|
|
|
const childDef = this.view.def.nodes[i];
|
|
|
|
if (childDef.type === NodeType.Provider) {
|
|
|
|
tokens.push(childDef.provider.token);
|
|
|
|
} else {
|
|
|
|
i += childDef.childCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tokens;
|
|
|
|
}
|
|
|
|
get references(): {[key: string]: any} {
|
|
|
|
const references: {[key: string]: any} = {};
|
|
|
|
if (this.elDef) {
|
|
|
|
collectReferences(this.view, this.elDef, references);
|
|
|
|
|
|
|
|
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
|
|
|
|
const childDef = this.view.def.nodes[i];
|
|
|
|
if (childDef.type === NodeType.Provider) {
|
|
|
|
collectReferences(this.view, childDef, references);
|
|
|
|
} else {
|
|
|
|
i += childDef.childCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return references;
|
|
|
|
}
|
|
|
|
get context(): any { return this.view.context; }
|
|
|
|
get source(): string {
|
|
|
|
if (this.nodeDef.type === NodeType.Text) {
|
|
|
|
return this.nodeDef.text.source;
|
|
|
|
} else {
|
|
|
|
return this.elDef.element.source;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
get componentRenderElement() {
|
|
|
|
const elData = findHostElement(this.view);
|
|
|
|
return elData ? elData.renderElement : undefined;
|
|
|
|
}
|
|
|
|
get renderNode(): any {
|
|
|
|
let nodeDef = this.nodeDef.type === NodeType.Text ? this.nodeDef : this.elDef;
|
|
|
|
return renderNode(this.view, nodeDef);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function findHostElement(view: ViewData): ElementData {
|
|
|
|
while (view && !isComponentView(view)) {
|
|
|
|
view = view.parent;
|
|
|
|
}
|
|
|
|
if (view.parent) {
|
|
|
|
const hostData = asElementData(view.parent, view.parentIndex);
|
|
|
|
return hostData;
|
|
|
|
}
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
function findElementDef(view: ViewData, nodeIndex: number): NodeDef {
|
|
|
|
const viewDef = view.def;
|
|
|
|
let nodeDef = viewDef.nodes[nodeIndex];
|
|
|
|
while (nodeDef) {
|
|
|
|
if (nodeDef.type === NodeType.Element) {
|
|
|
|
return nodeDef;
|
|
|
|
}
|
|
|
|
nodeDef = nodeDef.parent != null ? viewDef.nodes[nodeDef.parent] : undefined;
|
|
|
|
}
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key: string]: any}) {
|
|
|
|
for (let queryId in nodeDef.matchedQueries) {
|
|
|
|
if (queryId.startsWith('#')) {
|
|
|
|
references[queryId.slice(1)] = getQueryValue(view, nodeDef, queryId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|