Nativescript: Model variables are undefined when using $Parents

Created on 13 Mar 2017  Â·  5Comments  Â·  Source: NativeScript/NativeScript

### Tell us about the problem
I'm using Nativescript with Typescript and I have found a problem when I use the special keyword _$Parents_.

Main.xml:

<Page loaded="pageLoaded">
    ...
        <ListView items="{{ items }}">
            <ListView.itemTemplate>
                    <Label text="{{ $value }}" col="0"/>
                    <Label text="myTest" tap="{{ $parents['ListView'].onTap, $parents['ListView'].onTap}}" />
            </ListView.itemTemplate>
        </ListView>
    ...
</Page>

Main.ts:

export function pageLoaded(args: EventData) {

    // Gets the page.
    let page = <Page>args.object;
    page.bindingContext= new MainModel("test");
}

Main-Model.ts:

export class MainModel extends Observable {

    ....
    private name: string;

    constructor(name: string) {
          ....
          this.name = name;
    }

    public onTap(event: EventData) {
          console.log(this.name); // Prints undefined..
    }

When I use the keyword _$Parents_ the method _onTap_ is called correctly, but the variable _name_ has the value _undefined_.

If the method is called outside the ListView the variable _name_ has the correct value "_test_".

### Which platform(s) does your issue occur on?
Android

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

  • CLI: 2.5.0
  • Cross-platform modules: 2.5.0
  • Runtime(s): 2.5.0 [Android]
question

Most helpful comment

Indeed at this very moment using the parent annotation is one of the applicable way I can think of for solving your issue. To avoid using endless .parent annotations which may lead to confusion in your code (and also as you mentioned are not very good solution when the layouts are changed) you can attach directly to the binding context of your current view's page (assuming that you have provided the binding context for the page and not for some nested elements).

e.g. (for your case)

var currentPage = args.object.page;
console.log(currentPage.bindingContext.name)

All properties available for your View can be found here

All 5 comments

In your particular case, the scope of this in your onTap method will return the binded data for your tapped list view cell. So the scope of this is no longer your view model but the object passed as binding for your cell.
e.g.

<GridLayout rows="*, auto" columns="*">

    <ListView row="0" items="{{ items }}">
        <ListView.itemTemplate>
            <StackLayout>
                <Label text="{{ $value }}" col="0"/>
                <Label text="{{ $parents['ListView'].name, $parents['ListView'].name }}" tap="{{ $parents['ListView'].onTap, $parents['ListView'].onTap }}" />
            </StackLayout>
        </ListView.itemTemplate>
    </ListView>

    <Button row="1" text="onOtherTap" tap="{{ onOtherTap }}" />

</GridLayout>
export class HelloWorldModel extends Observable {

    public name: string;
    public items: Array<number>

    constructor() {
        super();

        // Initialize default values.
        this.name = "Cell name";
        this.items = [1, 2, 3, 4, 5, 6, 7];
    }


    public onTap(args: EventData) {
        console.log(args.object); // e.g. Label

        var tappedLabel = <Label>args.object;
        console.log(tappedLabel.text)

        console.log(args.eventName); // e.g. tap

        console.log(this) // binded data for the cell template (e.g. 7)
    }

    public onOtherTap(args: EventData) {
        console.log(this); // HelloWorldModel
        console.log(this.name) // "Cell name"
    }
}

Thanks @NickIliev,
I understand..
Now to get the correct value of the variable _name_ I'm using the _.parent_

args.object.parent ... .parent

But I don't like this type of solution, because there could be problems if the DOM is changed.
Is there another practice to get the value of the model? What is the best practice?

EDIT
I could use: let context = args.object.page.bindingContext; console.log(context.name);

Indeed at this very moment using the parent annotation is one of the applicable way I can think of for solving your issue. To avoid using endless .parent annotations which may lead to confusion in your code (and also as you mentioned are not very good solution when the layouts are changed) you can attach directly to the binding context of your current view's page (assuming that you have provided the binding context for the page and not for some nested elements).

e.g. (for your case)

var currentPage = args.object.page;
console.log(currentPage.bindingContext.name)

All properties available for your View can be found here

Thank you guys, I was trying to figure this out and this is the answer.

I had to import the View first,
import { View } from ‘ui/core/view’;

Then, inside my onTap() method,

var view = <View>args.object; var model = view.page.bindingContext;

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

guillaume-roy picture guillaume-roy  Â·  3Comments

dhanalakshmitawwa picture dhanalakshmitawwa  Â·  3Comments

valentinstoychev picture valentinstoychev  Â·  3Comments

NordlingDev picture NordlingDev  Â·  3Comments

nirsalon picture nirsalon  Â·  3Comments