@@ -15,8 +15,7 @@ import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection
1515
1616import { DirectiveResolver } from './directive_resolver' ;
1717
18- import { AppProtoView } from './view' ;
19- import { ElementBinder } from './element_binder' ;
18+ import { AppProtoView , AppProtoViewMergeMapping } from './view' ;
2019import { ProtoViewRef } from './view_ref' ;
2120import { DirectiveBinding } from './element_injector' ;
2221import { ViewResolver } from './view_resolver' ;
@@ -91,6 +90,7 @@ export class Compiler {
9190 private _appUrl : string ;
9291 private _render : renderApi . RenderCompiler ;
9392 private _protoViewFactory : ProtoViewFactory ;
93+ private _unmergedCyclicEmbeddedProtoViews : RecursiveEmbeddedProtoView [ ] = [ ] ;
9494
9595 /**
9696 * @private
@@ -137,13 +137,17 @@ export class Compiler {
137137 Compiler . _assertTypeIsComponent ( componentBinding ) ;
138138
139139 var directiveMetadata = componentBinding . metadata ;
140- hostPvPromise = this . _render . compileHost ( directiveMetadata )
141- . then ( ( hostRenderPv ) => {
142- return this . _compileNestedProtoViews ( componentBinding , hostRenderPv ,
143- [ componentBinding ] ) ;
144- } ) ;
140+ hostPvPromise =
141+ this . _render . compileHost ( directiveMetadata )
142+ . then ( ( hostRenderPv ) => {
143+ var protoView = this . _protoViewFactory . createAppProtoViews (
144+ componentBinding , hostRenderPv , [ componentBinding ] ) ;
145+ this . _compilerCache . setHost ( componentType , protoView ) ;
146+ return this . _compileNestedProtoViews ( hostRenderPv , protoView , componentType ) ;
147+ } ) ;
145148 }
146- return hostPvPromise . then ( ( hostAppProtoView ) => { return new ProtoViewRef ( hostAppProtoView ) ; } ) ;
149+ return hostPvPromise . then ( hostAppProtoView => this . _mergeCyclicEmbeddedProtoViews ( ) . then (
150+ _ => new ProtoViewRef ( hostAppProtoView ) ) ) ;
147151 }
148152
149153 private _compile ( componentBinding : DirectiveBinding ) : Promise < AppProtoView > | AppProtoView {
@@ -156,12 +160,12 @@ export class Compiler {
156160 return protoView ;
157161 }
158162
159- var pvPromise = this . _compiling . get ( component ) ;
160- if ( isPresent ( pvPromise ) ) {
163+ var resultPromise = this . _compiling . get ( component ) ;
164+ if ( isPresent ( resultPromise ) ) {
161165 // The component is already being compiled, attach to the existing Promise
162166 // instead of re-compiling the component.
163167 // It happens when a template references a component multiple times.
164- return pvPromise ;
168+ return resultPromise ;
165169 }
166170 var view = this . _viewResolver . resolve ( component ) ;
167171
@@ -178,14 +182,19 @@ export class Compiler {
178182 ListWrapper . map ( directives , ( directive ) => this . _bindDirective ( directive ) ) ) ;
179183
180184 var renderTemplate = this . _buildRenderTemplate ( component , view , boundDirectives ) ;
181- pvPromise =
182- this . _render . compile ( renderTemplate )
183- . then ( ( renderPv ) => {
184- return this . _compileNestedProtoViews ( componentBinding , renderPv , boundDirectives ) ;
185- } ) ;
186-
187- this . _compiling . set ( component , pvPromise ) ;
188- return pvPromise ;
185+ resultPromise = this . _render . compile ( renderTemplate )
186+ . then ( ( renderPv ) => {
187+ var protoView = this . _protoViewFactory . createAppProtoViews (
188+ componentBinding , renderPv , boundDirectives ) ;
189+ // Populate the cache before compiling the nested components,
190+ // so that components can reference themselves in their template.
191+ this . _compilerCache . set ( component , protoView ) ;
192+ MapWrapper . delete ( this . _compiling , component ) ;
193+
194+ return this . _compileNestedProtoViews ( renderPv , protoView , component ) ;
195+ } ) ;
196+ this . _compiling . set ( component , resultPromise ) ;
197+ return resultPromise ;
189198 }
190199
191200 private _removeDuplicatedDirectives ( directives : List < DirectiveBinding > ) : List < DirectiveBinding > {
@@ -194,24 +203,11 @@ export class Compiler {
194203 return MapWrapper . values ( directivesMap ) ;
195204 }
196205
197- private _compileNestedProtoViews ( componentBinding , renderPv , directives ) : Promise < AppProtoView > |
198- AppProtoView {
199- var protoViews =
200- this . _protoViewFactory . createAppProtoViews ( componentBinding , renderPv , directives ) ;
201- var protoView = protoViews [ 0 ] ;
202- if ( isPresent ( componentBinding ) ) {
203- var component = componentBinding . key . token ;
204- if ( renderPv . type === renderApi . ViewType . COMPONENT ) {
205- // Populate the cache before compiling the nested components,
206- // so that components can reference themselves in their template.
207- this . _compilerCache . set ( component , protoView ) ;
208- MapWrapper . delete ( this . _compiling , component ) ;
209- } else {
210- this . _compilerCache . setHost ( component , protoView ) ;
211- }
212- }
206+ private _compileNestedProtoViews ( renderProtoView : renderApi . ProtoViewDto ,
207+ appProtoView : AppProtoView ,
208+ componentType : Type ) : Promise < AppProtoView > {
213209 var nestedPVPromises = [ ] ;
214- ListWrapper . forEach ( this . _collectComponentElementBinders ( protoViews ) , ( elementBinder ) => {
210+ this . _loopComponentElementBinders ( appProtoView , ( parentPv , elementBinder ) => {
215211 var nestedComponent = elementBinder . componentDirective ;
216212 var elementBinderDone =
217213 ( nestedPv : AppProtoView ) => { elementBinder . nestedProtoView = nestedPv ; } ;
@@ -222,24 +218,85 @@ export class Compiler {
222218 elementBinderDone ( < AppProtoView > nestedCall ) ;
223219 }
224220 } ) ;
221+ return PromiseWrapper . all ( nestedPVPromises )
222+ . then ( ( _ ) => {
223+ var appProtoViewsToMergeInto = [ ] ;
224+ var mergeRenderProtoViews = this . _collectMergeRenderProtoViewsRecurse (
225+ renderProtoView , appProtoView , appProtoViewsToMergeInto ) ;
226+ if ( isBlank ( mergeRenderProtoViews ) ) {
227+ throw new BaseException ( `Unconditional component cycle in ${ stringify ( componentType ) } ` ) ;
228+ }
229+ return this . _mergeProtoViews ( appProtoViewsToMergeInto , mergeRenderProtoViews ) ;
230+ } ) ;
231+ }
225232
226- if ( nestedPVPromises . length > 0 ) {
227- return PromiseWrapper . all ( nestedPVPromises ) . then ( ( _ ) => protoView ) ;
228- } else {
229- return protoView ;
233+ private _mergeProtoViews (
234+ appProtoViewsToMergeInto : AppProtoView [ ] ,
235+ mergeRenderProtoViews :
236+ List < renderApi . RenderProtoViewRef | List < any > > ) : Promise < AppProtoView > {
237+ return this . _render . mergeProtoViewsRecursively ( mergeRenderProtoViews )
238+ . then ( ( mergeResults : List < renderApi . RenderProtoViewMergeMapping > ) => {
239+ // Note: We don't need to check for nulls here as we filtered them out before!
240+ // (in RenderCompiler.mergeProtoViewsRecursively and
241+ // _collectMergeRenderProtoViewsRecurse).
242+ for ( var i = 0 ; i < mergeResults . length ; i ++ ) {
243+ appProtoViewsToMergeInto [ i ] . mergeMapping =
244+ new AppProtoViewMergeMapping ( mergeResults [ i ] ) ;
245+ }
246+ return appProtoViewsToMergeInto [ 0 ] ;
247+ } ) ;
248+ }
249+
250+ private _loopComponentElementBinders ( appProtoView : AppProtoView , callback : Function ) {
251+ appProtoView . elementBinders . forEach ( ( elementBinder ) => {
252+ if ( isPresent ( elementBinder . componentDirective ) ) {
253+ callback ( appProtoView , elementBinder ) ;
254+ } else if ( isPresent ( elementBinder . nestedProtoView ) ) {
255+ this . _loopComponentElementBinders ( elementBinder . nestedProtoView , callback ) ;
256+ }
257+ } ) ;
258+ }
259+
260+ private _collectMergeRenderProtoViewsRecurse (
261+ renderProtoView : renderApi . ProtoViewDto , appProtoView : AppProtoView ,
262+ targetAppProtoViews : AppProtoView [ ] ) : List < renderApi . RenderProtoViewRef | List < any > > {
263+ targetAppProtoViews . push ( appProtoView ) ;
264+ var result = [ renderProtoView . render ] ;
265+ for ( var i = 0 ; i < appProtoView . elementBinders . length ; i ++ ) {
266+ var binder = appProtoView . elementBinders [ i ] ;
267+ if ( binder . hasStaticComponent ( ) ) {
268+ if ( isBlank ( binder . nestedProtoView . mergeMapping ) ) {
269+ // cycle via an embedded ProtoView. store the AppProtoView and ProtoViewDto for later.
270+ this . _unmergedCyclicEmbeddedProtoViews . push (
271+ new RecursiveEmbeddedProtoView ( appProtoView , renderProtoView ) ) ;
272+ return null ;
273+ }
274+ result . push ( binder . nestedProtoView . mergeMapping . renderProtoViewRef ) ;
275+ } else if ( binder . hasEmbeddedProtoView ( ) ) {
276+ result . push ( this . _collectMergeRenderProtoViewsRecurse (
277+ renderProtoView . elementBinders [ i ] . nestedProtoView , binder . nestedProtoView ,
278+ targetAppProtoViews ) ) ;
279+ }
230280 }
281+ return result ;
231282 }
232283
233- private _collectComponentElementBinders ( protoViews : List < AppProtoView > ) : List < ElementBinder > {
234- var componentElementBinders = [ ] ;
235- ListWrapper . forEach ( protoViews , ( protoView ) => {
236- ListWrapper . forEach ( protoView . elementBinders , ( elementBinder ) => {
237- if ( isPresent ( elementBinder . componentDirective ) ) {
238- componentElementBinders . push ( elementBinder ) ;
284+ private _mergeCyclicEmbeddedProtoViews ( ) {
285+ var pvs = this . _unmergedCyclicEmbeddedProtoViews ;
286+ this . _unmergedCyclicEmbeddedProtoViews = [ ] ;
287+ var promises = pvs . map ( entry => {
288+ var appProtoView = entry . appProtoView ;
289+ var mergeRenderProtoViews = [ entry . renderProtoView . render ] ;
290+ appProtoView . elementBinders . forEach ( ( binder ) => {
291+ if ( binder . hasStaticComponent ( ) ) {
292+ mergeRenderProtoViews . push ( binder . nestedProtoView . mergeMapping . renderProtoViewRef ) ;
293+ } else if ( binder . hasEmbeddedProtoView ( ) ) {
294+ mergeRenderProtoViews . push ( null ) ;
239295 }
240296 } ) ;
297+ return this . _mergeProtoViews ( [ appProtoView ] , mergeRenderProtoViews ) ;
241298 } ) ;
242- return componentElementBinders ;
299+ return PromiseWrapper . all ( promises ) ;
243300 }
244301
245302 private _buildRenderTemplate ( component , view , directives ) : renderApi . ViewDefinition {
@@ -299,3 +356,7 @@ export class Compiler {
299356 }
300357 }
301358}
359+
360+ class RecursiveEmbeddedProtoView {
361+ constructor ( public appProtoView : AppProtoView , public renderProtoView : renderApi . ProtoViewDto ) { }
362+ }
0 commit comments