@@ -58,6 +58,8 @@ export function renderFlagCheckIfStmt(
5858 return o . ifStmt ( o . variable ( RENDER_FLAGS ) . bitwiseAnd ( o . literal ( flags ) , null , false ) , statements ) ;
5959}
6060
61+ // Default selector used by `<ng-content>` if none specified
62+ const DEFAULT_CONTENT_SELECTOR = '*' ;
6163export class TemplateDefinitionBuilder implements t . Visitor < void > , LocalResolver {
6264 private _dataIndex = 0 ;
6365 private _bindingContext = 0 ;
@@ -102,6 +104,12 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
102104
103105 private fileBasedI18nSuffix : string ;
104106
107+ // Whether the template includes <ng-content> tags.
108+ private _hasNgContent : boolean = false ;
109+
110+ // Selectors found in the <ng-content> tags in the template.
111+ private _ngContentSelectors : string [ ] = [ ] ;
112+
105113 constructor (
106114 private constantPool : ConstantPool , parentBindingScope : BindingScope , private level = 0 ,
107115 private contextName : string | null , private i18nContext : I18nContext | null ,
@@ -154,32 +162,14 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
154162 } ) ;
155163 }
156164
157- buildTemplateFunction (
158- nodes : t . Node [ ] , variables : t . Variable [ ] , hasNgContent : boolean = false ,
159- ngContentSelectors : string [ ] = [ ] , i18n ?: i18n . AST ) : o . FunctionExpr {
165+ buildTemplateFunction ( nodes : t . Node [ ] , variables : t . Variable [ ] , i18n ?: i18n . AST ) : o . FunctionExpr {
160166 if ( this . _namespace !== R3 . namespaceHTML ) {
161167 this . creationInstruction ( null , this . _namespace ) ;
162168 }
163169
164170 // Create variable bindings
165171 variables . forEach ( v => this . registerContextVariables ( v ) ) ;
166172
167- // Output a `ProjectionDef` instruction when some `<ng-content>` are present
168- if ( hasNgContent ) {
169- const parameters : o . Expression [ ] = [ ] ;
170-
171- // Only selectors with a non-default value are generated
172- if ( ngContentSelectors . length > 1 ) {
173- const r3Selectors = ngContentSelectors . map ( s => core . parseSelectorToR3Selector ( s ) ) ;
174- // `projectionDef` needs both the parsed and raw value of the selectors
175- const parsed = this . constantPool . getConstLiteral ( asLiteral ( r3Selectors ) , true ) ;
176- const unParsed = this . constantPool . getConstLiteral ( asLiteral ( ngContentSelectors ) , true ) ;
177- parameters . push ( parsed , unParsed ) ;
178- }
179-
180- this . creationInstruction ( null , R3 . projectionDef , parameters ) ;
181- }
182-
183173 // Initiate i18n context in case:
184174 // - this template has parent i18n context
185175 // - or the template has i18n meta associated with it,
@@ -198,6 +188,26 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
198188 // resolving bindings. We also count bindings in this pass as we walk bound expressions.
199189 t . visitAll ( this , nodes ) ;
200190
191+ // Output a `ProjectionDef` instruction when some `<ng-content>` are present
192+ if ( this . _hasNgContent ) {
193+ const parameters : o . Expression [ ] = [ ] ;
194+
195+ // Only selectors with a non-default value are generated
196+ if ( this . _ngContentSelectors . length ) {
197+ const r3Selectors = this . _ngContentSelectors . map ( s => core . parseSelectorToR3Selector ( s ) ) ;
198+ // `projectionDef` needs both the parsed and raw value of the selectors
199+ const parsed = this . constantPool . getConstLiteral ( asLiteral ( r3Selectors ) , true ) ;
200+ const unParsed =
201+ this . constantPool . getConstLiteral ( asLiteral ( this . _ngContentSelectors ) , true ) ;
202+ parameters . push ( parsed , unParsed ) ;
203+ }
204+
205+ // Since we accumulate ngContent selectors while processing template elements,
206+ // we *prepend* `projectionDef` to creation instructions block, to put it before
207+ // any `projection` instructions
208+ this . creationInstruction ( null , R3 . projectionDef , parameters , /* prepend */ true ) ;
209+ }
210+
201211 // Add total binding count to pure function count so pure function instructions are
202212 // generated with the correct slot offset when update instructions are processed.
203213 this . _pureFunctionSlots += this . _bindingSlots ;
@@ -399,8 +409,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
399409 }
400410
401411 visitContent ( ngContent : t . Content ) {
412+ this . _hasNgContent = true ;
402413 const slot = this . allocateDataSlot ( ) ;
403- const selectorIndex = ngContent . selectorIndex ;
414+ let selectorIndex = ngContent . selector === DEFAULT_CONTENT_SELECTOR ?
415+ 0 :
416+ this . _ngContentSelectors . push ( ngContent . selector ) ;
404417 const parameters : o . Expression [ ] = [ o . literal ( slot ) ] ;
405418
406419 const attributeAsList : string [ ] = [ ] ;
@@ -724,7 +737,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
724737 // template definition. e.g. <div *ngIf="showing"> {{ foo }} </div> <div #foo></div>
725738 this . _nestedTemplateFns . push ( ( ) => {
726739 const templateFunctionExpr = templateVisitor . buildTemplateFunction (
727- template . children , template . variables , false , [ ] , template . i18n ) ;
740+ template . children , template . variables , template . i18n ) ;
728741 this . constantPool . statements . push ( templateFunctionExpr . toDeclStmt ( templateName , null ) ) ;
729742 } ) ;
730743
@@ -834,8 +847,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
834847 // bindings. e.g. {{ foo }} <div #foo></div>
835848 private instructionFn (
836849 fns : ( ( ) => o . Statement ) [ ] , span : ParseSourceSpan | null , reference : o . ExternalReference ,
837- paramsOrFn : o . Expression [ ] | ( ( ) => o . Expression [ ] ) ) : void {
838- fns . push ( ( ) => {
850+ paramsOrFn : o . Expression [ ] | ( ( ) => o . Expression [ ] ) , prepend : boolean = false ) : void {
851+ fns [ prepend ? 'unshift' : ' push' ] ( ( ) => {
839852 const params = Array . isArray ( paramsOrFn ) ? paramsOrFn : paramsOrFn ( ) ;
840853 return instruction ( span , reference , params ) . toStmt ( ) ;
841854 } ) ;
@@ -856,8 +869,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
856869
857870 private creationInstruction (
858871 span : ParseSourceSpan | null , reference : o . ExternalReference ,
859- paramsOrFn ?: o . Expression [ ] | ( ( ) => o . Expression [ ] ) ) {
860- this . instructionFn ( this . _creationCodeFns , span , reference , paramsOrFn || [ ] ) ;
872+ paramsOrFn ?: o . Expression [ ] | ( ( ) => o . Expression [ ] ) , prepend ?: boolean ) {
873+ this . instructionFn ( this . _creationCodeFns , span , reference , paramsOrFn || [ ] , prepend ) ;
861874 }
862875
863876 private updateInstruction (
@@ -1398,14 +1411,14 @@ function interpolate(args: o.Expression[]): o.Expression {
13981411export function parseTemplate (
13991412 template : string , templateUrl : string ,
14001413 options : { preserveWhitespaces ?: boolean , interpolationConfig ?: InterpolationConfig } = { } ) :
1401- { errors ?: ParseError [ ] , nodes : t . Node [ ] , hasNgContent : boolean , ngContentSelectors : string [ ] } {
1414+ { errors ?: ParseError [ ] , nodes : t . Node [ ] } {
14021415 const { interpolationConfig, preserveWhitespaces} = options ;
14031416 const bindingParser = makeBindingParser ( interpolationConfig ) ;
14041417 const htmlParser = new HtmlParser ( ) ;
14051418 const parseResult = htmlParser . parse ( template , templateUrl , true , interpolationConfig ) ;
14061419
14071420 if ( parseResult . errors && parseResult . errors . length > 0 ) {
1408- return { errors : parseResult . errors , nodes : [ ] , hasNgContent : false , ngContentSelectors : [ ] } ;
1421+ return { errors : parseResult . errors , nodes : [ ] } ;
14091422 }
14101423
14111424 let rootNodes : html . Node [ ] = parseResult . rootNodes ;
@@ -1428,13 +1441,12 @@ export function parseTemplate(
14281441 new I18nMetaVisitor ( interpolationConfig , /* keepI18nAttrs */ false ) , rootNodes ) ;
14291442 }
14301443
1431- const { nodes, hasNgContent, ngContentSelectors, errors} =
1432- htmlAstToRender3Ast ( rootNodes , bindingParser ) ;
1444+ const { nodes, errors} = htmlAstToRender3Ast ( rootNodes , bindingParser ) ;
14331445 if ( errors && errors . length > 0 ) {
1434- return { errors, nodes : [ ] , hasNgContent : false , ngContentSelectors : [ ] } ;
1446+ return { errors, nodes : [ ] } ;
14351447 }
14361448
1437- return { nodes, hasNgContent , ngContentSelectors } ;
1449+ return { nodes} ;
14381450}
14391451
14401452/**
0 commit comments