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