Creating a reusable button component in Angular
Asked Answered
D

4

17

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() {
      }
    }
Dekaliter answered 13/4, 2018 at 22:47 Comment(0)
B
24

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

Badger answered 13/4, 2018 at 23:23 Comment(4)
I refreshed the app with a new StackBlitz link as your link doesn't work anymore (stackblitz.com/edit/angular-gghsax). Furthermore I think you were missing @Input() label: string; as a variable declaration in button.component.ts (at least that error was thrown) - might be a compatiblity issue (Angular 7 now). However, awesome work!Roadblock
lets say i want a icon in the button (google icons like shopping cart). How do i add that dynamically?Zoophyte
You can use angular Content Projection to do that. see this example stackblitz.com/edit/…Badger
Hard coded IDs like id = "button" should be avoid in a component. Components are meant to be reusable and IDs must be unique. Otherwise markup conformity is compromised. One more point: I don't think wrapping each button in a div is a straight forward.Weltschmerz
C
11

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>
Contessacontest answered 25/4, 2019 at 20:52 Comment(2)
Seems like having the event named click will trigger the click event twice, so I would stick to the onClick name.Hardness
Or, you can use event.stopPropagation() like described here: https://mcmap.net/q/743784/-why-click-function-triggers-twice-for-custom-component-in-angular-2Hardness
T
1

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';

Tracheo answered 16/12, 2019 at 11:16 Comment(0)
C
0

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);
  }
}
Confessedly answered 6/4, 2024 at 0:5 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.