示例
概览
CollectionView 类是用于wijmo控件的主要数据管理类。此示例显示ICollectionView的工作方式。它基于包含500个项的数组创建一个CollectionView对象。CollectionView 配置为默认情况下显示包含10个项目的页面。该集合显示在HTML表中,您可以使用每列顶部的控件对其进行筛选、排序和分组。
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'); // +
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">> 0</wj-menu-item>
<wj-menu-item [value]="500">> 500</wj-menu-item>
<wj-menu-item [value]="1000">> 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 :header="pageHeader" displayMemberPath="header"
selectedValuePath="value" :itemsSource="pageSource" :itemClicked="selectionPageChanged"></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="filterHeader" displayMemberPath="header"
selectedValuePath="value" :itemClicked="selectionIDChanged" style="display:block" :itemsSource="filterSource"></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="countryHeader" :itemClicked="selectionCountryChanged" displayMemberPath="header"
selectedValuePath="value" style="display:block" :itemsSource="countrySource"></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="colorHeader" :itemClicked="selectionColorChanged" displayMemberPath="header"
selectedValuePath="value" style="display:block" :itemsSource="colorSource"></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="amountHeader" :itemClicked="selectionAmountChanged" displayMemberPath="header"
selectedValuePath="value" style="display:block" :itemsSource="amountSource"></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: {},
pageHeader: '',
pageSource: [{header: "No Paging", value: 0},{header: "10", value: 10},{header: "15", value: 15},{header: "30", value: 30}, {header: "50", value: 50}],
filterHeader: '',
filterSource:[{header: "(All)", value: ""},{header: "Odd", value: "odd"}, {header: "Even", value: "even"}],
countryHeader: '',
countrySource:[{header: "(All)", value:""}, {header: "US", value: "US"}, {header: "Germany", value: "Germany"}, {header: "UK", value: "UK"}, {header: "Japan", value: "Japan"}, {header: "Italy", value: "Italy"}, {header: "Greece", value: "Greece"}, {header: "France", value: "France"}],
colorHeader: '',
colorSource:[{header: "(All)", value: ""}, {header: "Black", value: 'Black'}, {header: "White", value: 'White'}, {header: "Red", value: 'Red'}, {header: "Green", value: 'Green'}, {header: "Blue", value: 'Blue'}, {header: "Yellow", value: 'Yellow'}, {header: "Brown", value: 'Brown'}, {header: "Orange", value: 'Orange'}],
amountHeader: '',
amountSource:[{header: "(All)", value: ""}, {header: "> 0", value: "0"}, {header: "> 500", value: "500"}, {header: "> 1,000", value: "1000"}]
}
},
mounted: function(){
this.filter = { id: '', country: '', color: '', minAmount: '' };
this.pageHeader = "Page Size: <b>" + this.cv.pageSize + "</b>";
this.filterHeader = 'ID: <b>' + this.getFilteredItem(this.filterSource, this.filter.id) + '</b>';
this.countryHeader = 'Country: <b>' + this.getFilteredItem(this.countrySource, this.filter.country) + '</b>';
this.colorHeader = 'Color: <b>' + this.getFilteredItem(this.colorSource, this.filter.color) + '</b>';
this.amountHeader = 'Amount: <b>' + this.getFilteredItem(this.amountSource, this.filter.minAmount) + '</b>';
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;
this.pageHeader = 'Page Size: <b>' + menu.selectedItem.header + '</b>';
},
selectionIDChanged(menu){
this.filter.id = menu.selectedValue;
this.filterHeader = 'ID: <b>' + this.getFilteredItem(this.filterSource, this.filter.id) + '</b>';
this.doFilter();
},
selectionCountryChanged(menu){
this.filter.country = menu.selectedValue;
this.countryHeader = 'ID: <b>' + this.getFilteredItem(this.countrySource, this.filter.country) + '</b>';
this.doFilter();
},
selectionColorChanged(menu){
this.filter.color = menu.selectedValue;
this.colorHeader = 'ID: <b>' + this.getFilteredItem(this.colorSource, this.filter.color) + '</b>';
this.doFilter();
},
selectionAmountChanged(menu){
this.filter.minAmount = menu.selectedValue;
this.amountHeader = 'ID: <b>' + this.getFilteredItem(this.amountSource, this.filter.minAmount) + '</b>';
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.doFilter = () => {
if (this.state._timeOut) {
clearTimeout(this.state._timeOut);
}
//
this.setState({
_timeOut: setTimeout(() => {
this.setState({ _timeOut: null });
this.state.cv.refresh();
}, 250)
});
};
this.getFilteredItem = (source, value) => {
let item = source.find((item) => { return item.value == value; });
if (item !== undefined) {
return item.header;
}
else {
return source[0].header;
}
};
this.selectionPageChanged = (menu) => {
let cv = this.state.cv;
cv.pageSize = menu.selectedValue;
this.setState({
cv: cv,
pageHeader: 'Page Size: <b>' + menu.selectedItem.header + '</b>'
});
};
this.selectionIDChanged = (menu) => {
let filter = this.state.filter;
filter.minAmount = menu.selectedValue;
this.setState({
filter: filter,
filterHeader: 'ID: <b>' + this.getFilteredItem(this.state.filterSource, this.state.filter.id) + '</b>'
});
this.doFilter();
};
this.selectionCountryChanged = (menu) => {
let filter = this.state.filter;
filter.country = menu.selectedValue;
this.setState({
filter: filter,
countryHeader: 'ID: <b>' + this.getFilteredItem(this.state.countrySource, this.state.filter.country) + '</b>'
});
this.doFilter();
};
this.selectionColorChanged = (menu) => {
let filter = this.state.filter;
filter.color = menu.selectedValue;
this.setState({
filter: filter,
colorHeader: 'ID: <b>' + this.getFilteredItem(this.state.colorSource, this.state.filter.color) + '</b>'
});
this.doFilter();
};
this.selectionAmountChanged = (menu) => {
let filter = this.state.filter;
filter.minAmount = menu.selectedValue;
this.setState({
filter: filter,
amountHeader: 'ID: <b>' + this.getFilteredItem(this.state.amountSource, this.state.filter.minAmount) + '</b>'
});
this.doFilter();
};
// IEditableCollectionView commands
this.isEditing = () => {
this.setState({ isEditing: this.state.cv.isEditingItem || this.state.cv.isAddingNew });
};
this.edit = () => {
this.state.cv.editItem(this.state.cv.currentItem);
this.isEditing();
};
this.add = () => {
this.state.cv.addNew();
this.isEditing();
};
this.deleteItem = () => {
this.state.cv.remove(this.state.cv.currentItem);
this.isEditing();
};
this.commit = () => {
this.state.cv.commitEdit();
this.state.cv.commitNew();
this.isEditing();
};
this.cancel = () => {
this.state.cv.cancelEdit();
this.state.cv.cancelNew();
this.isEditing();
};
this.moveCurrentTo = (item) => {
if (!this.state.isEditing && !this.isGroup(item)) {
let cv = this.state.cv;
cv.currentItem = item;
this.setState({ cv: cv });
}
};
// sorting
this.getSort = (propName) => {
let sd = this.state.cv.sortDescriptions;
if (sd.length > 0 && sd[0].property == propName) {
return sd[0].ascending ? '▲' : '▼';
}
return '◇';
};
this.toggleSort = (propName) => {
let sd = this.state.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
this.getGroup = (propName) => {
let index = this._findGroup(propName);
return index < 0
? /*'▯' +*/ Array(this.state.cv.groupDescriptions.length + 2).join('▷')
: /*'▮' +*/ Array(index + 2).join('▶');
};
this.toggleGroup = (propName) => {
let gd = this.state.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));
}
}
};
this.isGroup = (item) => {
return item instanceof wijmo.CollectionViewGroup;
};
this._addGroup = (g) => {
let groupedList = this.state.groupedList;
groupedList.push(g);
this.setState({ groupedList: groupedList });
//
if (g.isBottomLevel) {
g.items.forEach((item) => this.state.groupedList.push(item));
}
else {
g.groups.forEach((group) => this._addGroup(group));
}
};
this._findGroup = (propName) => {
let gd = this.state.cv.groupDescriptions;
//
for (let i = 0; i < gd.length; i++) {
if (gd[i].propertyName == propName) {
return i;
}
}
//
return -1;
};
// filtering
this._filterFun = (item) => {
// check each filter parameter
let f = this.state.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;
};
this.updateCurrentItem = (target, key) => {
let cv = this.state.cv, currentItem = this.state.cv.currentItem;
currentItem[key] = target.value;
cv.currentItem = currentItem;
this.setState({ cv: cv });
};
this.state = {
filter: {},
cv: new wijmo.CollectionView(getData(500), {
pageSize: 10,
filter: () => { },
newItemCreator: () => {
var newItem = getData(1)[0];
newItem.id = -1;
return newItem;
}
}),
isEditing: false,
groupedList: [],
pageHeader: '',
_timeOut: null,
pageSource: [{ header: "No Paging", value: 0 }, { header: "10", value: 10 }, { header: "15", value: 15 }, { header: "30", value: 30 }, { header: "50", value: 50 }],
filterHeader: '(All)',
filterSource: [{ header: "(All)", value: "" }, { header: "Odd", value: "odd" }, { header: "Even", value: "even" }],
countryHeader: '(All)',
countrySource: [{ header: "(All)", value: "" }, { header: "US", value: "US" }, { header: "Germany", value: "Germany" }, { header: "UK", value: "UK" }, { header: "Japan", value: "Japan" }, { header: "Italy", value: "Italy" }, { header: "Greece", value: "Greece" }, { header: "France", value: "France" }],
colorHeader: '(All)',
colorSource: [{ header: "(All)", value: "" }, { header: "Black", value: 'Black' }, { header: "White", value: 'White' }, { header: "Red", value: 'Red' }, { header: "Green", value: 'Green' }, { header: "Blue", value: 'Blue' }, { header: "Yellow", value: 'Yellow' }, { header: "Brown", value: 'Brown' }, { header: "Orange", value: 'Orange' }],
amountHeader: '(All)',
amountSource: [{ header: "(All)", value: "" }, { header: "> 0", value: "0" }, { header: "> 500", value: "500" }, { header: "> 1,000", value: "1000" }]
};
}
;
componentWillMount() {
let cv = this.state.cv;
cv.filter = this._filterFun;
cv.currentItem = this.state.cv.items[0];
this.setState({ cv: cv });
}
componentDidMount() {
this.setState({
filter: { id: '', country: '', color: '', minAmount: '' },
pageHeader: "Page Size: <b>" + this.state.cv.pageSize + "</b>",
filterHeader: 'ID: <b>' + this.getFilteredItem(this.state.filterSource, this.state.filter.id) + '</b>',
countryHeader: 'Country: <b>' + this.getFilteredItem(this.state.countrySource, this.state.filter.country) + '</b>',
colorHeader: 'Color: <b>' + this.getFilteredItem(this.state.colorSource, this.state.filter.color) + '</b>',
amountHeader: 'Amount: <b>' + this.getFilteredItem(this.state.amountSource, this.state.filter.minAmount) + '</b>'
});
new CollectionViewNavigator("#navigator", this.state.cv, this.moveCurrentTo);
new CollectionViewPager("#pager", this.state.cv);
this.setState({ groupedList: this.state.cv.items });
//
let cv = this.state.cv;
cv.collectionChanged.addHandler(() => {
this.setState({ groupedList: this.state.cv.items }, () => {
if (this.state.cv.groups && this.state.cv.groups.length > 0) {
this.setState({ groupedList: [] }, () => {
this.state.cv.groups.forEach((group) => this._addGroup(group));
});
}
});
});
this.setState({ cv: cv });
}
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.state.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.state.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.state.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.state.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} style={{ display: !this.state.isEditing ? '' : 'none' }} className="btn btn-default btn-sm">Edit</button>
<button onClick={this.add} style={{ display: !this.state.isEditing ? '' : 'none' }} className="btn btn-default btn-sm">Add</button>
<button onClick={this.deleteItem} style={{ display: !this.state.isEditing ? '' : 'none' }} className="btn btn-default btn-sm">Delete</button>
<button onClick={this.commit} style={{ display: this.state.isEditing ? '' : 'none' }} className="btn btn-default btn-sm">Commit</button>
<button onClick={this.cancel} 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={this.state.pageHeader} displayMemberPath="header" selectedValuePath="value" itemsSource={this.state.pageSource} itemClicked={this.selectionPageChanged}>
</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={this.state.filterHeader} displayMemberPath="header" selectedValuePath="value" itemClicked={this.selectionIDChanged} style={{ display: "block" }} itemsSource={this.state.filterSource}>
</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={this.state.countryHeader} itemClicked={this.selectionCountryChanged} displayMemberPath="header" selectedValuePath="value" style={{ display: "block" }} itemsSource={this.state.countrySource}>
</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={this.state.colorHeader} itemClicked={this.selectionColorChanged} displayMemberPath="header" selectedValuePath="value" style={{ display: "block" }} itemsSource={this.state.colorSource}></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={this.state.amountHeader} itemClicked={this.selectionAmountChanged} displayMemberPath="header" selectedValuePath="value" style={{ display: "block" }} itemsSource={this.state.amountSource}>
</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.state.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>;
}
}
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>AutoComplete</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;
}