NativeScript / NativeScript

⚡ Empowering JavaScript with native platform APIs. ✨ Best of all worlds (TypeScript, Swift, Objective C, Kotlin, Java, Dart). Use what you love ❤️ Angular, Capacitor, Ionic, React, Solid, Svelte, Vue with: iOS (UIKit, SwiftUI), Android (View, Jetpack Compose), Dart (Flutter) and you name it compatible.
https://nativescript.org
MIT License
24.2k stars 1.64k forks source link

[Angular 2] ListView itemTap, tap, longPress events handling #2977

Open michelebombardi opened 7 years ago

michelebombardi commented 7 years ago

Did you verify this is a real problem by searching [Stack Overflow]

Yes

Tell us about the problem

If both longPress and tap event are bound to a ListView, it's difficult handling both events without losing some feature.

Which platform(s) does your issue occur on?

Android (not tested on iOS yet)

Please provide the following version numbers that your issue occurs with:

I will report my tests to explain in detail the problem:

CASE 1: itemTap and longPress events on ListView

<ListView id="contactsList" [items]='contacts' (itemTap)="onItemTap($event)" (longPress)="onItemPress($event)">
    <template let-contact='item'>
        <GridLayout columns="80, *">
            <Image col="0" [src]="contact.icon" stretch="aspectFill" verticalAlignment="center" horizontalAlignment="center" class="contact-icon"></Image>
            <StackLayout col="1" verticalAlignment="center" horizontalAlignment="left" color="black" margin="5 0">
                <Label [text]="contact.name" verticalAlignment="center" horizontalAlignment="left" textWrap="true" id="personalContactName" class="h2"></Label>
                <Label [text]="contact.phone" verticalAlignment="center" horizontalAlignment="left" class="h3"></Label> 
                <Label [text]="contact.notes" verticalAlignment="center" horizontalAlignment="left" textWrap="true" class="h3 font-italic"></Label>
            </StackLayout>
        </GridLayout>
     </template>
</ListView>

CASE 2: tap and longPress events on ListView

<ListView id="contactsList" [items]='contacts' (tap)="onItemTap($event)" (longPress)="onItemPress($event)">
    <template let-contact='item'>
        <GridLayout columns="80, *">
            <Image col="0" [src]="contact.icon" stretch="aspectFill" verticalAlignment="center" horizontalAlignment="center" class="contact-icon"></Image>
            <StackLayout col="1" verticalAlignment="center" horizontalAlignment="left" color="black" margin="5 0">
                <Label [text]="contact.name" verticalAlignment="center" horizontalAlignment="left" textWrap="true" id="personalContactName" class="h2"></Label>
                <Label [text]="contact.phone" verticalAlignment="center" horizontalAlignment="left" class="h3"></Label> 
                <Label [text]="contact.notes" verticalAlignment="center" horizontalAlignment="left" textWrap="true" class="h3 font-italic"></Label>
            </StackLayout>
        </GridLayout>
     </template>
</ListView>

CASE 3: tap and longPress events on GridLayout

<ListView id="contactsList" [items]='contacts' >
    <template let-contact='item'>
        <GridLayout columns="80, *" (tap)="onTap(contact)" (longPress)="onLongPress(contact)"> 
            <Image col="0" [src]="contact.icon" stretch="aspectFill" verticalAlignment="center" horizontalAlignment="center" class="contact-icon"></Image>
            <StackLayout col="1" verticalAlignment="center" horizontalAlignment="left" color="black" margin="5 0">
                <Label [text]="contact.name" verticalAlignment="center" horizontalAlignment="left" textWrap="true" id="personalContactName" class="h2"></Label>
                <Label [text]="contact.phone" verticalAlignment="center" horizontalAlignment="left" class="h3"></Label> 
                <Label [text]="contact.notes" verticalAlignment="center" horizontalAlignment="left" textWrap="true" class="h3 font-italic"></Label>
            </StackLayout>
        </GridLayout>
     </template>
</ListView>

CASE 4: loaded event on ListView

I've also tried to attach events using the loaded event and putting the following code in my component:

contactsListLoaded(args) {
    var listView = args.object as ListView;
    listView.on("itemTap, longPress", (args) => {
        console.log("Event: " + args.eventName + ", sender: " + args.object);
    });
}

In this case, also changing the attached events similarly to previous cases, I experience same issues described above.

NOTE I tried to access the long pressed item by using args.object.bindingContext (as explained in another closed issue) but that property always return undefined.

How can I get tap/itemTap and longPress events work correctly, access index of tapped/longPressed item and keep the ripple effect active?

Thank you very much!

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/38751917-angular-2-listview-itemtap-tap-longpress-events-handling?utm_campaign=plugin&utm_content=tracker%2F12908224&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F12908224&utm_medium=issues&utm_source=github).
NathanaelA commented 7 years ago

I'll chime in here; I've noticed the same thing, but I can fill in one thing on iOS; longPress will not trigger a tap, where the tap event is still fired on Android as bm-software stated. Please note, if you add the "touch" even on iOS; it messes up both the other events. On Android all three can works together.

NickIliev commented 7 years ago

Hello @bm-software

Indeed LongPress gesture will trigger itemTap for the NativeScript list-view on gesture end. We will investigate that behavior further. Meanwhile, it is expected taht Tap and LongPress will not return the index of your list-view item. The list view has its own gesture itemTap with ItemEventData which returns the item index.

However, in angular you can create your own index for the item template and pass it to the gestures you are using for your inner component. e.g. list.component.html

<ListView [items]="countries" class="list-group">
    <template let-country="item" let-myIndex="index">
        <GridLayout class="list-group-item" [id]="myIndex" (tap)="onTap($event)" (longPress)="onLongPress($event)">
            <Label [text]="country.name"></Label>
        </GridLayout>
    </template>
</ListView>

list.component.ts

public onTap(args: EventData) {
    console.log(args.object);
    console.log("onTap id: "  + args.object.get("id"));
}

public onLongPress(args: EventData) {
    console.log(args.object);
    console.log("onLongPress id: "  + args.object.get("id"));
}

Still, this won't resolve the missing ripple effect - as a workaround, you can create your own ripple with native code and attach it to the clickable components in your template.

michelebombardi commented 7 years ago

@NickIliev Thank you for your exaustive answer. A solution may not be adding an itemHold event like Telerik UI RadListView? I'm trying using it and events works fine but also in RadListView the ripple effect doesn't work :(

imaginationcoder commented 7 years ago

Hi, Some times longPress event automatically calling tap functionality.. for example I want to show some dialog data with 'close' button onLongPress. When I longPress it showing Dialog but page is redirected i.e calling the (tap) functionality. I want to prevent the tap on longPress any one can help me how to handle this?

<StackLayout class="list-group-item" (longPress)="onLongPressKid($event, item)" (tap)="redirectToKidDashboard(item)"> </StackLayout>

darkbasic commented 6 years ago

Any chance to get this fixed? Events are supposed to bubble, propagating up through the "DOM" (I'm not sure how they are supposed to work in Nativescript, but the concept should be similar). Yet if I listen for them they seem to stop propagating (that's why we loose the ripple effect).

Also I don't understand why putting an @HostListener directive inside a Label works, while using the very same directive against a custom component doesn't.

What's even more strange is that if I put my custom component inside a StackLayout and I apply my directive to it then it works once again.

Can you please explain me how Event propagation works in Nativescript and how I am supposed to solve this? Thanks

tsonevn commented 6 years ago

reproducible with modules 4.1

rjgpereira commented 5 years ago

any update on this? I'm also experiencing this behaviour (only in iOS, Android works fine)

jerbob92 commented 4 years ago

I also still have this issue on iOS. We currently fixed it by making our own tap/longpress handler by using the touch event. I explained it here: https://stackoverflow.com/a/60724213/999424

zeejay09 commented 3 years ago

is this resolved already?

darkbasic commented 3 years ago

Guys it's been five years without any official response from the nativescript team, I'm pretty confident it won't ever be fixed at this point.