LIDOR SYSTEMS

Advanced User Interface Controls and Components

Overview of IntegralUI ListView for Angular 2

Created: 19 September 2016

Updated: 24 March 2017

IntegralUI ListView is a native Angular 2 component that displays list of items either in horizontal or vertical layout. Items can be reordered using advanced drag drop operations. You can load data on demand during run-time from local or remote data sources, and add custom HTML content in each item. In following sections, you can find details about various features available in the ListView component.

* Icons used in this sample are provided by icons8.com

ListView component is part of IntegralUI Web
a suite of UI Components for development of web apps

If you have any questions, don't hesitate to contact us at support@lidorsystems.com

Above demonstration shows two different views:

  • Horizontal layout where items are arranged from top to bottom with horizontal scrolling
  • Vertical layout where items are arranged from left to right with vertical scrolling

In addition, item content is arranged differently: horizontal layout shows item content in one line, while vertical layout shows item content in a box.

You can reorder items by click and drag over specific item. A dragging window will appear, stating the target item and position at which item can be dropped. During drag drop operations, you can also create a copy of an item by holding the SHIFT key. The dragging window will change its icon, showing a + sign next to position marker.

How to Use IntegralUI ListView in Angular 2

In order to use the ListView component in your app, you need to do the following:

  • Place the ListView using the iui-listview tag name
  • Add items using the iui-listitem tag name
  • Add custom HTML elements as item content
  • Connect the ListView to your data source

To specify how you want items to be arranged, use the scrollMode property, which can accept one of two values: 'horizontal' or 'vertical':

//

// main.ts file

//

 

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app.module';

 

const platform = platformBrowserDynamic();

platform.bootstrapModule(AppModule);

 

 

 

//

// app.module.ts file

//

 

import { NgModule } from '@angular/core';

import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';

import { IntegralUIModule } from 'integralui/integralui.module';

 

@NgModule({

imports: [ BrowserModule, IntegralUIModule ],

declarations: [ AppComponent ],

bootstrap: [ AppComponent ]

})

export class AppModule { }

 

 

 

//

// app.component.ts file

//

 

import { Component, ViewEncapsulation } from '@angular/core';

import { IntegralUIScrollMode } from './integralui/components/integralui.core';

 

@Component({

selector: 'iui-app',

templateUrl: 'app.template.html',

styleUrls: ['listview-overview.css'],

encapsulation: ViewEncapsulation.None

})

export class AppComponent {

// Get a reference of application view

@ViewChild('application', {read: ViewContainerRef}) applicationRef: ViewContainerRef;

 

// Get a reference of ListView component

@ViewChild('listview') listview: IntegralUIListView;

 

// An Array object that holds all item objects shown in ListView

// It is set as a list of any custom objects, you can use any custom fields and bind them with ListView using its properties

private items: Array<any>;

 

private scrollType: IntegralUIScrollMode = IntegralUIScrollMode.Horizontal;

private currentSelection = null;

 

// Initialize items in component constructor

constructor(){

this.items = [

{ id: 1, icon: "sci-fi", text: "Star Trek", year: "2009", rating: 8.1 },

{ id: 2, icon: "adventure", text: "Cast Away", year: "2000", rating: 7.7 },

{ id: 3, icon: "action", text: "Gladiator", year: "2000", rating: 8.5 },

{ id: 4, icon: "drama", text: "Malèna", year: "2000", rating: 7.5 },

{ id: 5, icon: "music", text: "Moulin Rouge", year: "2001", rating: 7.6 },

{ id: 6, icon: "comedy", text: "Snatch", year: "2000", rating: 8.3 },

{ id: 7, icon: "biography", text: "A Beautiful Mind", year: "2001", rating: 8.2 },

{ id: 8, icon: "crime", text: "Black Hawk Down", year: "2001", rating: 7.7 },

{ id: 9, icon: "western", text: "Django Unchained", year: "2012", rating: 8.5 },

{ id: 10, icon: "sci-fi", text: "Man of Steel", year: "2013", rating: 7.2 },

{ id: 11, icon: "horror", text: "The Ring", year: "2002", rating: 7.1 },

{ id: 12, icon: "romance", text: "40 Days and 40 Nights", year: "2002", rating: 5.6 },

{ id: 13, icon: "sci-fi", text: "Minority Report", year: "2002", rating: 7.7 },

{ id: 14, icon: "comedy", text: "Scary Movie 3", year: "2003", rating: 5.5 },

{ id: 15, icon: "music", text: "Walk the Line", year: "2005", rating: 7.9 },

{ id: 16, icon: "romance", text: "How to Lose a Guy in 10 Days", year: "2003", rating: 6.4 },

{ id: 17, icon: "crime", text: "The Dark Knight", year: "2008", rating: 9.1 },

{ id: 18, icon: "horror", text: "American Psycho", year: "2000", rating: 7.6 },

{ id: 19, icon: "drama", text: "The Grand Budapest Hotel", year: "2014", rating: 8.1 },

{ id: 20, icon: "comedy", text: "The Wolf of Wall Street", year: "2013", rating: 8.2 }

];

}

 

isScrollHorizontal(){

return this.scrollType == IntegralUIScrollMode.Horizontal ? true : false

;

}

 

isScrollVertical(){

return this.scrollType == IntegralUIScrollMode.Vertical ? true : false;

}

 

scrollTypeClicked(flag?: boolean){

if (flag)

this.scrollType = IntegralUIScrollMode.Vertical;

else

this.scrollType = IntegralUIScrollMode.Horizontal;

}

getRating(value){

 

if (this.scrollType == IntegralUIScrollMode.Horizontal)

return "../resources/star.png";

else {

let stars = Math.floor(value / 2);

return "../resources/stars-small-" + stars + ".png";

}

}

 

getRatingValue(value){

return Math.floor(value);

}

 

private horItemStyle = {

general: {

normal: 'item'

}

}

 

private verItemStyle = {

general: {

normal: 'item',

selected: 'item-selected'

}

}

 

itemSelectionChanged(item){

this.currentSelection = item;

}

 

}

 

bootstrap(AppComponent);

//

// app.template.html file

//

 

<div #application>

<div>

<label><input type="radio" [checked]="isScrollHorizontal()" (click)="scrollTypeClicked()" />Horizontal</label>

<label><input type="radio" [checked]="isScrollVertical()" (click)="scrollTypeClicked(true)" />Vertical</label>

</div>

<iui-listview [items]="items" [appRef]="applicationRef" [scrollMode]="scrollType" (selectionChanged)="itemSelectionChanged($event)" #listview>

<div *ngIf="scrollType == 'horizontal'">

<iui-listitem *ngFor="let item of items; let i = index" [controlStyle]="horItemStyle">

<div class="custom-item" (mouseenter)="hoverItem=item" (mouseleave)="hoverItem=null">

<span class="rating">{{getRatingValue(item.rating)}}</span>

<span class="num">{{i+1}}.</span>

<span class="icons {{item.icon}}"></span>

<span class="title">{{item.text}}</span>

<img class="star" src="{{getRating(item.rating)}}" />

</div>

</iui-listitem>

</div>

<div *ngIf="scrollType == 'vertical'">

<iui-listitem *ngFor="let item of items; let i = index" [controlStyle]="verItemStyle">

<div class="custom-item-large" (mouseenter)="hoverItem=item" (mouseleave)="hoverItem=null">

<span *ngIf="item == currentSelection" class="corner"></span>

<span class="num-corner">{{i+1}}</span><br />

<div class="icon-large">

<span class="icons {{item.icon}}"></span>

</div>

<span class="title-large">{{item.text}}</span><br />

<img class="star-inline" src="{{getRating(item.rating)}}" />

</div>

</iui-listitem>

</div>

</iui-listview>

</div>

/*

listview-overview.css file

*/

 

.iui-listview

{

width: 400px;

height: 400px;

}

.num

{

display: inline-block;

vertical-align: middle;

}

.num-corner

{

display: inline-block;

font-size: 0.875em;

margin: -3px 0 0 -3px;

position: relative;

vertical-align: middle;

z-index: 1;

}

.corner

{

content: "";

border: 20px solid #357ebd;

border-color: #357ebd transparent transparent #357ebd;

position: absolute;

top: -5px;

left: -5px;

width: 0;

height: 0;

z-index: 0;

}

.title

{

display: inline-block;

overflow: hidden;

text-overflow: ellipsis;

vertical-align: middle;

width: 50%;

}

.icons

{

background: url(../resources/movie-genres.png) no-repeat 0 0;

display: inline-block;

padding: 0;

margin: 3px;

width: 24px;

height: 24px;

vertical-align: middle;

}

.action

{

background-position: 0 0;

}

.adventure

{

background-position: -24px 0;

}

.comedy

{

background-position: -48px 0;

}

.action

{

background-position: -72px 0;

}

.sci-fi

{

background-position: -120px 0;

}

.biography

{

background-position: 0 -24px;

}

.horror

{

background-position: -24px -24px;

}

.drama

{

background-position: -48px -24px;

}

.music

{

background-position: -72px -24px;

}

.romance

{

background-position: -96px -24px;

}

.western

{

background-position: -120px -24px;

}

.star

{

display: inline-block;

float: right;

vertical-align: middle;

}

.rating

{

display: inline-block;

font-weight: bold;

font-size: 0.75em

position: absolute;

top: 18px;

right: 20px;

vertical-align: middle;

}

.item

{

display: inline-block;

}

.custom-item

{

border: thin solid #e9e9e9;

margin: 0;

overflow: hidden;

padding: 5px;

text-overflow: ellipsis;

width: 180px;

white-space: nowrap;

}

.custom-item-large

{

border: thin solid #e9e9e9;

margin: 0;

overflow: hidden;

padding: 5px;

position: relative;

width: 100px;

white-space: nowrap;

z-index: 0;

}

.title-large

{

color: black;

display: inline-block;

overflow: hidden;

padding: 3px 0;

text-overflow: ellipsis;

width: 100%;

vertical-align: middle;

}

.star-inline

{

display: inline-block;

vertical-align: middle;

}

.icon-large

{

padding: 10px;

margin: 0 auto;

width: 24px;

}

.item-selected

{

background-color: transparent;

border-color: #357ebd !important;

color: white;

}

Note If you want to show item with different content then other items, you can use the ngIf directive and set condition by which a different content will appear.

In this example, each item has an icon, label, rating and index. Depending on which layout is currently active, the item content is arranged either in one line or in a box.

Furthermore, the visual appearance of items is also changed. By using controlStyle property, you can set up custom CSS classes that will override default appearance of items.

private horItemStyle = {

general: {

normal: 'item'

}

}

 

private verItemStyle = {

general: {

normal: 'item',

selected: 'item-selected'

}

}

<iui-listview [items]="items" [appRef]="applicationRef" [scrollMode]="scrollType" (selectionChanged)="itemSelectionChanged($event)" #listview>

<div *ngIf="scrollType == 'horizontal'">

<iui-listitem *ngFor="let item of items; let i = index" [controlStyle]="horItemStyle">

<div class="custom-item" (mouseenter)="hoverItem=item" (mouseleave)="hoverItem=null">

<span class="rating">{{getRatingValue(item.rating)}}</span>

<span class="num">{{i+1}}.</span>

<span class="icons {{item.icon}}"></span>

<span class="title">{{item.text}}</span>

<img class="star" src="{{getRating(item.rating)}}" />

</div>

</iui-listitem>

</div>

<div *ngIf="scrollType == 'vertical'">

<iui-listitem *ngFor="let item of items; let i = index" [controlStyle]="verItemStyle">

<div class="custom-item-large" (mouseenter)="hoverItem=item" (mouseleave)="hoverItem=null">

<span *ngIf="item == currentSelection" class="corner"></span>

<span class="num-corner">{{i+1}}</span><br />

<div class="icon-large">

<span class="icons {{item.icon}}"></span>

</div>

<span class="title-large">{{item.text}}</span><br />

<img class="star-inline" src="{{getRating(item.rating)}}" />

</div>

</iui-listitem>

</div>

</iui-listview>

.item

{

display: inline-block;

}

.item-selected

{

background-color: transparent;

border-color: #357ebd !important;

color: white;

}

Supported Properties

All properties of the ListView component can be set using a separate attribute that is linked to an object in your application scope or to a specified value.

Here is a list of available properties:

  • appRef - holds a reference to application view
  • controlStyle - specifies an object that holds names for custom CSS classes
  • dataFields - specifies an object that map the fields names from data source to the ones used by the ListView
  • items - holds a reference to the list of items defined in your application component
  • scrollMode - specifies whether view is scrolled horizontally or vertically
  • selectedItem - an object that points to the currently selected item

To avoid using plain javascript manipulation of the DOM and because of the way Angular 2 is structured, in order to show a tooltip, context menu or any other popup window, a reference to the root application component is required. You can provide a value to appRef property by simply setting a variable to the root component, and then access it by calling the ViewChild with specified variable name.

// Get a reference of application view

@ViewChild('application', {read: ViewContainerRef}) applicationRef: ViewContainerRef;

<div #application>

<iui-listview [items]="items" [appRef]="applicationRef">

<iui-listitem *ngFor="let item of items">

{{item.text}}

</iui-listitem>

</iui-listview>

</div>

If you want to have a specified item selected initially, you can set the selectedItem property to point to the item object in your data source.

Data Binding in ListView

When you have a custom data source tat may differ from internal data settings of ListView component, you can use data binding which will match the names of data fields in your data source with those used by the ListView.

By specifying the items property to point to your data object, along with dataFields property that holds an object that maps the names of fields in the data object, you can populate the ListView using any kind of custom data source.

This feature is also usable when you need to load data on demand in the ListView, from a remote data source.

Supported Events

When interacting with the ListView component, depending on the action, a corresponding event is fired. For example adding an item will fire itemAdding and itemAdded events, selecting an item will fire beforeSelect, afterSelect and selectionChanged events, etc.

Here is a list of available events:

  • afterSelect - occurs after item is selected
  • beforeSelect - occurs before item is selected
  • clear - occurs when all items are removed from the ListView
  • dragDrop - occurs when item is dropped over target item or empty space
  • dragOver - occurs when item is dragged
  • itemAdded - occurs when new item is added to the ListView
  • itemAdding - occurs before item is added
  • itemClick - occurs when item header is clicked
  • itemDblClick - occurs when item header is double-clicked
  • itemRemoved - occurs when item is removed from the ListView
  • itemRemoving - occurs before item is removed
  • selectionChanged - occurs when currently selected item has changed

By handling these events in your code, you can add custom actions that may alter the default behavior of the ListView component. For example, by handling the dragOver event, you can exclude a specified item as a target during drag drop operation, by setting a condition.

How to Add/Remove Items Dynamically

In some cases, you may need to add new items or remove existing items in the ListView during run-time. For this purpose, there are built-in methods available that allow you to change the structure of the ListView:

//

// app.component.ts file

//

 

export class AppComponent {

// An Array object that holds all item objects shown in ListView

private data: Array;

 

// Get a reference to the ListView component using a variable set in HTML

@ViewChild('listview') listview: IntegralUIListView;

 

// Initialize items in component constructor

constructor(){

this.data = [];

}

 

// Adds a new item to the end of the ListView

addItem(){

this.listview.addItem({ text: "Item " + (this.data.length+1).toString() });

}

 

// Fired whenever a new item is added to the ListView

itemAddedEvent(e){

if (e.item)

console.log("itemAdded: " + e.item.text);

}

}

 

bootstrap(AppComponent);

//

// app.template.html file

//

 

<iui-listview [items]="data" (itemAdded)="itemAddedEvent($event)" #listview>

<iui-listitem *ngFor="let item of items">

{{item.text}}

</iui-listitem>

</iui-listview>

In above code, we are adding a new item with some items using the addItem method. To access this method, at first we need to get a reference to the ListView component. In HTML we are adding the #listview variable, which is used to locate the ListView within your application component.

After we have a reference to the ListView component, we can get access to all public methods available:

  • addItem - inserts a new item at the end of ListView
  • clearItems - removes all items from the ListView
  • findItemById - searches through the list for an item that matches specified id
  • findItemByText - searches through the list for an item that matches specified text
  • insertItemAfter - inserts a new item in a position after specified item
  • insertItemBefore - inserts a new item in a position before specified item
  • insertItemAt - inserts a new item at specified position
  • removeItem - removes the specified item from the ListView
  • scrollPos - gets or sets the current scrolling position

Virtualization

When you have large data source, you can populate the ListView by using virtualization. This allows you to load thousands of records into the ListView component and work in the client. To enable this mode, set the virtualMode property to true.

Advanced Drag and Drop Operations

ListView component comes with advanced drag drop that allows you to reorder items by simply dragging one or multiple items from one place to another within the same or other components.

During this process, events are fired that can help you to add custom actions that may alter the default built-in drag drop functionality. In each event, you can set up custom conditions that can prevent or allow drag drop in special cases, depending on your application requirements.

Whenever an item is dragged, a dragging window will appear showing the target item and position at which item can be dropped. There are three possible positions:

  • up arrow - states that item will be dropped above target item
  • down arrow - states that item will be dropped below target item
  • down arrow with a line - states that item will be dropped at the end of the list

By default, during drag and drop items are moved from their original position to a new one. In some cases, instead of moving you may need to create a copy of dragged items. Copy operation is already built-in, you only need to press and hold the SHIFT key, when item is dropped. The dragging window will change its icon showing a + sign. This states that copy of dragged item will drop at specified position.

How to Customize the ListView Appearance

Each part of IntegralUI ListView component is fully customizable. There are different CSS classes for each component part. Although changing the attributes of built-in classes is possible, you can completely override them using the controlStyle property.

The controlStyle property accpets an object that holds all or partial list of CSS class names that will override the default CSS class names. For each component part, a different CSS class governs its appearance. This allows you to specify a set of different CSS classes for each component part and alter the appearance of the ListView in whole. In this way, you can make it more suitable for your application requirements.

Conclusion

IntegralUI ListView component allows you to display items in horizontal or vertical layout, and allows reordering during run-time using drag drop operations. You can populate the ListView using custom data source, locally or remotely.

By default, each item contains an icon and a label. By adding custom HTML elements or other Angular components as item content, you can create custom view of the ListView component.

The ListView component is part of IntegralUI Web.

Newsletter


Sign-up to our newsletter and you will receive news on upcoming events, latest articles, samples and special offers.
Name: Email: *
*By checking this box, I agree to receive a newsletter from Lidor Systems in accordance with the Privacy Policy. I understand that I can unsubscribe from these communications at any time by clicking on the unsubscribe link in all emails.