<!-- Documentation snapshot: package 5.0.0-rc.2; channel latest; archived channel latest; base revision f4243b8334cafe0bd1b06eba85d87e2310cb3618; local changes false; original source SHA-256 c85bef28dbf1a11cb74526dc630bdf25705442f4920ca64549e17b77e4df25ac; reading source revision f4243b8334cafe0bd1b06eba85d87e2310cb3618; publication edits /docs/publication.json. -->

# Show, sort, and filter a list

Use CollectionView for repeated children that share one placement rule. A surrounding View can own the list's local controls and Region. Add an Application when the feature needs API readiness or broader coordination; sorting a displayed list does not require one.

This catalog uses the optional `@mnjs/data` Collection for observable membership. It is incomplete as an application data solution: fetching and persistence need an API layer or another data provider. Install the rendering and data packages from [setup](/docs/integrations/setup.md).

## Own the controls and repeated rows

```js
import { CollectionView, Region, View } from 'marionette';
import { Collection, DataApi } from '@mnjs/data';
import LitDomApi from '@mnjs/adapters/dom/lit-html';
import { html } from 'lit-html';

const ItemView = View.extend({
  tagName: 'li',
  template: ({ label }) => html`
    <strong>${label}</strong>
    <label>Notes for ${label} <input></label>`,
  modelEvents: { change: 'render' },
}).setDataApi(DataApi).setDomApi(LitDomApi);
const EmptyView = View.extend({
  tagName: 'li',
  template: () => html`No matching items.`,
}).setDomApi(LitDomApi);
const ItemList = CollectionView.extend({
  tagName: 'ul',
  childView: ItemView,
  emptyView: EmptyView,
  viewComparator: 'label',
}).setDataApi(DataApi);
const CatalogView = View.extend({
  template: () => html`
    <label><input type="checkbox" class="available"> Available only</label>
    <button type="button" class="reverse">Reverse order</button>
    <section class="items" aria-label="Catalog"></section>`,
  regions: { items: '.items' },
  ui: { available: '.available' },
  events: { 'change @ui.available': 'filterItems', 'click .reverse': 'reverseItems' },
  onRender() {
    this.descending = false;
    this.getUI('available')[0].checked = false;
    this.list = new ItemList({ collection: this.options.items });
    this.showChildView('items', this.list);
  },
  filterItems() {
    this.list.setFilter(this.getUI('available')[0].checked ? { available: true } : null);
  },
  reverseItems() {
    this.descending = !this.descending;
    this.list.setComparator(this.descending ?
      (left, right) => right.model.get('label').localeCompare(left.model.get('label')) : 'label');
  },
}).setDomApi(LitDomApi);

const items = new Collection([
  { id: 'a', label: 'Apricot', available: true },
  { id: 'b', label: 'Blueberry', available: false },
]);
const catalog = new CatalogView({ items });
const mount = document.createElement('section');
document.body.append(mount);
const region = new Region({ el: mount });
region.show(catalog);
```

The outer Region owns CatalogView; its named Region owns ItemList; the list owns rows and its empty View. The collection is borrowed. Destroy the outer Region when this feature ends, then dispose data according to the lifetime of the code that created it.

The notes fields are temporary DOM input. Sorting moves the existing rows; filtering detaches excluded rows and retains their Views and input values. Returning a row preserves its nodes, but leaving the document can lose focus. When a filter hides the focused row, choose a focus destination such as the filter control. See [accessibility and rendering](/docs/guides/accessibility-rendering.md).

## Update without rebuilding everything

| Change | Operation | Effect |
| --- | --- | --- |
| Add or remove a member | `items.add(...)` / `items.remove(...)` | Creates/destroys affected rows and retains surviving rows. Removing membership does not destroy the Model. |
| Change a displayed attribute | `model.set(...)` | This example's `modelEvents` renders that row. |
| Change an attribute used by sorting/filtering | Then call `catalog.list.sort()` / `filter()` | Recomputes presentation; a native Model change does not automatically rerun the parent criteria. |
| Show all rows | `catalog.list.removeFilter()` | Restores retained hidden rows. |
| Replace the membership snapshot | `items.reset(...)` | Destroys and rebuilds rows, including rows for retained Models. |

Avoid full `list.render()` to apply a filter or sort: it rebuilds managed rows. Rendering CatalogView again also destroys its Region children; this example deliberately resets its local controls when rebuilding the list. If notes must survive those operations or navigation, put the draft in an appropriately owned data source.

An empty presentation can mean all rows are filtered out even when the collection has members. `emptyView` describes that presentation, rather than storing an empty record in the collection. Give it a root valid for the container, such as `li` inside `ul`.

## Identity and scale

The configured DataApi determines keys and collection observation. With `@mnjs/data`, the key is Model `cid`; reusing a server `id` in a new Model does not reuse the old row. Other providers must supply stable unique keys, but a replacement model object still replaces its View.

`list.children` describes the currently presented rows, excluding hidden rows and the empty View. Save a row reference before hiding it when checking identity. CollectionView is not a virtualized viewport: hidden rows remain alive. For large datasets, choose paging or a measured windowing solution based on the application's needs.

## Check the behavior

Enter notes, sort, hide and restore that row, and check the same input and value return. Add/remove members and verify surviving row identity and outgoing destruction. With Available only selected, call `items.at(0).set('available', false)` and `catalog.list.filter()` to filter every row out; verify empty presentation without changing collection length. Finish by destroying the Region and checking that no borrowed data was destroyed or continues rendering the old list. [CollectionView reference](/docs/api/collection-view.md) defines the full contract; [consumer testing](/docs/guides/testing.md) gives a test setup.


[Canonical source](/docs/markdown/docs/guides/lists.md) · [Source identity](/docs/manifest.json)
