NativeScript Angular自定义组件(tap)事件绑定问题求助
解决NativeScript Angular自定义组件Tap事件绑定问题
Hey,我刚好碰到过这个NativeScript Angular的小坑,给你梳理下问题原因和两个优雅的解决方案~
首先先理清为什么会出现这两个问题:
- 直接给自定义组件绑
(tap)不生效:NativeScript的tap是原生视图级别的事件,而你的<ns-custom>是Angular组件,不是NativeScript原生视图元素,所以原生事件不会自动冒泡到组件标签上——这和纯Angular里的(click)不一样,纯Angular的click是DOM事件,自定义组件作为DOM节点会接收,但NativeScript的事件系统是针对原生控件的。 - 转发事件后触发两次:当你在内部
StackLayout绑定tap并emit自定义事件时,NativeScript的原生事件会继续冒泡到组件的宿主原生容器,而父组件给<ns-custom>绑定的(tap),其实也会被NativeScript解析为给宿主容器绑定原生tap事件,所以就出现了“自己emit一次+原生事件触发一次”的重复。
下面是两种优雅的解决思路:
方案一:用@HostListener监听宿主事件(推荐)
这种方式不需要修改模板,直接在组件类里监听宿主元素的tap事件,然后转发为组件的输出事件,逻辑更集中,也不会有重复触发的问题。
修改自定义组件的TS代码:
import { Component, EventEmitter, Output, HostListener } from '@angular/core'; @Component({ selector: 'ns-custom', templateUrl: './custom.component.html', styleUrls: ['./custom.component.css'] }) export class CustomComponent{ @Output() tap = new EventEmitter<void>(); constructor() { } // 监听宿主元素的tap事件 @HostListener('tap') onHostTap() { this.tap.emit(); } }
模板保持原样即可,不需要绑定tap:
<StackLayout> <Label text="Title"></Label> <Label text="some text"></Label> </StackLayout>
父组件的使用方式完全不变:
<ns-custom (tap)="onCustomComponentClick()"></ns-custom>
onCustomComponentClick() { console.log('custom-component was tapped'); }
这个方案的核心是@HostListener会监听组件宿主(也就是NativeScript渲染该组件时生成的根原生视图)的tap事件,然后我们把这个事件转发为组件的输出事件,这样就只会触发一次,而且完全符合Angular组件的设计模式。
方案二:阻止内部事件冒泡
如果你更倾向于在模板里处理,可以在内部StackLayout的tap事件中阻止事件冒泡,避免原生事件继续传到宿主容器,这样就不会出现重复触发。
修改自定义组件的模板:
<StackLayout (tap)="emitTap($event)"> <Label text="Title"></Label> <Label text="some text"></Label> </StackLayout>
TS代码调整为:
import { Component, EventEmitter, Output } from '@angular/core'; import { TapGestureEventData } from '@nativescript/core'; @Component({ selector: 'ns-custom', templateUrl: './custom.component.html', styleUrls: ['./custom.component.css'] }) export class CustomComponent{ @Output() tap = new EventEmitter<void>(); emitTap(event: TapGestureEventData) { // 阻止事件冒泡到宿主元素 event.stopPropagation(); this.tap.emit(); } }
父组件的使用方式同样不变,这样内部的tap事件就只会触发我们自定义的输出事件,不会再触发宿主容器的原生tap事件,也就解决了重复触发的问题。
总结
方案一更优雅,因为它把事件处理逻辑集中在组件类中,不需要修改模板,也更符合Angular的组件封装思想;方案二则适合习惯在模板中处理事件的场景。两种方案都能完美解决你的问题,不需要修改事件名称或者用hack的方式处理。
内容的提问来源于stack exchange,提问作者Lorraine Ram-El
相关产品推荐
相关产品推荐

