Search code examples
nativescriptangular2-nativescript

Nativescript (tap) event on a custom Angular component


I'm trying to bind a (tap) event on a custom Angular component in nativescript.

I created a custom component called 'ns-custom' and tried to bind the (tap) event to it. But it doesn't work.

In the custom component I'm doing this:

<StackLayout>
    <Label text="Title"></Label>
    <Label text="some text"></Label>
</StackLayout>
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'ns-custom',
  templateUrl: './custom.component.html',
  styleUrls: ['./custom.component.css']
})
export class CustomComponent{

  constructor() { }
}

And in the parent element I'm doing this:

<ns-custom (tap)="onCustomComponentClick()"></ns-custom>
    onCustomComponentClick() {
        console.log('custom-component was tapped');
    }

I expect the (tap) event to fire when I tap the custom component, but it does not. I built the same structure in pure Angular, and the (click) event does fire if put to a custom component.

I tried to propogate the (tap) event from the custom component like below, but then it fired twice (as expected because the tap event would propogate up to the parent component if I don't use event.stopPropagation()):

<StackLayout (tap)="emitTap()">
    <Label text="Title"></Label>
    <Label text="some text"></Label>
</StackLayout>

@Component({
  selector: 'ns-custom',
  templateUrl: './custom.component.html',
  styleUrls: ['./custom.component.css']
})
export class CustomComponent{
   @Output() tap = new EventEmitter();

   emitTap() {
       this.tap.emit();
   }
}

And then catch the event on the parent component:

<ns-custom (tap)="onCustomComponentClick()"></ns-custom>
    onCustomComponentClick() {
        console.log('custom-component was tapped');
    }

But now the tap event fires twice. I can solve it by changing the EventEmitter name to something other than 'tap' (for ex. @Output() childTapped = new EventEmitter(); and <ns-custom (childTapped)="onCustomComponentClick()"></ns-custom>) or pass the $event object on tap and then use event.stopPropagation(), but this is not elegant at all.

Any idea how to solve this simple problem in an elegant way?


Solution

  • This is basically answered by @mick-morely in the comments but I thought I would write up a more descriptive example and why I think it is a useful way of doing it.

    You basically need to create a custom event for your custom component. While it seems tedious not to be able to re-use the tap event, it can actually improve your code quality, making it more descriptive.

    So if I have a custom HoleComponent describing a "Hole", it can look like this:

    Hole Template

    <GridLayout rows="*" columns="*" (tap)="emitTap()">
        <Label row="0" text="A very deep hole"></Label>
    </GridLayout>
    

    Hole Code

    @Output() holeTap = new EventEmitter();
    
    emitTap() {
        this.holeTap.emit();
    }
    

    This Hole component can then be used by a parent like this:

    Parent template

    <Hole (holeTap)="onHoleTap()"></Hole>
    

    Making the event explicitly named actually helps to make the code more readable imho. It also forces the developer to think more about the Domain they are working with which helps in conforming to the ubiquitous language if working with DDD is your thing.