1

I have a custom button component with a structure like this one (simplified version of code for readability purposes):

HTML:

<button type="button" class="btn btn-primary">
    <ng-content select="[appButtonIcon]"></ng-content>
    <i *ngIf="iconClass && !customIcon" class="fa {{iconClass}}"></i>
    {{isIconButton ? '' : text}}
</button>

TS:

@ContentChild(ButtonIconDirective, { static: true }) customIcon: ButtonIconDirective;

And it works fine. Note, please, the line <ng-content select="[appButtonIcon]"></ng-content>. I created a custom directive appButtonIcon so the user can add a custom icon, like SVG content, for example. This is working properly, I can do both:

1

<custom-button text="Test" (clicked)="onClick()">
    <svg appButtonIcon ...>
        <path ... />
    </svg>
</custom-button>

2

<custom-button text="Test" (clicked)="onClick()" iconClass="fa-folder">
</custom-button>

And they work properly. The problem starts when I add this custom button as part of another custom component, my custom datepicker toggle. When I do so, only of the options will work. I can only use a regular icon class or always use a custom icon with the appButtonIcon directive:

Custom datepicker toggle HTML

Ideal(?):

<app-custom-button (click)="open($event)" iconClass="fa-calendar" isIconButton="true">
    <ng-content select="[appButtonIcon]"></ng-content>
</app-custom-button>

But it doesn't work, app-custom-button can never read the content I add inside the custom datepicker toggle with the directive appButtonIcon.

Version only working if custom icons are set:

<app-custom-button (click)="open($event)" iconClass="fa-calendar" isIconButton="true">
    <ng-container appButtonIcon>
        <ng-content select="[appButtonIcon]"></ng-content>
    </ng-container>
</app-custom-button>

The problem with this approach is, that, since I had to manually add the ng-container with the appButtonIcon directive, it's always going to be read by the button (obviously), even when there's no content set by the user of the custom datepicker toggle. But if I remove that, the inner content is never read, and the fa-calendar class always used. Is there a way to fix that?

Please take a look at StackBlitz for a "working" example: https://stackblitz.com/edit/angular-ymxtmv

1 Answer 1

1

This is possible using the ngProjectAs directive. You can forward the reference to the custom-button by doing the following:

<app-custom-button (click)="open($event)" iconClass="fa-calendar" isIconButton="true">
    <ng-content ngProjectAs="[appButtonIcon]" select="[appButtonIcon]"></ng-content>
</app-custom-button>

With this change you will need to modify the custom button a bit to account for the case when no customIcon is provided. ngProjectAs is a compiler trick to tell angular to interpret it as a ButtonIconDirective but it is not actually one so at run time the reference in the class is undefined. Update the custom button template to the following:

<button type="button" class="btn btn-primary">
  <span #ref>
    <ng-content select="[appButtonIcon]"></ng-content>
  </span>
  <i *ngIf="iconClass && ref.childNodes.length === 0" class="fa {{iconClass}}"></i>
  {{isIconButton ? '' : text}}
</button>

This ref logic was derived from this question: In Angular 2 how to check whether <ng-content> is empty?

StackBlitz

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! It sounds a little bit "hacky" to me, but I honestly didn't think it'd look any different than that :D

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.