Search code examples
javascriptqmlblackberry-10

Accessing parent functions in QML


I'm currently creating a Twitter client as a way of learning to develop for Blackberry 10. I'm currently trying to create a context menu for a custom ListView item, which on selection will show a dialog in the main layout. However, I cannot get the selected list item to call any functions from the parent Page.

Here is the QML from my custom list item:

import bb.cascades 1.0

Container {

    property alias avatar: tracker.imageSource
    property alias username: txtTweetUser.text
    property alias tweetText: txtTweetContent.text
    property alias tweetTime: txtTweetTime.text

    signal sendReply(string username)

    function cout(text) {
        console.debug("[DEBUG] " + text);
    }

    id: itemTweet

    preferredWidth: 768;
    preferredHeight: 200

    // Actions
    contextActions: [
        ActionSet {


            title: "Action Set"
            subtitle: "This is an action set."

            actions: [
                ActionItem {                    
                    title: "Reply"
                    imageSource: "asset:///reply.png"
                    onTriggered: {
                        itemTweet.sendReply(txtTweetUser.text);
                    }
                },
                ActionItem {
                    title: "Retweet"
                    imageSource: "asset:///retweet.png"
                },
                ActionItem {
                    title: "Favourite"
                    imageSource: "asset:///fav.png"
                }
            ]
        } // end of ActionSet   
    ] // end of contextActions list

   <Layout for the List Item>...

    }
}

And for the main QML file:

import bb.cascades 1.0

TabbedPane {
    id: tabbedPane
    showTabsOnActionBar: true

    Tab {
        id: tabTimeline
        title: "Timeline"
        imageSource: "asset:///twitter-white.png"

        Page {         
            id: pageTimeline

            signal openReply
            signal showNewTweet

            function cout(text) {
            console.debug("[DEBUG] " + text);
        }

            function showTweetWindow(username) {
                pageTimeline.dialogNewTweet.text = username;
                pageTimeline.dialogNewTweet.visible = true;
            }

            // Title bar
            titleBar: TitleBar {
                visibility: Overlay
                title: "Twitter"

                acceptAction: ActionItem {
                    id: btnNewTweet
                    title: "+"
                    ActionBar.placement: ActionBarPlacement.OnBar
                    onTriggered: {
                       pageTimeline.cout("action selected");
                       dialogNewTweet.visible = true;
                    }
                }
            }

            // Main content
            content: Container {

                layout: AbsoluteLayout {}

                Container { 
                    // Listview for the tweets
                    ListView {
                        id: lstTweets
                        objectName: "lstTweets"                 

                // Components to display the rows                   
                        listItemComponents: [
                            ListItemComponent {
                                id: listItem
                                type: "listItem"
                                TweetItem {                                                                                     
                                    tweetText: ListItemData.content
                                    tweetTime: ListItemData.time
                                    avatar: ListItemData.avatar
                                    username: ListItemData.username

                                    onSendReply: {
                                        cout("Reply selected in parent to " + username);
                                        pageTimeline.showTweetWindow(username);
                                    }
                                }
                            }
                        ]

                        onSelectionChanged: {
                        }

                        function itemType(data, indexPath) {
                            if (indexPath.length == 1) {
                                return "header";
                            } else {
                                return "listItem";
                            }
                        } 

                    }
                }

                DialogNewTweet {
                    id: dialogNewTweet
                    visible: false

                    onShowNewTweet: {
                        dialogNewTweet.visible = true;
                    }
                }

            }
            // End container    
        }
    }
    ... <Other tabs> ...

}

So when the main QML file receives the SendReply signal, it's suppposed to call showTweetWindow(username) which then makes dialogNewTweet visible, but instead I get the error ReferenceError: Can't find variable: pageTimeline. It's definitely a scope issue, but I can't figure out what I'm doing wrong, or if I need to restructure this.


Solution

  • Answer here:

    https://community.blackberry.com/message/182467#182467

    What I need to achieve is to operate on a list of data, e.g. delete an item using ContextAction in ListItem in QML, then call a C++ method of an object in QmlDocument's contextProperty. here is how I did it:

    C++ code:

    QmlDocument *qml = QmlDocument::create("accounts.qml");
    root = qml->createRootNode<AbstractPane>();
    
    //Here set the context property, accountsManager was already created
    qml->setContextProperty("accountsManager", accountsManager);
    
    //Set the model for the list view
    ListView* accountsListView = root->findChild<ListView*>("accountsListView");
    accountsListView->setDataModel(accountsManager->getDataModel());
    

    QML code:

    Page {
        content: Container {     
            ListView {
                id: listView
                objectName: "accountsListView"     
    
            listItemComponents: [
                    ListItemComponent {
                        type: "listItem"
                        StandardListItem{
                            id: accountItem
                            title: ListItemData.username                        
                            contextActions: [
                                ActionSet {
                                    deleteAction: DeleteActionItem {
                                        title: "Delete"
                                        onTriggered: {
                                            //it does not work here, "ReferenceError: Can't find variable: accountsManager"
                                            accountsManager.doSometing();
                                        }
                                    }
                                }
                            ]
                        }
                    } 
                ]
    
            function itemType(data, indexPath) {
                return "listItem";
              }     
            } // end of ListView
        } // end of Container
    
        actions: [
            ActionItem {
                title: "Add account"
                ActionBar.placement: ActionBarPlacement.OnBar
    
                onTriggered: {
                    //It works fine here
                    accountsManager.doSomething();
                }
            }
        ]
    } // end of Page
    

    And another method:

    https://community.blackberry.com/message/453794#453794

    At the top Page level add the following line to make the label accessible.

    Page {
        id: topPage    
        onCreationCompleted: { Qt.topLabel = topLabel; }
    

    Then in the button definition you can reference the Qt.topLabel from within the list.

        listItemComponents: [
                ListItemComponent {
                    type: "item"
                    Button {
                        text: "Change To Three"
                        onClicked: {
                            Qt.topLabel.text = "Three";
                        }                        
                    }
                }
            ]