ListView — Scrolling (100 items)
The supplied model contains one task. A Razor loop adds 98 tasks, followed by the Inspect item: 100 items in a scrollable view capped at 400px. The root tag overrides the model's selection and checkbox settings. Adaptive spacing aligns labels even when only one item has a start glyph. Pills use the end positions.
Select a task or activate Inspect.
Selection and committed edits appear in the submission envelope. This example displays the envelope locally; application code owns saving and acknowledgment.
Razor
@model ListViewExampleModel
<form id="tasks-example-form">
<bs-listview model="@Model.Tasks" selection-mode="single" checkable="false" search="false"
track-changes="true" selection-bound-name="SelectedTask" submission-bound-name="TaskChanges"
event-selected="listExample.selected">
@for (var index = 0; index < 98; index++)
{
var code = (index + 44).ToString("D4");
<bs-listview-item item-id="@($"task-{code}")" label="@($"Documentation task {code}")"
subtitle="Reviewing typography and page layout" bound-value="@($"TASK-{code}")"
item-data="@(new { code, owner = "Documentation" })" />
}
<bs-listview-item item-id="publish" label="Publish documentation" subtitle="Waiting for review"
bound-value="TASK-0043">
<bs-end-near-pill text="Waiting" variant="secondary" />
<bs-end-far-pill text="Inspect" intent="functional" on-activate="listExample.inspect" action-name="inspect" />
</bs-listview-item>
</bs-listview>
<button type="button" id="tasks-example-rename" class="btn btn-outline-primary mt-3">Update review label</button>
<button type="button" id="tasks-example-inspect" class="btn btn-primary mt-3">Show selection and changes</button>
<button type="button" id="tasks-example-reset" class="btn btn-outline-secondary mt-3">Reset this example</button>
</form>
<p id="tasks-example-status" role="status">Select a task or activate Inspect.</p>
<pre id="tasks-example-output" aria-label="Selection and change submission"></pre>
<script type="module" src="/js/listview-example.js"></script>
Supplied model
using CopelandSyst.BootstrapComponents.ViewModels;
namespace CopelandSyst.Docs.Models;
public sealed class ListViewExampleModel
{
public BootstrapListViewModel Tasks { get; } = new()
{
Id = "tasks-example", SelectionMode = "multiple", Checkable = true,
ItemsCapture =
{
new BootstrapListViewItemModel
{
ItemId = "review", Label = "Review typography", Subtitle = "Check g, p and y descenders",
BoundValue = "TASK-0042", Data = new { code = "0042", owner = "Documentation" },
StartSlotCapture = new BootstrapViewItemIconModel { Class = "bi bi-file-text", Intent = "decorative" },
EndNearSlotCapture = new BootstrapViewItemPillModel { Text = "Ready", Variant = "success" },
},
},
};
}
Selection, changes and actions
import { ListView } from '/_content/CopelandSyst.BootstrapComponents/lib/Bootstrap/dist/listview/bs-listview.js';
const output = document.getElementById('tasks-example-output');
const status = document.getElementById('tasks-example-status');
window.listExample = {
selected(event, view) {
output.textContent = JSON.stringify(view.createSubmission(), null, 2);
},
inspect(event) {
status.textContent = `Inspect action for ${event.detail.id}; selection remains independent.`;
}
};
const view = ListView.getOrCreateInstance(document.getElementById('tasks-example'));
document.getElementById('tasks-example-reset').addEventListener('click', () => {
view.resetChanges();
output.textContent = '';
status.textContent = 'Select a task or activate Inspect.';
});
document.getElementById('tasks-example-rename').addEventListener('click', () => {
view.updateItem('review', { label: 'Typography reviewed' });
output.textContent = JSON.stringify(view.createSubmission(), null, 2);
});
document.getElementById('tasks-example-inspect').addEventListener('click', () => {
output.textContent = JSON.stringify(view.createSubmission(), null, 2);
});
Viewport CSS
.view-example-nav-viewport.docs-sticky {
position: relative;
top: auto;
height: 28rem;
max-height: 65vh;
overflow: auto;
border: 1px solid var(--bs-border-color);
border-radius: var(--bs-border-radius);
}
#tasks-example {
max-height: 400px;
overflow-y: auto;
}
#tasks-example-output {
max-height: 24rem;
overflow: auto;
}
#ajax-demo { max-height: 400px; overflow: auto; }
#ajax-example pre { max-height: 24rem; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; }
#ajax-example[data-state="error"] #ajax-status { color: var(--bs-danger-text-emphasis); }
#ajax-example[data-state="saving"] #ajax-status { color: var(--bs-primary-text-emphasis); }
Model merging checks exercise both DOM and JSON payloads. TreeView has its own navigation example.