Michael Mason
Michael Mason

Reputation: 173

Creating a reusable button component in Angular

I am trying to create a component in Angular 5 that will house a reusable template for a button. In different parts of my app buttons will call different functions, so I would like to be able to tell the given instance of the button what function to call. I know I could create an HTML tag for a button wherever I need it, but I was hoping I could create a reusable component so I can ensure formatting is consistent throughout the app.

Error

 Got interpolation ({{}}) where expression was expected at column 0 in 
        [{{functioncall}}]

Component

<div id = "button">
  <button type="button" class= "btn" (click) ="{{functioncall}}" >{{label}}</button>
</div>

And HTML

 import { Component, OnInit, Input } from '@angular/core';

    @Component({
      selector: 'app-button',
      templateUrl: './button.component.html',
      styleUrls: ['./button.component.css']
    })
    export class ButtonComponent implements OnInit {
      @Input() label:string;
      @Input() functionCall:string;

      constructor() { }

      ngOnInit() {
      }
    }

Upvotes: 17

Views: 36394

Answers (4)

ardianmaliqaj
ardianmaliqaj

Reputation: 1

This implementation allows for direct access to the host events using @HostListener without the need to add additional HTML elements such as divs or buttons within your template just to be able to access these events.

Here's an example of its usage

app.component.html

<app-button (app-mousedown)="navigateHome()">Click Me!</app-button>

button.component.ts

import { Component, EventEmitter, HostListener, Input, Output } from '@angular/core';
import { LoadingSpinnerComponent } from '../loading-spinner/loading-spinner.component';

@Component({
  selector: 'app-button',
  standalone: true,
  imports: [LoadingSpinnerComponent],
  templateUrl: './button.component.html',
  styleUrl: './button.component.scss',
})
export class ButtonComponent {
  @Input() loading = false;
  @Output('app-mousedown') event = new EventEmitter<MouseEvent>();

  @HostListener('mousedown', ['$event'])
  handleMousedown(event: MouseEvent) {
    this.event.emit(event);
  }
}

button.component.html

@if (loading) {
  <app-loading-spinner [width]="17" [height]="17" [borderWidth]="2" />
} @else {
  <ng-content> </ng-content>
}

button.component.scss

@import "../../styles.scss";

:host {
  position: relative;
  display: flex;
  padding: 10px 30px;
  justify-content: center;
  align-items: center;
  width: fit-content;
  border-radius: 10px;
  font-size: 15px;
  min-width: 100px;
  min-height: 40px;
  outline: none;
  margin-right: 7px;
  overflow: hidden;
  transition: background-color 0.1s ease;
  background-color: white;
  outline: 1px solid $color;
  cursor: pointer;
  &:hover {
    background-color: rgb(239, 243, 246, 0.8);
  }
}

Upvotes: 0

Abdullah
Abdullah

Reputation: 2953

Reusable Component - it's work for me

I have created button as reusable component

  • button.component.html

    < button type="button" class= "btn" >{{label}} < / button >

  • button.component.ts

    export class ButtonComponent {
    
          @Input() label: string;
    
          @Output() onClick = new EventEmitter<any>();
    
          constructor() {}
    
          onClickButton(event) {
    
              this.onClick.emit(event);
    
          }
     }
    
  • user.component.html

    < app-button (click)="functioncall($event)" [label]="label"> < /app-button >

  • user.component.ts

    label = 'Hello World';

Upvotes: 1

TheScrappyDev
TheScrappyDev

Reputation: 4963

In addition to @miladfm answer, I'd recommend using the <ng-content></ng-content> directive here to pass content through instead of pulling in {{label}}, assigning the @Output decorator to click instead of onClick, and using the MouseEvent type instead of any. Using these changes will allow the button component to behave syntactically more like a native button when it's consumed:

button.component.ts

...
@Output() click = new EventEmitter<MouseEvent>();

onClickButton(event) {
  this.onClick.emit(event);
}
...

button.component.html

<div id = "button">
  <button type="button" class="btn" (click)="onClickbutton($event)">
    <ng-content></ng-content>
  </button>
</div>

parent.component.ts

...
functioncall(e: MouseEvent) {
 // do stuff
}
...

parent.component.html

<app-button  (click)="functioncall($event)">Your Label Here</app-button>

Upvotes: 11

miladfm
miladfm

Reputation: 1526

you have to use the @Output decorator to emit some event (from child to parent)

button.component.ts:

@Input() label: string;
@Output() onClick = new EventEmitter<any>();

onClickButton(event) {
    this.onClick.emit(event);
  }

button.component.html:

<div id = "button">
  <button type="button" class= "btn" (click)="onClickbutton($event)" >{{label}}</button>
</div>

parent.component.ts

label = "button label"

  functioncall(event) {
    console.log('functioncall', event);
  }

parent.component.html

<app-button (onClick)="functioncall($event)" [label]="label"></app-button>

See example: https://stackblitz.com/edit/angular-gghsax

Upvotes: 24

Related Questions