新規項目の挿入位置

このサンプルでは、クライアント側でページングされた CollectionView における、ページ基準の挿入を確認できます。

ラジオボタンを使って addNew に対する newItemIndex の設定を切り替え、 ボタンを使って有効なインデックスや境界値で insertAt を試せます。

このサンプルでは、並べ替え、フィルター、グループ化の切り替えも行え、意図した動作を確認できます。

  • ページングが有効な場合、newItemIndex が有効なのは 0..pageSize - 1 の範囲内だけです。 負の値、および pageSize 以上の値では元の addNew の動作が維持され、 項目は sourceCollection の末尾に追加されます。

  • ページングが無効な場合、newItemIndexsourceCollection 全体の中での位置として扱われます。

  • insertAt は、インデックスが有効なページ基準の範囲内にある場合にのみ、 指定した現在のページ位置へ挿入します。無効なインデックスでは挿入は実行されません。

  • 並べ替え、フィルター、またはグループ化が有効な場合、ページ基準の位置指定は適用されません。 その場合、有効な newItemIndex および insertAt の指定は sourceCollection の末尾への追加に フォールバックし、無効な insertAt のインデックスでは何も挿入されません。

import 'bootstrap.css'; import '@mescius/wijmo.styles/wijmo.css'; import './styles.css'; import { CollectionView, PropertyGroupDescription, SortDescription } from '@mescius/wijmo'; import { CollectionViewNavigator } from '@mescius/wijmo.input'; import { FlexGrid } from '@mescius/wijmo.grid'; var COUNTRIES = createCountries(); var PRODUCTS = createProducts(); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } function init() { var view = new CollectionView(getData(30), { pageSize: 6 }); view.newItemIndex = -1; var nextId = view.sourceCollection.length; new CollectionViewNavigator('#thePager', { byPage: true, headerFormat: 'ページ {currentPage:n0} / {pageCount:n0}', cv: view }); createControls(view, function (label) { var item = createRecord(nextId, label); nextId++; return item; }); var grid = new FlexGrid('#theGrid', { headersVisibility: 'Column', columns: [ { binding: 'country', header: '国', width: 80 }, { binding: 'product', header: '商品', width: 80 }, { binding: 'sales', header: '売上', format: 'n0', width: 80 }, { binding: 'expenses', header: '費用', format: 'n0', width: 80 } ], autoGenerateColumns: false, alternatingRowStep: 0, itemsSource: view, allowAddNew: true, allowDelete: true }); } function getData(count) { var data = []; for (var i = 0; i < count; i++) { data.push(createRecord(i)); } return data; } function createCountries() { return 'アメリカ,ドイツ,イギリス,日本,イタリア,ギリシャ'.split(','); } function createProducts() { return 'ピアノ,バイオリン,フルート,ギター,チェロ'.split(','); } function createRecord(id, label) { if (!COUNTRIES || !COUNTRIES.length) { COUNTRIES = createCountries(); } if (!PRODUCTS || !PRODUCTS.length) { PRODUCTS = createProducts(); } return { id: id, country: COUNTRIES[id % COUNTRIES.length], product: label ? label : PRODUCTS[id % PRODUCTS.length], sales: 1000 + id * 37, expenses: 500 + id * 13 }; } function createControls(view, createItem) { var pager = document.getElementById('thePager'); var grid = document.getElementById('theGrid'); if (!pager || !grid || !pager.parentElement) { return; } var host = document.createElement('div'); host.className = 'demo-controls'; host.innerHTML = '<div class="control-section">' + '<h4>AddNew / newItemIndex</h4>' + '<div class="option-row">' + '<label><input type="radio" name="addPos" value="default" checked> sourceCollection の末尾に追加</label>' + '<label><input type="radio" name="addPos" value="bottom"> 現在のページの末尾に挿入</label>' + '<label><input type="radio" name="addPos" value="top"> 現在のページの先頭に挿入</label>' + '</div>' + '<div class="button-row">' + '<button id="runAddNew" class="demo-button" type="button">Run addNew()</button>' + '<span class="hint">グリッドの新規行は引き続き有効ですが、このボタンを使うと結果を確認しやすくなります。</span>' + '</div>' + '</div>' + '<div class="control-section">' + '<h4>insertAt(index)</h4>' + '<div class="button-row">' + '<button id="insertTop" class="demo-button" type="button">insertAt(0)</button>' + '<button id="insertBottom" class="demo-button" type="button">insertAt(pageSize - 1)</button>' + '<button id="insertNegative" class="demo-button" type="button">insertAt(-1)</button>' + '<button id="insertOverflow" class="demo-button" type="button">insertAt(pageSize)</button>' + '</div>' + '</div>' + '<div class="control-section">' + '<h4>モード切り替え</h4>' + '<div class="option-row">' + '<label><input id="sortById" type="checkbox"> id の降順で並べ替え</label>' + '<label><input id="filterEven" type="checkbox"> 偶数の id のみを表示</label>' + '<label><input id="groupByCountry" type="checkbox"> country ごとにグループ化</label>' + '</div>' + '</div>' + '<div id="statusPanel" class="status-panel"></div>'; pager.parentElement.insertBefore(host, grid); function getModeSummary() { var modes = []; if (view.sortDescriptions.length > 0) { modes.push('並べ替え'); } if (view.filter || (view.filters && view.filters.length > 0)) { modes.push('フィルター'); } if (view.groupDescriptions.length > 0) { modes.push('グループ化'); } return modes.length ? modes.join('、') : 'なし'; } function hasActiveViewTransform() { return view.sortDescriptions.length > 0 || !!view.filter || !!(view.filters && view.filters.length) || view.groupDescriptions.length > 0; } function setStatus(action, item, note) { var statusPanel = document.getElementById('statusPanel'); if (!statusPanel) { return; } var sourceIndex = item ? view.sourceCollection.indexOf(item) : -1; var pageIndex = item ? view.items.indexOf(item) : -1; statusPanel.innerHTML = '<div><strong>最後の操作:</strong> ' + action + '</div>' + '<div><strong>適用中の変換:</strong> ' + getModeSummary() + '</div>' + '<div><strong>ソース インデックス:</strong> ' + (sourceIndex > -1 ? sourceIndex : '-') + '</div>' + '<div><strong>現在のページ インデックス:</strong> ' + (pageIndex > -1 ? pageIndex : '現在のページ外') + '</div>' + '<div><strong>メモ:</strong> ' + note + '</div>'; } function populatePendingItem(item, label) { var record = createItem(label); item.id = record.id; item.country = record.country; item.product = record.product; item.sales = record.sales; item.expenses = record.expenses; } function updateNewItemIndex(value) { switch (value) { case 'top': view.newItemIndex = 0; break; case 'bottom': view.newItemIndex = view.pageSize - 1; break; default: view.newItemIndex = -1; break; } } function applyModes() { var sortCheckbox = document.getElementById('sortById'); var filterCheckbox = document.getElementById('filterEven'); var groupCheckbox = document.getElementById('groupByCountry'); var sortEnabled = sortCheckbox ? sortCheckbox.checked : false; var filterEnabled = filterCheckbox ? filterCheckbox.checked : false; var groupEnabled = groupCheckbox ? groupCheckbox.checked : false; view.sortDescriptions.clear(); view.groupDescriptions.clear(); if (sortEnabled) { view.sortDescriptions.push(new SortDescription('id', false)); } if (groupEnabled) { view.groupDescriptions.push(new PropertyGroupDescription('country')); } view.filter = filterEnabled ? function (item) { return item.id % 2 === 0; } : null; setStatus('モード更新', null, 'ページ基準の位置指定は、並べ替え、フィルター、グループ化がすべて無効な場合にのみ有効です。'); } function runAddNew() { var item = view.addNew(); populatePendingItem(item, 'addNew'); view.commitNew(); var note = hasActiveViewTransform() ? '並べ替え、フィルター、またはグループ化が有効なため、newItemIndex は sourceCollection の末尾への追加にフォールバックします。' : view.newItemIndex < 0 ? '既定の addNew の動作では、項目は sourceCollection の末尾に追加されます。' : 'newItemIndex により、項目は現在のページを基準に再配置されました。'; setStatus('addNew()', item, note); } function runInsertAt(index) { var item = view.insertAt(index); if (!item) { setStatus('insertAt(' + index + ')', null, 'インデックスが有効なページ基準の範囲外のため、挿入は実行されませんでした。'); return; } populatePendingItem(item, 'insertAt(' + index + ')'); view.commitNew(); var note = hasActiveViewTransform() ? '並べ替え、フィルター、またはグループ化が有効なため、有効な insertAt 要求は sourceCollection の末尾への追加にフォールバックします。' : 'insertAt により、項目は現在のページを基準に配置されました。'; setStatus('insertAt(' + index + ')', item, note); } var addPosInputs = host.querySelectorAll('input[name="addPos"]'); for (var i = 0; i < addPosInputs.length; i++) { addPosInputs[i].addEventListener('change', function (e) { var target = e.target; updateNewItemIndex(target ? target.value : 'default'); }); } document.getElementById('runAddNew').addEventListener('click', function () { runAddNew(); }); document.getElementById('insertTop').addEventListener('click', function () { runInsertAt(0); }); document.getElementById('insertBottom').addEventListener('click', function () { runInsertAt(view.pageSize - 1); }); document.getElementById('insertNegative').addEventListener('click', function () { runInsertAt(-1); }); document.getElementById('insertOverflow').addEventListener('click', function () { runInsertAt(view.pageSize); }); document.getElementById('sortById').addEventListener('change', applyModes); document.getElementById('filterEven').addEventListener('change', applyModes); document.getElementById('groupByCountry').addEventListener('change', applyModes); setStatus('準備完了', null, 'addNew() または insertAt() ボタンを使って、sourceCollection の末尾追加とページ基準の挿入の違いを比較できます。'); }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>MESCIUS Wijmo FlexGrid InsertAt and NewItemIndex</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.21.5/system.src.js" integrity="sha512-skZbMyvYdNoZfLmiGn5ii6KmklM82rYX2uWctBhzaXPxJgiv4XBwJnFGr5k8s+6tE1pcR1nuTKghozJHyzMcoA==" crossorigin="anonymous"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <main class="container-fluid"> <div id="thePager"></div> <div id="theGrid"></div> </main> </body> </html>
.demo-controls { margin: 12px 0; } .control-section { background: #f8fafc; border: 1px solid #d8dee9; border-radius: 6px; margin-bottom: 12px; padding: 12px; } .control-section h4 { font-size: 14px; font-weight: 600; margin: 0 0 8px; } .option-row, .button-row { align-items: center; display: flex; flex-wrap: wrap; gap: 10px 16px; } .option-row label { align-items: center; display: inline-flex; font-weight: 400; gap: 6px; margin: 0; } .hint { color: #666; font-size: 12px; } .demo-button { background: #ffffff; border: 1px solid #c7d2df; border-radius: 4px; color: #243447; cursor: pointer; font-size: 12px; line-height: 1.4; padding: 6px 12px; transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease; } .demo-button:hover { background: #f4f7fb; border-color: #b3c0cf; } .demo-button:focus { box-shadow: 0 0 0 2px rgba(69, 117, 180, 0.2); outline: none; } .demo-button:disabled { cursor: not-allowed; opacity: 0.55; } .status-panel { background: #fff7e6; border: 1px solid #f0c36d; border-radius: 6px; padding: 12px; } .status-panel div + div { margin-top: 6px; } .wj-flexgrid { height: 260px; } body { margin-bottom: 24px; }
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'jszip': 'npm:jszip/dist/jszip.js', '@mescius/wijmo': 'npm:@mescius/wijmo/index.js', '@mescius/wijmo.input': 'npm:@mescius/wijmo.input/index.js', '@mescius/wijmo.styles': 'npm:@mescius/wijmo.styles', '@mescius/wijmo.cultures': 'npm:@mescius/wijmo.cultures', '@mescius/wijmo.chart': 'npm:@mescius/wijmo.chart/index.js', '@mescius/wijmo.chart.analytics': 'npm:@mescius/wijmo.chart.analytics/index.js', '@mescius/wijmo.chart.animation': 'npm:@mescius/wijmo.chart.animation/index.js', '@mescius/wijmo.chart.annotation': 'npm:@mescius/wijmo.chart.annotation/index.js', '@mescius/wijmo.chart.finance': 'npm:@mescius/wijmo.chart.finance/index.js', '@mescius/wijmo.chart.finance.analytics': 'npm:@mescius/wijmo.chart.finance.analytics/index.js', '@mescius/wijmo.chart.hierarchical': 'npm:@mescius/wijmo.chart.hierarchical/index.js', '@mescius/wijmo.chart.interaction': 'npm:@mescius/wijmo.chart.interaction/index.js', '@mescius/wijmo.chart.radar': 'npm:@mescius/wijmo.chart.radar/index.js', '@mescius/wijmo.chart.render': 'npm:@mescius/wijmo.chart.render/index.js', '@mescius/wijmo.chart.webgl': 'npm:@mescius/wijmo.chart.webgl/index.js', '@mescius/wijmo.chart.map': 'npm:@mescius/wijmo.chart.map/index.js', '@mescius/wijmo.gauge': 'npm:@mescius/wijmo.gauge/index.js', '@mescius/wijmo.grid': 'npm:@mescius/wijmo.grid/index.js', '@mescius/wijmo.grid.detail': 'npm:@mescius/wijmo.grid.detail/index.js', '@mescius/wijmo.grid.filter': 'npm:@mescius/wijmo.grid.filter/index.js', '@mescius/wijmo.grid.search': 'npm:@mescius/wijmo.grid.search/index.js', '@mescius/wijmo.grid.style': 'npm:@mescius/wijmo.grid.style/index.js', '@mescius/wijmo.grid.grouppanel': 'npm:@mescius/wijmo.grid.grouppanel/index.js', '@mescius/wijmo.grid.multirow': 'npm:@mescius/wijmo.grid.multirow/index.js', '@mescius/wijmo.grid.transposed': 'npm:@mescius/wijmo.grid.transposed/index.js', '@mescius/wijmo.grid.transposedmultirow': 'npm:@mescius/wijmo.grid.transposedmultirow/index.js', '@mescius/wijmo.grid.pdf': 'npm:@mescius/wijmo.grid.pdf/index.js', '@mescius/wijmo.grid.sheet': 'npm:@mescius/wijmo.grid.sheet/index.js', '@mescius/wijmo.grid.xlsx': 'npm:@mescius/wijmo.grid.xlsx/index.js', '@mescius/wijmo.grid.selector': 'npm:@mescius/wijmo.grid.selector/index.js', '@mescius/wijmo.grid.cellmaker': 'npm:@mescius/wijmo.grid.cellmaker/index.js', '@mescius/wijmo.nav': 'npm:@mescius/wijmo.nav/index.js', '@mescius/wijmo.odata': 'npm:@mescius/wijmo.odata/index.js', '@mescius/wijmo.olap': 'npm:@mescius/wijmo.olap/index.js', '@mescius/wijmo.rest': 'npm:@mescius/wijmo.rest/index.js', '@mescius/wijmo.pdf': 'npm:@mescius/wijmo.pdf/index.js', '@mescius/wijmo.pdf.security': 'npm:@mescius/wijmo.pdf.security/index.js', '@mescius/wijmo.viewer': 'npm:@mescius/wijmo.viewer/index.js', '@mescius/wijmo.xlsx': 'npm:@mescius/wijmo.xlsx/index.js', '@mescius/wijmo.undo': 'npm:@mescius/wijmo.undo/index.js', '@mescius/wijmo.interop.grid': 'npm:@mescius/wijmo.interop.grid/index.js', '@mescius/wijmo.touch': 'npm:@mescius/wijmo.touch/index.js', '@mescius/wijmo.cloud': 'npm:@mescius/wijmo.cloud/index.js', '@mescius/wijmo.barcode': 'npm:@mescius/wijmo.barcode/index.js', '@mescius/wijmo.barcode.common': 'npm:@mescius/wijmo.barcode.common/index.js', '@mescius/wijmo.barcode.composite': 'npm:@mescius/wijmo.barcode.composite/index.js', '@mescius/wijmo.barcode.specialized': 'npm:@mescius/wijmo.barcode.specialized/index.js', 'jszip': 'npm:jszip/dist/jszip.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'js' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);