2

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?

5
  • 1
    Unlike Angular Web, property / event binding on a custom component doesn't automatically map to it's root element. You must use Input / Output decorators to expose attribute / events. If your event is fired twice, then please share a Playground sample where the issue can be reproduced. Commented Sep 29, 2019 at 8:54
  • 2
    I was unable to reproduce this in playground. I'm using tns v5.4 and can't upgrade ATM. It emits tap twice if both child and parent component have (tap) binding. The way to solve this is by binding (tap) on the custom child component, and then emit an event with an @Output decorator named something else other than tap (say tap_) so that the event propagation has this logic: custom component listens to tap and then emits a different event tap_ to its parent, that listens to tap_ events. This way only the custom component's tap event is picked by angular and is fired only once Commented Sep 29, 2019 at 12:42
  • May be you can setup a Git repo in that case. Commented Sep 29, 2019 at 13:27
  • Did you get any fixes or workover? Commented May 22, 2020 at 4:48
  • You can workaround this only by "bubbling up" a tap event from child to parent, like this: In the child component, set an @Output() tap_ = new EventEmitter();, and a function emitTap() { this.tap_.emit(); }. In the html, bind this function to the tap event on the element: (tap)="emitTap(). Then, in the parent element, set an event listener on the tap_ event: (tap_)="doSomethingByParent()", and write the code you need in parent's doSomethingByParent() { // ... }. You can pass angular's $event between those events if needed. Commented May 24, 2020 at 7:42

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.