Listview

Using a ListView control inside Angular app requires some special attention due to the complexity of the NativeScript ListView control, with custom item templates, bindings and so on.

The NativeScript-angular plugin provides a custom Angular component which simplifies the way native ListView is used.

API Reference for the ListView Class

Native Component

Android iOS
android.widget.ListView UITableView

Image

Creating Listview

HTML

<ListView [items]="countries" (itemTap)="onItemTap($event)" class="list-group">
    <ng-template let-country="item" let-i="index" let-odd="odd" let-even="even">
        <GridLayout class="list-group-item">
            <Label [text]="country.name"></Label>
        </GridLayout>
    </ng-template>
</ListView>

TypeScript

import { Component, ChangeDetectionStrategy } from "@angular/core";

class Country {
    constructor(public name: string) { }
}

let europianCountries = ["Austria", "Belgium", "Bulgaria", "Croatia", "Cyprus", "Czech Republic",
    "Denmark", "Estonia", "Finland", "France", "Germany", "Greece", "Hungary", "Ireland", "Italy",
    "Latvia", "Lithuania", "Luxembourg", "Malta", "Netherlands", "Poland", "Portugal", "Romania", "Slovakia",
    "Slovenia", "Spain", "Sweden", "United Kingdom"];

@Component({
    moduleId: module.id,
    templateUrl: "./creating-listview.component.html",
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class CreatingListViewComponent {
    public countries: Array<Country>;

    constructor() {
        this.countries = [];

        for (let i = 0; i < europianCountries.length; i++) {
            this.countries.push(new Country(europianCountries[i]));
        }
    }

    public onItemTap(args) {
        console.log("Item Tapped at cell index: " + args.index);
    }
}

Improve this document

Demo Source


Customizing Listview

Demonstration of ListView customization. In the code snippets below we will show how to:

  • create a custom ListView header
  • create a custom Listview footer
  • customize every third ListView item.

HTML

<ListView [items]="dataItems" (setupItemView)="onSetupItemView($event)" class="list-group">
    <ng-template let-dataitem="item" let-i="index" let-third="third" let-header="header" let-footer="footer">
        <GridLayout [class.third]="third" [class.header]="header" [class.footer]="footer" class="list-group-item">
            <Label [text]="dataitem.name" android:class="label-item"></Label>
        </GridLayout>
    </ng-template>
</ListView>

TypeScript

import { Component, ChangeDetectionStrategy } from "@angular/core";
import { SetupItemViewArgs } from "nativescript-angular/directives";

class Item {
    constructor(public name: string) { }
}

let items = ["ALL Heroes (header)", "Razor", "Rubick", "Phantom Lancer", "Legion Commander", "Brewmaster",
    "Outworld Devourer", "Sniper", "Lina", "Sven", "Visage", "Undying", "Tiny", "Tidehunter", "Puck", "Ursa",
    "Magnus", "Earthshaker", "Windrunner", "Techies", "Crystal Maiden", "Batrider", "Riki", "Invoker", "Venomancer",
    "Timbersaw", "Wraithking", "Anti Mage", "Ancient Apparition", "Troll Warlord", "Lich", "Enchantress",
    "Bristleback", "Pudge", "(footer)"];

@Component({
    moduleId: module.id,
    styleUrls: ["./customizing-listview.component.css"],
    templateUrl: "./customizing-listview.component.html",
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class CustomizingListViewComponent {
    public dataItems: Array<Item>;

    constructor() {
        this.dataItems = [];

        for (let i = 0; i < items.length; i++) {
            this.dataItems.push(new Item(items[i]));
        }
    }

    onSetupItemView(args: SetupItemViewArgs) {
        args.view.context.third = (args.index % 3 === 0);
        args.view.context.header = ((args.index + 1) % items.length === 1);
        args.view.context.footer = (args.index + 1 === items.length);
    }
}

CSS

.third {
    background-color: #0099CC;
    color: whitesmoke;
    text-align: left;
    vertical-align: center;
}

.header {
    background-color: #8C489F;
    color: whitesmoke;
    border-radius: 3;
}

.footer {
    background-color: #8C489F;
    color: whitesmoke;
    border-radius: 3;
}

.item {
    text-align: left;
    vertical-align: center;
}

Improve this document

Demo Source


Update Child Component

This example demonstrates how to use OnChanges to trigger updates in child components for data passed from parent component. The data is pased with @Input and the change is triggered via`ngOnChanges with injecting SimpleChanges in the child's constructor.

TypeScript

import { Component, ChangeDetectionStrategy, Input, OnChanges, SimpleChanges } from "@angular/core";

@Component({
    selector: "sdk-child-component",
    moduleId: module.id,
    template: `
        <StackLayout orientation="horizontal">
            <Label text="This is item with ID: " class="m-16" textWrap="true"></Label>
            <Label [text]="myData.id" textWrap="true"></Label>
        </StackLayout>
    `
})
export class ItemComponent implements OnChanges {
    @Input() data: any;
    public myData: any;

    ngOnChanges(changes: SimpleChanges) {
        this.myData = changes["data"].currentValue;
    }
}

@Component({
    styleUrls: ["./update-child-component.component.css"],
    moduleId: module.id,
    templateUrl: "./update-child-component.component.html"
})
export class UpdateChildComponent {
    public items: Array<any> = [];

    constructor() {
        for (let index = 0; index < 20; index++) {
            this.items.push({ "id": index + 1 });
        }
    }
}

Improve this document

Demo Source


Using Async Pipe

An Angular pipe is a simple display-value transformation that can be declared in HTML. A pipe takes an input and transforms it to a desired output. One of the built-in Angular pipes is very commonly used with ListView like controls. This is the async pipe. The input of this pipe is either Promise or Observable (Observable actually stands for RxJS.Observable). This pipe subscribes to the observable and returns the value inside of it as a property value. The following is a simple example of using async pipe with the NativeScript-Angular ListView.

import { Component, ChangeDetectionStrategy } from "@angular/core";
import { Observable as RxObservable } from "rxjs/Observable";

export class DataItem {
    constructor(public id: number, public name: string) { }
}

@Component({
    moduleId: module.id,
    styleUrls: ["./using-async-pipe.component.css"],
    templateUrl: "./using-async-pipe.component.html",
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UsingAsyncPipeComponent {
    public myItems: RxObservable<Array<DataItem>>;

    constructor() {
        let items = [];
        for (let i = 0; i < 3; i++) {
            items.push(new DataItem(i, "data item " + i));
        }

        let subscr;
        this.myItems = RxObservable.create(subscriber => {
            subscr = subscriber;
            subscriber.next(items);
            return function () {
                console.log("Unsubscribe called!");
            };
        });

        let counter = 2;
        let intervalId = setInterval(() => {
            counter++;
            items.push(new DataItem(counter + 1, "data item " + (counter + 1)));
            subscr.next(items);
        }, 1000);

        setTimeout(() => {
            clearInterval(intervalId);
        }, 15000);
    }
}

Improve this document

Demo Source


Stay connected with NativeScript

(expect a newsletter every 4-8 weeks)

NativeScript
NativeScript is licensed under the Apache 2.0 license .
© Progress Software Corporation. All Rights Reserved.