Search code examples
angulartypescriptangular-content-projection

Angular content projection when wrapping another component


I'm using a third-party component repeatedly in my application. To simplify usage, I would like to wrap it in a custom component, which sets a few default attributes.

The problem with this is, that the 3rd party component uses different ng-content select slots, which I am not able to pass through my wrapper to the 3rd party component. Is it somehow possible to work around this problem?

https://stackblitz.com/edit/angular11-contentprojection-hn4cdv?file=src%2Fapp%2Fapp.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <h4>Direct usage of ThirdPartyCard</h4>
    <third-party-card>
      <span head>This is the head.... I guess</span>
      <span body>This is just a nonsense text</span>
      Even more
    </third-party-card>

    <hr />

    <h4>Wrapped usage of ThirdPartyCard</h4>
    <app-wrapper-card>
      <span head>This is the head.... I guess</span>
      <span body>This is just a nonsense text</span>
      Even more
    </app-wrapper-card>
  `,
})
export class AppComponent {
  constructor() {}
}

@Component({
  selector: 'app-wrapper-card',
  template: `
    <third-party-card someattribute=defaultvalue1 someattribute2=defaultvalue2>
      <ng-content />
    </third-party-card>
  `,
})
export class WrapperComponent {
  constructor() {}
}

@Component({
  selector: 'third-party-card',
  template: `
    <div>Head: <ng-content select="[head]" /></div>
    <div>Body: <ng-content select="[body]" /></div>
    <div>Rest: <ng-content /></div>
  `,
})
export class ThirdPartyCardComponent {
  constructor() {}
}

results in

enter image description here


Solution

  • Your wrapper component should wrap its content slots with the 3rd party. This should do what you want.

    Your wrapper:

    <div>
       <ng-container thirdPartyHead>
          <ng-content select="[yourHead]"></ng-content>
       </ng-container>
    
       <ng-container thirdPartyBody>
          <ng-content select="[yourBody]"></ng-content>
       </ng-container>
    </div>