CollectionView (Angular)

The CollectionView class is the main data management class used with Wijmo controls.

This creates a CollectionView object based on an array with 500 items.

The CollectionView is configured to show pages with 10 items each. The collection is shown in an HTML table that you can filter, sort, and group using the controls at the top of each column.

Learn about FlexGrid | CollectionView Documentation | CollectionView API Reference

This example uses Angular.

import 'bootstrap.css'; import '@mescius/wijmo.styles/wijmo.css'; import './styles.css'; import * as wijmo from '@mescius/wijmo'; import '@angular/compiler'; import { Component, Inject, enableProdMode, NgModule } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { WjInputModule } from '@mescius/wijmo.angular2.input'; import { DataService, TDataItem } from './app.data'; @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { private _timeOut: any; cv: wijmo.CollectionView; groupedList: any[]; filter: { id?: string, country?: string, color?: string, minAmount?: number | string }; constructor(@Inject(DataService) private dataService: DataService) { this.cv = new wijmo.CollectionView(dataService.getData(500), { pageSize: 10, filter: this._filterFun.bind(this), newItemCreator: () => { var newItem = dataService.getData(1)[0]; newItem.id = -1; return newItem; } }); this.groupedList = this.cv.items; this.cv.collectionChanged.addHandler(() => { this.groupedList = this.cv.items; if (this.cv.groups && this.cv.groups.length > 0) { this.groupedList = []; this.cv.groups.forEach(group => this._addGroup(group)); } }); this.filter = { id: '', country: '', color: '', minAmount: '' }; } doFilter() { if (this._timeOut) { clearTimeout(this._timeOut); } // this._timeOut = setTimeout(() => { this._timeOut = null; this.cv.refresh(); }, 250); } // IEditableCollectionView commands isEditing() { return this.cv.isEditingItem || this.cv.isAddingNew; } edit() { this.cv.editItem(this.cv.currentItem); } add() { this.cv.addNew(); } delete() { this.cv.remove(this.cv.currentItem); } commit() { this.cv.commitEdit(); this.cv.commitNew(); } cancel() { this.cv.cancelEdit(); this.cv.cancelNew(); } moveCurrentTo(item: any) { if (!this.isEditing() && !this.isGroup(item)) { this.cv.moveCurrentTo(item); } } // sorting getSort(propName: string) { let sd = this.cv.sortDescriptions; if (sd.length > 0 && sd[0].property == propName) { return sd[0].ascending ? '▲' : '▼'; } return '◇'; } toggleSort(propName: string) { let sd = this.cv.sortDescriptions, ascending = true; if (sd.length > 0 && sd[0].property == propName) { ascending = !sd[0].ascending; } // remove any old sort descriptors and add the new one sd.splice(0, sd.length, new wijmo.SortDescription(propName, ascending)); } // grouping getGroup(propName: string) { let index = this._findGroup(propName); return index < 0 ? /*'▯' +*/ Array(this.cv.groupDescriptions.length + 2).join('▷') : /*'▮' +*/ Array(index + 2).join('▶'); } toggleGroup(propName: string) { let gd = this.cv.groupDescriptions, index = this._findGroup(propName); if (index >= 0) { gd.removeAt(index); } else { if (propName == 'amount') { // when grouping by amount, use ranges instead of specific values gd.push(new wijmo.PropertyGroupDescription(propName, (item: TDataItem) => { if (item.amount > 1000) return 'Large Amounts'; if (item.amount > 100) return 'Medium Amounts'; if (item.amount > 0) return 'Small Amounts'; return 'Negative Amounts'; })); } else { // group by specific property values gd.push(new wijmo.PropertyGroupDescription(propName)); } } } isGroup(item: any) { return item instanceof wijmo.CollectionViewGroup; } private _addGroup(g: wijmo.CollectionViewGroup) { this.groupedList.push(g); if (g.isBottomLevel) { g.items.forEach(item => this.groupedList.push(item)); } else { g.groups.forEach(group => this._addGroup(group)); } } private _findGroup(propName: string) { let gd = this.cv.groupDescriptions; for (let i = 0; i < gd.length; i++) { let pgd = gd[i] as wijmo.PropertyGroupDescription; if (pgd.propertyName == propName) { return i; } } return -1; } // filtering private _filterFun(item: TDataItem) { // check each filter parameter let f = this.filter; if (f) { if ((f.id == 'odd' && item.id % 2 == 0) || (f.id == 'even' && item.id % 2 != 0)) { return false; } if (f.country && item.country.indexOf(f.country) < 0) { return false; } if (f.color && item.color.indexOf(f.color) < 0) { return false; } if ((f.minAmount || f.minAmount === 0) && item.amount < Number(f.minAmount)) { return false; } } // all passed, return true to include the item return true; } } @NgModule({ imports: [WjInputModule, FormsModule, BrowserModule], declarations: [AppComponent], providers: [DataService], bootstrap: [AppComponent] }) export class AppModule { } enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule);
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>MESCIUS Wijmo CollectionView Overview</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Polyfills --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/fesm2015/zone.min.js"></script> <!-- SystemJS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.21.5/system.src.js" integrity="sha512-skZbMyvYdNoZfLmiGn5ii6KmklM82rYX2uWctBhzaXPxJgiv4XBwJnFGr5k8s+6tE1pcR1nuTKghozJHyzMcoA==" crossorigin="anonymous"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html>
<div class="container-fluid"> <div class="row"> <div class="col-md-6"> <h4>Current Item</h4> <dl class="dl-horizontal"> <dt>ID</dt> <dd> <input type="text" class="form-control" [ngModel]="cv.currentItem?.id" (ngModelChange)="cv.currentItem.id=$event" [disabled]="!isEditing()" /> </dd> <dt>Country</dt> <dd> <input type="text" class="form-control" [ngModel]="cv.currentItem?.country" (ngModelChange)="cv.currentItem.country=$event" [disabled]="!isEditing()" /> </dd> <dt>Color</dt> <dd> <input type="text" class="form-control" [ngModel]="cv.currentItem?.color" (ngModelChange)="cv.currentItem.color=$event" [disabled]="!isEditing()" /> </dd> <dt>Amount</dt> <dd> <input type="number" class="form-control" [ngModel]="cv.currentItem?.amount" (ngModelChange)="cv.currentItem.amount=$event" [disabled]="!isEditing()" /> </dd> <dt></dt> <dd> <div class="btn-group data-btn-group"> <button (click)="edit()" [ngStyle]="{display: !isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">Edit</button> <button (click)="add()" [ngStyle]="{display: !isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">Add</button> <button (click)="delete()" [ngStyle]="{display: !isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">Delete</button> <button (click)="commit()" [ngStyle]="{display: isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">Commit</button> <button (click)="cancel()" [ngStyle]="{display: isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">Cancel</button> </div> </dd> </dl> </div> <div class="col-md-6"> <h4>Navigation</h4> <dl> <dt>items</dt> <dd> <wj-collection-view-navigator [cv]="cv"></wj-collection-view-navigator> </dd> <dt>pages</dt> <dd> <wj-collection-view-pager [cv]="cv"></wj-collection-view-pager> </dd> </dl> <wj-menu [(value)]="cv.pageSize" [header]="'Page Size'"> <wj-menu-item [value]="0">No Paging</wj-menu-item> <wj-menu-item [value]="10">10</wj-menu-item> <wj-menu-item [value]="15">15</wj-menu-item> <wj-menu-item [value]="30">30</wj-menu-item> <wj-menu-item [value]="50">50</wj-menu-item> </wj-menu> </div> </div> <table class="table table-condensed table-bordered"> <thead> <tr class="active"> <th class="text-center"> <div class="btn-group"> <wj-menu [(value)]="filter.id" [header]="'ID'" (itemClicked)="doFilter()" style="display:block"> <wj-menu-item [value]="''">(All)</wj-menu-item> <wj-menu-item [value]="'odd'">Odd</wj-menu-item> <wj-menu-item [value]="'even'">Even</wj-menu-item> </wj-menu> <button class="btn btn-default" (click)="toggleSort('id')">{{getSort('id')}}</button> </div> </th> <th class="text-center"> <div class="btn-group"> <wj-menu [(value)]="filter.country" [header]="'Country'" (itemClicked)="doFilter()" style="display:block"> <wj-menu-item [value]="''">(All)</wj-menu-item> <wj-menu-item [value]="'US'">US</wj-menu-item> <wj-menu-item [value]="'Germany'">Germany</wj-menu-item> <wj-menu-item [value]="'UK'">UK</wj-menu-item> <wj-menu-item [value]="'Japan'">Japan</wj-menu-item> <wj-menu-item [value]="'Italy'">Italy</wj-menu-item> <wj-menu-item [value]="'Greece'">Greece</wj-menu-item> <wj-menu-item [value]="'France'">France</wj-menu-item> </wj-menu> <button class="btn btn-default" (click)="toggleSort('country')">{{getSort('country')}}</button> <button class="btn btn-default" (click)="toggleGroup('country')">{{getGroup('country')}}</button> </div> </th> <th class="text-center"> <div class="btn-group"> <wj-menu [(value)]="filter.color" [header]="'Color'" (itemClicked)="doFilter()" style="display:block"> <wj-menu-item [value]="''">(All)</wj-menu-item> <wj-menu-item [value]="'Black'">Black</wj-menu-item> <wj-menu-item [value]="'White'">White</wj-menu-item> <wj-menu-item [value]="'Red'">Red</wj-menu-item> <wj-menu-item [value]="'Green'">Green</wj-menu-item> <wj-menu-item [value]="'Blue'">Blue</wj-menu-item> <wj-menu-item [value]="'Yellow'">Yellow</wj-menu-item> <wj-menu-item [value]="'Brown'">Brown</wj-menu-item> <wj-menu-item [value]="'Orange'">Orange</wj-menu-item> </wj-menu> <button class="btn btn-default" (click)="toggleSort('color')">{{getSort('color')}}</button> <button class="btn btn-default" (click)="toggleGroup('color')">{{getGroup('color')}}</button> </div> </th> <th class="text-center"> <div class="btn-group"> <wj-menu [(value)]="filter.minAmount" [header]="'Amount'" (itemClicked)="doFilter()" style="display:block"> <wj-menu-item [value]="''">(All)</wj-menu-item> <wj-menu-item [value]="0">&gt; 0</wj-menu-item> <wj-menu-item [value]="500">&gt; 500</wj-menu-item> <wj-menu-item [value]="1000">&gt; 1,000</wj-menu-item> </wj-menu> <button class="btn btn-default" (click)="toggleSort('amount')">{{getSort('amount')}}</button> <button class="btn btn-default" (click)="toggleGroup('amount')">{{getGroup('amount')}}</button> </div> </th> </tr> </thead> <tbody> <tr *ngFor="let item of groupedList" [ngClass]="{success: item == cv.currentItem}" (click)="moveCurrentTo(item)"> <!-- group row --> <td [ngStyle]="{display:isGroup(item)?'':'none'}" colspan="4" class="active"> <span [ngStyle]="{display:'inline-block', width: (item.level * 25) + 'px'}"></span> <b>{{item.name}}</b> ({{item.items?.length}} items) </td> <!-- data row --> <td [ngStyle]="{display:isGroup(item)?'none':''}" class="text-center">{{item.id}}</td> <td [ngStyle]="{display:isGroup(item)?'none':''}" class="text-center">{{item.country}}</td> <td [ngStyle]="{display:isGroup(item)?'none':''}" class="text-center">{{item.color}}</td> <td [ngStyle]="{display:isGroup(item)?'none':''}" class="text-center">{{item.amount | number:'1.2-2'}} </td> </tr> </tbody> </table> </div>
import { Injectable } from '@angular/core'; // export type TDataItem = { id: number; country: string; color: string; amount: number; } // @Injectable() export class DataService { // data used to generate random items private _colors = ['Black', 'White', 'Red', 'Green', 'Blue']; private _countries = ['US', 'Germany', 'UK', 'Japan', 'Italy', 'Greece']; // getData(count: number) { let data: TDataItem[] = []; // // add count items for (let i = 0; i < count; i++) { // constants used to create data items let countryId = Math.floor(Math.random() * this._countries.length), colorId = Math.floor(Math.random() * this._colors.length); // // add the item to the list data.push({ id: i, country: this._countries[countryId], color: this._colors[colorId], amount: Math.random() * 10000 - 5000 }); } // return data; } }
.table { margin-bottom: 0px !important; }
(function (global) { SystemJS.config({ transpiler: './plugin-typescript.js', typescriptOptions: { "target": "ES2022", "module": "system", "emitDecoratorMetadata": true, "experimentalDecorators": true, }, baseURL: 'node_modules/', meta: { 'typescript': { "exports": "ts" }, '*.css': { loader: 'systemjs-plugin-css' } }, paths: { // paths serve as alias 'npm:': '' }, packageConfigPaths: [ '/node_modules/*/package.json', "/node_modules/@angular/*/package.json", "/node_modules/@mescius/*/package.json" ], map: { 'core-js': 'https://cdn.jsdelivr.net/npm/core-js@2.6.12/client/shim.min.js', 'typescript': 'https://cdnjs.cloudflare.com/ajax/libs/typescript/5.2.2/typescript.min.js', "rxjs": "https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.1/rxjs.umd.min.js", 'systemjs-plugin-css': 'https://cdn.jsdelivr.net/npm/systemjs-plugin-css@0.1.37/css.js', '@mescius/wijmo': 'npm:@mescius/wijmo/index.js', '@mescius/wijmo.input': 'npm:@mescius/wijmo.input/index.js', '@mescius/wijmo.styles': 'npm:@mescius/wijmo.styles', '@mescius/wijmo.cultures': 'npm:@mescius/wijmo.cultures', '@mescius/wijmo.chart': 'npm:@mescius/wijmo.chart/index.js', '@mescius/wijmo.chart.analytics': 'npm:@mescius/wijmo.chart.analytics/index.js', '@mescius/wijmo.chart.animation': 'npm:@mescius/wijmo.chart.animation/index.js', '@mescius/wijmo.chart.annotation': 'npm:@mescius/wijmo.chart.annotation/index.js', '@mescius/wijmo.chart.finance': 'npm:@mescius/wijmo.chart.finance/index.js', '@mescius/wijmo.chart.finance.analytics': 'npm:@mescius/wijmo.chart.finance.analytics/index.js', '@mescius/wijmo.chart.hierarchical': 'npm:@mescius/wijmo.chart.hierarchical/index.js', '@mescius/wijmo.chart.interaction': 'npm:@mescius/wijmo.chart.interaction/index.js', '@mescius/wijmo.chart.radar': 'npm:@mescius/wijmo.chart.radar/index.js', '@mescius/wijmo.chart.render': 'npm:@mescius/wijmo.chart.render/index.js', '@mescius/wijmo.chart.webgl': 'npm:@mescius/wijmo.chart.webgl/index.js', '@mescius/wijmo.chart.map': 'npm:@mescius/wijmo.chart.map/index.js', '@mescius/wijmo.gauge': 'npm:@mescius/wijmo.gauge/index.js', '@mescius/wijmo.grid': 'npm:@mescius/wijmo.grid/index.js', '@mescius/wijmo.grid.detail': 'npm:@mescius/wijmo.grid.detail/index.js', '@mescius/wijmo.grid.filter': 'npm:@mescius/wijmo.grid.filter/index.js', '@mescius/wijmo.grid.search': 'npm:@mescius/wijmo.grid.search/index.js', '@mescius/wijmo.grid.grouppanel': 'npm:@mescius/wijmo.grid.grouppanel/index.js', '@mescius/wijmo.grid.multirow': 'npm:@mescius/wijmo.grid.multirow/index.js', '@mescius/wijmo.grid.transposed': 'npm:@mescius/wijmo.grid.transposed/index.js', '@mescius/wijmo.grid.transposedmultirow': 'npm:@mescius/wijmo.grid.transposedmultirow/index.js', '@mescius/wijmo.grid.pdf': 'npm:@mescius/wijmo.grid.pdf/index.js', '@mescius/wijmo.grid.sheet': 'npm:@mescius/wijmo.grid.sheet/index.js', '@mescius/wijmo.grid.xlsx': 'npm:@mescius/wijmo.grid.xlsx/index.js', '@mescius/wijmo.grid.selector': 'npm:@mescius/wijmo.grid.selector/index.js', '@mescius/wijmo.grid.cellmaker': 'npm:@mescius/wijmo.grid.cellmaker/index.js', '@mescius/wijmo.nav': 'npm:@mescius/wijmo.nav/index.js', '@mescius/wijmo.odata': 'npm:@mescius/wijmo.odata/index.js', '@mescius/wijmo.olap': 'npm:@mescius/wijmo.olap/index.js', '@mescius/wijmo.rest': 'npm:@mescius/wijmo.rest/index.js', '@mescius/wijmo.pdf': 'npm:@mescius/wijmo.pdf/index.js', '@mescius/wijmo.pdf.security': 'npm:@mescius/wijmo.pdf.security/index.js', '@mescius/wijmo.viewer': 'npm:@mescius/wijmo.viewer/index.js', '@mescius/wijmo.xlsx': 'npm:@mescius/wijmo.xlsx/index.js', '@mescius/wijmo.undo': 'npm:@mescius/wijmo.undo/index.js', '@mescius/wijmo.interop.grid': 'npm:@mescius/wijmo.interop.grid/index.js', '@mescius/wijmo.touch': 'npm:@mescius/wijmo.touch/index.js', '@mescius/wijmo.cloud': 'npm:@mescius/wijmo.cloud/index.js', '@mescius/wijmo.barcode': 'npm:@mescius/wijmo.barcode/index.js', '@mescius/wijmo.barcode.common': 'npm:@mescius/wijmo.barcode.common/index.js', '@mescius/wijmo.barcode.composite': 'npm:@mescius/wijmo.barcode.composite/index.js', '@mescius/wijmo.barcode.specialized': 'npm:@mescius/wijmo.barcode.specialized/index.js', "@mescius/wijmo.angular2.chart.analytics": "npm:@mescius/wijmo.angular2.chart.analytics/index.js", "@mescius/wijmo.angular2.chart.animation": "npm:@mescius/wijmo.angular2.chart.animation/index.js", "@mescius/wijmo.angular2.chart.annotation": "npm:@mescius/wijmo.angular2.chart.annotation/index.js", "@mescius/wijmo.angular2.chart.finance.analytics": "npm:@mescius/wijmo.angular2.chart.finance.analytics/index.js", "@mescius/wijmo.angular2.chart.finance": "npm:@mescius/wijmo.angular2.chart.finance/index.js", "@mescius/wijmo.angular2.chart.hierarchical": "npm:@mescius/wijmo.angular2.chart.hierarchical/index.js", "@mescius/wijmo.angular2.chart.interaction": "npm:@mescius/wijmo.angular2.chart.interaction/index.js", "@mescius/wijmo.angular2.chart.radar": "npm:@mescius/wijmo.angular2.chart.radar/index.js", '@mescius/wijmo.angular2.chart.map': 'npm:@mescius/wijmo.angular2.chart.map/index.js', "@mescius/wijmo.angular2.chart": "npm:@mescius/wijmo.angular2.chart/index.js", "@mescius/wijmo.angular2.core": "npm:@mescius/wijmo.angular2.core/index.js", "@mescius/wijmo.angular2.gauge": "npm:@mescius/wijmo.angular2.gauge/index.js", "@mescius/wijmo.angular2.grid.detail": "npm:@mescius/wijmo.angular2.grid.detail/index.js", "@mescius/wijmo.angular2.grid.filter": "npm:@mescius/wijmo.angular2.grid.filter/index.js", "@mescius/wijmo.angular2.grid.grouppanel": "npm:@mescius/wijmo.angular2.grid.grouppanel/index.js", "@mescius/wijmo.angular2.grid.search": "npm:@mescius/wijmo.angular2.grid.search/index.js", "@mescius/wijmo.angular2.grid.multirow": "npm:@mescius/wijmo.angular2.grid.multirow/index.js", "@mescius/wijmo.angular2.grid.sheet": "npm:@mescius/wijmo.angular2.grid.sheet/index.js", '@mescius/wijmo.angular2.grid.transposed': 'npm:@mescius/wijmo.angular2.grid.transposed/index.js', '@mescius/wijmo.angular2.grid.transposedmultirow': 'npm:@mescius/wijmo.angular2.grid.transposedmultirow/index.js', "@mescius/wijmo.angular2.grid": "npm:@mescius/wijmo.angular2.grid/index.js", "@mescius/wijmo.angular2.input": "npm:@mescius/wijmo.angular2.input/index.js", "@mescius/wijmo.angular2.olap": "npm:@mescius/wijmo.angular2.olap/index.js", "@mescius/wijmo.angular2.viewer": "npm:@mescius/wijmo.angular2.viewer/index.js", "@mescius/wijmo.angular2.nav": "npm:@mescius/wijmo.angular2.nav/index.js", "@mescius/wijmo.angular2.directivebase": "npm:@mescius/wijmo.angular2.directivebase/index.js", '@mescius/wijmo.angular2.barcode.common': 'npm:@mescius/wijmo.angular2.barcode.common/index.js', '@mescius/wijmo.angular2.barcode.composite': 'npm:@mescius/wijmo.angular2.barcode.composite/index.js', '@mescius/wijmo.angular2.barcode.specialized': 'npm:@mescius/wijmo.angular2.barcode.specialized/index.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'jszip': 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js', "@angular/common/http": "https://cdn.jsdelivr.net/npm/@angular/common@16.2.6/fesm2022/http.mjs", "@angular/core": "https://cdn.jsdelivr.net/npm/@angular/core@16.2.6/fesm2022/core.mjs", "@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@angular/platform-browser@16.2.6/fesm2022/platform-browser.mjs", "@angular/common": "https://cdn.jsdelivr.net/npm/@angular/common@16.2.6/fesm2022/common.mjs", "@angular/compiler": "https://cdn.jsdelivr.net/npm/@angular/compiler@16.2.6/fesm2022/compiler.mjs", "@angular/forms": "https://cdn.jsdelivr.net/npm/@angular/forms@16.2.6/fesm2022/forms.mjs", "@angular/localize": "https://cdn.jsdelivr.net/npm/@angular/localize@16.2.6/fesm2022/localize.mjs", "@angular/platform-browser-dynamic": "https://cdn.jsdelivr.net/npm/@angular/platform-browser-dynamic@16.2.6/fesm2022/platform-browser-dynamic.mjs", }, // packages tells the System loader how to load when no filename and/or no extension packages: { "./src": { defaultExtension: 'ts' }, "node_modules": { defaultExtension: 'js' }, wijmo: { defaultExtension: 'js', } } }); })(this);