NativeScript Core

RadListView Item Selection States

When using Item Selection with RadListView one would want to have a different visual state for selected vs. deselected items. The default selection behavior of RadListView differs in terms of how selection is represented. It is a common scenario to unify the behavior by providing a single visual representation for the state of the items. This article represents a step-by-step guide for implementing visual states for the item containers depending on whether they are selected or not.

Prerequisites

For the purposes of this article, we need to have a RadListView instance defined on our page with the following properties set:

We also need to provide handlers and subscribe for the following events:

Implementing Selection States

The View Model

When implementing a visual state we normally associate a given property of a visual element with a property value on our model. We create a 'binding' which makes sure that once the model property changes the container's appearance is also changed. For that purpose we will create a model which introduces a selected property. We will update this property when an item is selected/deselected and will wire the background color of the container to it. Here's the model:

export class DataItem extends Observable {

    public get selected() {
        return this.get('_selected');
    }

    public set selected(value: boolean) {
        this.set('_selected', value);
    }

    public get name() {
        return this.get('_itemName');
    }

    public set name(value: string) {
        this.set('_itemName', value);
    }

    public get description() {
        return this.get('_itemDescription');
    }

    public set description(value: string) {
        this.set('_itemDescription', value);
    }

    public get id() {
        return this.get('_id');
    }

    public set id(value: number) {
        this.set('_id', value);
    }

    constructor(id: number, name: string, description: string) {
        super();
        this.id = id;
        this.name = name;
        this.description = description;
        this.selected = false;
    }
}

Using instances of this class we go on to bind RadListView by creating a View Model which we assign as a bindingContext of the page. The View Model exposed a property called dataItems which returns an ObservableArray object containing the DataItem instances:

import { ObservableArray } from "tns-core-modules/data/observable-array";
import { Observable } from "tns-core-modules/data/observable";
import { ListViewEventData } from "nativescript-ui-listview";

export class ViewModel extends Observable {
    constructor() {
        super();
        this.initDataItems();
    }

    get dataItems(): ObservableArray<DataItem> {
        return this.get("_dataItems");
    }

    set dataItems(value: ObservableArray<DataItem>) {
        this.set("_dataItems", value);
    }

    public itemSelected(args: ListViewEventData) {
        const item = this.dataItems.getItem(args.index);
        console.log("Selected item: " + item.name);
        item.selected = true;
    }

    public itemSelecting(args: ListViewEventData) {
        const item = this.dataItems.getItem(args.index);
        console.log("Selecting item: " + item.name);
    }

    public itemDeselecting(args: ListViewEventData) {
        const item = this.dataItems.getItem(args.index);
        console.log("Deselecting item: " + item.name);
    }

    public itemDeselected(args: ListViewEventData) {
        const item = this.dataItems.getItem(args.index);
        console.log("Deselected item: " + item.name);
        item.selected = false;
    }

    private initDataItems() {
        this.dataItems = new ObservableArray<DataItem>();

        for (let i = 0; i < 25; i++) {
            this.dataItems.push(new DataItem(i, "Item " + i, "This is item description."));
        }
    }
}

The UI

Essential part of the implementation of Item Selection States is the template that defines the item's visual structure. To update the appearance of the containers according to the selection state we will update the background color of the root element of each container. For that purpose we will create a XML binding to the selected property defined on our model. This binding will return red when the item is selected and white when the item is deselected. Here's the XML:

<StackLayout orientation="vertical" backgroundColor="{{ selected ? 'red' : 'white'}}">
    <Label fontSize="20" text="{{ name }}"/>
    <Label fontSize="14" text="{{ description }}"/>
</StackLayout>

Handling the 'itemSelected' and 'itemDeselected' events

The last part of the task is to update the selected property of the models when the selection state is changed. This is done using the corresponding selection events exposed by RadListView:

The snippet demonstrates how these events are subscribed for:

<lv:RadListView items="{{ dataItems }}" row="1" multipleSelection="true" selectionBehavior="Press" itemSelected="{{itemSelected}}" itemSelecting="{{itemSelecting}}" itemDeselecting="{{itemDeselecting}}" itemDeselected="{{itemDeselected}}">

Having the selected property defined on our business objects we update this property as follows:

public itemSelected(args: ListViewEventData) {
    const item = this.dataItems.getItem(args.index);
    console.log("Selected item: " + item.name);
    item.selected = true;
}

public itemSelecting(args: ListViewEventData) {
    const item = this.dataItems.getItem(args.index);
    console.log("Selecting item: " + item.name);
}

public itemDeselecting(args: ListViewEventData) {
    const item = this.dataItems.getItem(args.index);
    console.log("Deselecting item: " + item.name);
}

public itemDeselected(args: ListViewEventData) {
    const item = this.dataItems.getItem(args.index);
    console.log("Deselected item: " + item.name);
    item.selected = false;
}