Source:  Twitter logo

I am trying to build a custom component using multiple ng-content in Angular 6, but this is not working and I have no idea why.

This is my component code:

<div class="header-css-class">
    <ng-content select="#header"></ng-content>
</div>
<div class="body-css-class">
    <ng-content select="#body"></ng-content>
</div>

I am trying to use this component in another place and render two different HTML code inside body and header select of ng-content, something like this:

<div #header>This should be rendered in header selection of ng-content</div>
<div #body>This should be rendered in body selection of ng-content</div>

But the component is rendering blank.

Do you guys know what I could be doing wrong or what is the best way to render two different sections in same component?

Thanks!

  1. You could add dummy attributes header and body as opposed to template references (#header, #body).
  2. And transclude using ng-content with select attribute like select="[header]".

app.comp.html

<app-child>
    <div header >This should be rendered in header selection of ng-content</div>
    <div body >This should be rendered in body selection of ng-content</div>
</app-child>

child.comp.html

<div class="header-css-class">
    <ng-content select="[header]"></ng-content>
</div>
<div class="body-css-class">
    <ng-content select="[body]"></ng-content>
</div>

DEMO

285 users liked answer #0dislike answer #0285
Amit Chigadani profile pic
Amit Chigadani

To fit the Web Component specs. Even if that's Angular. It's about avoiding attributes for selector like Angular directives or reserved attributes with another use. So, we just use the "slot" attribute. We'll see <ng-content select="[slot=foobar]"> as <slot name="foobar">.

Example:

hello-world.component.html

<ng-content select="[slot=start]"></ng-content>
<span>Hello World</span>
<ng-content select="[slot=end]"></ng-content>

app.component.html

<app-hello-world>
  <span slot="start">This is a </span>
  <span slot="end"> example.</span>
</app-hello-world>

Result

This is a Hello World example.

Stackblitz Example

You can use any name you want like "banana" or "fish". But "start" and "end" are a good convention to place elements before and after.

76 users liked answer #1dislike answer #176
Dominik profile pic
Dominik

alternatively you can use:

app.comp.html

<app-child>
    <div role="header">This should be rendered in header selection of ng-content</div>
    <div role="body">This should be rendered in body selection of ng-content</div>
</app-child>

child.comp.html

<div class="header-css-class">
    <ng-content select="div[role=header]"></ng-content>
</div>
<div class="body-css-class">
    <ng-content select="div[role=body]"></ng-content>
</div>
15 users liked answer #2dislike answer #215
Angelo Radici profile pic
Angelo Radici

Complementing the other answers:

You can also do it with custom tags (like <ion-card>, <ion-card-header>, and <ion-card-content>).

app.comp.html

<app-child>
    <app-child-header>This should be rendered in header selection of ng-content</app-child-header>
    <app-child-content>This should be rendered in content selection of ng-content</app-child-content>
</app-child>

child.comp.html

<div class="header-css-class">
    <ng-content select="app-child-header"></ng-content>
</div>
<div class="content-css-class">
    <ng-content select="app-child-content"></ng-content>
</div>

You'll get a warning message, but it will work. You can suppress the warning messages or use known tags such as header or footer. However, if you don't like any of these methods, you should go with one of the other solutions.

7 users liked answer #3dislike answer #37
Wesley Gonçalves profile pic
Wesley Gonçalves

as another option you can pass templates to the child component, and then you would have the benefit of being able to bind values to the content / templates

parent component html

<app-child
    [templateHeader]="header"
    [templateContent]="content">
</app-child>

<ng-template #header
     let-data="data"> < -- how you get dynamic data
     what ever you would like the header to say
     {{data}}
</ng-template>

<ng-template #content>
    what ever you would like the content to say or any other component
</ng-template>

child component ts

export class ChildComponent {
    @Input() templateHeader: TemplateRef<any>;
    @Input() templateContent: TemplateRef<any>;
}

child component html

<div class="header-css-class">
    <ng-container
        *ngTemplateOutlet="
        templateHeader;
        context: {   , < -- if you want to pass data to your template
            data: data
        }">
    </ng-container>
</div>
<div class="content-css-class">
    <ng-container *ngTemplateOutlet="templateContent">
    </ng-container>
</div>

for a more complete explanations of templates see this great article https://indepth.dev/posts/1405/ngtemplateoutlet

4 users liked answer #4dislike answer #44
chaimm profile pic
chaimm

if you just want to "accept" more than one component, you can use:

<ng-content select="custom-component,a"></ng-content>

This accepts elements of custom-component as well as anchor (a) elements and does not change the sequence.

1 users liked answer #5dislike answer #51
Julian Egner profile pic
Julian Egner

Copyright © 2022 QueryThreads

All content on Query Threads is licensed under the Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0).