Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 65 additions & 72 deletions modules/angular2/src/core/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -90,7 +91,7 @@ export class Compiler {
private _appUrl: string;
private _render: renderApi.RenderCompiler;
private _protoViewFactory: ProtoViewFactory;
private _unmergedCyclicEmbeddedProtoViews: RecursiveEmbeddedProtoView[] = [];
private _protoViewsToBeMerged: AppProtoView[] = [];

/**
* @private
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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; };
Expand All @@ -220,83 +250,50 @@ 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]);
}
return appProtoViewsToMergeInto[0];
});
}

private _loopComponentElementBinders(appProtoView: AppProtoView, callback: Function) {
appProtoView.elementBinders.forEach((elementBinder) => {
if (isPresent(elementBinder.componentDirective)) {
callback(appProtoView, elementBinder);
} else if (isPresent(elementBinder.nestedProtoView)) {
this._loopComponentElementBinders(elementBinder.nestedProtoView, callback);
}
});
}

private _collectMergeRenderProtoViewsRecurse(
renderProtoView: renderApi.ProtoViewDto, appProtoView: AppProtoView,
targetAppProtoViews: AppProtoView[]): List<renderApi.RenderProtoViewRef | List<any>> {
targetAppProtoViews.push(appProtoView);
var result = [renderProtoView.render];
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.mergeMapping)) {
// cycle via an embedded ProtoView. store the AppProtoView and ProtoViewDto for later.
this._unmergedCyclicEmbeddedProtoViews.push(
new RecursiveEmbeddedProtoView(appProtoView, renderProtoView));
return null;
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;
}
result.push(binder.nestedProtoView.mergeMapping.renderProtoViewRef);
} else if (binder.hasEmbeddedProtoView()) {
result.push(this._collectMergeRenderProtoViewsRecurse(
renderProtoView.elementBinders[i].nestedProtoView, binder.nestedProtoView,
targetAppProtoViews));
this._collectMergableProtoViews(binder.nestedProtoView, componentType);
}
}
return result;
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 _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);
private _loopComponentElementBinders(appProtoView: AppProtoView, callback: Function) {
appProtoView.elementBinders.forEach((elementBinder) => {
if (isPresent(elementBinder.componentDirective)) {
callback(appProtoView, elementBinder);
} else if (isPresent(elementBinder.nestedProtoView)) {
this._loopComponentElementBinders(elementBinder.nestedProtoView, callback);
}
});
return PromiseWrapper.all(promises);
}

private _buildRenderTemplate(component, view, directives): renderApi.ViewDefinition {
Expand Down Expand Up @@ -356,7 +353,3 @@ export class Compiler {
}
}
}

class RecursiveEmbeddedProtoView {
constructor(public appProtoView: AppProtoView, public renderProtoView: renderApi.ProtoViewDto) {}
}
9 changes: 6 additions & 3 deletions modules/angular2/src/core/compiler/proto_view_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,12 @@ 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),
renderProtoView.textBindings.length);
// 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);

Expand Down
14 changes: 8 additions & 6 deletions modules/angular2/src/core/compiler/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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[] {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
19 changes: 13 additions & 6 deletions modules/angular2/src/core/compiler/view_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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];
if (isPresent(vc)) {
for (var j = vc.views.length - 1; j >= 0; j--) {
this._destroyViewInContainer(view, i, j);
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(currView, elementOffset, j);
}
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions modules/angular2/src/core/compiler/view_manager_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)) {
Expand Down
16 changes: 11 additions & 5 deletions modules/angular2/src/render/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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;
}
}
Expand Down
3 changes: 1 addition & 2 deletions modules/angular2/src/render/dom/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Loading