Nativescript: Tap with Parameters/Arguments

Created on 19 May 2016  路  14Comments  路  Source: NativeScript/NativeScript

Hi, I'd like to pass some parameters to my tap event, similar vanilla javascript in the browser.

Example:

<Button tap="doSomething(myObject)" />

Than, in javascript I would do, similar to:

exports.doSomething = function(myObject) {
    console.log('param1', myObject.param1);
};

PS: I've tried args.view, but it get's me all the page, I also tried to add as a resource, but it happens before each tap.

Thanks

question

Most helpful comment

You can define the phone number as expando:

<Label text="{{ Contact.phone2 }}" />
<Button text="sms" phone="{{ Contact.phone2 }}" tap="sendSMS" />
Contacts.sendSMS = function(args) {
   var phone = args.object.phone;
}

Also even if your original API is not flexible enough to use it in the way you want you can always wrap it in a new view-model with desired structure. That's the beauty of MVVM.

All 14 comments

Hey @leocaseiro,

You can get the button instance from the tap event args like this:

exports.doSomething = function(arg) {
    var button = args.object;
}

If you use same tap handler for multiple buttons you can define id similar to this example:
https://github.com/NativeScript/NativeScript/issues/1769#issuecomment-198268655

You can also put something in the global context if you want to access it across the entire app.

Hi @enchev,
Thank you for your quick answer.
I was expecting something more dynamic, otherwise I'll have to duplicate a lot of code...

Am I able to duplicated id? In my scenario, I have up to 4 phone numbers(I can't change that because of the legacy API), so I have a method to call the user and another one to send SMS, for example:

<Label text="{{Contact.phone1}}" />
<Button id="phone1" tap="sendSms" />
<Button id="phone1" tap="makeCall" />

<Label text="{{Contact.phone2}}" />
<Button text="sms" id="phone2" tap="sendSMS" />
<Button text="call" id="phone2" tap="makeCall" />
exports.doSomething = function(args) {
    // use id
    console.log('phone id: ', args.object.id);
    var Contact = pageData.get('Contact');

    callTo(Contact[args.object.id]);
}

Questions:
1) Can I use the same id more than once?
2) Any way to send the values to my function similar the Angular way?

Thanks a lot!

Hi @enchev,
I didn't know, but I realised that I'm able to create any attribute and the object will receive a attribute.

Doesn't work with bind values, but works, like so:

<Label text="{{Contact.phone1}}" />
<Button objNode="phone1" tap="sendSms" />
<Button objNode="phone1" tap="makeCall" />

<Label text="{{Contact.phone2}}" />
<Button text="sms" objNode="phone2" tap="sendSMS" />
<Button text="call" objNode="phone2" tap="makeCall" />

So I can use objNode as my attribute and get the global observable.

exports.doSomething = function(args) {
    console.log('phone id: ', args.object.objNode);
    var Contact = pageData.get('Contact');

    callTo(Contact[args.object.objNode]);
}

Cheers

Hey @leocaseiro,

You can bind the tap event:

<Label text="{{ Contact.phone1 }}" />
<Button tap="{{ Contact.sendSms }}" />
<Button tap="{{ Contact.makeCall }}" />

You need to have functions to call or sms for the Contact object.

Expandos like in your last reply can work as well!

Hi @enchev,

I can bind, but I can't receive the Object values, can I? In that case, my solution would work the same way....

If your Contact object have everything needed to make a call (phone and function to call/sms the phone) you can use binding. Even if you have multiple phones per contact you can use for example Repeater to display all call/sms options. For example

 <StackLayout>
   <Label text="{Contact.name}" />
   <Repeater items="{{ Contact.phones }}">
     <Repeater.itemTemplate>
        <Label text="{{ phone_number }}" />
        <Button tap="{{ sendSms }}" />
        <Button tap="{{ makeCall }}" />
     </Repeater.itemTemplate>
   </Repeater>
 </StackLayout>

In this case your Contact object will be something like this:

class Contact {
    phones: Array<Phone>;
}

class Phone {
    phone_number: any;
    call();
    sendSms();
}

Hi @enchev, I'm aware of the repeaters which I use a lot, however, in this specific scenario, I have 4 string fields(phone1, phone2, phone3 and phone4) instead of Array<phone> as supposed to be.

Would you mind showing me an example of the Contacts with a function to send the sms? How do I'd know which button I press, then?

The far I went is:

Contacts.sendSMS = function(number) {
   //how I get the right number here, than?
}

You can define the phone number as expando:

<Label text="{{ Contact.phone2 }}" />
<Button text="sms" phone="{{ Contact.phone2 }}" tap="sendSMS" />
Contacts.sendSMS = function(args) {
   var phone = args.object.phone;
}

Also even if your original API is not flexible enough to use it in the way you want you can always wrap it in a new view-model with desired structure. That's the beauty of MVVM.

Perfect! That what exactly what I needed.

When using {N} with Angular, how would I get the GridLayout object for the below tap handler? Args.object does not seem to work. The GridLayout is a repeated element in a list view, so using template binding does not seem to be an option.

<GridLayout (tap)="doSomething()"></GridLayout>

In component file:

doSomething(){
  // want to access the GridLayout instance here, so I can give it to the native script screenshot plugin
}

Hi @karaahmed, perhaps you should open an issue on the {N} angular repo.

However, I might be able to help you.

The same way you can access any DOM element with ElementRef with Angular 2, you can with {N} Angular.

First add an id with the hash(#), like #mygrid, and set as a @ViewChild.

Then, you can access on your Component Class, on the hook ngAfterViewInit:

import {AfterViewInit, Component, ElementRef, ViewChild} from '@angular/core';
@Component({
   selector: 'my-component',
   template: `<GridLayout #mygrid (tap)="doSomething()"></GridLayout>`
})
export class MyComponent implements AfterViewInit {
    @ViewChild("mygrid") mygrid: ElementRef;
    ngAfterViewInit() {
        let myGrid = this.mygrid.nativeElement;
        console.dump(myGrid); // I am a NativeScript Grid, with `.ios`, `.android` and so on...
    }
}

PS: After that, you can use myGrid as a GridLayout with all properties.

I can't access the element that way because the GridView is in a ListView, so there are multiple instances of the GridView but only one that was tapped (that's the instance I want).

@karan1149 you can add a local variable #gridView to the specific view you want and then pass it as a parameter in your tap method

<GridLayout #gridView><Button (tap)="select(gridView)"></Button></GridView>

select(gridView) {
  let grid = gridView;
  grid.cssName = "newView";
}

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tsonevn picture tsonevn  路  3Comments

Leo-lay picture Leo-lay  路  3Comments

Pourya8366 picture Pourya8366  路  3Comments

nirsalon picture nirsalon  路  3Comments

guillaume-roy picture guillaume-roy  路  3Comments