panta82
panta82

Reputation: 2721

In Angular 2+, can I detect when a directive (like routerLink) is applied to my custom component?

I have a custom button component, like this (simplified):

@Component({
    selector: 'my-button',
    template: `
        <button (click)="handleClick($event)">
            <ng-content></ng-content>
        </button>
    `
})
export class MyButtonComponent {
    @Output() public onClick: EventEmitter<Event> = new EventEmitter();

    handleClick(e) {
        this.onClick.emit(e);
    }
}

Normal use case for the button is for user to attach an onClick handler. However, some users will want to use button as a link instead, like this:

<my-button [routerLink]="'/dashboard'">Go to dashboard</my-button>

What I'd like to do in this case is render my-button as an <a> element instead of literal <button>. But in order to do that, I need to detect that user has attached the [routerLink] directive to it.

Is there any way to do that?

I know I can just add a boolean @Input() prop that will switch the rendering to <a> mode, but that feels clunky.

Upvotes: 1

Views: 360

Answers (2)

penleychan
penleychan

Reputation: 5470

You would have to use @Optional and @Host both are located in @angular/core.

On your my-button component for typescript you would need to do dependency injection like so:

export class MyButtonComponent implements OnInit {
  constructor(@Optional() @Host() routerLink: RouterLink) {
     console.log(routerLink)
  }
  ...
}

Then on your template you can check if either routerLink is null or not and render either <a> tag or <button> tag

Edit:

To get link working you would need to do this:

<a [routerLink]="routerLink.commands">
   Test
</a> 

Unfortunately I wasn't able to get <ng-content> working in a tag for some reason.

Edit 2: Reason why ng-content wasn't working is due to multiple ng-content in the component, one way to solve this is to use ng-template, for example:

<ng-container *ngIf="hasRouterLink">
  <a [routerLink]="routerLink.commands">
    <ng-container *ngTemplateOutlet="contentTpl"></ng-container>
  </a> 
</ng-container>


<ng-container *ngIf="!hasRouterLink">
  <button>
    <ng-container *ngTemplateOutlet="contentTpl"></ng-container> 
  </button> 
</ng-container>

<ng-template #contentTpl><ng-content></ng-content></ng-template>

Upvotes: 5

Yegor Kozlov
Yegor Kozlov

Reputation: 61

Do you plan to add something bigger than just text as you ng-content or there will be some complicated logic for onClick? I think your case is pretty common and usually it's solved by creating css classes to make buttons and links looks the same. And you can use <a> or <button> depends on situation and just add necessary classes

Upvotes: 0

Related Questions