refactor(compiler): speed up proto view merging
- Don't create intermediate merge results - Only merge embedded ProtoViews that contain `<ng-content>` tags Closes #3150 Closes #3177
This commit is contained in:
parent
de18da2a0d
commit
078475a082
|
@ -24,6 +24,7 @@ import {ComponentUrlMapper} from './component_url_mapper';
|
|||
import {ProtoViewFactory} from './proto_view_factory';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {AppRootUrl} from 'angular2/src/services/app_root_url';
|
||||
import {ElementBinder} from './element_binder';
|
||||
|
||||
import * as renderApi from 'angular2/src/render/api';
|
||||
|
||||
|
@ -90,7 +91,7 @@ export class Compiler {
|
|||
private _appUrl: string;
|
||||
private _render: renderApi.RenderCompiler;
|
||||
private _protoViewFactory: ProtoViewFactory;
|
||||
private _unmergedCyclicEmbeddedProtoViews: RecursiveEmbeddedProtoView[] = [];
|
||||
private _protoViewsToBeMerged: AppProtoView[] = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
|
@ -146,8 +147,37 @@ export class Compiler {
|
|||
return this._compileNestedProtoViews(hostRenderPv, protoView, componentType);
|
||||
});
|
||||
}
|
||||
return hostPvPromise.then(
|
||||
hostAppProtoView => this._mergeCyclicEmbeddedProtoViews().then(_ => hostAppProtoView.ref));
|
||||
return hostPvPromise.then(hostAppProtoView =>
|
||||
this._mergeUnmergedProtoViews().then(_ => hostAppProtoView.ref));
|
||||
}
|
||||
|
||||
private _mergeUnmergedProtoViews(): Promise<any> {
|
||||
var protoViewsToBeMerged = this._protoViewsToBeMerged;
|
||||
this._protoViewsToBeMerged = [];
|
||||
return PromiseWrapper.all(protoViewsToBeMerged.map((appProtoView) => {
|
||||
return this._render.mergeProtoViewsRecursively(
|
||||
this._collectMergeRenderProtoViews(appProtoView))
|
||||
.then((mergeResult: renderApi.RenderProtoViewMergeMapping) => {
|
||||
appProtoView.mergeMapping = new AppProtoViewMergeMapping(mergeResult);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
private _collectMergeRenderProtoViews(
|
||||
appProtoView: AppProtoView): List<renderApi.RenderProtoViewRef | List<any>> {
|
||||
var result = [appProtoView.render];
|
||||
for (var i = 0; i < appProtoView.elementBinders.length; i++) {
|
||||
var binder = appProtoView.elementBinders[i];
|
||||
if (isPresent(binder.nestedProtoView)) {
|
||||
if (binder.hasStaticComponent() ||
|
||||
(binder.hasEmbeddedProtoView() && binder.nestedProtoView.isEmbeddedFragment)) {
|
||||
result.push(this._collectMergeRenderProtoViews(binder.nestedProtoView));
|
||||
} else {
|
||||
result.push(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _compile(componentBinding: DirectiveBinding): Promise<AppProtoView>| AppProtoView {
|
||||
|
@ -207,7 +237,7 @@ export class Compiler {
|
|||
appProtoView: AppProtoView,
|
||||
componentType: Type): Promise<AppProtoView> {
|
||||
var nestedPVPromises = [];
|
||||
this._loopComponentElementBinders(appProtoView, (parentPv, elementBinder) => {
|
||||
this._loopComponentElementBinders(appProtoView, (parentPv, elementBinder: ElementBinder) => {
|
||||
var nestedComponent = elementBinder.componentDirective;
|
||||
var elementBinderDone =
|
||||
(nestedPv: AppProtoView) => { elementBinder.nestedProtoView = nestedPv; };
|
||||
|
@ -220,31 +250,40 @@ export class Compiler {
|
|||
});
|
||||
return PromiseWrapper.all(nestedPVPromises)
|
||||
.then((_) => {
|
||||
var appProtoViewsToMergeInto = [];
|
||||
var mergeRenderProtoViews = this._collectMergeRenderProtoViewsRecurse(
|
||||
renderProtoView, appProtoView, appProtoViewsToMergeInto);
|
||||
if (isBlank(mergeRenderProtoViews)) {
|
||||
throw new BaseException(`Unconditional component cycle in ${stringify(componentType)}`);
|
||||
}
|
||||
return this._mergeProtoViews(appProtoViewsToMergeInto, mergeRenderProtoViews);
|
||||
this._collectMergableProtoViews(appProtoView, componentType);
|
||||
return appProtoView;
|
||||
});
|
||||
}
|
||||
|
||||
private _mergeProtoViews(
|
||||
appProtoViewsToMergeInto: AppProtoView[],
|
||||
mergeRenderProtoViews:
|
||||
List<renderApi.RenderProtoViewRef | List<any>>): Promise<AppProtoView> {
|
||||
return this._render.mergeProtoViewsRecursively(mergeRenderProtoViews)
|
||||
.then((mergeResults: List<renderApi.RenderProtoViewMergeMapping>) => {
|
||||
// Note: We don't need to check for nulls here as we filtered them out before!
|
||||
// (in RenderCompiler.mergeProtoViewsRecursively and
|
||||
// _collectMergeRenderProtoViewsRecurse).
|
||||
for (var i = 0; i < mergeResults.length; i++) {
|
||||
appProtoViewsToMergeInto[i].mergeMapping =
|
||||
new AppProtoViewMergeMapping(mergeResults[i]);
|
||||
private _collectMergableProtoViews(appProtoView: AppProtoView, componentType: Type) {
|
||||
var isRecursive = false;
|
||||
for (var i = 0; i < appProtoView.elementBinders.length; i++) {
|
||||
var binder = appProtoView.elementBinders[i];
|
||||
if (binder.hasStaticComponent()) {
|
||||
if (isBlank(binder.nestedProtoView.isRecursive)) {
|
||||
// cycle via a component. We are in the tail recursion,
|
||||
// so all components should have their isRecursive flag set already.
|
||||
isRecursive = true;
|
||||
break;
|
||||
}
|
||||
return appProtoViewsToMergeInto[0];
|
||||
});
|
||||
} else if (binder.hasEmbeddedProtoView()) {
|
||||
this._collectMergableProtoViews(binder.nestedProtoView, componentType);
|
||||
}
|
||||
}
|
||||
if (isRecursive) {
|
||||
if (appProtoView.isEmbeddedFragment) {
|
||||
throw new BaseException(
|
||||
`<ng-content> is used within the recursive path of ${stringify(componentType)}`);
|
||||
}
|
||||
if (appProtoView.type === renderApi.ViewType.COMPONENT) {
|
||||
throw new BaseException(`Unconditional component cycle in ${stringify(componentType)}`);
|
||||
}
|
||||
}
|
||||
if (appProtoView.type === renderApi.ViewType.EMBEDDED ||
|
||||
appProtoView.type === renderApi.ViewType.HOST) {
|
||||
this._protoViewsToBeMerged.push(appProtoView);
|
||||
}
|
||||
appProtoView.isRecursive = isRecursive;
|
||||
}
|
||||
|
||||
private _loopComponentElementBinders(appProtoView: AppProtoView, callback: Function) {
|
||||
|
@ -257,48 +296,6 @@ export class Compiler {
|
|||
});
|
||||
}
|
||||
|
||||
private _collectMergeRenderProtoViewsRecurse(
|
||||
renderProtoView: renderApi.ProtoViewDto, appProtoView: AppProtoView,
|
||||
targetAppProtoViews: AppProtoView[]): List<renderApi.RenderProtoViewRef | List<any>> {
|
||||
targetAppProtoViews.push(appProtoView);
|
||||
var result = [renderProtoView.render];
|
||||
for (var i = 0; i < appProtoView.elementBinders.length; i++) {
|
||||
var binder = appProtoView.elementBinders[i];
|
||||
if (binder.hasStaticComponent()) {
|
||||
if (isBlank(binder.nestedProtoView.mergeMapping)) {
|
||||
// cycle via an embedded ProtoView. store the AppProtoView and ProtoViewDto for later.
|
||||
this._unmergedCyclicEmbeddedProtoViews.push(
|
||||
new RecursiveEmbeddedProtoView(appProtoView, renderProtoView));
|
||||
return null;
|
||||
}
|
||||
result.push(binder.nestedProtoView.mergeMapping.renderProtoViewRef);
|
||||
} else if (binder.hasEmbeddedProtoView()) {
|
||||
result.push(this._collectMergeRenderProtoViewsRecurse(
|
||||
renderProtoView.elementBinders[i].nestedProtoView, binder.nestedProtoView,
|
||||
targetAppProtoViews));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _mergeCyclicEmbeddedProtoViews() {
|
||||
var pvs = this._unmergedCyclicEmbeddedProtoViews;
|
||||
this._unmergedCyclicEmbeddedProtoViews = [];
|
||||
var promises = pvs.map(entry => {
|
||||
var appProtoView = entry.appProtoView;
|
||||
var mergeRenderProtoViews = [entry.renderProtoView.render];
|
||||
appProtoView.elementBinders.forEach((binder) => {
|
||||
if (binder.hasStaticComponent()) {
|
||||
mergeRenderProtoViews.push(binder.nestedProtoView.mergeMapping.renderProtoViewRef);
|
||||
} else if (binder.hasEmbeddedProtoView()) {
|
||||
mergeRenderProtoViews.push(null);
|
||||
}
|
||||
});
|
||||
return this._mergeProtoViews([appProtoView], mergeRenderProtoViews);
|
||||
});
|
||||
return PromiseWrapper.all(promises);
|
||||
}
|
||||
|
||||
private _buildRenderTemplate(component, view, directives): renderApi.ViewDefinition {
|
||||
var componentUrl =
|
||||
this._urlResolver.resolve(this._appUrl, this._componentUrlMapper.getUrl(component));
|
||||
|
@ -356,7 +353,3 @@ export class Compiler {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RecursiveEmbeddedProtoView {
|
||||
constructor(public appProtoView: AppProtoView, public renderProtoView: renderApi.ProtoViewDto) {}
|
||||
}
|
||||
|
|
|
@ -254,8 +254,11 @@ function _createAppProtoView(
|
|||
renderProtoView: renderApi.ProtoViewDto, protoChangeDetector: ProtoChangeDetector,
|
||||
variableBindings: Map<string, string>, allDirectives: List<DirectiveBinding>): AppProtoView {
|
||||
var elementBinders = renderProtoView.elementBinders;
|
||||
var protoView = new AppProtoView(renderProtoView.type, protoChangeDetector, variableBindings,
|
||||
createVariableLocations(elementBinders),
|
||||
// Embedded ProtoViews that contain `<ng-content>` will be merged into their parents and use
|
||||
// a RenderFragmentRef. I.e. renderProtoView.transitiveNgContentCount > 0.
|
||||
var protoView = new AppProtoView(
|
||||
renderProtoView.type, renderProtoView.transitiveNgContentCount > 0, renderProtoView.render,
|
||||
protoChangeDetector, variableBindings, createVariableLocations(elementBinders),
|
||||
renderProtoView.textBindings.length);
|
||||
_createElementBinders(protoView, elementBinders, allDirectives);
|
||||
_bindDirectiveEvents(protoView, elementBinders);
|
||||
|
|
|
@ -32,6 +32,7 @@ export class AppProtoViewMergeMapping {
|
|||
renderTextIndices: number[];
|
||||
nestedViewIndicesByElementIndex: number[];
|
||||
hostElementIndicesByViewIndex: number[];
|
||||
nestedViewCountByViewIndex: number[];
|
||||
constructor(renderProtoViewMergeMapping: renderApi.RenderProtoViewMergeMapping) {
|
||||
this.renderProtoViewRef = renderProtoViewMergeMapping.mergedProtoViewRef;
|
||||
this.renderFragmentCount = renderProtoViewMergeMapping.fragmentCount;
|
||||
|
@ -42,11 +43,8 @@ export class AppProtoViewMergeMapping {
|
|||
this.hostElementIndicesByViewIndex = renderProtoViewMergeMapping.hostElementIndicesByViewIndex;
|
||||
this.nestedViewIndicesByElementIndex =
|
||||
inverseIndexMapping(this.hostElementIndicesByViewIndex, this.renderElementIndices.length);
|
||||
this.nestedViewCountByViewIndex = renderProtoViewMergeMapping.nestedViewCountByViewIndex;
|
||||
}
|
||||
|
||||
get viewCount() { return this.hostElementIndicesByViewIndex.length; }
|
||||
|
||||
get elementCount() { return this.renderElementIndices.length; }
|
||||
}
|
||||
|
||||
function inverseIndexMapping(input: number[], resultLength: number): number[] {
|
||||
|
@ -215,7 +213,7 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
|||
dispatchRenderEvent(renderElementIndex: number, eventName: string,
|
||||
locals: Map<string, any>): boolean {
|
||||
var elementRef =
|
||||
this.elementRefs[this.proto.mergeMapping.renderInverseElementIndices[renderElementIndex]];
|
||||
this.elementRefs[this.mainMergeMapping.renderInverseElementIndices[renderElementIndex]];
|
||||
var view = internalView(elementRef.parentView);
|
||||
return view.dispatchEvent(elementRef.boundElementIndex, eventName, locals);
|
||||
}
|
||||
|
@ -258,7 +256,11 @@ export class AppProtoView {
|
|||
mergeMapping: AppProtoViewMergeMapping;
|
||||
ref: ProtoViewRef;
|
||||
|
||||
constructor(public type: renderApi.ViewType, public protoChangeDetector: ProtoChangeDetector,
|
||||
isRecursive: boolean = null;
|
||||
|
||||
constructor(public type: renderApi.ViewType, public isEmbeddedFragment: boolean,
|
||||
public render: renderApi.RenderProtoViewRef,
|
||||
public protoChangeDetector: ProtoChangeDetector,
|
||||
public variableBindings: Map<string, string>,
|
||||
public variableLocations: Map<string, number>, public textBindingCount: number) {
|
||||
this.ref = new ProtoViewRef(this);
|
||||
|
|
|
@ -339,12 +339,19 @@ export class AppViewManager {
|
|||
this._utils.dehydrateView(view);
|
||||
}
|
||||
var viewContainers = view.viewContainers;
|
||||
for (var i = view.elementOffset, ii = view.elementOffset + view.proto.mergeMapping.elementCount;
|
||||
i < ii; i++) {
|
||||
var vc = viewContainers[i];
|
||||
var startViewOffset = view.viewOffset;
|
||||
var endViewOffset =
|
||||
view.viewOffset + view.mainMergeMapping.nestedViewCountByViewIndex[view.viewOffset];
|
||||
var elementOffset = view.elementOffset;
|
||||
for (var viewIdx = startViewOffset; viewIdx <= endViewOffset; viewIdx++) {
|
||||
var currView = view.views[viewIdx];
|
||||
for (var binderIdx = 0; binderIdx < currView.proto.elementBinders.length;
|
||||
binderIdx++, elementOffset++) {
|
||||
var vc = viewContainers[elementOffset];
|
||||
if (isPresent(vc)) {
|
||||
for (var j = vc.views.length - 1; j >= 0; j--) {
|
||||
this._destroyViewInContainer(view, i, j);
|
||||
this._destroyViewInContainer(currView, elementOffset, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@ export class AppViewManagerUtils {
|
|||
var renderFragments = renderViewWithFragments.fragmentRefs;
|
||||
var renderView = renderViewWithFragments.viewRef;
|
||||
|
||||
var elementCount = mergedParentViewProto.mergeMapping.elementCount;
|
||||
var viewCount = mergedParentViewProto.mergeMapping.viewCount;
|
||||
var elementCount = mergedParentViewProto.mergeMapping.renderElementIndices.length;
|
||||
var viewCount = mergedParentViewProto.mergeMapping.nestedViewCountByViewIndex[0] + 1;
|
||||
var elementRefs: ElementRef[] = ListWrapper.createFixedSize(elementCount);
|
||||
var viewContainers = ListWrapper.createFixedSize(elementCount);
|
||||
var preBuiltObjects: eli.PreBuiltObjects[] = ListWrapper.createFixedSize(elementCount);
|
||||
|
@ -175,13 +175,13 @@ export class AppViewManagerUtils {
|
|||
_hydrateView(initView: viewModule.AppView, imperativelyCreatedInjector: Injector,
|
||||
hostElementInjector: eli.ElementInjector, context: Object, parentLocals: Locals) {
|
||||
var viewIdx = initView.viewOffset;
|
||||
var endViewOffset = viewIdx + initView.proto.mergeMapping.viewCount;
|
||||
while (viewIdx < endViewOffset) {
|
||||
var endViewOffset = viewIdx + initView.mainMergeMapping.nestedViewCountByViewIndex[viewIdx];
|
||||
while (viewIdx <= endViewOffset) {
|
||||
var currView = initView.views[viewIdx];
|
||||
var currProtoView = currView.proto;
|
||||
if (currView !== initView && currView.proto.type === ViewType.EMBEDDED) {
|
||||
// Don't hydrate components of embedded fragment views.
|
||||
viewIdx += currProtoView.mergeMapping.viewCount;
|
||||
viewIdx += initView.mainMergeMapping.nestedViewCountByViewIndex[viewIdx] + 1;
|
||||
} else {
|
||||
if (currView !== initView) {
|
||||
// hydrate a nested component view
|
||||
|
@ -263,9 +263,9 @@ export class AppViewManagerUtils {
|
|||
}
|
||||
|
||||
dehydrateView(initView: viewModule.AppView) {
|
||||
for (var viewIdx = initView.viewOffset,
|
||||
endViewOffset = viewIdx + initView.proto.mergeMapping.viewCount;
|
||||
viewIdx < endViewOffset; viewIdx++) {
|
||||
var endViewOffset = initView.viewOffset +
|
||||
initView.mainMergeMapping.nestedViewCountByViewIndex[initView.viewOffset];
|
||||
for (var viewIdx = initView.viewOffset; viewIdx <= endViewOffset; viewIdx++) {
|
||||
var currView = initView.views[viewIdx];
|
||||
if (currView.hydrated()) {
|
||||
if (isPresent(currView.locals)) {
|
||||
|
|
|
@ -114,19 +114,23 @@ export class ProtoViewDto {
|
|||
variableBindings: Map<string, string>;
|
||||
type: ViewType;
|
||||
textBindings: List<ASTWithSource>;
|
||||
transitiveNgContentCount: number;
|
||||
|
||||
constructor({render, elementBinders, variableBindings, type, textBindings}: {
|
||||
constructor({render, elementBinders, variableBindings, type, textBindings,
|
||||
transitiveNgContentCount}: {
|
||||
render?: RenderProtoViewRef,
|
||||
elementBinders?: List<ElementBinder>,
|
||||
variableBindings?: Map<string, string>,
|
||||
type?: ViewType,
|
||||
textBindings?: List<ASTWithSource>
|
||||
textBindings?: List<ASTWithSource>,
|
||||
transitiveNgContentCount?: number
|
||||
}) {
|
||||
this.render = render;
|
||||
this.elementBinders = elementBinders;
|
||||
this.variableBindings = variableBindings;
|
||||
this.type = type;
|
||||
this.textBindings = textBindings;
|
||||
this.transitiveNgContentCount = transitiveNgContentCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -308,7 +312,9 @@ export class RenderProtoViewMergeMapping {
|
|||
// indices for one ProtoView in a consecuitve block.
|
||||
public mappedTextIndices: number[],
|
||||
// Mapping from view index to app element index
|
||||
public hostElementIndicesByViewIndex: number[]) {}
|
||||
public hostElementIndicesByViewIndex: number[],
|
||||
// Number of contained views by view index
|
||||
public nestedViewCountByViewIndex: number[]) {}
|
||||
}
|
||||
|
||||
export class RenderCompiler {
|
||||
|
@ -331,10 +337,10 @@ export class RenderCompiler {
|
|||
* If the array contains other arrays, they will be merged before processing the parent array.
|
||||
* The array must contain an entry for every component and embedded ProtoView of the first entry.
|
||||
* @param protoViewRefs List of ProtoViewRefs or nested
|
||||
* @return the merge result for every input array in depth first order.
|
||||
* @return the merge result
|
||||
*/
|
||||
mergeProtoViewsRecursively(
|
||||
protoViewRefs: List<RenderProtoViewRef | List<any>>): Promise<RenderProtoViewMergeMapping[]> {
|
||||
protoViewRefs: List<RenderProtoViewRef | List<any>>): Promise<RenderProtoViewMergeMapping> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,8 +54,7 @@ export class DomCompiler extends RenderCompiler {
|
|||
}
|
||||
|
||||
mergeProtoViewsRecursively(
|
||||
protoViewRefs:
|
||||
List<RenderProtoViewRef | List<any>>): Promise<List<RenderProtoViewMergeMapping>> {
|
||||
protoViewRefs: List<RenderProtoViewRef | List<any>>): Promise<RenderProtoViewMergeMapping> {
|
||||
return PromiseWrapper.resolve(pvm.mergeProtoViewsRecursively(protoViewRefs));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {CompileStep} from '../compiler/compile_step';
|
||||
import {CompileElement} from '../compiler/compile_element';
|
||||
import {CompileControl} from '../compiler/compile_control';
|
||||
import {ViewDefinition} from '../../api';
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {NG_CONTENT_ELEMENT_NAME, isElementWithTag} from '../util';
|
||||
|
||||
export class ShadowDomCompileStep implements CompileStep {
|
||||
constructor(public _shadowDomStrategy: ShadowDomStrategy, public _view: ViewDefinition) {}
|
||||
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
var tagName = DOM.tagName(current.element).toUpperCase();
|
||||
if (tagName == 'STYLE') {
|
||||
if (isElementWithTag(current.element, NG_CONTENT_ELEMENT_NAME)) {
|
||||
current.inheritedProtoView.bindNgContent();
|
||||
} else if (isElementWithTag(current.element, 'style')) {
|
||||
this._processStyleElement(current, control);
|
||||
} else {
|
||||
var componentId = current.isBound() ? current.inheritedElementBinder.componentId : null;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {DomElementBinder} from './element_binder';
|
||||
|
@ -16,39 +15,21 @@ export class DomProtoViewRef extends RenderProtoViewRef {
|
|||
|
||||
export class DomProtoView {
|
||||
static create(type: ViewType, rootElement: Element, fragmentsRootNodeCount: number[],
|
||||
rootTextNodeIndices: number[], elementBinders: List<DomElementBinder>,
|
||||
mappedElementIndices: number[], mappedTextIndices: number[],
|
||||
hostElementIndicesByViewIndex: number[]): DomProtoView {
|
||||
rootTextNodeIndices: number[],
|
||||
elementBinders: List<DomElementBinder>): DomProtoView {
|
||||
var boundTextNodeCount = rootTextNodeIndices.length;
|
||||
for (var i = 0; i < elementBinders.length; i++) {
|
||||
boundTextNodeCount += elementBinders[i].textNodeIndices.length;
|
||||
}
|
||||
if (isBlank(mappedElementIndices)) {
|
||||
mappedElementIndices = ListWrapper.createFixedSize(elementBinders.length);
|
||||
for (var i = 0; i < mappedElementIndices.length; i++) {
|
||||
mappedElementIndices[i] = i;
|
||||
}
|
||||
}
|
||||
if (isBlank(mappedTextIndices)) {
|
||||
mappedTextIndices = ListWrapper.createFixedSize(boundTextNodeCount);
|
||||
for (var i = 0; i < mappedTextIndices.length; i++) {
|
||||
mappedTextIndices[i] = i;
|
||||
}
|
||||
}
|
||||
if (isBlank(hostElementIndicesByViewIndex)) {
|
||||
hostElementIndicesByViewIndex = [null];
|
||||
}
|
||||
var isSingleElementFragment = fragmentsRootNodeCount.length === 1 &&
|
||||
fragmentsRootNodeCount[0] === 1 &&
|
||||
DOM.isElementNode(DOM.firstChild(DOM.content(rootElement)));
|
||||
return new DomProtoView(type, rootElement, elementBinders, rootTextNodeIndices,
|
||||
boundTextNodeCount, fragmentsRootNodeCount, isSingleElementFragment,
|
||||
mappedElementIndices, mappedTextIndices, hostElementIndicesByViewIndex);
|
||||
boundTextNodeCount, fragmentsRootNodeCount, isSingleElementFragment);
|
||||
}
|
||||
|
||||
constructor(public type: ViewType, public rootElement: Element,
|
||||
public elementBinders: List<DomElementBinder>, public rootTextNodeIndices: number[],
|
||||
public boundTextNodeCount: number, public fragmentsRootNodeCount: number[],
|
||||
public isSingleElementFragment: boolean, public mappedElementIndices: number[],
|
||||
public mappedTextIndices: number[], public hostElementIndicesByViewIndex: number[]) {}
|
||||
public isSingleElementFragment: boolean) {}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ export class ProtoViewBuilder {
|
|||
variableBindings: Map<string, string> = new Map();
|
||||
elements: List<ElementBinderBuilder> = [];
|
||||
rootTextBindings: Map<Node, ASTWithSource> = new Map();
|
||||
ngContentCount: number = 0;
|
||||
|
||||
constructor(public rootElement, public type: api.ViewType,
|
||||
public useNativeShadowDom: boolean = false) {}
|
||||
|
@ -57,12 +58,15 @@ export class ProtoViewBuilder {
|
|||
this.rootTextBindings.set(textNode, expression);
|
||||
}
|
||||
|
||||
bindNgContent() { this.ngContentCount++; }
|
||||
|
||||
build(): api.ProtoViewDto {
|
||||
var domElementBinders = [];
|
||||
|
||||
var apiElementBinders = [];
|
||||
var textNodeExpressions = [];
|
||||
var rootTextNodeIndices = [];
|
||||
var transitiveNgContentCount = this.ngContentCount;
|
||||
queryBoundTextNodeIndices(DOM.content(this.rootElement), this.rootTextBindings,
|
||||
(node, nodeIndex, expression) => {
|
||||
textNodeExpressions.push(expression);
|
||||
|
@ -85,6 +89,9 @@ export class ProtoViewBuilder {
|
|||
});
|
||||
});
|
||||
var nestedProtoView = isPresent(ebb.nestedProtoView) ? ebb.nestedProtoView.build() : null;
|
||||
if (isPresent(nestedProtoView)) {
|
||||
transitiveNgContentCount += nestedProtoView.transitiveNgContentCount;
|
||||
}
|
||||
var parentIndex = isPresent(ebb.parent) ? ebb.parent.index : -1;
|
||||
var textNodeIndices = [];
|
||||
queryBoundTextNodeIndices(ebb.element, ebb.textBindings, (node, nodeIndex, expression) => {
|
||||
|
@ -116,12 +123,12 @@ export class ProtoViewBuilder {
|
|||
var rootNodeCount = DOM.childNodes(DOM.content(this.rootElement)).length;
|
||||
return new api.ProtoViewDto({
|
||||
render: new DomProtoViewRef(DomProtoView.create(this.type, this.rootElement, [rootNodeCount],
|
||||
rootTextNodeIndices, domElementBinders, null,
|
||||
null, null)),
|
||||
rootTextNodeIndices, domElementBinders)),
|
||||
type: this.type,
|
||||
elementBinders: apiElementBinders,
|
||||
variableBindings: this.variableBindings,
|
||||
textBindings: textNodeExpressions
|
||||
textBindings: textNodeExpressions,
|
||||
transitiveNgContentCount: transitiveNgContentCount
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {isPresent, isBlank, BaseException, isArray} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './proto_view';
|
||||
import {DomElementBinder} from './element_binder';
|
||||
|
@ -11,71 +12,25 @@ import {
|
|||
cloneAndQueryProtoView,
|
||||
queryBoundElements,
|
||||
queryBoundTextNodeIndices,
|
||||
NG_SHADOW_ROOT_ELEMENT_NAME,
|
||||
isElementWithTag
|
||||
NG_SHADOW_ROOT_ELEMENT_NAME
|
||||
} from '../util';
|
||||
import {CssSelector} from '../compiler/selector';
|
||||
|
||||
const NOT_MATCHABLE_SELECTOR = '_not-matchable_';
|
||||
|
||||
export function mergeProtoViewsRecursively(protoViewRefs: List<RenderProtoViewRef | List<any>>):
|
||||
RenderProtoViewMergeMapping[] {
|
||||
var target = [];
|
||||
_mergeProtoViewsRecursively(protoViewRefs, target);
|
||||
return target;
|
||||
}
|
||||
|
||||
function _mergeProtoViewsRecursively(protoViewRefs: List<RenderProtoViewRef | List<any>>,
|
||||
target: RenderProtoViewMergeMapping[]): RenderProtoViewRef {
|
||||
var targetIndex = target.length;
|
||||
target.push(null);
|
||||
|
||||
var resolvedProtoViewRefs = protoViewRefs.map((entry) => {
|
||||
if (isArray(entry)) {
|
||||
return _mergeProtoViewsRecursively(<List<any>>entry, target);
|
||||
} else {
|
||||
return entry;
|
||||
}
|
||||
});
|
||||
var mapping = mergeProtoViews(resolvedProtoViewRefs);
|
||||
target[targetIndex] = mapping;
|
||||
return mapping.mergedProtoViewRef;
|
||||
}
|
||||
|
||||
export function mergeProtoViews(protoViewRefs: RenderProtoViewRef[]): RenderProtoViewMergeMapping {
|
||||
var hostProtoView = resolveInternalDomProtoView(protoViewRefs[0]);
|
||||
|
||||
var mergeableProtoViews: DomProtoView[] = [];
|
||||
var hostElementIndices: number[] = [];
|
||||
|
||||
mergeableProtoViews.push(hostProtoView);
|
||||
var protoViewIdx = 1;
|
||||
for (var i = 0; i < hostProtoView.elementBinders.length; i++) {
|
||||
var binder = hostProtoView.elementBinders[i];
|
||||
if (binder.hasNestedProtoView) {
|
||||
var nestedProtoViewRef = protoViewRefs[protoViewIdx++];
|
||||
if (isPresent(nestedProtoViewRef)) {
|
||||
mergeableProtoViews.push(resolveInternalDomProtoView(nestedProtoViewRef));
|
||||
hostElementIndices.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _mergeProtoViews(mergeableProtoViews, hostElementIndices);
|
||||
}
|
||||
|
||||
|
||||
function _mergeProtoViews(mergeableProtoViews: DomProtoView[], hostElementIndices: number[]):
|
||||
RenderProtoViewMergeMapping {
|
||||
var clonedProtoViews: ClonedProtoView[] =
|
||||
mergeableProtoViews.map(domProtoView => cloneAndQueryProtoView(domProtoView, false));
|
||||
var hostProtoView: ClonedProtoView = clonedProtoViews[0];
|
||||
// clone
|
||||
var clonedProtoViews = [];
|
||||
var hostViewAndBinderIndices: number[][] = [];
|
||||
cloneProtoViews(protoViewRefs, clonedProtoViews, hostViewAndBinderIndices);
|
||||
var mainProtoView: ClonedProtoView = clonedProtoViews[0];
|
||||
|
||||
// modify the DOM
|
||||
mergeDom(clonedProtoViews, hostElementIndices);
|
||||
mergeEmbeddedPvsIntoComponentOrRootPv(clonedProtoViews, hostViewAndBinderIndices);
|
||||
var fragments = [];
|
||||
mergeComponents(clonedProtoViews, hostViewAndBinderIndices, fragments);
|
||||
|
||||
// create a new root element with the changed fragments and elements
|
||||
var rootElement = createRootElementFromFragments(hostProtoView.fragments);
|
||||
var fragmentsRootNodeCount = hostProtoView.fragments.map(fragment => fragment.length);
|
||||
var rootElement = createRootElementFromFragments(fragments);
|
||||
var fragmentsRootNodeCount = fragments.map(fragment => fragment.length);
|
||||
var rootNode = DOM.content(rootElement);
|
||||
|
||||
// read out the new element / text node / ElementBinder order
|
||||
|
@ -90,16 +45,45 @@ function _mergeProtoViews(mergeableProtoViews: DomProtoView[], hostElementIndice
|
|||
// create element / text index mappings
|
||||
var mappedElementIndices = calcMappedElementIndices(clonedProtoViews, mergedBoundElements);
|
||||
var mappedTextIndices = calcMappedTextIndices(clonedProtoViews, mergedBoundTextIndices);
|
||||
var hostElementIndicesByViewIndex =
|
||||
calcHostElementIndicesByViewIndex(clonedProtoViews, hostElementIndices);
|
||||
|
||||
// create result
|
||||
var mergedProtoView = DomProtoView.create(
|
||||
hostProtoView.original.type, rootElement, fragmentsRootNodeCount, rootTextNodeIndices,
|
||||
mergedElementBinders, mappedElementIndices, mappedTextIndices, hostElementIndicesByViewIndex);
|
||||
return new RenderProtoViewMergeMapping(new DomProtoViewRef(mergedProtoView),
|
||||
fragmentsRootNodeCount.length, mappedElementIndices,
|
||||
mappedTextIndices, hostElementIndicesByViewIndex);
|
||||
var hostElementIndicesByViewIndex =
|
||||
calcHostElementIndicesByViewIndex(clonedProtoViews, hostViewAndBinderIndices);
|
||||
var nestedViewCounts = calcNestedViewCounts(hostViewAndBinderIndices);
|
||||
var mergedProtoView =
|
||||
DomProtoView.create(mainProtoView.original.type, rootElement, fragmentsRootNodeCount,
|
||||
rootTextNodeIndices, mergedElementBinders);
|
||||
return new RenderProtoViewMergeMapping(
|
||||
new DomProtoViewRef(mergedProtoView), fragmentsRootNodeCount.length, mappedElementIndices,
|
||||
mappedTextIndices, hostElementIndicesByViewIndex, nestedViewCounts);
|
||||
}
|
||||
|
||||
function cloneProtoViews(protoViewRefs: List<RenderProtoViewRef | List<any>>,
|
||||
targetClonedProtoViews: ClonedProtoView[],
|
||||
targetHostViewAndBinderIndices: number[][]) {
|
||||
var hostProtoView = resolveInternalDomProtoView(protoViewRefs[0]);
|
||||
var hostPvIdx = targetClonedProtoViews.length;
|
||||
targetClonedProtoViews.push(cloneAndQueryProtoView(hostProtoView, false));
|
||||
if (targetHostViewAndBinderIndices.length === 0) {
|
||||
targetHostViewAndBinderIndices.push([null, null]);
|
||||
}
|
||||
var protoViewIdx = 1;
|
||||
for (var i = 0; i < hostProtoView.elementBinders.length; i++) {
|
||||
var binder = hostProtoView.elementBinders[i];
|
||||
if (binder.hasNestedProtoView) {
|
||||
var nestedEntry = protoViewRefs[protoViewIdx++];
|
||||
if (isPresent(nestedEntry)) {
|
||||
targetHostViewAndBinderIndices.push([hostPvIdx, i]);
|
||||
if (isArray(nestedEntry)) {
|
||||
cloneProtoViews(<any[]>nestedEntry, targetClonedProtoViews,
|
||||
targetHostViewAndBinderIndices);
|
||||
} else {
|
||||
targetClonedProtoViews.push(
|
||||
cloneAndQueryProtoView(resolveInternalDomProtoView(nestedEntry), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function indexBoundTextNodes(mergableProtoViews: ClonedProtoView[]): Map<Node, any> {
|
||||
|
@ -112,42 +96,56 @@ function indexBoundTextNodes(mergableProtoViews: ClonedProtoView[]): Map<Node, a
|
|||
return boundTextNodeMap;
|
||||
}
|
||||
|
||||
function mergeDom(clonedProtoViews: ClonedProtoView[], hostElementIndices: number[]) {
|
||||
var nestedProtoViewByHostElement: Map<Element, ClonedProtoView> =
|
||||
indexProtoViewsByHostElement(clonedProtoViews, hostElementIndices);
|
||||
function mergeEmbeddedPvsIntoComponentOrRootPv(clonedProtoViews: ClonedProtoView[],
|
||||
hostViewAndBinderIndices: number[][]) {
|
||||
var nearestHostComponentOrRootPvIndices =
|
||||
calcNearestHostComponentOrRootPvIndices(clonedProtoViews, hostViewAndBinderIndices);
|
||||
for (var viewIdx = 1; viewIdx < clonedProtoViews.length; viewIdx++) {
|
||||
var clonedProtoView = clonedProtoViews[viewIdx];
|
||||
if (clonedProtoView.original.type === ViewType.EMBEDDED) {
|
||||
var hostComponentIdx = nearestHostComponentOrRootPvIndices[viewIdx];
|
||||
var hostPv = clonedProtoViews[hostComponentIdx];
|
||||
clonedProtoView.fragments.forEach((fragment) => hostPv.fragments.push(fragment));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var hostProtoView = clonedProtoViews[0];
|
||||
var mergableProtoViewIdx = 1;
|
||||
hostElementIndices.forEach((boundElementIndex) => {
|
||||
var binder = hostProtoView.original.elementBinders[boundElementIndex];
|
||||
if (binder.hasNestedProtoView) {
|
||||
var mergableNestedProtoView: ClonedProtoView = clonedProtoViews[mergableProtoViewIdx++];
|
||||
if (mergableNestedProtoView.original.type === ViewType.COMPONENT) {
|
||||
mergeComponentDom(hostProtoView, boundElementIndex, mergableNestedProtoView,
|
||||
nestedProtoViewByHostElement);
|
||||
function calcNearestHostComponentOrRootPvIndices(clonedProtoViews: ClonedProtoView[],
|
||||
hostViewAndBinderIndices: number[][]): number[] {
|
||||
var nearestHostComponentOrRootPvIndices = ListWrapper.createFixedSize(clonedProtoViews.length);
|
||||
nearestHostComponentOrRootPvIndices[0] = null;
|
||||
for (var viewIdx = 1; viewIdx < hostViewAndBinderIndices.length; viewIdx++) {
|
||||
var hostViewIdx = hostViewAndBinderIndices[viewIdx][0];
|
||||
var hostProtoView = clonedProtoViews[hostViewIdx];
|
||||
if (hostViewIdx === 0 || hostProtoView.original.type === ViewType.COMPONENT) {
|
||||
nearestHostComponentOrRootPvIndices[viewIdx] = hostViewIdx;
|
||||
} else {
|
||||
mergeEmbeddedDom(hostProtoView, mergableNestedProtoView);
|
||||
nearestHostComponentOrRootPvIndices[viewIdx] =
|
||||
nearestHostComponentOrRootPvIndices[hostViewIdx];
|
||||
}
|
||||
}
|
||||
});
|
||||
return nearestHostComponentOrRootPvIndices;
|
||||
}
|
||||
|
||||
function indexProtoViewsByHostElement(mergableProtoViews: ClonedProtoView[],
|
||||
hostElementIndices: number[]): Map<Element, ClonedProtoView> {
|
||||
var hostProtoView = mergableProtoViews[0];
|
||||
var mergableProtoViewIdx = 1;
|
||||
var nestedProtoViewByHostElement: Map<Element, ClonedProtoView> = new Map();
|
||||
hostElementIndices.forEach((hostElementIndex) => {
|
||||
nestedProtoViewByHostElement.set(hostProtoView.boundElements[hostElementIndex],
|
||||
mergableProtoViews[mergableProtoViewIdx++]);
|
||||
});
|
||||
return nestedProtoViewByHostElement;
|
||||
function mergeComponents(clonedProtoViews: ClonedProtoView[], hostViewAndBinderIndices: number[][],
|
||||
targetFragments: Node[][]) {
|
||||
var hostProtoView = clonedProtoViews[0];
|
||||
hostProtoView.fragments.forEach((fragment) => targetFragments.push(fragment));
|
||||
|
||||
for (var viewIdx = 1; viewIdx < clonedProtoViews.length; viewIdx++) {
|
||||
var hostViewIdx = hostViewAndBinderIndices[viewIdx][0];
|
||||
var hostBinderIdx = hostViewAndBinderIndices[viewIdx][1];
|
||||
var hostProtoView = clonedProtoViews[hostViewIdx];
|
||||
var clonedProtoView = clonedProtoViews[viewIdx];
|
||||
if (clonedProtoView.original.type === ViewType.COMPONENT) {
|
||||
mergeComponent(hostProtoView, hostBinderIdx, clonedProtoView, targetFragments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mergeComponentDom(hostProtoView: ClonedProtoView, boundElementIndex: number,
|
||||
nestedProtoView: ClonedProtoView,
|
||||
nestedProtoViewByHostElement: Map<Element, ClonedProtoView>) {
|
||||
var hostElement = hostProtoView.boundElements[boundElementIndex];
|
||||
function mergeComponent(hostProtoView: ClonedProtoView, binderIdx: number,
|
||||
nestedProtoView: ClonedProtoView, targetFragments: Node[][]) {
|
||||
var hostElement = hostProtoView.boundElements[binderIdx];
|
||||
|
||||
// We wrap the fragments into elements so that we can expand <ng-content>
|
||||
// even for root nodes in the fragment without special casing them.
|
||||
|
@ -163,15 +161,15 @@ function mergeComponentDom(hostProtoView: ClonedProtoView, boundElementIndex: nu
|
|||
|
||||
// unwrap the fragment elements into arrays of nodes after projecting
|
||||
var fragments = extractFragmentNodesFromElements(fragmentElements);
|
||||
appendComponentNodesToHost(hostProtoView, boundElementIndex, fragments[0]);
|
||||
appendComponentNodesToHost(hostProtoView, binderIdx, fragments[0]);
|
||||
|
||||
for (var i = 1; i < fragments.length; i++) {
|
||||
hostProtoView.fragments.push(fragments[i]);
|
||||
targetFragments.push(fragments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function mapFragmentsIntoElements(fragments: Node[][]): Element[] {
|
||||
return fragments.map((fragment) => {
|
||||
return fragments.map(fragment => {
|
||||
var fragmentElement = DOM.createTemplate('');
|
||||
fragment.forEach(node => DOM.appendChild(DOM.content(fragmentElement), node));
|
||||
return fragmentElement;
|
||||
|
@ -195,10 +193,10 @@ function findContentElements(fragmentElements: Element[]): Element[] {
|
|||
return sortContentElements(contentElements);
|
||||
}
|
||||
|
||||
function appendComponentNodesToHost(hostProtoView: ClonedProtoView, boundElementIndex: number,
|
||||
function appendComponentNodesToHost(hostProtoView: ClonedProtoView, binderIdx: number,
|
||||
componentRootNodes: Node[]) {
|
||||
var hostElement = hostProtoView.boundElements[boundElementIndex];
|
||||
var binder = hostProtoView.original.elementBinders[boundElementIndex];
|
||||
var hostElement = hostProtoView.boundElements[binderIdx];
|
||||
var binder = hostProtoView.original.elementBinders[binderIdx];
|
||||
if (binder.hasNativeShadowRoot) {
|
||||
var shadowRootWrapper = DOM.createElement(NG_SHADOW_ROOT_ELEMENT_NAME);
|
||||
for (var i = 0; i < componentRootNodes.length; i++) {
|
||||
|
@ -218,40 +216,23 @@ function appendComponentNodesToHost(hostProtoView: ClonedProtoView, boundElement
|
|||
}
|
||||
}
|
||||
|
||||
function mergeEmbeddedDom(parentProtoView: ClonedProtoView, nestedProtoView: ClonedProtoView) {
|
||||
nestedProtoView.fragments.forEach((fragment) => parentProtoView.fragments.push(fragment));
|
||||
}
|
||||
|
||||
function projectMatchingNodes(selector: string, contentElement: Element, nodes: Node[]): Node[] {
|
||||
var remaining = [];
|
||||
var removeContentElement = true;
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var node = nodes[i];
|
||||
var matches = false;
|
||||
if (isWildcard(selector)) {
|
||||
DOM.insertBefore(contentElement, node);
|
||||
} else if (DOM.isElementNode(node)) {
|
||||
if (isElementWithTag(node, NG_CONTENT_ELEMENT_NAME)) {
|
||||
// keep the projected content as other <ng-content> elements
|
||||
// might want to use it as well.
|
||||
remaining.push(node);
|
||||
DOM.setAttribute(contentElement, 'select',
|
||||
mergeSelectors(selector, DOM.getAttribute(node, 'select')));
|
||||
removeContentElement = false;
|
||||
} else {
|
||||
if (DOM.elementMatches(node, selector)) {
|
||||
matches = true;
|
||||
} else if (DOM.isElementNode(node) && DOM.elementMatches(node, selector)) {
|
||||
matches = true;
|
||||
}
|
||||
if (matches) {
|
||||
DOM.insertBefore(contentElement, node);
|
||||
} else {
|
||||
remaining.push(node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// non projected text nodes
|
||||
remaining.push(node);
|
||||
}
|
||||
}
|
||||
if (removeContentElement) {
|
||||
DOM.remove(contentElement);
|
||||
}
|
||||
return remaining;
|
||||
}
|
||||
|
||||
|
@ -259,36 +240,6 @@ function isWildcard(selector): boolean {
|
|||
return isBlank(selector) || selector.length === 0 || selector == '*';
|
||||
}
|
||||
|
||||
export function mergeSelectors(selector1: string, selector2: string): string {
|
||||
if (isWildcard(selector1)) {
|
||||
return isBlank(selector2) ? '' : selector2;
|
||||
} else if (isWildcard(selector2)) {
|
||||
return isBlank(selector1) ? '' : selector1;
|
||||
} else {
|
||||
var sels1 = CssSelector.parse(selector1);
|
||||
var sels2 = CssSelector.parse(selector2);
|
||||
if (sels1.length > 1 || sels2.length > 1) {
|
||||
throw new BaseException('multiple selectors are not supported in ng-content');
|
||||
}
|
||||
var sel1 = sels1[0];
|
||||
var sel2 = sels2[0];
|
||||
if (sel1.notSelectors.length > 0 || sel2.notSelectors.length > 0) {
|
||||
throw new BaseException(':not selector is not supported in ng-content');
|
||||
}
|
||||
var merged = new CssSelector();
|
||||
if (isBlank(sel1.element)) {
|
||||
merged.setElement(sel2.element);
|
||||
} else if (isBlank(sel2.element)) {
|
||||
merged.setElement(sel1.element);
|
||||
} else {
|
||||
return NOT_MATCHABLE_SELECTOR;
|
||||
}
|
||||
merged.attrs = sel1.attrs.concat(sel2.attrs);
|
||||
merged.classNames = sel1.classNames.concat(sel2.classNames);
|
||||
return merged.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// we need to sort content elements as they can originate from
|
||||
// different sub views
|
||||
function sortContentElements(contentElements: Element[]): Element[] {
|
||||
|
@ -394,12 +345,8 @@ function calcMappedElementIndices(clonedProtoViews: ClonedProtoView[],
|
|||
var mergedBoundElementIndices: Map<Element, number> = indexArray(mergedBoundElements);
|
||||
var mappedElementIndices = [];
|
||||
clonedProtoViews.forEach((clonedProtoView) => {
|
||||
clonedProtoView.original.mappedElementIndices.forEach((boundElementIndex) => {
|
||||
var mappedElementIndex = null;
|
||||
if (isPresent(boundElementIndex)) {
|
||||
var boundElement = clonedProtoView.boundElements[boundElementIndex];
|
||||
mappedElementIndex = mergedBoundElementIndices.get(boundElement);
|
||||
}
|
||||
clonedProtoView.boundElements.forEach((boundElement) => {
|
||||
var mappedElementIndex = mergedBoundElementIndices.get(boundElement);
|
||||
mappedElementIndices.push(mappedElementIndex);
|
||||
});
|
||||
});
|
||||
|
@ -410,12 +357,8 @@ function calcMappedTextIndices(clonedProtoViews: ClonedProtoView[],
|
|||
mergedBoundTextIndices: Map<Node, number>): number[] {
|
||||
var mappedTextIndices = [];
|
||||
clonedProtoViews.forEach((clonedProtoView) => {
|
||||
clonedProtoView.original.mappedTextIndices.forEach((textNodeIndex) => {
|
||||
var mappedTextIndex = null;
|
||||
if (isPresent(textNodeIndex)) {
|
||||
var textNode = clonedProtoView.boundTextNodes[textNodeIndex];
|
||||
mappedTextIndex = mergedBoundTextIndices.get(textNode);
|
||||
}
|
||||
clonedProtoView.boundTextNodes.forEach((textNode) => {
|
||||
var mappedTextIndex = mergedBoundTextIndices.get(textNode);
|
||||
mappedTextIndices.push(mappedTextIndex);
|
||||
});
|
||||
});
|
||||
|
@ -423,23 +366,30 @@ function calcMappedTextIndices(clonedProtoViews: ClonedProtoView[],
|
|||
}
|
||||
|
||||
function calcHostElementIndicesByViewIndex(clonedProtoViews: ClonedProtoView[],
|
||||
hostElementIndices: number[]): number[] {
|
||||
var mergedElementCount = 0;
|
||||
var hostElementIndicesByViewIndex = [];
|
||||
for (var i = 0; i < clonedProtoViews.length; i++) {
|
||||
var clonedProtoView = clonedProtoViews[i];
|
||||
clonedProtoView.original.hostElementIndicesByViewIndex.forEach((hostElementIndex) => {
|
||||
var mappedHostElementIndex;
|
||||
if (isBlank(hostElementIndex)) {
|
||||
mappedHostElementIndex = i > 0 ? hostElementIndices[i - 1] : null;
|
||||
} else {
|
||||
mappedHostElementIndex = hostElementIndex + mergedElementCount;
|
||||
hostViewAndBinderIndices: number[][]): number[] {
|
||||
var hostElementIndices = [null];
|
||||
var viewElementOffsets = [0];
|
||||
var elementIndex = clonedProtoViews[0].original.elementBinders.length;
|
||||
for (var viewIdx = 1; viewIdx < hostViewAndBinderIndices.length; viewIdx++) {
|
||||
viewElementOffsets.push(elementIndex);
|
||||
elementIndex += clonedProtoViews[viewIdx].original.elementBinders.length;
|
||||
var hostViewIdx = hostViewAndBinderIndices[viewIdx][0];
|
||||
var hostBinderIdx = hostViewAndBinderIndices[viewIdx][1];
|
||||
hostElementIndices.push(viewElementOffsets[hostViewIdx] + hostBinderIdx);
|
||||
}
|
||||
hostElementIndicesByViewIndex.push(mappedHostElementIndex);
|
||||
});
|
||||
mergedElementCount += clonedProtoView.original.mappedElementIndices.length;
|
||||
return hostElementIndices;
|
||||
}
|
||||
|
||||
function calcNestedViewCounts(hostViewAndBinderIndices: number[][]): number[] {
|
||||
var nestedViewCounts = ListWrapper.createFixedSize(hostViewAndBinderIndices.length);
|
||||
ListWrapper.fill(nestedViewCounts, 0);
|
||||
for (var viewIdx = hostViewAndBinderIndices.length - 1; viewIdx >= 1; viewIdx--) {
|
||||
var hostViewAndElementIdx = hostViewAndBinderIndices[viewIdx];
|
||||
if (isPresent(hostViewAndElementIdx)) {
|
||||
nestedViewCounts[hostViewAndElementIdx[0]] += nestedViewCounts[viewIdx] + 1;
|
||||
}
|
||||
return hostElementIndicesByViewIndex;
|
||||
}
|
||||
return nestedViewCounts;
|
||||
}
|
||||
|
||||
function indexArray(arr: any[]): Map<any, number> {
|
||||
|
|
|
@ -45,35 +45,6 @@ export function main() {
|
|||
rootProtoView;
|
||||
var renderCompileRequests: any[];
|
||||
|
||||
function mergeProtoViewsRecursively(
|
||||
protoViewRefs: List<renderApi.RenderProtoViewRef | List<any>>,
|
||||
target: renderApi.RenderProtoViewMergeMapping[]): renderApi.RenderProtoViewRef {
|
||||
var targetIndex = target.length;
|
||||
target.push(null);
|
||||
|
||||
var flattended = protoViewRefs.map(protoViewRefOrArray => {
|
||||
var resolvedProtoViewRef;
|
||||
if (isArray(protoViewRefOrArray)) {
|
||||
resolvedProtoViewRef = mergeProtoViewsRecursively(
|
||||
<List<renderApi.RenderProtoViewRef>>protoViewRefOrArray, target);
|
||||
} else {
|
||||
resolvedProtoViewRef = protoViewRefOrArray;
|
||||
}
|
||||
return resolvedProtoViewRef;
|
||||
});
|
||||
var merged = [];
|
||||
flattended.forEach((entry) => {
|
||||
if (entry instanceof MergedRenderProtoViewRef) {
|
||||
entry.originals.forEach(ref => merged.push(ref));
|
||||
} else {
|
||||
merged.push(entry);
|
||||
}
|
||||
});
|
||||
var result = new MergedRenderProtoViewRef(merged);
|
||||
target[targetIndex] = new renderApi.RenderProtoViewMergeMapping(result, 1, [], [], []);
|
||||
return result;
|
||||
}
|
||||
|
||||
function createCompiler(renderCompileResults:
|
||||
List<renderApi.ProtoViewDto | Promise<renderApi.ProtoViewDto>>,
|
||||
protoViewFactoryResults: List<AppProtoView>) {
|
||||
|
@ -102,10 +73,10 @@ export function main() {
|
|||
});
|
||||
renderCompiler.spy('mergeProtoViewsRecursively')
|
||||
.andCallFake((protoViewRefs: List<renderApi.RenderProtoViewRef | List<any>>) => {
|
||||
var result: renderApi.RenderProtoViewMergeMapping[] = [];
|
||||
mergeProtoViewsRecursively(protoViewRefs, result);
|
||||
return PromiseWrapper.resolve(result);
|
||||
return PromiseWrapper.resolve(new renderApi.RenderProtoViewMergeMapping(
|
||||
new MergedRenderProtoViewRef(protoViewRefs), 1, [], [], [], [null]));
|
||||
});
|
||||
// TODO spy on .compile and return RenderProtoViewRef, same for compileHost
|
||||
rootProtoView = createRootProtoView(directiveResolver, MainComponent);
|
||||
});
|
||||
|
||||
|
@ -360,13 +331,12 @@ export function main() {
|
|||
createCompiler(renderPvDtos, [rootProtoView, mainProtoView, nestedProtoView]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(originalRenderProtoViewRefs(internalProtoView(protoViewRef)))
|
||||
.toEqual(
|
||||
[rootProtoView.render, [mainProtoView.render, [nestedProtoView.render]]]);
|
||||
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||
.toBe(mainProtoView);
|
||||
expect(originalRenderProtoViewRefs(mainProtoView))
|
||||
.toEqual([renderPvDtos[0].render, renderPvDtos[1].render]);
|
||||
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
||||
expect(originalRenderProtoViewRefs(nestedProtoView))
|
||||
.toEqual([renderPvDtos[1].render]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
@ -375,7 +345,8 @@ export function main() {
|
|||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
tplResolver.setView(NestedComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var viewportProtoView =
|
||||
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
|
||||
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)],
|
||||
renderApi.ViewType.EMBEDDED);
|
||||
var mainProtoView = createProtoView([createViewportElementBinder(viewportProtoView)]);
|
||||
var nestedProtoView = createProtoView();
|
||||
var renderPvDtos = [
|
||||
|
@ -391,23 +362,11 @@ export function main() {
|
|||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||
.toBe(mainProtoView);
|
||||
expect(originalRenderProtoViewRefs(mainProtoView))
|
||||
.toEqual([
|
||||
renderPvDtos[0]
|
||||
.render,
|
||||
renderPvDtos[0].elementBinders[0].nestedProtoView.render,
|
||||
renderPvDtos[1].render
|
||||
]);
|
||||
expect(originalRenderProtoViewRefs(internalProtoView(protoViewRef)))
|
||||
.toEqual([rootProtoView.render, [mainProtoView.render, null]]);
|
||||
expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
||||
expect(originalRenderProtoViewRefs(viewportProtoView))
|
||||
.toEqual([
|
||||
renderPvDtos[0]
|
||||
.elementBinders[0]
|
||||
.nestedProtoView.render,
|
||||
renderPvDtos[1].render
|
||||
]);
|
||||
expect(originalRenderProtoViewRefs(nestedProtoView))
|
||||
.toEqual([renderPvDtos[1].render]);
|
||||
.toEqual([viewportProtoView.render, [nestedProtoView.render]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
@ -520,7 +479,8 @@ export function main() {
|
|||
inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var viewportProtoView =
|
||||
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
|
||||
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)],
|
||||
renderApi.ViewType.EMBEDDED);
|
||||
var mainProtoView = createProtoView([createViewportElementBinder(viewportProtoView)]);
|
||||
var renderPvDtos = [
|
||||
createRenderProtoView([
|
||||
|
@ -539,20 +499,39 @@ export function main() {
|
|||
.nestedProtoView)
|
||||
.toBe(mainProtoView);
|
||||
// In case of a cycle, don't merge the embedded proto views into the component!
|
||||
expect(originalRenderProtoViewRefs(mainProtoView))
|
||||
.toEqual([renderPvDtos[0].render, null]);
|
||||
expect(originalRenderProtoViewRefs(internalProtoView(protoViewRef)))
|
||||
.toEqual([rootProtoView.render, [mainProtoView.render, null]]);
|
||||
expect(originalRenderProtoViewRefs(viewportProtoView))
|
||||
.toEqual([
|
||||
renderPvDtos[0]
|
||||
.elementBinders[0]
|
||||
.nestedProtoView.render,
|
||||
renderPvDtos[1].render,
|
||||
null
|
||||
]);
|
||||
.toEqual([viewportProtoView.render, [mainProtoView.render, null]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw on recursive components that are connected via an embedded ProtoView with <ng-content>',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var viewportProtoView =
|
||||
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)],
|
||||
renderApi.ViewType.EMBEDDED, true);
|
||||
var mainProtoView = createProtoView([createViewportElementBinder(viewportProtoView)]);
|
||||
var renderPvDtos = [
|
||||
createRenderProtoView([
|
||||
createRenderViewportElementBinder(createRenderProtoView(
|
||||
[createRenderComponentElementBinder(0)], renderApi.ViewType.EMBEDDED))
|
||||
]),
|
||||
createRenderProtoView()
|
||||
];
|
||||
var compiler = createCompiler(renderPvDtos, [rootProtoView, mainProtoView]);
|
||||
PromiseWrapper.catchError(compiler.compileInHost(MainComponent), (e) => {
|
||||
expect(() => { throw e; })
|
||||
.toThrowError(
|
||||
`<ng-content> is used within the recursive path of ${stringify(MainComponent)}`);
|
||||
async.done();
|
||||
return null;
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should create host proto views', inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var rootProtoView =
|
||||
|
@ -581,8 +560,13 @@ function createDirectiveBinding(directiveResolver, type): DirectiveBinding {
|
|||
return DirectiveBinding.createFromType(type, annotation);
|
||||
}
|
||||
|
||||
function createProtoView(elementBinders = null): AppProtoView {
|
||||
var pv = new AppProtoView(null, null, null, new Map(), null);
|
||||
function createProtoView(elementBinders = null, type: renderApi.ViewType = null,
|
||||
isEmbeddedFragment: boolean = false): AppProtoView {
|
||||
if (isBlank(type)) {
|
||||
type = renderApi.ViewType.COMPONENT;
|
||||
}
|
||||
var pv = new AppProtoView(type, isEmbeddedFragment, new renderApi.RenderProtoViewRef(), null,
|
||||
null, new Map(), null);
|
||||
if (isBlank(elementBinders)) {
|
||||
elementBinders = [];
|
||||
}
|
||||
|
@ -623,7 +607,8 @@ function createRenderViewportElementBinder(nestedProtoView): renderApi.ElementBi
|
|||
}
|
||||
|
||||
function createRootProtoView(directiveResolver, type): AppProtoView {
|
||||
return createProtoView([createComponentElementBinder(directiveResolver, type)]);
|
||||
return createProtoView([createComponentElementBinder(directiveResolver, type)],
|
||||
renderApi.ViewType.HOST);
|
||||
}
|
||||
|
||||
@Component({selector: 'main-comp'})
|
||||
|
|
|
@ -182,7 +182,8 @@ function createRenderProtoView(elementBinders = null, type: renderApi.ViewType =
|
|||
elementBinders: elementBinders,
|
||||
type: type,
|
||||
variableBindings: variableBindings,
|
||||
textBindings: []
|
||||
textBindings: [],
|
||||
transitiveNgContentCount: 0
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -289,13 +289,32 @@ function calcHostElementIndicesByViewIndex(pv: AppProtoView, elementOffset = 0,
|
|||
return target;
|
||||
}
|
||||
|
||||
function countNestedProtoViews(pv: AppProtoView, target: number[] = null): number[] {
|
||||
if (isBlank(target)) {
|
||||
target = [];
|
||||
}
|
||||
target.push(null);
|
||||
var resultIndex = target.length - 1;
|
||||
var count = 0;
|
||||
for (var binderIdx = 0; binderIdx < pv.elementBinders.length; binderIdx++) {
|
||||
var binder = pv.elementBinders[binderIdx];
|
||||
if (isPresent(binder.nestedProtoView)) {
|
||||
var nextResultIndex = target.length;
|
||||
countNestedProtoViews(binder.nestedProtoView, target);
|
||||
count += target[nextResultIndex] + 1;
|
||||
}
|
||||
}
|
||||
target[resultIndex] = count;
|
||||
return target;
|
||||
}
|
||||
|
||||
function _createProtoView(type: ViewType, binders: ElementBinder[] = null) {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var protoChangeDetector = <any>new SpyProtoChangeDetector();
|
||||
protoChangeDetector.spy('instantiate').andReturn(new SpyChangeDetector());
|
||||
var res = new AppProtoView(type, protoChangeDetector, null, null, 0);
|
||||
var res = new AppProtoView(type, null, null, protoChangeDetector, null, null, 0);
|
||||
res.elementBinders = binders;
|
||||
var mappedElementIndices = ListWrapper.createFixedSize(countNestedElementBinders(res));
|
||||
for (var i = 0; i < binders.length; i++) {
|
||||
|
@ -304,9 +323,11 @@ function _createProtoView(type: ViewType, binders: ElementBinder[] = null) {
|
|||
binder.protoElementInjector.index = i;
|
||||
}
|
||||
var hostElementIndicesByViewIndex = calcHostElementIndicesByViewIndex(res);
|
||||
res.mergeMapping = new AppProtoViewMergeMapping(
|
||||
new RenderProtoViewMergeMapping(null, hostElementIndicesByViewIndex.length,
|
||||
mappedElementIndices, [], hostElementIndicesByViewIndex));
|
||||
if (type === ViewType.EMBEDDED || type === ViewType.HOST) {
|
||||
res.mergeMapping = new AppProtoViewMergeMapping(new RenderProtoViewMergeMapping(
|
||||
null, hostElementIndicesByViewIndex.length, mappedElementIndices, [],
|
||||
hostElementIndicesByViewIndex, countNestedProtoViews(res)));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,9 @@ export function main() {
|
|||
|
||||
function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); }
|
||||
|
||||
function createProtoView() { return new AppProtoView(null, null, null, null, null); }
|
||||
function createProtoView() {
|
||||
return new AppProtoView(null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
function createView(pv) {
|
||||
return new AppView(null, pv, null, null, null, null, new Map(), null, null);
|
||||
|
|
|
@ -53,7 +53,7 @@ export function main() {
|
|||
{componentId: 'someComponent', template: '{{a}}', directives: []})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
var rootView = tb.createView(protoViewMergeMappings);
|
||||
|
||||
tb.renderer.setText(rootView.viewRef, 0, 'hello');
|
||||
expect(rootView.hostElement).toHaveText('hello');
|
||||
|
@ -92,7 +92,7 @@ export function main() {
|
|||
expect(DOM.getAttribute(el, 'some-attr')).toEqual('someValue');
|
||||
};
|
||||
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
var rootView = tb.createView(protoViewMergeMappings);
|
||||
// root element
|
||||
checkSetters(elRef(rootView.viewRef, 0), rootView.hostElement);
|
||||
// nested elements
|
||||
|
@ -114,7 +114,7 @@ export function main() {
|
|||
})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
var rootView = tb.createView(protoViewMergeMappings);
|
||||
var el = DOM.childNodes(rootView.hostElement)[0];
|
||||
tb.renderer.setElementProperty(elRef(rootView.viewRef, 1), 'maxLength', '20');
|
||||
expect(DOM.getAttribute(<HTMLInputElement>el, 'ng-reflect-max-length'))
|
||||
|
@ -138,7 +138,7 @@ export function main() {
|
|||
})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
var rootView = tb.createView(protoViewMergeMappings);
|
||||
var el = DOM.childNodes(rootView.hostElement)[0];
|
||||
tb.renderer.setElementProperty(elRef(rootView.viewRef, 1), 'maxLength', '20');
|
||||
expect(DOM.getAttribute(<HTMLInputElement>el, 'ng-reflect-max-length'))
|
||||
|
@ -160,7 +160,7 @@ export function main() {
|
|||
})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
var rootView = tb.createView(protoViewMergeMappings);
|
||||
|
||||
tb.renderer.invokeElementMethod(elRef(rootView.viewRef, 1), 'setAttribute',
|
||||
['a', 'b']);
|
||||
|
@ -183,7 +183,7 @@ export function main() {
|
|||
})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
var rootView = tb.createView(protoViewMergeMappings);
|
||||
|
||||
var elr = elRef(rootView.viewRef, 1);
|
||||
expect(rootView.hostElement).toHaveText('');
|
||||
|
@ -208,7 +208,7 @@ export function main() {
|
|||
})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
var rootView = tb.createView(protoViewMergeMappings);
|
||||
|
||||
var elr = elRef(rootView.viewRef, 1);
|
||||
expect(rootView.hostElement).toHaveText('');
|
||||
|
@ -233,8 +233,8 @@ export function main() {
|
|||
directives: []
|
||||
})
|
||||
])
|
||||
.then((protoViewDtos) => {
|
||||
var rootView = tb.createView(protoViewDtos[0]);
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings);
|
||||
|
||||
tb.triggerEvent(elRef(rootView.viewRef, 1), 'change');
|
||||
var eventEntry = rootView.events[0];
|
||||
|
@ -263,7 +263,7 @@ export function main() {
|
|||
{componentId: 'someComponent', template: 'hello', directives: []})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
var rootView = tb.createView(protoViewMergeMappings);
|
||||
expect(DOM.getShadowRoot(rootView.hostElement)).toHaveText('hello');
|
||||
async.done();
|
||||
});
|
||||
|
|
|
@ -82,14 +82,13 @@ export class DomTestbed {
|
|||
return PromiseWrapper.all(promises);
|
||||
}
|
||||
|
||||
merge(protoViews:
|
||||
List<ProtoViewDto | RenderProtoViewRef>): Promise<RenderProtoViewMergeMapping[]> {
|
||||
merge(protoViews: List<ProtoViewDto | RenderProtoViewRef>): Promise<RenderProtoViewMergeMapping> {
|
||||
return this.compiler.mergeProtoViewsRecursively(collectMergeRenderProtoViewsRecurse(
|
||||
<ProtoViewDto>protoViews[0], ListWrapper.slice(protoViews, 1)));
|
||||
}
|
||||
|
||||
compileAndMerge(host: DirectiveMetadata,
|
||||
componentViews: ViewDefinition[]): Promise<RenderProtoViewMergeMapping[]> {
|
||||
componentViews: ViewDefinition[]): Promise<RenderProtoViewMergeMapping> {
|
||||
return this.compile(host, componentViews).then(protoViewDtos => this.merge(protoViewDtos));
|
||||
}
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ export function main() {
|
|||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<b class="ng-binding" idx="2">B(<div class="x y"></div>)</b>)</a></root>'
|
||||
]));
|
||||
|
||||
it('should keep non projected embedded views (so that they can be moved manually)',
|
||||
it('should keep non projected embedded views as fragments (so that they can be moved manually)',
|
||||
runAndAssert(
|
||||
'root', ['<a><template class="x">b</template></a>', ''],
|
||||
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"></a></root>', 'b']));
|
||||
|
@ -145,13 +145,6 @@ export function main() {
|
|||
'b'
|
||||
]));
|
||||
|
||||
it('should project embedded views and match the single root element',
|
||||
runAndAssert(
|
||||
'root', ['<a><div class="x" *ng-if></div></a>', 'A(<ng-content></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>)</a></root>',
|
||||
'<div *ng-if="" class="x"></div>'
|
||||
]));
|
||||
|
||||
it('should project nodes using the ng-content in embedded views',
|
||||
runAndAssert('root', ['<a>b</a>', 'A(<ng-content *ng-if></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>)</a></root>',
|
||||
|
@ -258,7 +251,7 @@ function runAndAssert(hostElementName: string, componentTemplates: string[],
|
|||
directives: [aComp, bComp, cComp]
|
||||
})))
|
||||
.then((mergeMappings) => {
|
||||
expect(stringify(mergeMappings[0])).toEqual(expectedFragments);
|
||||
expect(stringify(mergeMappings)).toEqual(expectedFragments);
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
beforeEachBindings,
|
||||
SpyObject,
|
||||
stringifyElement
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {mergeSelectors} from 'angular2/src/render/dom/view/proto_view_merger';
|
||||
|
||||
export function main() {
|
||||
describe('ProtoViewMerger test', () => {
|
||||
|
||||
describe('mergeSelectors', () => {
|
||||
it('should merge empty selectors', () => {
|
||||
expect(mergeSelectors('', 'a')).toEqual('a');
|
||||
expect(mergeSelectors('a', '')).toEqual('a');
|
||||
expect(mergeSelectors('', '')).toEqual('');
|
||||
});
|
||||
|
||||
it('should merge wildcard selectors', () => {
|
||||
expect(mergeSelectors('*', 'a')).toEqual('a');
|
||||
expect(mergeSelectors('a', '*')).toEqual('a');
|
||||
expect(mergeSelectors('*', '*')).toEqual('*');
|
||||
});
|
||||
|
||||
it('should merge 2 element selectors',
|
||||
() => { expect(mergeSelectors('a', 'b')).toEqual('_not-matchable_'); });
|
||||
|
||||
it('should merge elements and non element selector', () => {
|
||||
expect(mergeSelectors('a', '.b')).toEqual('a.b');
|
||||
expect(mergeSelectors('.b', 'a')).toEqual('a.b');
|
||||
});
|
||||
|
||||
it('should merge attributes', () => {
|
||||
expect(mergeSelectors('[a]', '[b]')).toEqual('[a][b]');
|
||||
expect(mergeSelectors('[a][b]', '[c][d]')).toEqual('[a][b][c][d]');
|
||||
expect(mergeSelectors('[a=1]', '[b=2]')).toEqual('[a=1][b=2]');
|
||||
});
|
||||
|
||||
it('should merge classes', () => {
|
||||
expect(mergeSelectors('.a', '.b')).toEqual('.a.b');
|
||||
expect(mergeSelectors('.a.b', '.c.d')).toEqual('.a.b.c.d');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
|
@ -30,7 +30,7 @@ export function main() {
|
|||
binders = [];
|
||||
}
|
||||
var rootEl = DOM.createTemplate('<div></div>');
|
||||
return DomProtoView.create(null, <Element>rootEl, [1], [], binders, null, null, null);
|
||||
return DomProtoView.create(null, <Element>rootEl, [1], [], binders);
|
||||
}
|
||||
|
||||
function createElementBinder() { return new DomElementBinder({textNodeIndices: []}); }
|
||||
|
|
Loading…
Reference in New Issue