16

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() {
      }
    }
HDJEMAI
  • 9,436
  • 46
  • 67
  • 93
Michael Mason
  • 163
  • 1
  • 1
  • 5

3 Answers3

23

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

chriszo111
  • 343
  • 5
  • 17
miladfm
  • 1,508
  • 12
  • 20
  • I refreshed the app with a new StackBlitz link as your link doesn't work anymore (https://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! – chriszo111 Oct 25 '18 at 13:58
  • lets say i want a icon in the button (google icons like shopping cart). How do i add that dynamically? – Tejaswi Pandava Jul 09 '20 at 17:20
  • You can use angular Content Projection to do that. see this example https://stackblitz.com/edit/angular-imiejj?file=src%2Fapp%2Fparent.component.html – miladfm Jul 09 '20 at 22:47
  • 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. – Pateta Dec 11 '21 at 00:46
10

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>
TheScrappyDev
  • 4,375
  • 2
  • 21
  • 25
  • 1
    Seems like having the event named `click` will trigger the click event twice, so I would stick to the `onClick` name. – Sergiu May 23 '19 at 10:16
  • Or, you can use `event.stopPropagation()` like described here: https://stackoverflow.com/a/42112272/612847 – Sergiu May 23 '19 at 10:23
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';

Abdullah
  • 2,393
  • 1
  • 16
  • 29