36

Is it possible to use <ng-content> (and its select option) inside a <ng-template> or does it only works within a component ?

<ng-container *ngTemplateOutlet="tpl">
  <span greetings>Hello</span>
</ng-container>

<ng-template #tpl>
  <ng-content select="[greetings]"></ng-content> World !
</ng-template>

The above code does just render World ! :(

Here is the live example

Jordan
  • 594
  • 1
  • 6
  • 14
  • AFAIK the answer is "No", because ng-content applies on component level, not template level. I won't post it as an answer since I'm not 100% it can't be changed, but I'm fairly sure. – Ingo Bürk Aug 21 '18 at 15:56
  • You may be able to do something with portals and pass a portal as a parameter. https://material.angular.io/cdk/portal/overview – Simon_Weaver Feb 26 '21 at 18:06

3 Answers3

40

As far as i know it is not possible using ng-content, but you can provide parameters to the template. So it's possible to pass another NgTemplate, which can again be used with an NgTemplateOutlet inside the original template. Here's a working example:

<ng-container *ngTemplateOutlet="tpl, context: {$implicit: paramTemplate}">
</ng-container>

<ng-template #paramTemplate>
  <span>Hello</span>
</ng-template>


<ng-template #tpl let-param>
  <ng-container *ngTemplateOutlet="param"></ng-container> World !
</ng-template>

Actually it is even possible to pass multiple templates to the original template:

<ng-container *ngTemplateOutlet="tpl, context: {'param1': paramTemplate1, 'param2': paramTemplate2}">
</ng-container>

<ng-template #paramTemplate1>
  <span>Hello</span>
</ng-template>

<ng-template #paramTemplate2>
  <span>World</span>
</ng-template>


<ng-template #tpl let-param1="param1" let-param2="param2">
  <ng-container *ngTemplateOutlet="param1"></ng-container>
  <ng-container *ngTemplateOutlet="param2"></ng-container>
</ng-template>
Grochni
  • 1,663
  • 20
  • 25
1

With some trick it's possible to use a kind of ng-content in ng-template:

    <h2>ngMacro (component)</h2>    

    <ng-template #tpl21 let-ctx>      
      <div>
      <ng-macro-content></ng-macro-content> World {{ctx.mark}}       
      </div>
    </ng-template>

    <ng-macro [template]="tpl21" [context]="{ mark: '!' }">
      Hello
    </ng-macro>

    <ng-macro [template]="tpl21" [context]="{ mark: '!!!' }">
      Hi
    </ng-macro>
    
    <h2>ngMacro (directive)</h2>    

    <ng-template #tpl22 let-ctx>      
      <div>
      <span *ngMacroContent></span>
      World {{ctx.mark}}       
      </div>
    </ng-template>

    <ng-container *ngMacro="tpl22; context: { mark: '!' }" >
      Hello
    </ng-container>

    <span *ngMacro="tpl22; context: { mark: '!!!' }" >
      Hi
    </span>

Exaple on StackBlitz for Angular 14 (can be adapted for earlier versions)

Alexander Shagin
  • 485
  • 5
  • 13
-3

You can use ng-content inside ng-template.

This is something I put together a while back demonstrating putting the ng-content somewhere on dom based on property. look at the html of tabs.component to see this in use.

https://stackblitz.com/edit/mobile-ng-content

Jason
  • 1,316
  • 13
  • 25
  • 2
    seems like the example is not doing what i wished for. it's the content of your tab **component** (`my content`) which is rendered into some of its inner template (mobile or desktop) – Jordan Jun 20 '19 at 15:09
  • 2
    You asked about putting ng-content inside ng-template. that example does that – Jason Jun 20 '19 at 15:14
  • yes but it falls into the "only works within a component" because the content comes from completely outside the current template – Jordan Jun 20 '19 at 15:22
  • 1
    Mr. Jason Edwards -- thank you. This was EXACTLY what I was looking for. Honestly, I can't understand why Jordan doesn't find this solvent. – Cody Nov 07 '19 at 23:38
  • That create this error in console : [ExpressionChangedAfterItHasBeenCheckedError](https://hackernoon.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4). So it´s wrong even if, you cannot see the bug directly on the web page. – J.BizMai May 13 '20 at 16:05
  • This was the exact answer to my situation. Thanks @Jason! Basically encapsulating the in an ng-template. Then use it to conditionally populate via ng-containers. Brilliant! – DriLLFreAK100 Jan 31 '22 at 11:09
  • @Cody it's because it doesn't solve the OP's problem, which is that they wanted the source and destination of the ng-content to all be in the same component, not to have to put the ng-content in the child component. – rooby Feb 24 '22 at 07:32
  • @rooby, it does solve what the OP asked for. The problem is that it is a rather poorly articulated question. I would even say, the problem is really that this Jason guy is getting dinged for offering a solution that several people have already found useful in searching for a solution to their problems and arriving at this article. Reread the question'; does it do that? Yes. Your interpretation says no, but look at what is actually, objectively asked. – Cody Feb 25 '22 at 22:00
  • 1
    @Cody that’s true. I agree the question is poorly worded and I don’t think this question should be downvoted as a result (except it would be better if the explanation could be in the answer instead of in stack blitz only). But I also understand why it doesn’t do what the OP was wanting to do, because the content that is output in the my-content in the tab component isn’t coming from elsewhere inside the tab component, it is coming from the parent app component. That’s what the “ or does it…” part of the question means. The OP also clarified this in a comment on the answer. – rooby Feb 26 '22 at 23:37
  • Copy. I believe you are correct. The question should have been updated after the details were worked out in the comments. – Cody Feb 28 '22 at 05:39