InsertAt and NewItemIndex

This sample demonstrates page-relative insertion in a client-side paged CollectionView.

Use the radio buttons to change the newItemIndex strategy for addNew, and use the buttons to test insertAt with valid and boundary indexes.

The sample also lets you toggle sorting, filtering, and grouping so you can verify the intended behavior:

  • When paging is active, newItemIndex is honored only for values in the range 0..pageSize - 1. Negative values and values greater than or equal to pageSize keep the original addNew behavior and append the item to the end of the sourceCollection.

  • When paging is not active, newItemIndex is treated as a position within the full sourceCollection.

  • insertAt inserts at the requested current-page position only when the index is in the valid page-relative range. Invalid indexes do not execute insertion.

  • When sorting, filtering, or grouping is active, page-relative positioning is not applied. In those cases, valid newItemIndex and insertAt requests fall back to appending at the end of the sourceCollection, while invalid insertAt indexes still do not insert anything.

Learn about FlexGrid | Paging Documentation | CollectionView API Reference

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.20261.52-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);