@@ -24,6 +24,7 @@ import {ComponentUrlMapper} from './component_url_mapper';
2424import { ProtoViewFactory } from './proto_view_factory' ;
2525import { UrlResolver } from 'angular2/src/services/url_resolver' ;
2626import { AppRootUrl } from 'angular2/src/services/app_root_url' ;
27+ import { ElementBinder } from './element_binder' ;
2728
2829import * as renderApi from 'angular2/src/render/api' ;
2930
@@ -90,7 +91,7 @@ export class Compiler {
9091 private _appUrl : string ;
9192 private _render : renderApi . RenderCompiler ;
9293 private _protoViewFactory : ProtoViewFactory ;
93- private _unmergedCyclicEmbeddedProtoViews : RecursiveEmbeddedProtoView [ ] = [ ] ;
94+ private _protoViewsToBeMerged : AppProtoView [ ] = [ ] ;
9495
9596 /**
9697 * @private
@@ -146,8 +147,37 @@ export class Compiler {
146147 return this . _compileNestedProtoViews ( hostRenderPv , protoView , componentType ) ;
147148 } ) ;
148149 }
149- return hostPvPromise . then (
150- hostAppProtoView => this . _mergeCyclicEmbeddedProtoViews ( ) . then ( _ => hostAppProtoView . ref ) ) ;
150+ return hostPvPromise . then ( hostAppProtoView =>
151+ this . _mergeUnmergedProtoViews ( ) . then ( _ => hostAppProtoView . ref ) ) ;
152+ }
153+
154+ private _mergeUnmergedProtoViews ( ) : Promise < any > {
155+ var protoViewsToBeMerged = this . _protoViewsToBeMerged ;
156+ this . _protoViewsToBeMerged = [ ] ;
157+ return PromiseWrapper . all ( protoViewsToBeMerged . map ( ( appProtoView ) => {
158+ return this . _render . mergeProtoViewsRecursively (
159+ this . _collectMergeRenderProtoViews ( appProtoView ) )
160+ . then ( ( mergeResult : renderApi . RenderProtoViewMergeMapping ) => {
161+ appProtoView . mergeMapping = new AppProtoViewMergeMapping ( mergeResult ) ;
162+ } ) ;
163+ } ) ) ;
164+ }
165+
166+ private _collectMergeRenderProtoViews (
167+ appProtoView : AppProtoView ) : List < renderApi . RenderProtoViewRef | List < any > > {
168+ var result = [ appProtoView . render ] ;
169+ for ( var i = 0 ; i < appProtoView . elementBinders . length ; i ++ ) {
170+ var binder = appProtoView . elementBinders [ i ] ;
171+ if ( isPresent ( binder . nestedProtoView ) ) {
172+ if ( binder . hasStaticComponent ( ) ||
173+ ( binder . hasEmbeddedProtoView ( ) && binder . nestedProtoView . isEmbeddedFragment ) ) {
174+ result . push ( this . _collectMergeRenderProtoViews ( binder . nestedProtoView ) ) ;
175+ } else {
176+ result . push ( null ) ;
177+ }
178+ }
179+ }
180+ return result ;
151181 }
152182
153183 private _compile ( componentBinding : DirectiveBinding ) : Promise < AppProtoView > | AppProtoView {
@@ -207,7 +237,7 @@ export class Compiler {
207237 appProtoView : AppProtoView ,
208238 componentType : Type ) : Promise < AppProtoView > {
209239 var nestedPVPromises = [ ] ;
210- this . _loopComponentElementBinders ( appProtoView , ( parentPv , elementBinder ) => {
240+ this . _loopComponentElementBinders ( appProtoView , ( parentPv , elementBinder : ElementBinder ) => {
211241 var nestedComponent = elementBinder . componentDirective ;
212242 var elementBinderDone =
213243 ( nestedPv : AppProtoView ) => { elementBinder . nestedProtoView = nestedPv ; } ;
@@ -220,83 +250,50 @@ export class Compiler {
220250 } ) ;
221251 return PromiseWrapper . all ( nestedPVPromises )
222252 . 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 ) ;
253+ this . _collectMergableProtoViews ( appProtoView , componentType ) ;
254+ return appProtoView ;
230255 } ) ;
231256 }
232257
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 ] ;
258+ private _collectMergableProtoViews ( appProtoView : AppProtoView , componentType : Type ) {
259+ var isRecursive = false ;
265260 for ( var i = 0 ; i < appProtoView . elementBinders . length ; i ++ ) {
266261 var binder = appProtoView . elementBinders [ i ] ;
267262 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 ;
263+ if ( isBlank ( binder . nestedProtoView . isRecursive ) ) {
264+ // cycle via a component. We are in the tail recursion,
265+ // so all components should have their isRecursive flag set already.
266+ isRecursive = true ;
267+ break ;
273268 }
274- result . push ( binder . nestedProtoView . mergeMapping . renderProtoViewRef ) ;
275269 } else if ( binder . hasEmbeddedProtoView ( ) ) {
276- result . push ( this . _collectMergeRenderProtoViewsRecurse (
277- renderProtoView . elementBinders [ i ] . nestedProtoView , binder . nestedProtoView ,
278- targetAppProtoViews ) ) ;
270+ this . _collectMergableProtoViews ( binder . nestedProtoView , componentType ) ;
279271 }
280272 }
281- return result ;
273+ if ( isRecursive ) {
274+ if ( appProtoView . isEmbeddedFragment ) {
275+ throw new BaseException (
276+ `<ng-content> is used within the recursive path of ${ stringify ( componentType ) } ` ) ;
277+ }
278+ if ( appProtoView . type === renderApi . ViewType . COMPONENT ) {
279+ throw new BaseException ( `Unconditional component cycle in ${ stringify ( componentType ) } ` ) ;
280+ }
281+ }
282+ if ( appProtoView . type === renderApi . ViewType . EMBEDDED ||
283+ appProtoView . type === renderApi . ViewType . HOST ) {
284+ this . _protoViewsToBeMerged . push ( appProtoView ) ;
285+ }
286+ appProtoView . isRecursive = isRecursive ;
282287 }
283288
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 ) ;
295- }
296- } ) ;
297- return this . _mergeProtoViews ( [ appProtoView ] , mergeRenderProtoViews ) ;
289+ private _loopComponentElementBinders ( appProtoView : AppProtoView , callback : Function ) {
290+ appProtoView . elementBinders . forEach ( ( elementBinder ) => {
291+ if ( isPresent ( elementBinder . componentDirective ) ) {
292+ callback ( appProtoView , elementBinder ) ;
293+ } else if ( isPresent ( elementBinder . nestedProtoView ) ) {
294+ this . _loopComponentElementBinders ( elementBinder . nestedProtoView , callback ) ;
295+ }
298296 } ) ;
299- return PromiseWrapper . all ( promises ) ;
300297 }
301298
302299 private _buildRenderTemplate ( component , view , directives ) : renderApi . ViewDefinition {
@@ -356,7 +353,3 @@ export class Compiler {
356353 }
357354 }
358355}
359-
360- class RecursiveEmbeddedProtoView {
361- constructor ( public appProtoView : AppProtoView , public renderProtoView : renderApi . ProtoViewDto ) { }
362- }
0 commit comments