Skip to content
Merged
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
6 changes: 4 additions & 2 deletions modules/angular2/angular2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,13 @@ export {

export * from './http';
export {
EventDispatcher,
RenderEventDispatcher,
Renderer,
RenderElementRef,
RenderViewRef,
RenderProtoViewRef
RenderProtoViewRef,
RenderFragmentRef,
RenderViewWithFragments
} from 'angular2/src/render/api';
export {
DomRenderer,
Expand Down
2 changes: 1 addition & 1 deletion modules/angular2/docs/core/02_directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class Pane { | Component controller class
<div class="outer">
<h1>{{title}}</h1>
<div class="inner" [hidden]="!open">
<content></content>
<ng-content></ng-content>
</div>
</div>
```
Expand Down
155 changes: 108 additions & 47 deletions modules/angular2/src/core/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection

import {DirectiveResolver} from './directive_resolver';

import {AppProtoView} from './view';
import {ElementBinder} from './element_binder';
import {AppProtoView, AppProtoViewMergeMapping} from './view';
import {ProtoViewRef} from './view_ref';
import {DirectiveBinding} from './element_injector';
import {ViewResolver} from './view_resolver';
Expand Down Expand Up @@ -91,6 +90,7 @@ export class Compiler {
private _appUrl: string;
private _render: renderApi.RenderCompiler;
private _protoViewFactory: ProtoViewFactory;
private _unmergedCyclicEmbeddedProtoViews: RecursiveEmbeddedProtoView[] = [];

/**
* @private
Expand Down Expand Up @@ -137,13 +137,17 @@ export class Compiler {
Compiler._assertTypeIsComponent(componentBinding);

var directiveMetadata = componentBinding.metadata;
hostPvPromise = this._render.compileHost(directiveMetadata)
.then((hostRenderPv) => {
return this._compileNestedProtoViews(componentBinding, hostRenderPv,
[componentBinding]);
});
hostPvPromise =
this._render.compileHost(directiveMetadata)
.then((hostRenderPv) => {
var protoView = this._protoViewFactory.createAppProtoViews(
componentBinding, hostRenderPv, [componentBinding]);
this._compilerCache.setHost(componentType, protoView);
return this._compileNestedProtoViews(hostRenderPv, protoView, componentType);
});
}
return hostPvPromise.then((hostAppProtoView) => { return new ProtoViewRef(hostAppProtoView); });
return hostPvPromise.then(hostAppProtoView => this._mergeCyclicEmbeddedProtoViews().then(
_ => new ProtoViewRef(hostAppProtoView)));
}

private _compile(componentBinding: DirectiveBinding): Promise<AppProtoView>| AppProtoView {
Expand All @@ -156,12 +160,12 @@ export class Compiler {
return protoView;
}

var pvPromise = this._compiling.get(component);
if (isPresent(pvPromise)) {
var resultPromise = this._compiling.get(component);
if (isPresent(resultPromise)) {
// The component is already being compiled, attach to the existing Promise
// instead of re-compiling the component.
// It happens when a template references a component multiple times.
return pvPromise;
return resultPromise;
}
var view = this._viewResolver.resolve(component);

Expand All @@ -178,14 +182,19 @@ export class Compiler {
ListWrapper.map(directives, (directive) => this._bindDirective(directive)));

var renderTemplate = this._buildRenderTemplate(component, view, boundDirectives);
pvPromise =
this._render.compile(renderTemplate)
.then((renderPv) => {
return this._compileNestedProtoViews(componentBinding, renderPv, boundDirectives);
});

this._compiling.set(component, pvPromise);
return pvPromise;
resultPromise = this._render.compile(renderTemplate)
.then((renderPv) => {
var protoView = this._protoViewFactory.createAppProtoViews(
componentBinding, renderPv, boundDirectives);
// Populate the cache before compiling the nested components,
// so that components can reference themselves in their template.
this._compilerCache.set(component, protoView);
MapWrapper.delete(this._compiling, component);

return this._compileNestedProtoViews(renderPv, protoView, component);
});
this._compiling.set(component, resultPromise);
return resultPromise;
}

private _removeDuplicatedDirectives(directives: List<DirectiveBinding>): List<DirectiveBinding> {
Expand All @@ -194,24 +203,11 @@ export class Compiler {
return MapWrapper.values(directivesMap);
}

private _compileNestedProtoViews(componentBinding, renderPv, directives): Promise<AppProtoView>|
AppProtoView {
var protoViews =
this._protoViewFactory.createAppProtoViews(componentBinding, renderPv, directives);
var protoView = protoViews[0];
if (isPresent(componentBinding)) {
var component = componentBinding.key.token;
if (renderPv.type === renderApi.ViewType.COMPONENT) {
// Populate the cache before compiling the nested components,
// so that components can reference themselves in their template.
this._compilerCache.set(component, protoView);
MapWrapper.delete(this._compiling, component);
} else {
this._compilerCache.setHost(component, protoView);
}
}
private _compileNestedProtoViews(renderProtoView: renderApi.ProtoViewDto,
appProtoView: AppProtoView,
componentType: Type): Promise<AppProtoView> {
var nestedPVPromises = [];
ListWrapper.forEach(this._collectComponentElementBinders(protoViews), (elementBinder) => {
this._loopComponentElementBinders(appProtoView, (parentPv, elementBinder) => {
var nestedComponent = elementBinder.componentDirective;
var elementBinderDone =
(nestedPv: AppProtoView) => { elementBinder.nestedProtoView = nestedPv; };
Expand All @@ -222,24 +218,85 @@ export class Compiler {
elementBinderDone(<AppProtoView>nestedCall);
}
});
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);
});
}

if (nestedPVPromises.length > 0) {
return PromiseWrapper.all(nestedPVPromises).then((_) => protoView);
} else {
return protoView;
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];
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 _collectComponentElementBinders(protoViews: List<AppProtoView>): List<ElementBinder> {
var componentElementBinders = [];
ListWrapper.forEach(protoViews, (protoView) => {
ListWrapper.forEach(protoView.elementBinders, (elementBinder) => {
if (isPresent(elementBinder.componentDirective)) {
componentElementBinders.push(elementBinder);
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 componentElementBinders;
return PromiseWrapper.all(promises);
}

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

class RecursiveEmbeddedProtoView {
constructor(public appProtoView: AppProtoView, public renderProtoView: renderApi.ProtoViewDto) {}
}
7 changes: 4 additions & 3 deletions modules/angular2/src/core/compiler/element_injector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ export class DirectiveBinding extends ResolvedBinding {
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
export class PreBuiltObjects {
constructor(public viewManager: avmModule.AppViewManager, public view: viewModule.AppView,
public protoView: viewModule.AppProtoView) {}
public elementRef: ElementRef, public protoView: viewModule.AppProtoView) {}
}

export class EventEmitterAccessor {
Expand Down Expand Up @@ -575,7 +575,7 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend

getComponent(): any { return this._strategy.getComponent(); }

getElementRef(): ElementRef { return this._preBuiltObjects.view.elementRefs[this._proto.index]; }
getElementRef(): ElementRef { return this._preBuiltObjects.elementRef; }

getViewContainerRef(): ViewContainerRef {
return new ViewContainerRef(this._preBuiltObjects.viewManager, this.getElementRef());
Expand Down Expand Up @@ -606,7 +606,8 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
// We provide the component's view change detector to components and
// the surrounding component's change detector to directives.
if (dirBin.metadata.type === DirectiveMetadata.COMPONENT_TYPE) {
var componentView = this._preBuiltObjects.view.componentChildViews[this._proto.index];
var componentView = this._preBuiltObjects.view.getNestedView(
this._preBuiltObjects.elementRef.boundElementIndex);
return componentView.changeDetector.ref;
} else {
return this._preBuiltObjects.view.changeDetector.ref;
Expand Down
13 changes: 11 additions & 2 deletions modules/angular2/src/core/compiler/element_ref.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {BaseException} from 'angular2/src/facade/lang';
import {BaseException, isPresent} from 'angular2/src/facade/lang';
import {ViewRef} from './view_ref';
import {RenderViewRef, RenderElementRef, Renderer} from 'angular2/src/render/api';

Expand All @@ -24,9 +24,18 @@ export class ElementRef implements RenderElementRef {
*/
boundElementIndex: number;

constructor(parentView: ViewRef, boundElementIndex: number, private _renderer: Renderer) {
/**
* Index of the element inside the {@link RenderViewRef}.
*
* This is used internally by the Angular framework to locate elements.
*/
renderBoundElementIndex: number;

constructor(parentView: ViewRef, boundElementIndex: number, renderBoundElementIndex: number,
private _renderer: Renderer) {
this.parentView = parentView;
this.boundElementIndex = boundElementIndex;
this.renderBoundElementIndex = renderBoundElementIndex;
}

/**
Expand Down
Loading