fix(render): allow to configure when templates are serialized to strings
Introduces the injectable `TemplateCloner` that can be configured via the new token `MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN`. Also replaces `document.adoptNode` with `document.importNode` as otherwise custom elements are not triggered in chrome 43. Closes #3418 Closes #3433
This commit is contained in:
parent
014b6cb397
commit
dd06a871b7
|
@ -17,5 +17,6 @@ export {
|
||||||
ViewDefinition,
|
ViewDefinition,
|
||||||
DOCUMENT_TOKEN,
|
DOCUMENT_TOKEN,
|
||||||
APP_ID_TOKEN,
|
APP_ID_TOKEN,
|
||||||
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
|
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES,
|
||||||
|
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN
|
||||||
} from './src/render/render';
|
} from './src/render/render';
|
||||||
|
|
|
@ -59,7 +59,9 @@ import {
|
||||||
DOCUMENT_TOKEN,
|
DOCUMENT_TOKEN,
|
||||||
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES,
|
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES,
|
||||||
DefaultDomCompiler,
|
DefaultDomCompiler,
|
||||||
APP_ID_RANDOM_BINDING
|
APP_ID_RANDOM_BINDING,
|
||||||
|
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN,
|
||||||
|
TemplateCloner
|
||||||
} from 'angular2/src/render/render';
|
} from 'angular2/src/render/render';
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
|
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
|
||||||
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
|
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
|
||||||
|
@ -114,6 +116,8 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
||||||
DomRenderer,
|
DomRenderer,
|
||||||
bind(Renderer).toAlias(DomRenderer),
|
bind(Renderer).toAlias(DomRenderer),
|
||||||
APP_ID_RANDOM_BINDING,
|
APP_ID_RANDOM_BINDING,
|
||||||
|
TemplateCloner,
|
||||||
|
bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN).toValue(20),
|
||||||
DefaultDomCompiler,
|
DefaultDomCompiler,
|
||||||
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
|
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
|
||||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
||||||
|
|
|
@ -77,7 +77,7 @@ export class Parse5DomAdapter extends DomAdapter {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
elementMatches(node, selector: string, matcher = null): boolean {
|
elementMatches(node, selector: string, matcher = null): boolean {
|
||||||
if (!selector || selector === '*') {
|
if (this.isElementNode(node) && selector === '*') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
var result = false;
|
var result = false;
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {DOCUMENT_TOKEN, APP_ID_TOKEN} from '../dom_tokens';
|
||||||
import {Inject} from 'angular2/di';
|
import {Inject} from 'angular2/di';
|
||||||
import {SharedStylesHost} from '../view/shared_styles_host';
|
import {SharedStylesHost} from '../view/shared_styles_host';
|
||||||
import {prependAll} from '../util';
|
import {prependAll} from '../util';
|
||||||
|
import {TemplateCloner} from '../template_cloner';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The compiler loads and translates the html templates of components into
|
* The compiler loads and translates the html templates of components into
|
||||||
|
@ -32,8 +33,8 @@ import {prependAll} from '../util';
|
||||||
*/
|
*/
|
||||||
export class DomCompiler extends RenderCompiler {
|
export class DomCompiler extends RenderCompiler {
|
||||||
constructor(private _schemaRegistry: ElementSchemaRegistry,
|
constructor(private _schemaRegistry: ElementSchemaRegistry,
|
||||||
private _stepFactory: CompileStepFactory, private _viewLoader: ViewLoader,
|
private _templateCloner: TemplateCloner, private _stepFactory: CompileStepFactory,
|
||||||
private _sharedStylesHost: SharedStylesHost) {
|
private _viewLoader: ViewLoader, private _sharedStylesHost: SharedStylesHost) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +66,8 @@ export class DomCompiler extends RenderCompiler {
|
||||||
|
|
||||||
mergeProtoViewsRecursively(
|
mergeProtoViewsRecursively(
|
||||||
protoViewRefs: List<RenderProtoViewRef | List<any>>): Promise<RenderProtoViewMergeMapping> {
|
protoViewRefs: List<RenderProtoViewRef | List<any>>): Promise<RenderProtoViewMergeMapping> {
|
||||||
return PromiseWrapper.resolve(pvm.mergeProtoViewsRecursively(protoViewRefs));
|
return PromiseWrapper.resolve(
|
||||||
|
pvm.mergeProtoViewsRecursively(this._templateCloner, protoViewRefs));
|
||||||
}
|
}
|
||||||
|
|
||||||
_compileView(viewDef: ViewDefinition, templateAndStyles: TemplateAndStyles,
|
_compileView(viewDef: ViewDefinition, templateAndStyles: TemplateAndStyles,
|
||||||
|
@ -87,7 +89,7 @@ export class DomCompiler extends RenderCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
return PromiseWrapper.resolve(
|
return PromiseWrapper.resolve(
|
||||||
compileElements[0].inheritedProtoView.build(this._schemaRegistry));
|
compileElements[0].inheritedProtoView.build(this._schemaRegistry, this._templateCloner));
|
||||||
}
|
}
|
||||||
|
|
||||||
_normalizeViewEncapsulationIfThereAreNoStyles(viewDef: ViewDefinition): ViewDefinition {
|
_normalizeViewEncapsulationIfThereAreNoStyles(viewDef: ViewDefinition): ViewDefinition {
|
||||||
|
@ -108,8 +110,10 @@ export class DomCompiler extends RenderCompiler {
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DefaultDomCompiler extends DomCompiler {
|
export class DefaultDomCompiler extends DomCompiler {
|
||||||
constructor(schemaRegistry: ElementSchemaRegistry, parser: Parser, viewLoader: ViewLoader,
|
constructor(schemaRegistry: ElementSchemaRegistry, templateCloner: TemplateCloner, parser: Parser,
|
||||||
sharedStylesHost: SharedStylesHost, @Inject(APP_ID_TOKEN) appId: any) {
|
viewLoader: ViewLoader, sharedStylesHost: SharedStylesHost,
|
||||||
super(schemaRegistry, new DefaultStepFactory(parser, appId), viewLoader, sharedStylesHost);
|
@Inject(APP_ID_TOKEN) appId: any) {
|
||||||
|
super(schemaRegistry, templateCloner, new DefaultStepFactory(parser, appId), viewLoader,
|
||||||
|
sharedStylesHost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ import {
|
||||||
RenderViewWithFragments
|
RenderViewWithFragments
|
||||||
} from '../api';
|
} from '../api';
|
||||||
|
|
||||||
|
import {TemplateCloner} from './template_cloner';
|
||||||
|
|
||||||
import {DOCUMENT_TOKEN, DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES} from './dom_tokens';
|
import {DOCUMENT_TOKEN, DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES} from './dom_tokens';
|
||||||
|
|
||||||
const REFLECT_PREFIX: string = 'ng-reflect-';
|
const REFLECT_PREFIX: string = 'ng-reflect-';
|
||||||
|
@ -41,8 +43,9 @@ export class DomRenderer extends Renderer {
|
||||||
_document;
|
_document;
|
||||||
_reflectPropertiesAsAttributes: boolean;
|
_reflectPropertiesAsAttributes: boolean;
|
||||||
|
|
||||||
constructor(public _eventManager: EventManager, private _domSharedStylesHost: DomSharedStylesHost,
|
constructor(private _eventManager: EventManager,
|
||||||
@Inject(DOCUMENT_TOKEN) document,
|
private _domSharedStylesHost: DomSharedStylesHost,
|
||||||
|
private _templateCloner: TemplateCloner, @Inject(DOCUMENT_TOKEN) document,
|
||||||
@Inject(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES) reflectPropertiesAsAttributes:
|
@Inject(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES) reflectPropertiesAsAttributes:
|
||||||
boolean) {
|
boolean) {
|
||||||
super();
|
super();
|
||||||
|
@ -206,7 +209,7 @@ export class DomRenderer extends Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
_createView(protoView: DomProtoView, inplaceElement: HTMLElement): RenderViewWithFragments {
|
_createView(protoView: DomProtoView, inplaceElement: HTMLElement): RenderViewWithFragments {
|
||||||
var clonedProtoView = cloneAndQueryProtoView(protoView, true);
|
var clonedProtoView = cloneAndQueryProtoView(this._templateCloner, protoView, true);
|
||||||
|
|
||||||
var boundElements = clonedProtoView.boundElements;
|
var boundElements = clonedProtoView.boundElements;
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,12 @@ export const APP_ID_TOKEN: OpaqueToken = CONST_EXPR(new OpaqueToken('AppId'));
|
||||||
export var APP_ID_RANDOM_BINDING: Binding =
|
export var APP_ID_RANDOM_BINDING: Binding =
|
||||||
bind(APP_ID_TOKEN).toFactory(() => `${randomChar()}${randomChar()}${randomChar()}`, []);
|
bind(APP_ID_TOKEN).toFactory(() => `${randomChar()}${randomChar()}${randomChar()}`, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines when a compiled template should be stored as a string
|
||||||
|
* rather than keeping its Nodes to preserve memory.
|
||||||
|
*/
|
||||||
|
export const MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN: OpaqueToken =
|
||||||
|
CONST_EXPR(new OpaqueToken('MaxInMemoryElementsPerTemplate'));
|
||||||
|
|
||||||
function randomChar(): string {
|
function randomChar(): string {
|
||||||
return StringWrapper.fromCharCode(97 + Math.floor(Math.random() * 25));
|
return StringWrapper.fromCharCode(97 + Math.floor(Math.random() * 25));
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import {isString} from 'angular2/src/facade/lang';
|
||||||
|
import {Injectable, Inject} from 'angular2/di';
|
||||||
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
import {MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN} from './dom_tokens';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TemplateCloner {
|
||||||
|
maxInMemoryElementsPerTemplate: number;
|
||||||
|
|
||||||
|
constructor(@Inject(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN) maxInMemoryElementsPerTemplate) {
|
||||||
|
this.maxInMemoryElementsPerTemplate = maxInMemoryElementsPerTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareForClone(templateRoot: Element): Element | string {
|
||||||
|
var elementCount = DOM.querySelectorAll(DOM.content(templateRoot), '*').length;
|
||||||
|
if (this.maxInMemoryElementsPerTemplate >= 0 &&
|
||||||
|
elementCount >= this.maxInMemoryElementsPerTemplate) {
|
||||||
|
return DOM.getInnerHTML(templateRoot);
|
||||||
|
} else {
|
||||||
|
return templateRoot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cloneContent(preparedTemplateRoot: Element | string, importNode: boolean): Node {
|
||||||
|
var templateContent;
|
||||||
|
if (isString(preparedTemplateRoot)) {
|
||||||
|
templateContent = DOM.content(DOM.createTemplate(preparedTemplateRoot));
|
||||||
|
if (importNode) {
|
||||||
|
// Attention: We can't use document.adoptNode here
|
||||||
|
// as this does NOT wake up custom elements in Chrome 43
|
||||||
|
// TODO: Use div.innerHTML instead of template.innerHTML when we
|
||||||
|
// have code to support the various special cases and
|
||||||
|
// don't use importNode additionally (e.g. for <tr>, svg elements, ...)
|
||||||
|
// see https://github.com/angular/angular/issues/3364
|
||||||
|
templateContent = DOM.importIntoDoc(templateContent);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templateContent = DOM.content(<Element>preparedTemplateRoot);
|
||||||
|
if (importNode) {
|
||||||
|
templateContent = DOM.importIntoDoc(templateContent);
|
||||||
|
} else {
|
||||||
|
templateContent = DOM.clone(templateContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return templateContent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {DomProtoView} from './view/proto_view';
|
import {DomProtoView} from './view/proto_view';
|
||||||
import {DomElementBinder} from './view/element_binder';
|
import {DomElementBinder} from './view/element_binder';
|
||||||
|
import {TemplateCloner} from './template_cloner';
|
||||||
|
|
||||||
export const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
|
export const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
|
||||||
export const NG_BINDING_CLASS = 'ng-binding';
|
export const NG_BINDING_CLASS = 'ng-binding';
|
||||||
|
@ -57,9 +58,9 @@ export class ClonedProtoView {
|
||||||
public boundElements: Element[], public boundTextNodes: Node[]) {}
|
public boundElements: Element[], public boundTextNodes: Node[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cloneAndQueryProtoView(pv: DomProtoView, importIntoDocument: boolean):
|
export function cloneAndQueryProtoView(templateCloner: TemplateCloner, pv: DomProtoView,
|
||||||
ClonedProtoView {
|
importIntoDocument: boolean): ClonedProtoView {
|
||||||
var templateContent = pv.cloneableTemplate.clone(importIntoDocument);
|
var templateContent = templateCloner.cloneContent(pv.cloneableTemplate, importIntoDocument);
|
||||||
|
|
||||||
var boundElements = queryBoundElements(templateContent, pv.isSingleElementFragment);
|
var boundElements = queryBoundElements(templateContent, pv.isSingleElementFragment);
|
||||||
var boundTextNodes = queryBoundTextNodes(templateContent, pv.rootTextNodeIndices, boundElements,
|
var boundTextNodes = queryBoundTextNodes(templateContent, pv.rootTextNodeIndices, boundElements,
|
||||||
|
@ -77,6 +78,10 @@ function queryFragments(templateContent: Node, fragmentsRootNodeCount: number[])
|
||||||
for (var fragmentIndex = 0; fragmentIndex < fragments.length; fragmentIndex++) {
|
for (var fragmentIndex = 0; fragmentIndex < fragments.length; fragmentIndex++) {
|
||||||
var fragment = ListWrapper.createFixedSize(fragmentsRootNodeCount[fragmentIndex]);
|
var fragment = ListWrapper.createFixedSize(fragmentsRootNodeCount[fragmentIndex]);
|
||||||
fragments[fragmentIndex] = fragment;
|
fragments[fragmentIndex] = fragment;
|
||||||
|
// Note: the 2nd, 3rd, ... fragments are separated by each other via a '|'
|
||||||
|
if (fragmentIndex >= 1) {
|
||||||
|
childNode = DOM.nextSibling(childNode);
|
||||||
|
}
|
||||||
for (var i = 0; i < fragment.length; i++) {
|
for (var i = 0; i < fragment.length; i++) {
|
||||||
fragment[i] = childNode;
|
fragment[i] = childNode;
|
||||||
childNode = DOM.nextSibling(childNode);
|
childNode = DOM.nextSibling(childNode);
|
||||||
|
@ -141,45 +146,3 @@ export function prependAll(parentNode: Node, nodes: Node[]) {
|
||||||
lastInsertedNode = node;
|
lastInsertedNode = node;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CloneableTemplate { clone(importIntoDoc: boolean): Node; }
|
|
||||||
|
|
||||||
export class SerializedCloneableTemplate implements CloneableTemplate {
|
|
||||||
templateString: string;
|
|
||||||
constructor(templateRoot: Element) { this.templateString = DOM.getInnerHTML(templateRoot); }
|
|
||||||
clone(importIntoDoc: boolean): Node {
|
|
||||||
var result = DOM.content(DOM.createTemplate(this.templateString));
|
|
||||||
if (importIntoDoc) {
|
|
||||||
result = DOM.adoptNode(result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ReferenceCloneableTemplate implements CloneableTemplate {
|
|
||||||
constructor(public templateRoot: Element) {}
|
|
||||||
clone(importIntoDoc: boolean): Node {
|
|
||||||
if (importIntoDoc) {
|
|
||||||
return DOM.importIntoDoc(DOM.content(this.templateRoot));
|
|
||||||
} else {
|
|
||||||
return DOM.clone(DOM.content(this.templateRoot));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function prepareTemplateForClone(templateRoot: Element): CloneableTemplate {
|
|
||||||
var root = DOM.content(templateRoot);
|
|
||||||
var elementCount = DOM.querySelectorAll(root, '*').length;
|
|
||||||
var firstChild = DOM.firstChild(root);
|
|
||||||
var forceSerialize =
|
|
||||||
isPresent(firstChild) && DOM.isCommentNode(firstChild) ? DOM.nodeValue(firstChild) : null;
|
|
||||||
if (forceSerialize == 'nocache') {
|
|
||||||
return new SerializedCloneableTemplate(templateRoot);
|
|
||||||
} else if (forceSerialize == 'cache') {
|
|
||||||
return new ReferenceCloneableTemplate(templateRoot);
|
|
||||||
} else if (elementCount > MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE) {
|
|
||||||
return new SerializedCloneableTemplate(templateRoot);
|
|
||||||
} else {
|
|
||||||
return new ReferenceCloneableTemplate(templateRoot);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,7 +5,7 @@ import {RenderProtoViewRef, ViewType, ViewEncapsulation} from '../../api';
|
||||||
|
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {prepareTemplateForClone, CloneableTemplate} from '../util';
|
import {TemplateCloner} from '../template_cloner';
|
||||||
|
|
||||||
export function resolveInternalDomProtoView(protoViewRef: RenderProtoViewRef): DomProtoView {
|
export function resolveInternalDomProtoView(protoViewRef: RenderProtoViewRef): DomProtoView {
|
||||||
return (<DomProtoViewRef>protoViewRef)._protoView;
|
return (<DomProtoViewRef>protoViewRef)._protoView;
|
||||||
|
@ -16,9 +16,9 @@ export class DomProtoViewRef extends RenderProtoViewRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DomProtoView {
|
export class DomProtoView {
|
||||||
static create(type: ViewType, rootElement: Element, viewEncapsulation: ViewEncapsulation,
|
static create(templateCloner: TemplateCloner, type: ViewType, rootElement: Element,
|
||||||
fragmentsRootNodeCount: number[], rootTextNodeIndices: number[],
|
viewEncapsulation: ViewEncapsulation, fragmentsRootNodeCount: number[],
|
||||||
elementBinders: List<DomElementBinder>,
|
rootTextNodeIndices: number[], elementBinders: List<DomElementBinder>,
|
||||||
hostAttributes: Map<string, string>): DomProtoView {
|
hostAttributes: Map<string, string>): DomProtoView {
|
||||||
var boundTextNodeCount = rootTextNodeIndices.length;
|
var boundTextNodeCount = rootTextNodeIndices.length;
|
||||||
for (var i = 0; i < elementBinders.length; i++) {
|
for (var i = 0; i < elementBinders.length; i++) {
|
||||||
|
@ -27,12 +27,12 @@ export class DomProtoView {
|
||||||
var isSingleElementFragment = fragmentsRootNodeCount.length === 1 &&
|
var isSingleElementFragment = fragmentsRootNodeCount.length === 1 &&
|
||||||
fragmentsRootNodeCount[0] === 1 &&
|
fragmentsRootNodeCount[0] === 1 &&
|
||||||
DOM.isElementNode(DOM.firstChild(DOM.content(rootElement)));
|
DOM.isElementNode(DOM.firstChild(DOM.content(rootElement)));
|
||||||
return new DomProtoView(type, prepareTemplateForClone(rootElement), viewEncapsulation,
|
return new DomProtoView(type, templateCloner.prepareForClone(rootElement), viewEncapsulation,
|
||||||
elementBinders, hostAttributes, rootTextNodeIndices, boundTextNodeCount,
|
elementBinders, hostAttributes, rootTextNodeIndices, boundTextNodeCount,
|
||||||
fragmentsRootNodeCount, isSingleElementFragment);
|
fragmentsRootNodeCount, isSingleElementFragment);
|
||||||
}
|
}
|
||||||
|
// Note: fragments are separated by a comment node that is not counted in fragmentsRootNodeCount!
|
||||||
constructor(public type: ViewType, public cloneableTemplate: CloneableTemplate,
|
constructor(public type: ViewType, public cloneableTemplate: Element | string,
|
||||||
public encapsulation: ViewEncapsulation,
|
public encapsulation: ViewEncapsulation,
|
||||||
public elementBinders: List<DomElementBinder>,
|
public elementBinders: List<DomElementBinder>,
|
||||||
public hostAttributes: Map<string, string>, public rootTextNodeIndices: number[],
|
public hostAttributes: Map<string, string>, public rootTextNodeIndices: number[],
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './proto_view';
|
import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './proto_view';
|
||||||
import {DomElementBinder, Event, HostAction} from './element_binder';
|
import {DomElementBinder, Event, HostAction} from './element_binder';
|
||||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
|
import {TemplateCloner} from '../template_cloner';
|
||||||
|
|
||||||
import * as api from '../../api';
|
import * as api from '../../api';
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ export class ProtoViewBuilder {
|
||||||
|
|
||||||
setHostAttribute(name: string, value: string) { this.hostAttributes.set(name, value); }
|
setHostAttribute(name: string, value: string) { this.hostAttributes.set(name, value); }
|
||||||
|
|
||||||
build(schemaRegistry: ElementSchemaRegistry): api.ProtoViewDto {
|
build(schemaRegistry: ElementSchemaRegistry, templateCloner: TemplateCloner): api.ProtoViewDto {
|
||||||
var domElementBinders = [];
|
var domElementBinders = [];
|
||||||
|
|
||||||
var apiElementBinders = [];
|
var apiElementBinders = [];
|
||||||
|
@ -96,8 +97,9 @@ export class ProtoViewBuilder {
|
||||||
dbb.hostPropertyBindings, null)
|
dbb.hostPropertyBindings, null)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
var nestedProtoView =
|
var nestedProtoView = isPresent(ebb.nestedProtoView) ?
|
||||||
isPresent(ebb.nestedProtoView) ? ebb.nestedProtoView.build(schemaRegistry) : null;
|
ebb.nestedProtoView.build(schemaRegistry, templateCloner) :
|
||||||
|
null;
|
||||||
if (isPresent(nestedProtoView)) {
|
if (isPresent(nestedProtoView)) {
|
||||||
transitiveNgContentCount += nestedProtoView.transitiveNgContentCount;
|
transitiveNgContentCount += nestedProtoView.transitiveNgContentCount;
|
||||||
}
|
}
|
||||||
|
@ -131,9 +133,9 @@ export class ProtoViewBuilder {
|
||||||
});
|
});
|
||||||
var rootNodeCount = DOM.childNodes(DOM.content(this.rootElement)).length;
|
var rootNodeCount = DOM.childNodes(DOM.content(this.rootElement)).length;
|
||||||
return new api.ProtoViewDto({
|
return new api.ProtoViewDto({
|
||||||
render: new DomProtoViewRef(
|
render: new DomProtoViewRef(DomProtoView.create(
|
||||||
DomProtoView.create(this.type, this.rootElement, this.viewEncapsulation, [rootNodeCount],
|
templateCloner, this.type, this.rootElement, this.viewEncapsulation, [rootNodeCount],
|
||||||
rootTextNodeIndices, domElementBinders, this.hostAttributes)),
|
rootTextNodeIndices, domElementBinders, this.hostAttributes)),
|
||||||
type: this.type,
|
type: this.type,
|
||||||
elementBinders: apiElementBinders,
|
elementBinders: apiElementBinders,
|
||||||
variableBindings: this.variableBindings,
|
variableBindings: this.variableBindings,
|
||||||
|
|
|
@ -22,12 +22,15 @@ import {
|
||||||
prependAll
|
prependAll
|
||||||
} from '../util';
|
} from '../util';
|
||||||
|
|
||||||
export function mergeProtoViewsRecursively(protoViewRefs: List<RenderProtoViewRef | List<any>>):
|
import {TemplateCloner} from '../template_cloner';
|
||||||
|
|
||||||
|
export function mergeProtoViewsRecursively(templateCloner: TemplateCloner,
|
||||||
|
protoViewRefs: List<RenderProtoViewRef | List<any>>):
|
||||||
RenderProtoViewMergeMapping {
|
RenderProtoViewMergeMapping {
|
||||||
// clone
|
// clone
|
||||||
var clonedProtoViews = [];
|
var clonedProtoViews = [];
|
||||||
var hostViewAndBinderIndices: number[][] = [];
|
var hostViewAndBinderIndices: number[][] = [];
|
||||||
cloneProtoViews(protoViewRefs, clonedProtoViews, hostViewAndBinderIndices);
|
cloneProtoViews(templateCloner, protoViewRefs, clonedProtoViews, hostViewAndBinderIndices);
|
||||||
var mainProtoView: ClonedProtoView = clonedProtoViews[0];
|
var mainProtoView: ClonedProtoView = clonedProtoViews[0];
|
||||||
|
|
||||||
// modify the DOM
|
// modify the DOM
|
||||||
|
@ -41,8 +44,8 @@ export function mergeProtoViewsRecursively(protoViewRefs: List<RenderProtoViewRe
|
||||||
markBoundTextNodeParentsAsBoundElements(clonedProtoViews);
|
markBoundTextNodeParentsAsBoundElements(clonedProtoViews);
|
||||||
|
|
||||||
// create a new root element with the changed fragments and elements
|
// create a new root element with the changed fragments and elements
|
||||||
var rootElement = createRootElementFromFragments(fragments);
|
|
||||||
var fragmentsRootNodeCount = fragments.map(fragment => fragment.length);
|
var fragmentsRootNodeCount = fragments.map(fragment => fragment.length);
|
||||||
|
var rootElement = createRootElementFromFragments(fragments);
|
||||||
var rootNode = DOM.content(rootElement);
|
var rootNode = DOM.content(rootElement);
|
||||||
|
|
||||||
// read out the new element / text node / ElementBinder order
|
// read out the new element / text node / ElementBinder order
|
||||||
|
@ -63,21 +66,22 @@ export function mergeProtoViewsRecursively(protoViewRefs: List<RenderProtoViewRe
|
||||||
var hostElementIndicesByViewIndex =
|
var hostElementIndicesByViewIndex =
|
||||||
calcHostElementIndicesByViewIndex(clonedProtoViews, hostViewAndBinderIndices);
|
calcHostElementIndicesByViewIndex(clonedProtoViews, hostViewAndBinderIndices);
|
||||||
var nestedViewCounts = calcNestedViewCounts(hostViewAndBinderIndices);
|
var nestedViewCounts = calcNestedViewCounts(hostViewAndBinderIndices);
|
||||||
var mergedProtoView = DomProtoView.create(
|
var mergedProtoView =
|
||||||
mainProtoView.original.type, rootElement, mainProtoView.original.encapsulation,
|
DomProtoView.create(templateCloner, mainProtoView.original.type, rootElement,
|
||||||
fragmentsRootNodeCount, rootTextNodeIndices, mergedElementBinders, new Map());
|
mainProtoView.original.encapsulation, fragmentsRootNodeCount,
|
||||||
|
rootTextNodeIndices, mergedElementBinders, new Map());
|
||||||
return new RenderProtoViewMergeMapping(new DomProtoViewRef(mergedProtoView),
|
return new RenderProtoViewMergeMapping(new DomProtoViewRef(mergedProtoView),
|
||||||
fragmentsRootNodeCount.length, mappedElementIndices,
|
fragmentsRootNodeCount.length, mappedElementIndices,
|
||||||
mergedBoundElements.length, mappedTextIndices,
|
mergedBoundElements.length, mappedTextIndices,
|
||||||
hostElementIndicesByViewIndex, nestedViewCounts);
|
hostElementIndicesByViewIndex, nestedViewCounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cloneProtoViews(protoViewRefs: List<RenderProtoViewRef | List<any>>,
|
function cloneProtoViews(
|
||||||
targetClonedProtoViews: ClonedProtoView[],
|
templateCloner: TemplateCloner, protoViewRefs: List<RenderProtoViewRef | List<any>>,
|
||||||
targetHostViewAndBinderIndices: number[][]) {
|
targetClonedProtoViews: ClonedProtoView[], targetHostViewAndBinderIndices: number[][]) {
|
||||||
var hostProtoView = resolveInternalDomProtoView(protoViewRefs[0]);
|
var hostProtoView = resolveInternalDomProtoView(protoViewRefs[0]);
|
||||||
var hostPvIdx = targetClonedProtoViews.length;
|
var hostPvIdx = targetClonedProtoViews.length;
|
||||||
targetClonedProtoViews.push(cloneAndQueryProtoView(hostProtoView, false));
|
targetClonedProtoViews.push(cloneAndQueryProtoView(templateCloner, hostProtoView, false));
|
||||||
if (targetHostViewAndBinderIndices.length === 0) {
|
if (targetHostViewAndBinderIndices.length === 0) {
|
||||||
targetHostViewAndBinderIndices.push([null, null]);
|
targetHostViewAndBinderIndices.push([null, null]);
|
||||||
}
|
}
|
||||||
|
@ -89,11 +93,11 @@ function cloneProtoViews(protoViewRefs: List<RenderProtoViewRef | List<any>>,
|
||||||
if (isPresent(nestedEntry)) {
|
if (isPresent(nestedEntry)) {
|
||||||
targetHostViewAndBinderIndices.push([hostPvIdx, i]);
|
targetHostViewAndBinderIndices.push([hostPvIdx, i]);
|
||||||
if (isArray(nestedEntry)) {
|
if (isArray(nestedEntry)) {
|
||||||
cloneProtoViews(<any[]>nestedEntry, targetClonedProtoViews,
|
cloneProtoViews(templateCloner, <any[]>nestedEntry, targetClonedProtoViews,
|
||||||
targetHostViewAndBinderIndices);
|
targetHostViewAndBinderIndices);
|
||||||
} else {
|
} else {
|
||||||
targetClonedProtoViews.push(
|
targetClonedProtoViews.push(cloneAndQueryProtoView(
|
||||||
cloneAndQueryProtoView(resolveInternalDomProtoView(nestedEntry), false));
|
templateCloner, resolveInternalDomProtoView(nestedEntry), false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,8 +306,16 @@ function sortContentElements(contentElements: Element[]): Element[] {
|
||||||
function createRootElementFromFragments(fragments: Node[][]): Element {
|
function createRootElementFromFragments(fragments: Node[][]): Element {
|
||||||
var rootElement = DOM.createTemplate('');
|
var rootElement = DOM.createTemplate('');
|
||||||
var rootNode = DOM.content(rootElement);
|
var rootNode = DOM.content(rootElement);
|
||||||
fragments.forEach(
|
for (var i = 0; i < fragments.length; i++) {
|
||||||
(fragment) => { fragment.forEach((node) => { DOM.appendChild(rootNode, node); }); });
|
var fragment = fragments[i];
|
||||||
|
if (i >= 1) {
|
||||||
|
// Note: We need to seprate fragments by a comment so that sibling
|
||||||
|
// text nodes don't get merged when we serialize the DomProtoView into a string
|
||||||
|
// and parse it back again.
|
||||||
|
DOM.appendChild(rootNode, DOM.createComment('|'));
|
||||||
|
}
|
||||||
|
fragment.forEach((node) => { DOM.appendChild(rootNode, node); });
|
||||||
|
}
|
||||||
return rootElement;
|
return rootElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,4 +9,5 @@ export * from './dom/view/shared_styles_host';
|
||||||
export * from './dom/compiler/compiler';
|
export * from './dom/compiler/compiler';
|
||||||
export * from './dom/dom_renderer';
|
export * from './dom/dom_renderer';
|
||||||
export * from './dom/dom_tokens';
|
export * from './dom/dom_tokens';
|
||||||
|
export * from './dom/template_cloner';
|
||||||
export * from './api';
|
export * from './api';
|
||||||
|
|
|
@ -54,7 +54,9 @@ import {
|
||||||
DefaultDomCompiler,
|
DefaultDomCompiler,
|
||||||
APP_ID_TOKEN,
|
APP_ID_TOKEN,
|
||||||
SharedStylesHost,
|
SharedStylesHost,
|
||||||
DomSharedStylesHost
|
DomSharedStylesHost,
|
||||||
|
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN,
|
||||||
|
TemplateCloner
|
||||||
} from 'angular2/src/render/render';
|
} from 'angular2/src/render/render';
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
|
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
|
||||||
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
|
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
|
||||||
|
@ -98,6 +100,8 @@ function _getAppBindings() {
|
||||||
DomRenderer,
|
DomRenderer,
|
||||||
bind(Renderer).toAlias(DomRenderer),
|
bind(Renderer).toAlias(DomRenderer),
|
||||||
bind(APP_ID_TOKEN).toValue('a'),
|
bind(APP_ID_TOKEN).toValue('a'),
|
||||||
|
TemplateCloner,
|
||||||
|
bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN).toValue(-1),
|
||||||
DefaultDomCompiler,
|
DefaultDomCompiler,
|
||||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
||||||
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
|
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
|
||||||
|
|
|
@ -13,6 +13,7 @@ import 'package:angular2/src/render/dom/compiler/style_url_resolver.dart';
|
||||||
import 'package:angular2/src/render/dom/compiler/view_loader.dart';
|
import 'package:angular2/src/render/dom/compiler/view_loader.dart';
|
||||||
import 'package:angular2/src/render/dom/schema/element_schema_registry.dart';
|
import 'package:angular2/src/render/dom/schema/element_schema_registry.dart';
|
||||||
import 'package:angular2/src/render/dom/schema/dom_element_schema_registry.dart';
|
import 'package:angular2/src/render/dom/schema/dom_element_schema_registry.dart';
|
||||||
|
import 'package:angular2/src/render/dom/template_cloner.dart';
|
||||||
import 'package:angular2/src/render/xhr.dart' show XHR;
|
import 'package:angular2/src/render/xhr.dart' show XHR;
|
||||||
import 'package:angular2/src/reflection/reflection.dart';
|
import 'package:angular2/src/reflection/reflection.dart';
|
||||||
import 'package:angular2/src/services/url_resolver.dart';
|
import 'package:angular2/src/services/url_resolver.dart';
|
||||||
|
@ -35,8 +36,11 @@ Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
|
||||||
{bool generateRegistrations: true,
|
{bool generateRegistrations: true,
|
||||||
bool generateChangeDetectors: true}) async {
|
bool generateChangeDetectors: true}) async {
|
||||||
var viewDefResults = await createViewDefinitions(reader, entryPoint);
|
var viewDefResults = await createViewDefinitions(reader, entryPoint);
|
||||||
|
// Note: TemplateCloner(-1) never serializes Nodes into strings.
|
||||||
|
// we might want to change this to TemplateCloner(0) to force the serialization
|
||||||
|
// later when the transformer also stores the proto view template.
|
||||||
var extractor = new _TemplateExtractor(
|
var extractor = new _TemplateExtractor(
|
||||||
new DomElementSchemaRegistry(), new XhrImpl(reader, entryPoint));
|
new DomElementSchemaRegistry(), new TemplateCloner(-1), new XhrImpl(reader, entryPoint));
|
||||||
|
|
||||||
var registrations = new reg.Codegen();
|
var registrations = new reg.Codegen();
|
||||||
var changeDetectorClasses = new change.Codegen();
|
var changeDetectorClasses = new change.Codegen();
|
||||||
|
@ -87,8 +91,9 @@ class _TemplateExtractor {
|
||||||
final CompileStepFactory _factory;
|
final CompileStepFactory _factory;
|
||||||
ViewLoader _loader;
|
ViewLoader _loader;
|
||||||
ElementSchemaRegistry _schemaRegistry;
|
ElementSchemaRegistry _schemaRegistry;
|
||||||
|
TemplateCloner _templateCloner;
|
||||||
|
|
||||||
_TemplateExtractor(ElementSchemaRegistry schemaRegistry, XHR xhr)
|
_TemplateExtractor(ElementSchemaRegistry schemaRegistry, TemplateCloner templateCloner, XHR xhr)
|
||||||
: _factory = new CompileStepFactory(new ng.Parser(new ng.Lexer())) {
|
: _factory = new CompileStepFactory(new ng.Parser(new ng.Lexer())) {
|
||||||
var urlResolver = new UrlResolver();
|
var urlResolver = new UrlResolver();
|
||||||
var styleUrlResolver = new StyleUrlResolver(urlResolver);
|
var styleUrlResolver = new StyleUrlResolver(urlResolver);
|
||||||
|
@ -96,6 +101,7 @@ class _TemplateExtractor {
|
||||||
|
|
||||||
_loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
|
_loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
|
||||||
_schemaRegistry = schemaRegistry;
|
_schemaRegistry = schemaRegistry;
|
||||||
|
_templateCloner = templateCloner;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<_ExtractResult> extractTemplates(ViewDefinition viewDef) async {
|
Future<_ExtractResult> extractTemplates(ViewDefinition viewDef) async {
|
||||||
|
@ -117,7 +123,7 @@ class _TemplateExtractor {
|
||||||
DOM.createTemplate(templateAndStyles.template), ViewType.COMPONENT,
|
DOM.createTemplate(templateAndStyles.template), ViewType.COMPONENT,
|
||||||
viewDef);
|
viewDef);
|
||||||
var protoViewDto =
|
var protoViewDto =
|
||||||
compileElements[0].inheritedProtoView.build(_schemaRegistry);
|
compileElements[0].inheritedProtoView.build(_schemaRegistry, _templateCloner);
|
||||||
|
|
||||||
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,9 @@ import {
|
||||||
DOCUMENT_TOKEN,
|
DOCUMENT_TOKEN,
|
||||||
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES,
|
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES,
|
||||||
DefaultDomCompiler,
|
DefaultDomCompiler,
|
||||||
APP_ID_RANDOM_BINDING
|
APP_ID_RANDOM_BINDING,
|
||||||
|
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN,
|
||||||
|
TemplateCloner
|
||||||
} from 'angular2/src/render/render';
|
} from 'angular2/src/render/render';
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
|
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
|
||||||
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
|
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
|
||||||
|
@ -92,6 +94,8 @@ function _injectorBindings(): List<Type | Binding | List<any>> {
|
||||||
DomRenderer,
|
DomRenderer,
|
||||||
bind(Renderer).toAlias(DomRenderer),
|
bind(Renderer).toAlias(DomRenderer),
|
||||||
APP_ID_RANDOM_BINDING,
|
APP_ID_RANDOM_BINDING,
|
||||||
|
TemplateCloner,
|
||||||
|
bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN).toValue(20),
|
||||||
DefaultDomCompiler,
|
DefaultDomCompiler,
|
||||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
||||||
DomSharedStylesHost,
|
DomSharedStylesHost,
|
||||||
|
|
|
@ -38,6 +38,8 @@ import {
|
||||||
ViewEncapsulation
|
ViewEncapsulation
|
||||||
} from 'angular2/angular2';
|
} from 'angular2/angular2';
|
||||||
|
|
||||||
|
import {MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN} from 'angular2/src/render/render';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('projection', () => {
|
describe('projection', () => {
|
||||||
it('should support simple components',
|
it('should support simple components',
|
||||||
|
@ -416,6 +418,44 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('different proto view storages', () => {
|
||||||
|
function runTests() {
|
||||||
|
it('should support nested conditionals that contain ng-contents',
|
||||||
|
inject(
|
||||||
|
[TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
|
tcb.overrideView(MainComp, new viewAnn.View({
|
||||||
|
template: `<conditional-text>a</conditional-text>`,
|
||||||
|
directives: [ConditionalTextComponent]
|
||||||
|
}))
|
||||||
|
.createAsync(MainComp)
|
||||||
|
.then((main) => {
|
||||||
|
expect(main.nativeElement).toHaveText('MAIN()');
|
||||||
|
|
||||||
|
var viewportElement = main.componentViewChildren[0].componentViewChildren[0];
|
||||||
|
viewportElement.inject(ManualViewportDirective).show();
|
||||||
|
expect(main.nativeElement).toHaveText('MAIN(FIRST())');
|
||||||
|
|
||||||
|
viewportElement = main.componentViewChildren[0].componentViewChildren[1];
|
||||||
|
viewportElement.inject(ManualViewportDirective).show();
|
||||||
|
expect(main.nativeElement).toHaveText('MAIN(FIRST(SECOND(a)))');
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('serialize templates', () => {
|
||||||
|
beforeEachBindings(() => [bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN).toValue(0)]);
|
||||||
|
runTests();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("don't serialize templates", () => {
|
||||||
|
beforeEachBindings(() => [bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE_TOKEN).toValue(-1)]);
|
||||||
|
runTests();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,6 +548,15 @@ class InnerInnerComponent {
|
||||||
class ConditionalContentComponent {
|
class ConditionalContentComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'conditional-text'})
|
||||||
|
@View({
|
||||||
|
template:
|
||||||
|
'MAIN(<template manual>FIRST(<template manual>SECOND(<ng-content></ng-content>)</template>)</template>)',
|
||||||
|
directives: [ManualViewportDirective]
|
||||||
|
})
|
||||||
|
class ConditionalTextComponent {
|
||||||
|
}
|
||||||
|
|
||||||
@Component({selector: 'tab'})
|
@Component({selector: 'tab'})
|
||||||
@View({
|
@View({
|
||||||
template: '<div><div *manual>TAB(<ng-content></ng-content>)</div></div>',
|
template: '<div><div *manual>TAB(<ng-content></ng-content>)</div></div>',
|
||||||
|
|
|
@ -31,11 +31,10 @@ import {ViewLoader, TemplateAndStyles} from 'angular2/src/render/dom/compiler/vi
|
||||||
|
|
||||||
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||||
import {SharedStylesHost} from 'angular2/src/render/dom/view/shared_styles_host';
|
import {SharedStylesHost} from 'angular2/src/render/dom/view/shared_styles_host';
|
||||||
|
import {TemplateCloner} from 'angular2/src/render/dom/template_cloner';
|
||||||
|
|
||||||
import {MockStep} from './pipeline_spec';
|
import {MockStep} from './pipeline_spec';
|
||||||
|
|
||||||
import {ReferenceCloneableTemplate} from 'angular2/src/render/dom/util';
|
|
||||||
|
|
||||||
export function runCompilerCommonTests() {
|
export function runCompilerCommonTests() {
|
||||||
describe('DomCompiler', function() {
|
describe('DomCompiler', function() {
|
||||||
var mockStepFactory: MockStepFactory;
|
var mockStepFactory: MockStepFactory;
|
||||||
|
@ -51,8 +50,8 @@ export function runCompilerCommonTests() {
|
||||||
var tplLoader = new FakeViewLoader(urlData);
|
var tplLoader = new FakeViewLoader(urlData);
|
||||||
mockStepFactory =
|
mockStepFactory =
|
||||||
new MockStepFactory([new MockStep(processElementClosure, processStyleClosure)]);
|
new MockStepFactory([new MockStep(processElementClosure, processStyleClosure)]);
|
||||||
return new DomCompiler(new ElementSchemaRegistry(), mockStepFactory, tplLoader,
|
return new DomCompiler(new ElementSchemaRegistry(), new TemplateCloner(-1), mockStepFactory,
|
||||||
sharedStylesHost);
|
tplLoader, sharedStylesHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('compile', () => {
|
describe('compile', () => {
|
||||||
|
@ -255,9 +254,9 @@ export function runCompilerCommonTests() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function templateRoot(protoViewDto: ProtoViewDto) {
|
function templateRoot(protoViewDto: ProtoViewDto): Element {
|
||||||
var pv = resolveInternalDomProtoView(protoViewDto.render);
|
var pv = resolveInternalDomProtoView(protoViewDto.render);
|
||||||
return (<ReferenceCloneableTemplate>pv.cloneableTemplate).templateRoot;
|
return (<Element>pv.cloneableTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockStepFactory extends CompileStepFactory {
|
class MockStepFactory extends CompileStepFactory {
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
beforeEach,
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
el,
|
||||||
|
expect,
|
||||||
|
iit,
|
||||||
|
inject,
|
||||||
|
it,
|
||||||
|
xit,
|
||||||
|
beforeEachBindings,
|
||||||
|
SpyObject,
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
import {TemplateCloner} from 'angular2/src/render/dom/template_cloner';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('TemplateCloner', () => {
|
||||||
|
var cloner: TemplateCloner;
|
||||||
|
var bigTemplate: Element;
|
||||||
|
var smallTemplate: Element;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cloner = new TemplateCloner(1);
|
||||||
|
bigTemplate = DOM.createTemplate('a<div></div>');
|
||||||
|
smallTemplate = DOM.createTemplate('a');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('prepareForClone', () => {
|
||||||
|
it('should use a reference for small templates',
|
||||||
|
() => { expect(cloner.prepareForClone(smallTemplate)).toBe(smallTemplate); });
|
||||||
|
|
||||||
|
it('should use a reference if the max element count is -1', () => {
|
||||||
|
cloner = new TemplateCloner(-1);
|
||||||
|
expect(cloner.prepareForClone(bigTemplate)).toBe(bigTemplate);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use a string for big templates', () => {
|
||||||
|
expect(cloner.prepareForClone(bigTemplate)).toEqual(DOM.getInnerHTML(bigTemplate));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cloneTemplate', () => {
|
||||||
|
|
||||||
|
function shouldReturnTemplateContentNodes(template: Element, importIntoDoc: boolean) {
|
||||||
|
var clone = cloner.cloneContent(cloner.prepareForClone(template), importIntoDoc);
|
||||||
|
expect(clone).not.toBe(DOM.content(template));
|
||||||
|
expect(DOM.getText(DOM.firstChild(clone))).toEqual('a');
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should return template.content nodes (small template, no import)',
|
||||||
|
() => { shouldReturnTemplateContentNodes(smallTemplate, false); });
|
||||||
|
|
||||||
|
it('should return template.content nodes (small template, import)',
|
||||||
|
() => { shouldReturnTemplateContentNodes(smallTemplate, true); });
|
||||||
|
|
||||||
|
it('should return template.content nodes (big template, no import)',
|
||||||
|
() => { shouldReturnTemplateContentNodes(bigTemplate, false); });
|
||||||
|
|
||||||
|
it('should return template.content nodes (big template, import)',
|
||||||
|
() => { shouldReturnTemplateContentNodes(bigTemplate, true); });
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,97 +0,0 @@
|
||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
el,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
it,
|
|
||||||
xit,
|
|
||||||
beforeEachBindings,
|
|
||||||
SpyObject,
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
|
||||||
import {
|
|
||||||
prepareTemplateForClone,
|
|
||||||
ReferenceCloneableTemplate,
|
|
||||||
SerializedCloneableTemplate
|
|
||||||
} from 'angular2/src/render/dom/util';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('Dom util', () => {
|
|
||||||
|
|
||||||
describe('prepareTemplateForClone', () => {
|
|
||||||
it('should use a reference for small templates', () => {
|
|
||||||
var t = DOM.createTemplate('');
|
|
||||||
var ct = prepareTemplateForClone(t);
|
|
||||||
expect((<ReferenceCloneableTemplate>ct).templateRoot).toBe(t);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use a reference for big templates with a force comment', () => {
|
|
||||||
var templateString = '<!--cache-->';
|
|
||||||
for (var i = 0; i < 100; i++) {
|
|
||||||
templateString += '<div></div>';
|
|
||||||
}
|
|
||||||
var t = DOM.createTemplate(templateString);
|
|
||||||
var ct = prepareTemplateForClone(t);
|
|
||||||
expect((<ReferenceCloneableTemplate>ct).templateRoot).toBe(t);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should serialize for big templates', () => {
|
|
||||||
var templateString = '';
|
|
||||||
for (var i = 0; i < 100; i++) {
|
|
||||||
templateString += '<div></div>';
|
|
||||||
}
|
|
||||||
var t = DOM.createTemplate(templateString);
|
|
||||||
var ct = prepareTemplateForClone(t);
|
|
||||||
expect((<SerializedCloneableTemplate>ct).templateString).toEqual(templateString);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should serialize for templates with the force comment', () => {
|
|
||||||
var templateString = '<!--nocache-->';
|
|
||||||
var t = DOM.createTemplate(templateString);
|
|
||||||
var ct = prepareTemplateForClone(t);
|
|
||||||
expect((<SerializedCloneableTemplate>ct).templateString).toEqual(templateString);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ReferenceCloneableTemplate', () => {
|
|
||||||
it('should return template.content nodes (no import)', () => {
|
|
||||||
var t = DOM.createTemplate('a');
|
|
||||||
var ct = new ReferenceCloneableTemplate(t);
|
|
||||||
var clone = ct.clone(false);
|
|
||||||
expect(clone).not.toBe(DOM.content(t));
|
|
||||||
expect(DOM.getText(DOM.firstChild(clone))).toEqual('a');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return template.content nodes (import into doc)', () => {
|
|
||||||
var t = DOM.createTemplate('a');
|
|
||||||
var ct = new ReferenceCloneableTemplate(t);
|
|
||||||
var clone = ct.clone(true);
|
|
||||||
expect(clone).not.toBe(DOM.content(t));
|
|
||||||
expect(DOM.getText(DOM.firstChild(clone))).toEqual('a');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('SerializedCloneableTemplate', () => {
|
|
||||||
it('should return template.content nodes (no import)', () => {
|
|
||||||
var t = DOM.createTemplate('a');
|
|
||||||
var ct = new SerializedCloneableTemplate(t);
|
|
||||||
var clone = ct.clone(false);
|
|
||||||
expect(clone).not.toBe(DOM.content(t));
|
|
||||||
expect(DOM.getText(DOM.firstChild(clone))).toEqual('a');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return template.content nodes (import into doc)', () => {
|
|
||||||
var t = DOM.createTemplate('a');
|
|
||||||
var ct = new SerializedCloneableTemplate(t);
|
|
||||||
var clone = ct.clone(true);
|
|
||||||
expect(clone).not.toBe(DOM.content(t));
|
|
||||||
expect(DOM.getText(DOM.firstChild(clone))).toEqual('a');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
|
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
|
||||||
|
import {TemplateCloner} from 'angular2/src/render/dom/template_cloner';
|
||||||
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||||
import {ASTWithSource, AST} from 'angular2/src/change_detection/change_detection';
|
import {ASTWithSource, AST} from 'angular2/src/change_detection/change_detection';
|
||||||
import {PropertyBindingType, ViewType, ViewEncapsulation} from 'angular2/src/render/api';
|
import {PropertyBindingType, ViewType, ViewEncapsulation} from 'angular2/src/render/api';
|
||||||
|
@ -22,7 +23,9 @@ export function main() {
|
||||||
|
|
||||||
describe('ProtoViewBuilder', () => {
|
describe('ProtoViewBuilder', () => {
|
||||||
var builder;
|
var builder;
|
||||||
|
var templateCloner;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
templateCloner = new TemplateCloner(-1);
|
||||||
builder =
|
builder =
|
||||||
new ProtoViewBuilder(DOM.createTemplate(''), ViewType.EMBEDDED, ViewEncapsulation.NONE);
|
new ProtoViewBuilder(DOM.createTemplate(''), ViewType.EMBEDDED, ViewEncapsulation.NONE);
|
||||||
});
|
});
|
||||||
|
@ -32,7 +35,7 @@ export function main() {
|
||||||
|
|
||||||
it('should throw for unknown properties', () => {
|
it('should throw for unknown properties', () => {
|
||||||
builder.bindElement(el('<div/>')).bindProperty('unknownProperty', emptyExpr());
|
builder.bindElement(el('<div/>')).bindProperty('unknownProperty', emptyExpr());
|
||||||
expect(() => builder.build(new DomElementSchemaRegistry()))
|
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Can't bind to 'unknownProperty' since it isn't a known property of the '<div>' element and there are no matching directives with a corresponding property`);
|
`Can't bind to 'unknownProperty' since it isn't a known property of the '<div>' element and there are no matching directives with a corresponding property`);
|
||||||
});
|
});
|
||||||
|
@ -41,7 +44,7 @@ export function main() {
|
||||||
var binder = builder.bindElement(el('<div/>'));
|
var binder = builder.bindElement(el('<div/>'));
|
||||||
binder.bindDirective(0).bindProperty('someDirProperty', emptyExpr(), 'directiveProperty');
|
binder.bindDirective(0).bindProperty('someDirProperty', emptyExpr(), 'directiveProperty');
|
||||||
binder.bindProperty('directiveProperty', emptyExpr());
|
binder.bindProperty('directiveProperty', emptyExpr());
|
||||||
expect(() => builder.build(new DomElementSchemaRegistry())).not.toThrow();
|
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner)).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw for unknown host properties even if another directive uses it', () => {
|
it('should throw for unknown host properties even if another directive uses it', () => {
|
||||||
|
@ -56,14 +59,14 @@ export function main() {
|
||||||
it('should allow unknown properties on custom elements', () => {
|
it('should allow unknown properties on custom elements', () => {
|
||||||
var binder = builder.bindElement(el('<some-custom/>'));
|
var binder = builder.bindElement(el('<some-custom/>'));
|
||||||
binder.bindProperty('unknownProperty', emptyExpr());
|
binder.bindProperty('unknownProperty', emptyExpr());
|
||||||
expect(() => builder.build(new DomElementSchemaRegistry())).not.toThrow();
|
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner)).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw for unknown properties on custom elements if there is an ng component', () => {
|
it('should throw for unknown properties on custom elements if there is an ng component', () => {
|
||||||
var binder = builder.bindElement(el('<some-custom/>'));
|
var binder = builder.bindElement(el('<some-custom/>'));
|
||||||
binder.bindProperty('unknownProperty', emptyExpr());
|
binder.bindProperty('unknownProperty', emptyExpr());
|
||||||
binder.setComponentId('someComponent');
|
binder.setComponentId('someComponent');
|
||||||
expect(() => builder.build(new DomElementSchemaRegistry()))
|
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Can't bind to 'unknownProperty' since it isn't a known property of the '<some-custom>' element and there are no matching directives with a corresponding property`);
|
`Can't bind to 'unknownProperty' since it isn't a known property of the '<some-custom>' element and there are no matching directives with a corresponding property`);
|
||||||
});
|
});
|
||||||
|
@ -77,7 +80,7 @@ export function main() {
|
||||||
// when https://github.com/angular/angular/issues/3019 is solved.
|
// when https://github.com/angular/angular/issues/3019 is solved.
|
||||||
it('should not throw for unknown properties', () => {
|
it('should not throw for unknown properties', () => {
|
||||||
builder.bindElement(el('<div/>')).bindProperty('unknownProperty', emptyExpr());
|
builder.bindElement(el('<div/>')).bindProperty('unknownProperty', emptyExpr());
|
||||||
expect(() => builder.build(new DomElementSchemaRegistry())).not.toThrow();
|
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner)).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -86,19 +89,19 @@ export function main() {
|
||||||
describe('property normalization', () => {
|
describe('property normalization', () => {
|
||||||
it('should normalize "innerHtml" to "innerHTML"', () => {
|
it('should normalize "innerHtml" to "innerHTML"', () => {
|
||||||
builder.bindElement(el('<div/>')).bindProperty('innerHtml', emptyExpr());
|
builder.bindElement(el('<div/>')).bindProperty('innerHtml', emptyExpr());
|
||||||
var pv = builder.build(new DomElementSchemaRegistry());
|
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('innerHTML');
|
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('innerHTML');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should normalize "tabindex" to "tabIndex"', () => {
|
it('should normalize "tabindex" to "tabIndex"', () => {
|
||||||
builder.bindElement(el('<div/>')).bindProperty('tabindex', emptyExpr());
|
builder.bindElement(el('<div/>')).bindProperty('tabindex', emptyExpr());
|
||||||
var pv = builder.build(new DomElementSchemaRegistry());
|
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('tabIndex');
|
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('tabIndex');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should normalize "readonly" to "readOnly"', () => {
|
it('should normalize "readonly" to "readOnly"', () => {
|
||||||
builder.bindElement(el('<input/>')).bindProperty('readonly', emptyExpr());
|
builder.bindElement(el('<input/>')).bindProperty('readonly', emptyExpr());
|
||||||
var pv = builder.build(new DomElementSchemaRegistry());
|
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('readOnly');
|
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('readOnly');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -107,32 +110,32 @@ export function main() {
|
||||||
describe('property binding types', () => {
|
describe('property binding types', () => {
|
||||||
it('should detect property names', () => {
|
it('should detect property names', () => {
|
||||||
builder.bindElement(el('<div/>')).bindProperty('tabindex', emptyExpr());
|
builder.bindElement(el('<div/>')).bindProperty('tabindex', emptyExpr());
|
||||||
var pv = builder.build(new DomElementSchemaRegistry());
|
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].type).toEqual(PropertyBindingType.PROPERTY);
|
expect(pv.elementBinders[0].propertyBindings[0].type).toEqual(PropertyBindingType.PROPERTY);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect attribute names', () => {
|
it('should detect attribute names', () => {
|
||||||
builder.bindElement(el('<div/>')).bindProperty('attr.someName', emptyExpr());
|
builder.bindElement(el('<div/>')).bindProperty('attr.someName', emptyExpr());
|
||||||
var pv = builder.build(new DomElementSchemaRegistry());
|
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].type)
|
expect(pv.elementBinders[0].propertyBindings[0].type)
|
||||||
.toEqual(PropertyBindingType.ATTRIBUTE);
|
.toEqual(PropertyBindingType.ATTRIBUTE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect class names', () => {
|
it('should detect class names', () => {
|
||||||
builder.bindElement(el('<div/>')).bindProperty('class.someName', emptyExpr());
|
builder.bindElement(el('<div/>')).bindProperty('class.someName', emptyExpr());
|
||||||
var pv = builder.build(new DomElementSchemaRegistry());
|
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].type).toEqual(PropertyBindingType.CLASS);
|
expect(pv.elementBinders[0].propertyBindings[0].type).toEqual(PropertyBindingType.CLASS);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect style names', () => {
|
it('should detect style names', () => {
|
||||||
builder.bindElement(el('<div/>')).bindProperty('style.someName', emptyExpr());
|
builder.bindElement(el('<div/>')).bindProperty('style.someName', emptyExpr());
|
||||||
var pv = builder.build(new DomElementSchemaRegistry());
|
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].type).toEqual(PropertyBindingType.STYLE);
|
expect(pv.elementBinders[0].propertyBindings[0].type).toEqual(PropertyBindingType.STYLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect style units', () => {
|
it('should detect style units', () => {
|
||||||
builder.bindElement(el('<div/>')).bindProperty('style.someName.someUnit', emptyExpr());
|
builder.bindElement(el('<div/>')).bindProperty('style.someName.someUnit', emptyExpr());
|
||||||
var pv = builder.build(new DomElementSchemaRegistry());
|
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
|
||||||
expect(pv.elementBinders[0].propertyBindings[0].unit).toEqual('someUnit');
|
expect(pv.elementBinders[0].propertyBindings[0].unit).toEqual('someUnit');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -28,7 +28,8 @@ import {
|
||||||
} from 'angular2/src/render/api';
|
} from 'angular2/src/render/api';
|
||||||
|
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {cloneAndQueryProtoView, ReferenceCloneableTemplate} from 'angular2/src/render/dom/util';
|
import {cloneAndQueryProtoView} from 'angular2/src/render/dom/util';
|
||||||
|
import {TemplateCloner} from 'angular2/src/render/dom/template_cloner';
|
||||||
import {resolveInternalDomProtoView, DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
import {resolveInternalDomProtoView, DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||||
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
|
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
|
||||||
|
@ -255,13 +256,14 @@ export function main() {
|
||||||
|
|
||||||
describe('host attributes', () => {
|
describe('host attributes', () => {
|
||||||
it('should set host attributes while merging',
|
it('should set host attributes while merging',
|
||||||
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
inject([AsyncTestCompleter, DomTestbed, TemplateCloner], (async, tb: DomTestbed,
|
||||||
|
cloner: TemplateCloner) => {
|
||||||
tb.compiler.compileHost(rootDirective('root'))
|
tb.compiler.compileHost(rootDirective('root'))
|
||||||
.then((rootProtoViewDto) => {
|
.then((rootProtoViewDto) => {
|
||||||
var builder = new ProtoViewBuilder(DOM.createTemplate(''), ViewType.COMPONENT,
|
var builder = new ProtoViewBuilder(DOM.createTemplate(''), ViewType.COMPONENT,
|
||||||
ViewEncapsulation.NONE);
|
ViewEncapsulation.NONE);
|
||||||
builder.setHostAttribute('a', 'b');
|
builder.setHostAttribute('a', 'b');
|
||||||
var componentProtoViewDto = builder.build(new ElementSchemaRegistry());
|
var componentProtoViewDto = builder.build(new ElementSchemaRegistry(), cloner);
|
||||||
tb.merge([rootProtoViewDto, componentProtoViewDto])
|
tb.merge([rootProtoViewDto, componentProtoViewDto])
|
||||||
.then(mergeMappings => {
|
.then(mergeMappings => {
|
||||||
var domPv = resolveInternalDomProtoView(mergeMappings.mergedProtoViewRef);
|
var domPv = resolveInternalDomProtoView(mergeMappings.mergedProtoViewRef);
|
||||||
|
@ -278,20 +280,21 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function templateRoot(pv: DomProtoView) {
|
function templateRoot(pv: DomProtoView) {
|
||||||
return (<ReferenceCloneableTemplate>pv.cloneableTemplate).templateRoot;
|
return <Element>pv.cloneableTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
function runAndAssert(hostElementName: string, componentTemplates: string[],
|
function runAndAssert(hostElementName: string, componentTemplates: string[],
|
||||||
expectedFragments: string[]) {
|
expectedFragments: string[]) {
|
||||||
var useNativeEncapsulation = hostElementName.startsWith('native-');
|
var useNativeEncapsulation = hostElementName.startsWith('native-');
|
||||||
var rootComp = rootDirective(hostElementName);
|
var rootComp = rootDirective(hostElementName);
|
||||||
return inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
return inject([AsyncTestCompleter, DomTestbed, TemplateCloner], (async, tb: DomTestbed,
|
||||||
|
cloner: TemplateCloner) => {
|
||||||
tb.compileAndMerge(rootComp, componentTemplates.map(template => componentView(
|
tb.compileAndMerge(rootComp, componentTemplates.map(template => componentView(
|
||||||
template, useNativeEncapsulation ?
|
template, useNativeEncapsulation ?
|
||||||
ViewEncapsulation.NATIVE :
|
ViewEncapsulation.NATIVE :
|
||||||
ViewEncapsulation.NONE)))
|
ViewEncapsulation.NONE)))
|
||||||
.then((mergeMappings) => {
|
.then((mergeMappings) => {
|
||||||
expect(stringify(mergeMappings)).toEqual(expectedFragments);
|
expect(stringify(cloner, mergeMappings)).toEqual(expectedFragments);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -312,9 +315,10 @@ function componentView(template: string,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function stringify(protoViewMergeMapping: RenderProtoViewMergeMapping): string[] {
|
function stringify(cloner: TemplateCloner, protoViewMergeMapping: RenderProtoViewMergeMapping):
|
||||||
|
string[] {
|
||||||
var testView = cloneAndQueryProtoView(
|
var testView = cloneAndQueryProtoView(
|
||||||
resolveInternalDomProtoView(protoViewMergeMapping.mergedProtoViewRef), false);
|
cloner, resolveInternalDomProtoView(protoViewMergeMapping.mergedProtoViewRef), false);
|
||||||
for (var i = 0; i < protoViewMergeMapping.mappedElementIndices.length; i++) {
|
for (var i = 0; i < protoViewMergeMapping.mappedElementIndices.length; i++) {
|
||||||
var renderElIdx = protoViewMergeMapping.mappedElementIndices[i];
|
var renderElIdx = protoViewMergeMapping.mappedElementIndices[i];
|
||||||
if (isPresent(renderElIdx)) {
|
if (isPresent(renderElIdx)) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||||
import {DomElementBinder} from 'angular2/src/render/dom/view/element_binder';
|
import {DomElementBinder} from 'angular2/src/render/dom/view/element_binder';
|
||||||
import {DomView} from 'angular2/src/render/dom/view/view';
|
import {DomView} from 'angular2/src/render/dom/view/view';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
import {TemplateCloner} from 'angular2/src/render/dom/template_cloner';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('DomView', () => {
|
describe('DomView', () => {
|
||||||
|
@ -30,7 +31,8 @@ export function main() {
|
||||||
binders = [];
|
binders = [];
|
||||||
}
|
}
|
||||||
var rootEl = DOM.createTemplate('<div></div>');
|
var rootEl = DOM.createTemplate('<div></div>');
|
||||||
return DomProtoView.create(null, <Element>rootEl, null, [1], [], binders, null);
|
return DomProtoView.create(new TemplateCloner(-1), null, <Element>rootEl, null, [1], [],
|
||||||
|
binders, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createElementBinder() { return new DomElementBinder({textNodeIndices: []}); }
|
function createElementBinder() { return new DomElementBinder({textNodeIndices: []}); }
|
||||||
|
|
|
@ -39,7 +39,6 @@ import {someComponent} from '../../render/dom/dom_renderer_integration_spec';
|
||||||
import {WebWorkerMain} from 'angular2/src/web-workers/ui/impl';
|
import {WebWorkerMain} from 'angular2/src/web-workers/ui/impl';
|
||||||
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||||
import {MockMessageBus, MockMessageBusSink, MockMessageBusSource} from './worker_test_util';
|
import {MockMessageBus, MockMessageBusSink, MockMessageBusSource} from './worker_test_util';
|
||||||
import {ReferenceCloneableTemplate} from 'angular2/src/render/dom/util';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
function createBroker(workerSerializer: Serializer, uiSerializer: Serializer, tb: DomTestbed,
|
function createBroker(workerSerializer: Serializer, uiSerializer: Serializer, tb: DomTestbed,
|
||||||
|
@ -299,7 +298,7 @@ class WorkerTestRootView extends TestRootView {
|
||||||
}
|
}
|
||||||
|
|
||||||
function templateRoot(pv: DomProtoView) {
|
function templateRoot(pv: DomProtoView) {
|
||||||
return (<ReferenceCloneableTemplate>pv.cloneableTemplate).templateRoot;
|
return <Element>pv.cloneableTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSerializer(protoViewRefStore: RenderProtoViewRefStore,
|
function createSerializer(protoViewRefStore: RenderProtoViewRefStore,
|
||||||
|
|
|
@ -24,7 +24,12 @@ import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabil
|
||||||
import {getIntParameter, bindAction} from 'angular2/src/test_lib/benchmark_util';
|
import {getIntParameter, bindAction} from 'angular2/src/test_lib/benchmark_util';
|
||||||
|
|
||||||
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||||
import {ViewLoader, DefaultDomCompiler, SharedStylesHost} from 'angular2/src/render/render';
|
import {
|
||||||
|
ViewLoader,
|
||||||
|
DefaultDomCompiler,
|
||||||
|
SharedStylesHost,
|
||||||
|
TemplateCloner
|
||||||
|
} from 'angular2/src/render/render';
|
||||||
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
|
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
@ -38,9 +43,9 @@ export function main() {
|
||||||
count, [BenchmarkComponentNoBindings, BenchmarkComponentWithBindings]);
|
count, [BenchmarkComponentNoBindings, BenchmarkComponentWithBindings]);
|
||||||
var urlResolver = new UrlResolver();
|
var urlResolver = new UrlResolver();
|
||||||
var appRootUrl = new AppRootUrl("");
|
var appRootUrl = new AppRootUrl("");
|
||||||
var renderCompiler =
|
var renderCompiler = new DefaultDomCompiler(
|
||||||
new DefaultDomCompiler(new DomElementSchemaRegistry(), new Parser(new Lexer()),
|
new DomElementSchemaRegistry(), new TemplateCloner(-1), new Parser(new Lexer()),
|
||||||
new ViewLoader(null, null, null), new SharedStylesHost(), 'a');
|
new ViewLoader(null, null, null), new SharedStylesHost(), 'a');
|
||||||
var compiler =
|
var compiler =
|
||||||
new Compiler(reader, cache, viewResolver, new ComponentUrlMapper(), urlResolver,
|
new Compiler(reader, cache, viewResolver, new ComponentUrlMapper(), urlResolver,
|
||||||
renderCompiler, new ProtoViewFactory(new DynamicChangeDetection()), appRootUrl);
|
renderCompiler, new ProtoViewFactory(new DynamicChangeDetection()), appRootUrl);
|
||||||
|
|
Loading…
Reference in New Issue