InsertAt 및 NewItemIndex

이 샘플은 클라이언트 측 페이징이 적용된 CollectionView에서 페이지 기준 삽입을 보여 줍니다.

라디오 버튼을 사용하여 addNew에 대한 newItemIndex 전략을 변경하고, 버튼을 사용하여 유효한 인덱스와 경계 인덱스로 insertAt을 테스트합니다.

이 샘플에서는 정렬, 필터링 및 그룹화를 토글하여 의도한 동작을 확인할 수도 있습니다.

  • 페이징이 활성화된 경우 newItemIndex는 0..pageSize - 1 범위의 값에 대해서만 적용됩니다. 음수 값과 pageSize보다 크거나 같은 값은 기존 addNew 동작을 유지하며 항목을 sourceCollection의 끝에 추가합니다.

  • 페이징이 활성화되지 않은 경우 newItemIndex는 전체 sourceCollection 내 위치로 처리됩니다.

  • insertAt은 인덱스가 유효한 페이지 기준 범위에 있는 경우에만 요청된 현재 페이지 위치에 삽입합니다. 유효하지 않은 인덱스는 삽입을 실행하지 않습니다.

  • 정렬, 필터링 또는 그룹화가 활성화된 경우 페이지 기준 위치 지정은 적용되지 않습니다. 이 경우 유효한 newItemIndex 및 insertAt 요청은 sourceCollection 끝에 추가하는 동작으로 대체되며, 유효하지 않은 insertAt 인덱스는 여전히 아무것도 삽입하지 않습니다.

FlexGrid 알아보기 | 페이징 문서 | CollectionView API 문서

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: 'Page {currentPage:n0} of {pageCount:n0}', cv: view }); createControls(view, function (label) { var item = createRecord(nextId, label); nextId++; return item; }); var grid = new FlexGrid('#theGrid', { headersVisibility: 'Column', 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 'US,Germany,UK,Japan,Italy,Greece'.split(','); } function createProducts() { return 'Piano,Violin,Flute,Guitar,Cello'.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> Append to sourceCollection tail</label>' + '<label><input type="radio" name="addPos" value="bottom"> Insert at current page bottom</label>' + '<label><input type="radio" name="addPos" value="top"> Insert at current page top</label>' + '</div>' + '<div class="button-row">' + '<button id="runAddNew" class="demo-button" type="button">Run addNew()</button>' + '<span class="hint">The grid new row is still enabled, but this button makes the result easier to inspect.</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>Mode switches</h4>' + '<div class="option-row">' + '<label><input id="sortById" type="checkbox"> Sort by id descending</label>' + '<label><input id="filterEven" type="checkbox"> Filter even ids only</label>' + '<label><input id="groupByCountry" type="checkbox"> Group by 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('sorting'); } if (view.filter || (view.filters && view.filters.length > 0)) { modes.push('filtering'); } if (view.groupDescriptions.length > 0) { modes.push('grouping'); } return modes.length ? modes.join(', ') : 'none'; } 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>Last action:</strong> ' + action + '</div>' + '<div><strong>Active transforms:</strong> ' + getModeSummary() + '</div>' + '<div><strong>Source index:</strong> ' + (sourceIndex > -1 ? sourceIndex : '-') + '</div>' + '<div><strong>Current page index:</strong> ' + (pageIndex > -1 ? pageIndex : 'not on current page') + '</div>' + '<div><strong>Note:</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('Mode update', null, 'Page-relative positioning is honored only when sorting, filtering, and grouping are all inactive.'); } function runAddNew() { var item = view.addNew(); populatePendingItem(item, 'addNew'); view.commitNew(); var note = hasActiveViewTransform() ? 'Sorting, filtering, or grouping is active, so newItemIndex falls back to appending at the sourceCollection tail.' : view.newItemIndex < 0 ? 'The default addNew behavior appended the item to the sourceCollection tail.' : 'newItemIndex repositioned the item relative to the current page.'; setStatus('addNew()', item, note); } function runInsertAt(index) { var item = view.insertAt(index); if (!item) { setStatus('insertAt(' + index + ')', null, 'No insertion was executed because the index is outside the valid page-relative range.'); return; } populatePendingItem(item, 'insertAt(' + index + ')'); view.commitNew(); var note = hasActiveViewTransform() ? 'Sorting, filtering, or grouping is active, so valid insertAt requests fall back to appending at the sourceCollection tail.' : 'insertAt positioned the item relative to the current page.'; 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('Ready', null, 'Use addNew() or the insertAt() buttons to compare sourceCollection append behavior with page-relative insertion. Grouping also disables page-relative positioning.'); }
<!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://cdn.jsdelivr.net/npm/systemjs@0.21.5/dist/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/', 'cdn:': 'https://cdn.mescius.io/demoapps/packages/wijmojs/5.20262.60-rc/', 'pcdn:': 'https://cdn.jsdelivr.net/npm/' }, // map tells the System loader where to look for things map: { '@mescius/wijmo': 'cdn:@mescius/wijmo/index.js', '@mescius/wijmo.input': 'cdn:@mescius/wijmo.input/index.js', '@mescius/wijmo.styles': 'cdn:@mescius/wijmo.styles', '@mescius/wijmo.cultures': 'cdn:@mescius/wijmo.cultures', '@mescius/wijmo.chart': 'cdn:@mescius/wijmo.chart/index.js', '@mescius/wijmo.chart.analytics': 'cdn:@mescius/wijmo.chart.analytics/index.js', '@mescius/wijmo.chart.animation': 'cdn:@mescius/wijmo.chart.animation/index.js', '@mescius/wijmo.chart.annotation': 'cdn:@mescius/wijmo.chart.annotation/index.js', '@mescius/wijmo.chart.finance': 'cdn:@mescius/wijmo.chart.finance/index.js', '@mescius/wijmo.chart.finance.analytics': 'cdn:@mescius/wijmo.chart.finance.analytics/index.js', '@mescius/wijmo.chart.hierarchical': 'cdn:@mescius/wijmo.chart.hierarchical/index.js', '@mescius/wijmo.chart.interaction': 'cdn:@mescius/wijmo.chart.interaction/index.js', '@mescius/wijmo.chart.radar': 'cdn:@mescius/wijmo.chart.radar/index.js', '@mescius/wijmo.chart.render': 'cdn:@mescius/wijmo.chart.render/index.js', '@mescius/wijmo.chart.webgl': 'cdn:@mescius/wijmo.chart.webgl/index.js', '@mescius/wijmo.chart.map': 'cdn:@mescius/wijmo.chart.map/index.js', '@mescius/wijmo.gauge': 'cdn:@mescius/wijmo.gauge/index.js', '@mescius/wijmo.grid': 'cdn:@mescius/wijmo.grid/index.js', '@mescius/wijmo.grid.detail': 'cdn:@mescius/wijmo.grid.detail/index.js', '@mescius/wijmo.grid.filter': 'cdn:@mescius/wijmo.grid.filter/index.js', '@mescius/wijmo.grid.search': 'cdn:@mescius/wijmo.grid.search/index.js', '@mescius/wijmo.grid.style': 'cdn:@mescius/wijmo.grid.style/index.js', '@mescius/wijmo.grid.grouppanel': 'cdn:@mescius/wijmo.grid.grouppanel/index.js', '@mescius/wijmo.grid.multirow': 'cdn:@mescius/wijmo.grid.multirow/index.js', '@mescius/wijmo.grid.transposed': 'cdn:@mescius/wijmo.grid.transposed/index.js', '@mescius/wijmo.grid.transposedmultirow': 'cdn:@mescius/wijmo.grid.transposedmultirow/index.js', '@mescius/wijmo.grid.pdf': 'cdn:@mescius/wijmo.grid.pdf/index.js', '@mescius/wijmo.grid.sheet': 'cdn:@mescius/wijmo.grid.sheet/index.js', '@mescius/wijmo.grid.xlsx': 'cdn:@mescius/wijmo.grid.xlsx/index.js', '@mescius/wijmo.grid.selector': 'cdn:@mescius/wijmo.grid.selector/index.js', '@mescius/wijmo.grid.cellmaker': 'cdn:@mescius/wijmo.grid.cellmaker/index.js', '@mescius/wijmo.nav': 'cdn:@mescius/wijmo.nav/index.js', '@mescius/wijmo.odata': 'cdn:@mescius/wijmo.odata/index.js', '@mescius/wijmo.olap': 'cdn:@mescius/wijmo.olap/index.js', '@mescius/wijmo.rest': 'cdn:@mescius/wijmo.rest/index.js', '@mescius/wijmo.pdf': 'cdn:@mescius/wijmo.pdf/index.js', '@mescius/wijmo.pdf.security': 'cdn:@mescius/wijmo.pdf.security/index.js', '@mescius/wijmo.viewer': 'cdn:@mescius/wijmo.viewer/index.js', '@mescius/wijmo.xlsx': 'cdn:@mescius/wijmo.xlsx/index.js', '@mescius/wijmo.undo': 'cdn:@mescius/wijmo.undo/index.js', '@mescius/wijmo.interop.grid': 'cdn:@mescius/wijmo.interop.grid/index.js', '@mescius/wijmo.touch': 'cdn:@mescius/wijmo.touch/index.js', '@mescius/wijmo.cloud': 'cdn:@mescius/wijmo.cloud/index.js', '@mescius/wijmo.barcode': 'cdn:@mescius/wijmo.barcode/index.js', '@mescius/wijmo.barcode.common': 'cdn:@mescius/wijmo.barcode.common/index.js', '@mescius/wijmo.barcode.composite': 'cdn:@mescius/wijmo.barcode.composite/index.js', '@mescius/wijmo.barcode.specialized': 'cdn:@mescius/wijmo.barcode.specialized/index.js', jszip: 'pcdn:jszip@3.8.0/dist/jszip.js', 'bootstrap.css': 'pcdn:bootstrap@5.3.8/dist/css/bootstrap.min.css', css: 'pcdn:systemjs-plugin-css@0.1.37/css.js', 'plugin-babel': 'pcdn:systemjs-plugin-babel@0.0.25/plugin-babel.js', 'systemjs-babel-build': 'pcdn:systemjs-plugin-babel@0.0.25/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);