Responding to selection

You can use a ListView to only display data, and this can be a good way to present the data neatly in your app. You can also use ListView to take action when a user selects an item in the list. To respond to the selection of an item, you need to know which item was selected. Cascades uses index paths to identify list items.

Index paths

When you construct a data model to use in a ListView, the model typically uses a hierarchical structure. Header items might be at the first level of the model, and normal items might be nested at the second level (or further levels down in the hierarchy). When a user selects an item in the list, you need to know exactly where the data for that item is located in the data model, so you can respond to the selection appropriately.

An index path represents the location of an item relative to the root item of the ListView. It's important to note that the root item of the ListView might not be the root item of the data model. The data model might contain a hierarchy that is many levels deep, but a ListView shows only the first two levels of items below its root item. You can set the item that should be used as the root item of the ListView by using the rootIndexPath property.

The index path of an item is an ordered list of integers, and each integer represents an ancestor of the item. For example, for an item that is a direct child of the root item, its index path contains a single integer. For an item that is a child of that item, its index path contains two integers. The value of each integer indicates the item's ordering relative to the other children of its parent, starting at 0. For example, a value of 3 indicates that the item is the fourth child of its parent, and a value of 0 indicates that the item is the first child of its parent.

To illustrate the concept of index paths, consider the following hierarchical data model. Each item in the model includes a piece of data, which is specified by the title property. The index path of each item (relative to the <root> item) is listed in square brackets.


A hierarchy tree demonstrating index paths.

If you use a ListView to represent this data and you don't specify a value for the rootIndexPathproperty, the ListView uses the <root> element as its root item. In this case, the list items in the first two levels of the data model are displayed in the list, but the items in the third level (the level that includes "Gala" and "Butternut") aren't displayed.

You can change the item that the ListView uses as its root item to alter the data that's displayed in the list. In the example above, you could specify the "Fruit" item as the root item, and then all child items of that item would be displayed in the list. The "Gala", "McIntosh", and "Empire" items would be included, because they would be at the second level relative to the new root item.

Responding to selection of a single item

There are two ways that you can respond to the selection of a single item in a ListView. You can perform an action immediately when a user taps a list item, or you can use a context menu for each list item to let the user select what action should be performed.

Responding to selection immediately

When a user taps an item in a list, the ListView emits the  triggered() signal and you can use the onTriggered signal handler to respond to this signal. The signal includes an indexPath parameter that specifies the index path of the tapped item. After you obtain the index path of the item that was tapped, you can use it to retrieve the item's data from the data model by calling  DataModel::data(). You can then access the item's properties and respond to the tap.

Here's how to create a list using an XML data model that's specified in an items.xml file. Each list item in the data model (not including header items) has a title property and status property. The values of the title properties are displayed as the list items, and when an item is tapped, the value of that item's status property is displayed in a  TextField.

import bb.cascades 1.0
 
Page {
    content: Container {
        // Create a ListView that uses an XML data model
        ListView {
            dataModel: XmlDataModel {
                source: "models/items.xml"
            }
             
            // Use a ListItemComponent to determine which property in the
            // data model is displayed for each list item
            listItemComponents: [
                ListItemComponent {
                    type: "listItem"
                     
                    StandardListItem {
                        // Display the value of an item's title property
                        // in the list
                        title: ListItemData.title
                    }
                }
            ]
  
            // When an item is selected, update the text in the TextField
            // to display the status of the new item
            onTriggered: {
                var selectedItem = dataModel.data(indexPath);
                textField.text = selectedItem.status;
            }
        }
      
        // Create a text field to display the status of the selected item
        TextField {
            id: textField
            text: ""
        }
    } // end of top-level Container
} // end of Page

Responding to selection using the context menu

There are times when you might not want to perform an action immediately when a user taps a list item. Instead, you may want to provide a set of multiple actions for that list item that the user can choose from. For example, for a list of contacts, you might want to let the user do any of the following when they select a contact from the list:

  • Send an email
  • Start a chat
  • Start a video conference
  • Delete the contact

To provide this choice, you can add a context menu to list items. The context menu appears when a user touches and holds an item, and it contains a set of actions that apply to that item. You can add context menus to any control in Cascades, not just list items. To learn more about adding context menus to other controls, see Adding a context menu.


Screens showing context menu for a list item selection.

When a user touches and holds an item, a partial context menu is displayed. This partial menu shows only the icons that are associated with each action in the menu. The user can either select an action icon from the partial menu (at which time, that action expands to display the title of the action) or swipe the menu to the left to display the full context menu.


Screens showing an expanded context menu.

You can add a context menu to a list item by using the contextActions list property. This property is associated with the content that you use for the ListItemComponent of the list item. For example, consider the following simple ListView:

ListView {
    dataModel: XmlDataModel {
        source: "models/myDataModel.xml"
    }
     
    listItemComponents: [
        ListItemComponent {
            type: "listItem"
             
            StandardListItem {
                title: ListItemData.title
                 
                contextActions: [
                    // Add actions for the context menu here
                ]
            }
        } // end of ListItemComponent
         
        ListItemComponent {
            type: "specialListItem"
             
            Container {
                Label {
                    ...
                }
                Label {
                    ...
                }
                 
                contextActions: [
                    // Add actions for the context menu here
                ]
            }
        } // end of ListItemComponent            
    ] // end of listItemComponents list
} // end of ListView

This ListView uses two ListItemComponent objects to define how list items look. In the first ListItemComponent, a  StandardListItem is used for items in the data model that have a type of "listItem". In the second ListItemComponent, a custom layout (with a  Container at the root) is used for items that have a type of "specialListItem". It's inside the content of each ListItemComponent (that is, within the StandardListItem and the Container) that you include the contextActions list property.

You add actions to the context menu by using an  ActionSet and filling it with  ActionItem objects. Each ActionItem corresponds to an action in the context menu. Here's how to create a context menu containing four actions that apply to list items.

import bb.cascades 1.0
  
Page {
    content: Container {
        ListView {
            // Use data from an XML file as the data model
            dataModel: XmlDataModel {
                source: "models/contacts.xml"
            }
              
            listItemComponents: [
                ListItemComponent {
                    type: "listItem"
                     
                    // For each item in the data model that has a type of
                    // "listItem", use a StandardListItem to display the item
                    StandardListItem {
                        title: ListItemData.firstname + " " +
                               ListItemData.lastname
                         
                        contextActions: [
                            // Add a set of four actions to the context menu for
                            // a list item
                            ActionSet {
                                title: "Contact"
                                ActionItem {
                                    title: "Send an Email"
                                    imageSource: "asset:///images/email.png"
                                }
                                ActionItem {
                                    title: "Start a Chat"
                                    imageSource: "asset:///images/chat.png"
                                }
                                ActionItem {
                                    title: "Start a Video Conference"
                                    imageSource: "asset:///images/video.png"
                                }
                                ActionItem {
                                    title: "Delete"
                                    imageSource: "asset:///images/delete.png"
                                }
                            } // end of ActionSet
                        ]
                    } // end of StandardListItem 
                } // end of ListItemComponent
            ]
        } // end of ListView
    } // end of top-level Container
} // end of Page

It's important to note that when a user touches and holds a list item to display the context menu for that item, the triggered() signal isn't emitted. You should make sure that you handle this behavior properly in your app. In general, when the user taps an item and the triggered() signal is emitted, your app should perform the most common action for that item. For example, tapping on an email in an email app should open the email, and tapping on a song in a music player app should play the song. Your app should include less common actions in the context menu.

Responding to selection of multiple items

In addition to performing actions on a single item in a list, you might want your app to provide actions that apply to multiple list items at once. For example, in a list of contacts, you might let users select multiple contacts and add them all to the To line of an email, set up a meeting with those contacts, or delete all of those contacts.

Cascades lets you support multiple selection in your lists with just a few lines of code. You don't need to implement the multiple selection mechanism or provide a visual appearance for the selection. Cascades handles these aspects automatically, so you simply need to do the following in your app:

  • Provide an action that enables multiple selection mode
  • Specify the actions that you want to include on the multiple selection menu

Enabling multiple selection mode

To enable multiple selection mode, you use a special type of action called a MultiSelectActionItem . You can add this action in different places in your app (such as the action menu or context menu), just like any other action item. A MultiSelectActionItem has a default image and text, but you can choose to customize these properties.

An action item for multiple selection.

When you create a MultiSelectActionItem, you need to associate it with a MultiSelectHandler (which you'll learn about in the next section). This handler is associated with a ListView and contains the actions that should appear on the context menu when multiple list items are selected. When a user selects the MultiSelectActionItem, the handler is invoked and displays the multiple selection interface and the actions that you specify.

Here's how to create a MultiSelectActionItem and add it to the action menu of a  Page. The MultiSelectActionItem is associated with the MultiSelectHandler of a ListView called list. In the next section, you'll learn how to populate the list's MultiSelectHandler with actions.

import bb.cascades 1.0
  
Page {
    actions: [
        MultiSelectActionItem {
            multiSelectHandler: list.multiSelectHandler
        }
    ]          
             
    ListView {
        id: list
        dataModel: XmlDataModel {
            source: "models/contacts.xml"
        }
    }
}

When a user enables multiple selection mode using a MultiSelectActionItem, the multiple selection interface is displayed. This interface includes a context menu on the right side of the screen, which contains actions that apply to the selected items. The interface also includes a status bar at the bottom of the screen, which contains a Cancel button (to disable multiple selection mode) and status text that you can customize. You can use the status property, which is part of MultiSelectHandler, to display this text. For example, you might want to show how many list items are currently selected.

While multiple selection mode is enabled, the user can tap list items to select them. A default visual style is used for selected list items. The user can also tap selected items to deselect them. Actions that apply to the selected items are displayed in a partial context menu, which the user can swipe or drag to display the full context menu.

Screen showing both the status bar and context menu.

Specifying actions for the multiple selection menu

To add actions to the context menu when multiple selection mode is enabled, you can use the multiSelectHandler property of a  ListView. This property defines a  MultiSelectHandler, which determines how multiple selection works for the ListView. Inside the multiSelectHandler property, you can use the actions: list property to add  ActionItem objects to the context menu.

Here's how to add actions to the context menu in multiple selection mode. This code sample includes a MultiSelectActionItem to enable multiple selection mode, and also includes a few other interesting additions that demonstrate some of the features of the mode:

  • The onActiveChanged signal handler is used to handle the  activeChanged() signal that's emitted by the list's MultiSelectHandler. This signal indicates when multiple selection mode is enabled or disabled, and the text of a  Label is updated accordingly.
  • The onSelectionChanged signal handler is used to handle the  selectionChanged() signal that's emitted by the ListView. This signal indicates that a list item has been selected or deselected, and the text in the status area is updated to reflect how many items are currently selected.
  • MultiSelectActionItem is assigned to the multiSelectAction property of the ListView. When you set this property, a MultiSelectActionItem is added automatically to the context menu of each list item, meaning that you don't need to add them manually to each item. Note that each list item must include an  ActionSet in order for the MultiSelectActionItem to be added to it. So, an empty ActionSet is added to the contextActions list of the  StandardListItem.
Screen showing multiple list items selected.
import bb.cascades 1.0
  
Page {
    actions: [
        // Add an action to enable multiple selection mode. This action
        // appears in the action menu on the main Page of the app.
        MultiSelectActionItem {
            multiSelectHandler: list.multiSelectHandler
        }
    ]
     
    Container {     
        ListView {
            id: list
            dataModel: XmlDataModel {
                source: "models/contacts.xml"
            }
             
            // Set the multiSelectAction property so that an action to enable
            // multiple selection mode appears in the context menu of each
            // list item
            multiSelectAction: MultiSelectActionItem {
            }
             
            multiSelectHandler {
                actions: [
                    // Add the actions that should appear on the context menu
                    // when multiple selection mode is enabled
                    ActionItem {
                        title: "Send an Email"
                        imageSource: "asset:///images/email.png"
                    },
                    ActionItem {
                        title: "Start a Chat"
                        imageSource: "asset:///images/chat.png"
                    },
                    ActionItem {
                        title: "Delete"
                        imageSource: "asset:///images/delete.png"
                    }
                ]
                 
                // Set the initial status text of multiple selection mode. When
                // the mode is first enabled, no items are selected.
                status: "None selected"
                 
                // When multiple selection mode is enabled or disabled, update
                // the label accordingly
                onActiveChanged: {
                    if (active == true) {
                        statusLabel.text = "Multiple selection mode is enabled.";
                    } else {
                        statusLabel.text = "Multiple selection mode is disabled";
                    }
                }
            }   
             
            // When a list item is selected or deselected, update the status text
            // to reflect the number of items that are currently selected
            onSelectionChanged: {
                if (selectionList().length > 1) {
                    multiSelectHandler.status = selectionList().length +
                                                " items selected";
                } else if (selectionList().length == 1) {
                    multiSelectHandler.status = "1 item selected";
                } else {
                    multiSelectHandler.status = "None selected";
                }
            }
             
            listItemComponents: [
                ListItemComponent {
                    type: "listItem"
                     
                    // Use a standard list item for the visual appearance of list
                    // items that have a type of "listItem"
                    StandardListItem {
                        title: ListItemData.firstname + " " +
                               ListItemData.lastname
                         
                        // For a MultiSelectActionItem to be added to each list
                        // item, the items must have an ActionSet. Create an
                        // empty ActionSet to hold the action. You can also add
                        // any other actions that should appear in the context
                        // menu of a list item.
                        contextActions: [
                            ActionSet {
                            }
                        ]
                    }
                } // end of ListItemComponent
            ]
        } // end of ListView
         
        Label {
            id: statusLabel
            text: "Multiple selection mode is disabled."
             
            textStyle {
                base: SystemDefaults.TextStyles.SubtitleText
            }
        }
    } // end of top-level Container
} // end of Page

Last modified: 2013-12-21

comments powered by Disqus