CollectionView

The CollectionView class is the main data management class used with Wijmo controls. This sample shows how ICollectionView works. It creates a CollectionView object based on an array with 500 items. The CollectionView is configured to show pages with 10 items each by default. The collection is shown in an HTML table that you can filter, sort, and group, using the controls at the top of each column.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import * as wijmo from '@grapecity/wijmo'; import * as input from '@grapecity/wijmo.input'; import { getData } from './data'; import { TableView } from './table-view'; import { CollectionViewPager } from './collection-view-pager'; import { CollectionViewNavigator } from './collection-view-navigator'; import { ItemEditor } from './item-editor'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // create a CollectionView let view = new wijmo.CollectionView(getData(500), { pageSize: 10, newItemCreator: () => { let newItem = getData(1)[0]; newItem.id = -1; return newItem; } }); // new TableView('#tableView', view, { amount: 'n2' }); new CollectionViewNavigator("#navigator", view); new CollectionViewPager("#pager", view); new ItemEditor('#editor', view); // let menu = new input.Menu("#pageSizeMenu", { itemsSource: [ { value: 0, text: 'No Paging' }, { value: 10, text: '10' }, { value: 15, text: '15' }, { value: 30, text: '30' }, { value: 50, text: '50' } ], displayMemberPath: 'text', selectedValuePath: 'value', selectedValue: view.pageSize, selectedIndexChanged: (sender) => { if (sender.selectedIndex >= 0) { updateMenuHeader(sender); view.pageSize = sender.selectedValue; } } }); updateMenuHeader(menu); // function updateMenuHeader(menu) { if (menu.selectedIndex >= 0) { menu.header = `Page Size: <b>${menu.selectedItem.text}</b>`; } } } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity CollectionView Overview</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <div id="menu"></div> <div class="row"> <div class="col-md-6"> <h4>Current Item</h4> <div id="editor"></div> </div> <div class="col-md-6"> <h4>Navigation</h4> <dl> <dt>items</dt> <dd> <div id="navigator"></div> </dd> <dt>pages</dt> <dd> <div id="pager"></div> </dd> </dl> <div id="pageSizeMenu"></div> </div> </div> <div id="tableView"></div> </div> </body> </html> // export function getData(count) { let countries = ['US', 'Germany', 'UK', 'Japan', 'Italy', 'Greece'], colors = ['Black', 'White', 'Red', 'Green', 'Blue'], data = []; // // add count items for (let i = 0; i < count; i++) { // constants used to create data items let countryId = Math.floor(Math.random() * countries.length), colorId = Math.floor(Math.random() * colors.length); // // create the item let item = { id: i, country: countries[countryId], color: colors[colorId], amount: Math.random() * 10000 - 5000 }; // // add the item to the list data.push(item); } // return data; } .table { margin-bottom: 0px !important; } import * as wijmo from '@grapecity/wijmo'; export class CollectionViewNavigatorBase extends wijmo.Control { // constructor(element, view) { wijmo.assert(!!view, 'view must not be null.'); // super(element); // this.applyTemplate('wj-control wj-content wj-pager', this.getTemplate(), { _first: 'first', _prev: 'prev', _next: 'next', _last: 'last', _input: 'input' }); // this._first.addEventListener('click', () => this._onFirstClick()); this._prev.addEventListener('click', () => this._onPrevClick()); this._next.addEventListener('click', () => this._onNextClick()); this._last.addEventListener('click', () => this._onLastClick()); // this._view = view; this._view.collectionChanged.addHandler(() => this.invalidate()); this.invalidate(); } // get view() { return this._view; } } CollectionViewNavigatorBase.controlTemplate = `<div class="wj-input-group"> <span class="wj-input-group-btn" > <button class="wj-btn wj-btn-default" type="button" wj-part="first"> <span class="wj-glyph-left" style="margin-right: -4px;"></span> <span class="wj-glyph-left"></span> </button> </span> <span class="wj-input-group-btn" > <button class="wj-btn wj-btn-default" type="button" wj-part="prev"> <span class="wj-glyph-left"></span> </button> </span> <input type="text" class="wj-form-control" wj-part="input" disabled /> <span class="wj-input-group-btn" > <button class="wj-btn wj-btn-default" type="button" wj-part="next"> <span class="wj-glyph-right"></span> </button> </span> <span class="wj-input-group-btn" > <button class="wj-btn wj-btn-default" type="button" wj-part="last"> <span class="wj-glyph-right"></span> <span class="wj-glyph-right" style="margin-left: -4px;"></span> </button> </span> </div>`; import { CollectionViewNavigatorBase } from './collection-view-navigator-base'; // export class CollectionViewNavigator extends CollectionViewNavigatorBase { constructor(element, view) { super(element, view); this.view.currentChanged.addHandler(() => this.invalidate()); } // refresh() { super.refresh(); this._first.disabled = this._prev.disabled = this.view.currentPosition <= 0; this._next.disabled = this._last.disabled = this.view.currentPosition >= this.view.itemCount - 1; this._input.value = `${this.view.currentPosition + 1} / ${this.view.itemCount}`; } // _onFirstClick() { this.view.moveCurrentToFirst(); } // _onPrevClick() { this.view.moveCurrentToPrevious(); } // _onNextClick() { this.view.moveCurrentToNext(); } // _onLastClick() { this.view.moveCurrentToLast(); } } import { CollectionViewNavigatorBase } from './collection-view-navigator-base'; // export class CollectionViewPager extends CollectionViewNavigatorBase { constructor(element, view) { super(element, view); this.view.pageChanged.addHandler(() => this.invalidate()); } // refresh() { super.refresh(); this._first.disabled = this._prev.disabled = this.view.pageIndex <= 0; this._next.disabled = this._last.disabled = this.view.pageIndex >= this.view.pageCount - 1; this._input.value = `${this.view.pageIndex + 1} / ${this.view.pageCount}`; } // _onFirstClick() { this.view.moveToFirstPage(); } // _onPrevClick() { this.view.moveToPreviousPage(); } // _onNextClick() { this.view.moveToNextPage(); } // _onLastClick() { this.view.moveToLastPage(); } } import * as wijmo from '@grapecity/wijmo'; // export class ItemEditor extends wijmo.Control { // constructor(element, view) { wijmo.assert(!!view, 'view must not be null.'); // super(element); // this.applyTemplate('', this.getTemplate(), { _tbId: 'id', _tbCountry: 'country', _tbColor: 'color', _tbAmount: 'amount', _btnEdit: 'edit', _btnAdd: 'add', _btnDelete: 'delete', _btnCommit: 'commit', _btnCancel: 'cancel' }); // this._btnEdit.addEventListener('click', () => this._edit()); this._btnAdd.addEventListener('click', () => this._add()); this._btnDelete.addEventListener('click', () => this._remove()); this._btnCommit.addEventListener('click', () => this._commit()); this._btnCancel.addEventListener('click', () => this._cancel()); // this._view = view; this._view.currentChanged.addHandler(() => this.invalidate()); // this.invalidate(); } refresh() { super.refresh(); // let cur = this._view.currentItem, edit = this._isEditing(); // this._tbId.disabled = !edit; this._tbCountry.disabled = !edit; this._tbColor.disabled = !edit; this._tbAmount.disabled = !edit; // this._tbId.value = cur ? cur.id.toString() : ''; this._tbCountry.value = cur ? cur.country.toString() : ''; this._tbColor.value = cur ? cur.color.toString() : ''; this._tbAmount.value = cur ? cur.amount.toString() : ''; // let display = edit ? 'none' : ''; this._btnEdit.style.display = display; this._btnAdd.style.display = display; this._btnDelete.style.display = display; // display = edit ? '' : 'none'; this._btnCommit.style.display = display; this._btnCancel.style.display = display; } // _isEditing() { return this._view.isEditingItem || this._view.isAddingNew; } // _edit() { this._view.editItem(this._view.currentItem); this.invalidate(); } // _add() { this._view.addNew(); } // _remove() { this._view.remove(this._view.currentItem); } // _commit() { let cur = this._view.currentItem; // cur.id = this._tbId.value; cur.country = this._tbCountry.value; cur.color = this._tbColor.value; cur.amount = this._tbAmount.value; // this._view.commitEdit(); this._view.commitNew(); this.invalidate(); } // _cancel() { this._view.cancelEdit(); this._view.cancelNew(); this.invalidate(); } } ItemEditor.controlTemplate = `<dl class="dl-horizontal"> <dt>ID</dt> <dd> <input type="text" class="form-control" wj-part="id" /> </dd> <dt>Country</dt> <dd> <input type="text" class="form-control" wj-part="country" /> </dd> <dt>Color</dt> <dd> <input type="text" class="form-control" wj-part="color" /> </dd> <dt>Amount</dt> <dd> <input type="number" class="form-control" wj-part="amount" /> </dd> <dt></dt> <dd> <div class="btn-group data-btn-group"> <button class="btn btn-default btn-sm" wj-part="edit">Edit</button> <button class="btn btn-default btn-sm" wj-part="add">Add</button> <button class="btn btn-default btn-sm" wj-part="delete">Delete</button> <button class="btn btn-default btn-sm" wj-part="commit">Commit</button> <button class="btn btn-default btn-sm" wj-part="cancel">Cancel</button> </div> </dd> </dl>`; import * as wijmo from '@grapecity/wijmo'; // export class TableView extends wijmo.Control { // constructor(selector, view, format) { super(selector); // this.applyTemplate('table-responsive', this.getTemplate(), { _table: 'table' }); this._format = format || {}; // this._view = view; this._view.collectionChanged.addHandler(() => this.invalidate()); this._view.currentChanged.addHandler(() => this.invalidate()); // this.invalidate(); } // refresh() { this._table.innerHTML = ''; // // create header if (this._view.itemCount > 0) { let item = this._view.items[0], head = this._table.createTHead(), row = head.insertRow(); // row.className = 'active'; Object.keys(item).forEach(key => { let cell = document.createElement('th'); cell.className = 'text-center'; // // header text let txt = document.createTextNode(wijmo.toHeaderCase(key) + '\u00A0'); // + &nbsp; cell.appendChild(txt); // // sort button let btn = document.createElement('button'); btn.className = 'btn btn-default'; btn.textContent = this._getSort(key); btn.addEventListener('click', () => this._toggleSort(key)); cell.appendChild(btn); // row.appendChild(cell); }); } // // create body let body = this._table.createTBody(); this._view.items.forEach(item => { let row = body.insertRow(); // if (item == this._view.currentItem) { row.className = 'success'; } // row.addEventListener('click', () => this._moveCurrentTo(row, item)); // Object.keys(item).forEach(key => { let cell = row.insertCell(), fmt = this._format[key], val = item[key]; // cell.textContent = fmt ? wijmo.format(`{val:${fmt}}`, { val: val }) : val; cell.className = 'text-center'; }); }); } // _moveCurrentTo(row, item) { if (!this._isEditingView && !this._isGroupRow(item)) { this._view.moveCurrentTo(item); } } // get _isEditingView() { return this._view.isEditingItem || this._view.isAddingNew; } // _isGroupRow(item) { return item instanceof wijmo.CollectionViewGroup; } // _getSort(field) { let sd = this._view.sortDescriptions; // if (sd.length > 0 && sd[0].property === field) { return sd[0].ascending ? '▲' : '▼'; } // return '◇'; } // _toggleSort(field) { let sd = this._view.sortDescriptions, ascending = true; // if (sd.length > 0 && sd[0].property === field) { ascending = !sd[0].ascending; } // // remove any old sort descriptors and add the new one sd.splice(0, sd.length, new wijmo.SortDescription(field, ascending)); } } TableView.controlTemplate = '<table class="table table-condensed table-bordered" wj-part="table"></table>'; import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import * as wijmo from '@grapecity/wijmo'; // 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 '@grapecity/wijmo.angular2.input'; import { DataService, TDataItem } from './app.data'; // @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { private _timeOut: number; // 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++) { if (gd[i].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 < 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>GrapeCity 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/dist/zone.min.js"></script> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.js"></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; } <template> <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" v-model="cv.currentItem.id" :disabled="!isEditing()" /> </dd> <dt>Country</dt> <dd> <input type="text" class="form-control" v-model="cv.currentItem.country" :disabled="!isEditing()" /> </dd> <dt>Color</dt> <dd> <input type="text" class="form-control" v-model="cv.currentItem.color" :disabled="!isEditing()" /> </dd> <dt>Amount</dt> <dd> <input type="number" class="form-control" v-model="cv.currentItem.amount" :disabled="!isEditing()" /> </dd> <dt></dt> <dd> <div class="btn-group data-btn-group"> <button @click="edit()" v-bind:style="{display: !isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">Edit</button> <button @click="add()" v-bind:style="{display: !isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">Add</button> <button @click="deleteItem()" v-bind:style="{display: !isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">Delete</button> <button @click="commit()" v-bind:style="{display: isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">Commit</button> <button @click="cancel()" v-bind:style="{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> <div id="navigator"></div> </dd> <dt>pages</dt> <dd> <div id="pager"></div> </dd> </dl> <wj-menu :value="cv.pageSize" :header="'Page Size'" :itemClicked="selectionPageChanged"> <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 :header="'ID'" :itemClicked="selectionIDChanged" :value="filter.id" 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 :header="'Country'" :itemClicked="selectionCountryChanged" :value="filter.country" 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 :header="'Color'" :itemClicked="selectionColorChanged" :value="filter.color" 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 :header="'Amount'" :itemClicked="selectionAmountChanged" :value="filter.minAmount" 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 v-for="item in groupedList" v-bind:key="item" v-bind:class="{success: item == cv.currentItem}" @click="moveCurrentTo(item)"> <!-- group row --> <td v-bind:style="{display:isGroup(item)?'':'none'}" colspan="4" class="active"> <span v-bind:style="{display:'inline-block', width: (item.level * 25) + 'px'}"></span> <b>{{item.name}}</b> ( items) </td> <!-- data row --> <td v-bind:style="{display:isGroup(item)?'none':''}" class="text-center"> {{item.id}} </td> <td v-bind:style="{display:isGroup(item)?'none':''}" class="text-center"> {{item.country}} </td> <td v-bind:style="{display:isGroup(item)?'none':''}" class="text-center"> {{item.color}} </td> <td v-bind:style="{display:isGroup(item)?'none':''}" class="text-center"> {{item.amount | number:'1.2-2'}} </td> </tr> </tbody> </table> </div> </template> <script> import "bootstrap.css"; import "@grapecity/wijmo.styles/wijmo.css"; import Vue from 'vue'; import { WjInputModule } from '@grapecity/wijmo.vue2.input'; import { getData } from './data'; import * as wijmo from '@grapecity/wijmo'; import { CollectionViewPager } from './collection-view-pager'; import { CollectionViewNavigator } from './collection-view-navigator'; new Vue({ el: '#app', data: function () { return { cv: new wijmo.CollectionView(getData(500), { pageSize: 10, filter: this._filterFun.bind(this), newItemCreator: () => { var newItem = getData(1)[0]; newItem.id = -1; return newItem; } }), groupedList: [], filter: {} } }, mounted: function(){ this.filter = { id: '', country: '', color: '', minAmount: '' }; new CollectionViewNavigator("#navigator", this.cv); new CollectionViewPager("#pager", this.cv); 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)); } }); }, methods:{ doFilter() { if (this._timeOut) { clearTimeout(this._timeOut); } // this._timeOut = setTimeout(() => { this._timeOut = null; this.cv.refresh(); }, 250); }, getFilteredItem(source, value){ let item = source.filter((item)=>{ return item.value == value })[0]; if(item !== undefined){ return item.header; } }, selectionPageChanged(menu){ this.cv.pageSize = menu.selectedValue; }, selectionIDChanged(menu){ this.filter.id = menu.selectedValue; this.doFilter(); }, selectionCountryChanged(menu){ this.filter.country = menu.selectedValue; this.doFilter(); }, selectionColorChanged(menu){ this.filter.color = menu.selectedValue; this.doFilter(); }, selectionAmountChanged(menu){ this.filter.minAmount = menu.selectedValue; this.doFilter(); }, // // IEditableCollectionView commands isEditing() { return this.cv.isEditingItem || this.cv.isAddingNew; }, // edit() { this.cv.editItem(this.cv.currentItem); }, // add() { this.cv.addNew(); }, // deleteItem() { this.cv.remove(this.cv.currentItem); }, // commit() { this.cv.commitEdit(); this.cv.commitNew(); }, // cancel() { this.cv.cancelEdit(); this.cv.cancelNew(); }, // moveCurrentTo(item) { if (!this.isEditing() && !this.isGroup(item)) { this.cv.moveCurrentTo(item); } }, // // sorting getSort(propName) { let sd = this.cv.sortDescriptions; if (sd.length > 0 && sd[0].property == propName) { return sd[0].ascending ? '▲' : '▼'; } return '◇'; }, // toggleSort(propName) { 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) { let index = this._findGroup(propName); return index < 0 ? /*'▯' +*/ Array(this.cv.groupDescriptions.length + 2).join('▷') : /*'▮' +*/ Array(index + 2).join('▶'); }, // toggleGroup(propName) { 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) => { 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) { return item instanceof wijmo.CollectionViewGroup; }, _addGroup(g) { this.groupedList.push(g); // if (g.isBottomLevel) { g.items.forEach(item => this.groupedList.push(item)); } else { g.groups.forEach(group => this._addGroup(group)); } }, // _findGroup(propName) { let gd = this.cv.groupDescriptions; // for (let i = 0; i < gd.length; i++) { if (gd[i].propertyName == propName) { return i; } } // return -1; }, // // filtering _filterFun(item) { // 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 < f.minAmount) { return false; } } // // all passed, return true to include the item return true; } } }) </script> <style> .table { margin-bottom: 0px !important; } </style> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity CollectionView Overview</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app.vue'); </script> </head> <body> <div id="app"> </div> </body> </html> export function getData(count) { // data used to generate random items let _colors = ['Black', 'White', 'Red', 'Green', 'Blue']; let _countries = ['US', 'Germany', 'UK', 'Japan', 'Italy', 'Greece']; let data = []; // // add count items for (let i = 0; i < count; i++) { // constants used to create data items let countryId = Math.floor(Math.random() * _countries.length), colorId = Math.floor(Math.random() * _colors.length); // // add the item to the list data.push({ id: i, country: _countries[countryId], color: _colors[colorId], amount: Math.random() * 10000 - 5000 }); } return data; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './app.css'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import * as wijmo from '@grapecity/wijmo'; import * as wjInput from '@grapecity/wijmo.react.input'; import { getData } from './data'; import { CollectionViewPager } from './collection-view-pager'; import { CollectionViewNavigator } from './collection-view-navigator'; class App extends React.Component { constructor(props) { super(props); this._filter = { id: '', country: '', color: '', minAmount: '' }; this.doFilter = () => { if (this._timeOut) { clearTimeout(this._timeOut); } // this._timeOut = setTimeout(() => { this._timeOut = null; this._cv.refresh(); }, 250); }; this._cv = new wijmo.CollectionView(getData(500), { pageSize: 10, filter: this._filterFn.bind(this), newItemCreator: () => { var newItem = getData(1)[0]; newItem.id = -1; return newItem; } }); this._cv.collectionChanged.addHandler(() => { let gl = this._cv.items; if (this._cv.groups && this._cv.groups.length > 0) { gl = []; this._cv.groups.forEach(group => this._addGroup(gl, group)); } this.setState({ groupedList: gl }); }); this._cv.currentChanged.addHandler(() => this.forceUpdate()); this.state = { isEditing: false, groupedList: this._cv.items }; } render() { return <div className="container-fluid"> <div className="row"> <div className="col-md-6"> <h4>Current Item</h4> <dl className="dl-horizontal"> <dt>ID</dt> <dd> <input type="text" className="form-control" value={this._cv.currentItem ? this._cv.currentItem.id : ''} onChange={e => this.updateCurrentItem(e.target, 'id')} disabled={!this.state.isEditing}/> </dd> <dt>Country</dt> <dd> <input type="text" className="form-control" value={this._cv.currentItem ? this._cv.currentItem.country : ''} onChange={e => this.updateCurrentItem(e.target, 'country')} disabled={!this.state.isEditing}/> </dd> <dt>Color</dt> <dd> <input type="text" className="form-control" value={this._cv.currentItem ? this._cv.currentItem.color : ''} onChange={e => this.updateCurrentItem(e.target, 'color')} disabled={!this.state.isEditing}/> </dd> <dt>Amount</dt> <dd> <input type="number" className="form-control" value={this._cv.currentItem ? this._cv.currentItem.amount : ''} onChange={e => this.updateCurrentItem(e.target, 'amount')} disabled={!this.state.isEditing}/> </dd> <dt></dt> <dd> <div className="btn-group data-btn-group"> <button onClick={this.edit.bind(this)} style={{ display: !this.state.isEditing ? '' : 'none' }} className="btn btn-default btn-sm">Edit</button> <button onClick={this.add.bind(this)} style={{ display: !this.state.isEditing ? '' : 'none' }} className="btn btn-default btn-sm">Add</button> <button onClick={this.deleteItem.bind(this)} style={{ display: !this.state.isEditing ? '' : 'none' }} className="btn btn-default btn-sm">Delete</button> <button onClick={this.commit.bind(this)} style={{ display: this.state.isEditing ? '' : 'none' }} className="btn btn-default btn-sm">Commit</button> <button onClick={this.cancel.bind(this)} style={{ display: this.state.isEditing ? '' : 'none' }} className="btn btn-default btn-sm">Cancel</button> </div> </dd> </dl> </div> <div className="col-md-6"> <h4>Navigation</h4> <dl> <dt>items</dt> <dd> <div id="navigator"></div> </dd> <dt>pages</dt> <dd> <div id="pager"></div> </dd> </dl> <wjInput.Menu header='Page Size' value={this._cv.pageSize} itemClicked={this._pageSizeChanged.bind(this)}> <wjInput.MenuItem value={0}>No Paging</wjInput.MenuItem> <wjInput.MenuItem value={10}>10</wjInput.MenuItem> <wjInput.MenuItem value={15}>15</wjInput.MenuItem> <wjInput.MenuItem value={30}>30</wjInput.MenuItem> <wjInput.MenuItem value={50}>50</wjInput.MenuItem> </wjInput.Menu> </div> </div> <table className="table table-condensed table-bordered"> <thead> <tr className="active"> <th className="text-center"> <div className="btn-group"> <wjInput.Menu header='ID' value={this._filter.id} itemClicked={this._idChanged.bind(this)} style={{ display: "block" }}> <wjInput.MenuItem value=''>(All)</wjInput.MenuItem> <wjInput.MenuItem value='odd'>Odd</wjInput.MenuItem> <wjInput.MenuItem value='even'>Even</wjInput.MenuItem> </wjInput.Menu> <button className="btn btn-default" onClick={e => this.toggleSort('id')}>{this.getSort('id')}</button> </div> </th> <th className="text-center"> <div className="btn-group"> <wjInput.Menu header='Country' value={this._filter.country} itemClicked={this._countryChanged.bind(this)} style={{ display: "block" }}> <wjInput.MenuItem value="">(All)</wjInput.MenuItem> <wjInput.MenuItem value="US">US</wjInput.MenuItem> <wjInput.MenuItem value="Germany">Germany</wjInput.MenuItem> <wjInput.MenuItem value="UK">UK</wjInput.MenuItem> <wjInput.MenuItem value="Japan">Japan</wjInput.MenuItem> <wjInput.MenuItem value="Italy">Italy</wjInput.MenuItem> <wjInput.MenuItem value="Greece">Greece</wjInput.MenuItem> <wjInput.MenuItem value="France">France</wjInput.MenuItem> </wjInput.Menu> <button className="btn btn-default" onClick={e => this.toggleSort('country')}>{this.getSort('country')}</button> <button className="btn btn-default" onClick={e => this.toggleGroup('country')}>{this.getGroup('country')}</button> </div> </th> <th className="text-center"> <div className="btn-group"> <wjInput.Menu header='Color' value={this._filter.color} itemClicked={this._colorChanged.bind(this)} style={{ display: "block" }}> <wjInput.MenuItem value="">(All)</wjInput.MenuItem> <wjInput.MenuItem value="Black">Black</wjInput.MenuItem> <wjInput.MenuItem value="White">White</wjInput.MenuItem> <wjInput.MenuItem value="Red">Red</wjInput.MenuItem> <wjInput.MenuItem value="Green">Green</wjInput.MenuItem> <wjInput.MenuItem value="Blue">Blue</wjInput.MenuItem> <wjInput.MenuItem value="Yellow">Yellow</wjInput.MenuItem> <wjInput.MenuItem value="Brown">Brown</wjInput.MenuItem> <wjInput.MenuItem value="Orange">Orange</wjInput.MenuItem> </wjInput.Menu> <button className="btn btn-default" onClick={e => this.toggleSort('color')}>{this.getSort('color')}</button> <button className="btn btn-default" onClick={e => this.toggleGroup('color')}>{this.getGroup('color')}</button> </div> </th> <th className="text-center"> <div className="btn-group"> <wjInput.Menu header='Amount' value={this._filter.minAmount} itemClicked={this._amountChanged.bind(this)} style={{ display: "block" }}> <wjInput.MenuItem value="">(All)</wjInput.MenuItem> <wjInput.MenuItem value={0}>> 0</wjInput.MenuItem> <wjInput.MenuItem value={500}>> 500</wjInput.MenuItem> <wjInput.MenuItem value={1000}>> 1000</wjInput.MenuItem> </wjInput.Menu> <button className="btn btn-default" onClick={e => this.toggleSort('amount')}>{this.getSort('amount')}</button> <button className="btn btn-default" onClick={e => this.toggleGroup('amount')}>{this.getGroup('amount')}</button> </div> </th> </tr> </thead> <tbody> {this.state.groupedList.map((item, index) => { return <tr key={index} className={item == this._cv.currentItem ? 'success' : ''} onClick={e => this.moveCurrentTo(item)}> <td style={{ display: this.isGroup(item) ? '' : 'none' }} colSpan={4} className="active"> <span style={{ display: 'inline-block', width: (item.level * 25) + 'px' }}></span> <b>{item.name}</b> ( items) </td> <td style={{ display: this.isGroup(item) ? 'none' : '' }} className="text-center">{item.id}</td> <td style={{ display: this.isGroup(item) ? 'none' : '' }} className="text-center">{item.country}</td> <td style={{ display: this.isGroup(item) ? 'none' : '' }} className="text-center">{item.color}</td> <td style={{ display: this.isGroup(item) ? 'none' : '' }} className="text-center">{item.amount}</td> </tr>; })} </tbody> </table> </div>; } componentDidMount() { new CollectionViewNavigator("#navigator", this._cv); new CollectionViewPager("#pager", this._cv); //this.setState({ groupedList: this._cv.items }); } _pageSizeChanged(sender) { if (sender.selectedItem) { this._cv.pageSize = sender.selectedValue; } } _idChanged(sender) { if (sender.selectedItem) { this._filter.id = sender.selectedValue; this.doFilter(); } } _countryChanged(sender) { if (sender.selectedItem) { this._filter.country = sender.selectedValue; this.doFilter(); } } _colorChanged(sender) { if (sender.selectedItem) { this._filter.color = sender.selectedValue; this.doFilter(); } } _amountChanged(sender) { if (sender.selectedItem) { this._filter.minAmount = sender.selectedValue; this.doFilter(); } } // IEditableCollectionView commands isEditing() { this.setState({ isEditing: this._cv.isEditingItem || this._cv.isAddingNew }); } edit() { this._cv.editItem(this._cv.currentItem); this.isEditing(); } add() { this._cv.addNew(); this.isEditing(); } deleteItem() { this._cv.remove(this._cv.currentItem); this.isEditing(); } commit() { this._cv.commitEdit(); this._cv.commitNew(); this.isEditing(); } cancel() { this._cv.cancelEdit(); this._cv.cancelNew(); this.isEditing(); } moveCurrentTo(item) { if (!this.state.isEditing && !this.isGroup(item)) { this._cv.moveCurrentTo(item); this.forceUpdate(); } } // sorting getSort(propName) { let sd = this._cv.sortDescriptions; if (sd.length > 0 && sd[0].property == propName) { return sd[0].ascending ? '▲' : '▼'; } return '◇'; } toggleSort(propName) { 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) { let index = this._findGroup(propName); return index < 0 ? /*'▯' +*/ Array(this._cv.groupDescriptions.length + 2).join('▷') : /*'▮' +*/ Array(index + 2).join('▶'); } toggleGroup(propName) { 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) => { 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) { return item instanceof wijmo.CollectionViewGroup; } _addGroup(groupedList, g) { groupedList.push(g); // if (g.isBottomLevel) { g.items.forEach((item) => groupedList.push(item)); } else { g.groups.forEach((group) => this._addGroup(groupedList, group)); } } _findGroup(propName) { let gd = this._cv.groupDescriptions; // for (let i = 0; i < gd.length; i++) { if (gd[i].propertyName == propName) { return i; } } // return -1; } // filtering _filterFn(item) { // 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 < f.minAmount) { return false; } } // // all passed, return true to include the item return true; } updateCurrentItem(target, binding) { this._cv.currentItem[binding] = target.value; this.forceUpdate(); } } ReactDOM.render(<App />, document.getElementById('app')); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity CollectionView Overview</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html> .table { margin-bottom: 0px !important; } // data used to generate random items const _colors = ['Black', 'White', 'Red', 'Green', 'Blue']; const _countries = ['US', 'Germany', 'UK', 'Japan', 'Italy', 'Greece']; // export function getData(count) { let data = []; // // add count items for (let i = 0; i < count; i++) { // constants used to create data items let countryId = Math.floor(Math.random() * _countries.length), colorId = Math.floor(Math.random() * _colors.length); // // add the item to the list data.push({ id: i, country: _countries[countryId], color: _colors[colorId], amount: Math.random() * 10000 - 5000 }); } // return data; }