#Problem with <ng-content/> How can my component detect if i'm inserting one or more elements inside?

7 messages · Page 1 of 1 (latest)

quasi maple
#

I have a custom footer component that should detect if i inserted one or more buttons inside, and depending on the amount of buttons inserted it should display them in a certain way, but i can't make it work properly:

app-footer-component:

export class FooterAdvancedComponent implements AfterContentInit {

  public theme = input<'primary' | 'secondary'>('secondary');
  public position = input<'start' | 'center' | 'end'>('start');

  public multiple = false;
  @ContentChildren('btn', { descendants: true }) buttons!: QueryList<any>;

  public ngAfterContentInit(): void {
    this.multiple = (this.buttons.length > 1);
  }

}
<div class="footer-container-{{theme()}}">
    <!-- Footer para múltiples botones -->
    @if (multiple) {
        <div class="footer-content flex justify-between">
            <ng-content></ng-content>      
        </div>
    }
    <!-- Footer para un solo botón -->
    @else {
        <div class="footer-content flex justify-{{position()}}">
            <ng-content></ng-content>
        </div>
    }
</div>

numeric-keyboard-component:

<app-footer-advanced>
    <app-btn-arrow-back #btn (click)="onBack()" theme="secondary" label="Volver"></app-btn-arrow-back>
    <app-btn-arrow-next #btn (click)="onContinue()" theme="tertiary" label="Continuar"></app-btn-arrow-next>
</app-footer-advanced>

(This should display both buttons with a justify-between, but instead i'm getting this on the html):

rain pecan
#

You may not have multiple ng-content. To have that, you would need to use an ng-template. But unless you simplifed the actual code, I really don't see why you have that if/else block.
Also, since you're using signal inputs, you should also use signal view children.

quasi maple
#

Hi, thanks for the answer!

I'm using that if/else block to make the footer component call as simple as possible, meaning that:

  • If i'm inserting one button, i can pass the position attribute where it should be (start, center or end, start as default).
  • If i'm inserting two or more buttons, i don't need to pass the position because multiple buttons should always have a space between (in the context of this project).

Apart from that, i don't understand how can i use multiple ng-content with ng-template

rain pecan
#

Well, don't. Just use class binding

<div [class]="'footer-container-' + theme()">
  <div 
      class="footer-content flex" 
      [class]="justifyClass()">
    <ng-content />
  </div>
}
</div>
export class FooterAdvancedComponent {
  readonly theme = input<'primary' | 'secondary'>('secondary');
  readonly position = input<'start' | 'center' | 'end'>('start');

  readonly buttons = viewChildren<unknown>('btn');
  
  readonly justifyClass = computed(() => this.buttons().length > 1 ? 'justify-between' : `justify-${this.position()});
}
quasi maple
#

I see, that's a shorter and much better solution, thanks!

night crater
#

Just as an additional info, ng-content can't be inside an if block at all

#

if you need conditional content projection you need to use ng-template