Nik
Nik

Reputation: 759

How to update QML ListView only after XmlListModel is ready?

I have a XmlListModel which initially queries a local XML file which has a list of common world cities along with their latitude and longitude. In case the user cannot find the city in that local list, the XML ListModel then queries an online API to retrieve the city's latitude, longitude.

So as shown in the code, by default it shows the local city list by using the localCityUrl() function as its source.

XmlListModel {
        id: searchCityModel;

        source: localCityUrl()
        query: "/geonames/geoname"

        XmlRole { name: "city"; query: "toponymName/string()"; isKey: true }
        XmlRole { name: "country"; query: "countryName/string()"; isKey: true }
        XmlRole { name: "lat"; query: "lat/string()"; isKey: true }
        XmlRole { name: "lng"; query: "lng/string()"; isKey: true }

        onSourceChanged: reload();
    }

ListView {
        id: worldList

        anchors { left: parent.left; right: parent.right }
        height: units.gu(50)
        model: searchCityModel
        currentIndex: -1            

        delegate: Column {
            text: searchCityModel.status == XmlListModel.Ready ? searchCityModel.get(index).city + ", " + searchCityModel.get(index).country : console.log("Loading..")
            selected: worldList.currentIndex == index;
        }
    }

At this point all is fine. However when the user searches for a city online (because it is not available in the local list), it changes the source to query the online API which by default always returns 5 results (the best matches to a user's search term) .

I have included a back button, which when clicked would change the XML ListModel source back to the local file using the code below,

searchCityModel.source = localCityUrl();

However on doing that, I get error messages that,

TypeError: Cannot read property 'city' of undefined

It is basically complaining about the line where it is trying to read the Xml ListModel's value to assign to the text delegate.

How do I ensure that it only tries to do that when the Xml ListModel is ready? I have already tried using onStatusChanged in the Xml ListModel.

The weird part is that it complains about the source change only when changing from online to local. So when the user searches online for the first time, it does not display any error. But the error only appears when the user presses the back button where it switches to the local source.

Upvotes: 0

Views: 2102

Answers (1)

Deadron
Deadron

Reputation: 5289

You are actually not properly using the model in your ListModel and this might be contributing to your problem. Your delegate should use the attributes exposed to the delegate by the underlying model. By calling searchCityModel.get(index) you are not using the model that you bound to the ListView. Your delegate should look more like the following

ListView {
        id: worldList

        anchors { left: parent.left; right: parent.right }
        height: units.gu(50)
        model: searchCityModel    

        delegate: Rectangle {
            width: worldList.width
            height: 20 //This has to be hardcoded to avoid the binding loop with the MouseArea

            text: city + ", " + country
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    worldList.currentIndex = index;
                }
            }
        }
    }

Upvotes: 2

Related Questions