Multiple ng-content

103

Estou tentando construir um componente personalizado usando vários ng-contentno Angular 6, mas isso não está funcionando e não tenho ideia do porquê.

Este é o meu código de componente:

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

Estou tentando usar este componente em outro lugar e renderizar dois códigos HTML diferentes dentro bodye no cabeçalho selectde ng-content, algo assim:

<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>

Mas o componente está sendo renderizado em branco.

Vocês sabem o que posso estar fazendo de errado ou qual é a melhor maneira de renderizar duas seções diferentes no mesmo componente?

Obrigado!

Lucas santos
fonte
O stackoverflow não salvou meu segundo snippet de código: O código que estou usando no componente é algo como: <div #header> Este é o conteúdo do cabeçalho </div> <div #body> Este é o conteúdo do corpo </div>
Lucas Santos

Respostas:

194
  1. Você pode adicionar atributos fictícios headere bodyem oposição a referências de modelo (#header, #body).
  2. E transcluir usando ng-contentcom selectatributo como 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

Amit Chigadani
fonte
7
Se você não quiser renderizar div adicional ou qualquer outra tag, você deve usar <ng-container>
sobczi
3
@AmitChigadani Acredito que @sobczi significa que você pode substituir <div header>por <ng-container header>.
user12893298320392
4
Confirmo a substituição <div header>com <ng-container header>trabalhos também.
azerafati
51

Para se adequar às especificações do Web Component . Mesmo que seja Angular. Trata-se de evitar atributos para seletores como diretivas angulares ou atributos reservados com outro uso. Portanto, usamos apenas o atributo "slot". Veremos <ng-content select="[slot=foobar]">como <slot name="foobar">.

Exemplo:

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>

Resultado

This is a Hello World example.

Exemplo Stackblitz

Você pode usar qualquer nome que quiser, como "banana" ou "peixe". Mas "início" e "fim" são uma boa convenção para colocar elementos antes e depois.

Dominik
fonte
Como posso consultar esses elementos? com slot de nome.
Nexeuz
Depende das configurações angulares e do componente e do que você deseja exatamente. Você pode usar ViewChild em TS ou :hoste ::ng-deepem SCSS. Mas este é apenas um exemplo. Veja Stackblitz Talvez ::slotted/ ::contenttambém funcione. Mas não tenho certeza. A web oferecerá mais sobre este assunto. Geralmente, você deve estilizar apenas o próprio componente. E evite estilizar coisas fora (global). Caso contrário, você terá efeitos colaterais indesejados.
Dominik,
Uma boa prática é embrulhar. Veja o exemplo Stackblitz atualizado em meu último comentário. Veja o arquivo html e css do componente. Você deve preferir isso a ng-deep. <div class="end"><ng-content></ng-content></div>Por exemplo, porque este elemento está acessível no componente. O ng-content é apenas um pseudo elemento que é substituído pelo elemento ancorado externo. Então você tem que usar o seletor ng-deep.
Dominik
9

alternativamente, você pode usar:

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>
Angelo Radici
fonte
4

Complementando as demais respostas:

Você também pode fazer com tags (como <ion-card>, <ion-card-header>, e <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>

Você receberá uma mensagem de aviso, mas funcionará. Você pode suprimir as mensagens de aviso ou usar marcas conhecidas como headerou footer. No entanto, se você não gosta de nenhum desses métodos, escolha uma das outras soluções.

Wesley Gonçalves
fonte