CollectionView

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: 'ページングなし' }, { 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 = `ページサイズ: <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>現在の項目</h4> <div id="editor"></div> </div> <div class="col-md-6"> <h4>ナビゲーション</h4> <dl> <dt>項目</dt> <dd> <div id="navigator"></div> </dd> <dt>ページ</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 = ['アメリカ', 'ドイツ', 'イギリス', '日本', 'イタリア', 'ギリシャ'], colors = ['黒', '白', '赤', '緑', '青'], 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.floor(Math.random() * 10000) }; // // 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>国</dt> <dd> <input type="text" class="form-control" wj-part="country" /> </dd> <dt>色</dt> <dd> <input type="text" class="form-control" wj-part="color" /> </dd> <dt>金額</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">編集</button> <button class="btn btn-default btn-sm" wj-part="add">追加</button> <button class="btn btn-default btn-sm" wj-part="delete">削除</button> <button class="btn btn-default btn-sm" wj-part="commit">確定</button> <button class="btn btn-default btn-sm" wj-part="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({ id: 'ID', country: '国', color: '色', amount: '金額' }[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 '多い'; if (item.amount > 100) return '普通'; if (item.amount > 0) return '少ない'; // return 'マイナス'; })); } 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>現在の項目</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>国</dt> <dd> <input type="text" class="form-control" [ngModel]="cv.currentItem?.country" (ngModelChange)="cv.currentItem.country=$event" [disabled]="!isEditing()" /> </dd> <dt>色</dt> <dd> <input type="text" class="form-control" [ngModel]="cv.currentItem?.color" (ngModelChange)="cv.currentItem.color=$event" [disabled]="!isEditing()" /> </dd> <dt>金額</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">編集</button> <button (click)="add()" [ngStyle]="{display: !isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">追加</button> <button (click)="delete()" [ngStyle]="{display: !isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">削除</button> <button (click)="commit()" [ngStyle]="{display: isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">確定</button> <button (click)="cancel()" [ngStyle]="{display: isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">キャンセル</button> </div> </dd> </dl> </div> <div class="col-md-6"> <h4>ナビゲーション</h4> <dl> <dt>項目</dt> <dd> <wj-collection-view-navigator [cv]="cv"></wj-collection-view-navigator> </dd> <dt>ページ</dt> <dd> <wj-collection-view-pager [cv]="cv"></wj-collection-view-pager> </dd> </dl> <wj-menu [(value)]="cv.pageSize" [header]="'ページサイズ'"> <wj-menu-item [value]="0">ページングなし</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]="''">(すべて)</wj-menu-item> <wj-menu-item [value]="'odd'">奇数</wj-menu-item> <wj-menu-item [value]="'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]="'国'" (itemClicked)="doFilter()" style="display:block"> <wj-menu-item [value]="''">(すべて)</wj-menu-item> <wj-menu-item [value]="'アメリカ'">アメリカ</wj-menu-item> <wj-menu-item [value]="'ドイツ'">ドイツ</wj-menu-item> <wj-menu-item [value]="'イギリス'">イギリス</wj-menu-item> <wj-menu-item [value]="'日本'">日本</wj-menu-item> <wj-menu-item [value]="'イタリア'">イタリア</wj-menu-item> <wj-menu-item [value]="'ギリシャ'">ギリシャ</wj-menu-item> <wj-menu-item [value]="'フランス'">フランス</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]="'色'" (itemClicked)="doFilter()" style="display:block"> <wj-menu-item [value]="''">(すべて)</wj-menu-item> <wj-menu-item [value]="'黒'">黒</wj-menu-item> <wj-menu-item [value]="'白'">白</wj-menu-item> <wj-menu-item [value]="'赤'">赤</wj-menu-item> <wj-menu-item [value]="'緑'">緑</wj-menu-item> <wj-menu-item [value]="'青'">青</wj-menu-item> <wj-menu-item [value]="'黄'">黄</wj-menu-item> <wj-menu-item [value]="'茶'">茶</wj-menu-item> <wj-menu-item [value]="'オレンジ'">オレンジ</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]="'金額'" (itemClicked)="doFilter()" style="display:block"> <wj-menu-item [value]="''">(すべて)</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 = ['黒', '白', '赤', '緑', '青']; private _countries = ['アメリカ', 'ドイツ', 'イギリス', '日本', 'イタリア', 'ギリシャ']; // 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.floor(Math.random() * 10000) }); } // return data; } } .table { margin-bottom: 0px !important; } <template> <div class="container-fluid"> <div class="row"> <div class="col-md-6"> <h4>現在の項目</h4> <dl class="dl-horizontal"> <dt>ID</dt> <dd> <input type="text" class="form-control" v-model="cv.currentItem.id" :disabled="!isEditing()" /> </dd> <dt>国</dt> <dd> <input type="text" class="form-control" v-model="cv.currentItem.country" :disabled="!isEditing()" /> </dd> <dt>色</dt> <dd> <input type="text" class="form-control" v-model="cv.currentItem.color" :disabled="!isEditing()" /> </dd> <dt>金額</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">編集</button> <button @click="add()" v-bind:style="{display: !isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">追加</button> <button @click="deleteItem()" v-bind:style="{display: !isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">削除</button> <button @click="commit()" v-bind:style="{display: isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">確定</button> <button @click="cancel()" v-bind:style="{display: isEditing() ? '' : 'none'}" class="btn btn-default btn-sm">キャンセル</button> </div> </dd> </dl> </div> <div class="col-md-6"> <h4>ナビゲーション</h4> <dl> <dt>項目</dt> <dd> <div id="navigator"></div> </dd> <dt>ページ</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: "ページングなし", value: 0},{header: "10", value: 10},{header: "15", value: 15},{header: "30", value: 30}, {header: "50", value: 50}], filterHeader: '', filterSource:[{header: "(すべて)", value: ""},{header: "奇数", value: "odd"}, {header: "偶数", value: "even"}], countryHeader: '', countrySource:[{header: "(すべて)", value:""}, {header: "アメリカ", value: "アメリカ"}, {header: "ドイツ", value: "ドイツ"}, {header: "イギリス", value: "イギリス"}, {header: "日本", value: "日本"}, {header: "イタリア", value: "イタリア"}, {header: "ギリシャ", value: "ギリシャ"}, {header: "フランス", value: "フランス"}], colorHeader: '', colorSource:[{header: "(すべて)", value: ""}, {header: "黒", value: '黒'}, {header: "白", value: '白'}, {header: "赤", value: '赤'}, {header: "緑", value: '緑'}, {header: "青", value: '青'}, {header: "黄", value: '黄'}, {header: "茶", value: '茶'}, {header: "オレンジ", value: 'オレンジ'}], amountHeader: '', amountSource:[{header: "(すべて)", value: ""}, {header: "&gt; 0", value: "0"}, {header: "&gt; 500", value: "500"}, {header: "&gt; 1,000", value: "1000"}] } }, mounted: function(){ this.filter = { id: '', country: '', color: '', minAmount: '' }; this.pageHeader = "ページサイズ: <b>" + this.cv.pageSize + "</b>"; this.filterHeader = 'ID: <b>' + this.getFilteredItem(this.filterSource, this.filter.id) + '</b>'; this.countryHeader = '国: <b>' + this.getFilteredItem(this.countrySource, this.filter.country) + '</b>'; this.colorHeader = '色: <b>' + this.getFilteredItem(this.colorSource, this.filter.color) + '</b>'; this.amountHeader = '金額: <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.find((item)=>{ return item.value == value }); if(item !== undefined){ return item.header; } }, selectionPageChanged(menu){ this.cv.pageSize = menu.selectedValue; this.pageHeader = 'ページサイズ: <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 = '国: <b>' + this.getFilteredItem(this.countrySource, this.filter.country) + '</b>'; this.doFilter(); }, selectionColorChanged(menu){ this.filter.color = menu.selectedValue; this.colorHeader = '色: <b>' + this.getFilteredItem(this.colorSource, this.filter.color) + '</b>'; this.doFilter(); }, selectionAmountChanged(menu){ this.filter.minAmount = menu.selectedValue; this.amountHeader = '金額: <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 '多い'; if (item.amount > 100) return '普通'; if (item.amount > 0) return '少ない'; // return 'マイナス'; })); } 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 = ['黒', '白', '赤', '緑', '青']; let _countries = ['アメリカ', 'ドイツ', 'イギリス', '日本', 'イタリア', 'ギリシャ']; 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.floor(Math.random() * 10000 - 5000) }); } return data; }