Skip to content

Sponsor

Sponsor

Advanced Features and API Reference

Beyond the basic collection, there are specialized factories for collections with computed properties, filtered views, and the toggleAll extension. This page also contains the complete API reference.


Collections with Computed Properties

What is it?

Collections.createWithComputed() creates a collection that has computed properties — values that automatically recalculate whenever the items change.

Syntax

javascript
Collections.createWithComputed(items, computedProperties);

Parameters

ParameterTypeDescription
itemsArrayInitial items (default: [])
computedPropertiesObjectKey-value pairs where each value is a function

Example

javascript
const todos = Collections.createWithComputed(
  [
    { id: 1, text: 'Buy milk', done: false },
    { id: 2, text: 'Walk dog', done: true }
  ],
  {
    doneCount: function() { return this.items.filter(t => t.done).length; },
    remaining: function() { return this.items.filter(t => !t.done).length; },
    allDone: function() { return this.items.every(t => t.done); }
  }
);

console.log(todos.doneCount);  // 1
console.log(todos.remaining);  // 1
console.log(todos.allDone);    // false

How it works

Collections.createWithComputed(items, computed)

1️⃣ Creates a normal collection with createCollection(items)

2️⃣ For each key in computed:
   └── Calls computed(collection, { [key]: fn })

3️⃣ Computed properties are now reactive
   → They recalculate when items change

4️⃣ Returns the collection

Practical use

javascript
const cart = Collections.createWithComputed([], {
  total: function() {
    return this.items.reduce((sum, i) => sum + i.price * i.qty, 0);
  },
  itemCount: function() {
    return this.items.reduce((sum, i) => sum + i.qty, 0);
  }
});

cart.add({ id: 1, name: 'Widget', price: 9.99, qty: 2 });
cart.add({ id: 2, name: 'Gadget', price: 24.99, qty: 1 });

console.log(cart.total);      // 44.97
console.log(cart.itemCount);  // 3

// Use in effects
effect(() => {
  Elements.cartTotal.update({ textContent: `$${cart.total.toFixed(2)}` });
  Elements.cartItems.update({ textContent: `${cart.itemCount} items` });
});

The computed properties recalculate automatically whenever items are added, removed, or changed.


Filtered Collections

What is it?

Collections.createFiltered() creates a live filtered view of another collection. Whenever the source collection changes, the filtered view updates automatically.

Syntax

javascript
Collections.createFiltered(sourceCollection, filterPredicate);

Parameters

ParameterTypeDescription
sourceCollectionCollectionThe source collection to filter
filterPredicateFunctionA function that returns true for items to include

Example

javascript
const todos = Collections.create([
  { id: 1, text: 'Buy milk', done: false },
  { id: 2, text: 'Walk dog', done: true },
  { id: 3, text: 'Clean house', done: false }
]);

// Create a filtered view that only shows active (not done) items
const activeTodos = Collections.createFiltered(todos, t => !t.done);

console.log(activeTodos.items);
// [{ id: 1, text: 'Buy milk', done: false }, { id: 3, text: 'Clean house', done: false }]

console.log(activeTodos.length);  // 2

How it works

Collections.createFiltered(source, predicate)

1️⃣ Creates an empty collection

2️⃣ Sets up an effect:
   └── Runs source.items.filter(predicate)
   └── Calls filtered.reset(newItems)

3️⃣ The effect re-runs whenever source.items changes
   → The filtered collection stays in sync automatically

4️⃣ Returns the filtered collection

Live updating

The filtered view stays in sync with the source:

javascript
const todos = Collections.create([
  { id: 1, text: 'Task A', done: false },
  { id: 2, text: 'Task B', done: false }
]);

const doneTodos = Collections.createFiltered(todos, t => t.done);
console.log(doneTodos.length);  // 0

// Mark one as done — the filtered view updates automatically
todos.toggle(t => t.id === 1, 'done');
// doneTodos now contains: [{ id: 1, text: 'Task A', done: true }]

// Add a new done item
todos.add({ id: 3, text: 'Task C', done: true });
// doneTodos now contains both done items

Multiple filtered views

You can create multiple views from the same source:

javascript
const tasks = Collections.create([
  { id: 1, text: 'Task A', priority: 'high', done: false },
  { id: 2, text: 'Task B', priority: 'low', done: true },
  { id: 3, text: 'Task C', priority: 'high', done: true }
]);

const highPriority = Collections.createFiltered(tasks, t => t.priority === 'high');
const completed = Collections.createFiltered(tasks, t => t.done);
const active = Collections.createFiltered(tasks, t => !t.done);

console.log(highPriority.length);  // 2
console.log(completed.length);     // 2
console.log(active.length);        // 1

The toggleAll Extension

What is it?

The toggleAll method is added by a second IIFE (Immediately Invoked Function Expression) that extends the collection factory. It toggles a boolean field on all items that match a predicate.

How it gets added

Second IIFE loads

1️⃣ Stores the original collection factory

2️⃣ Creates a new factory that:
   ├── Calls the original factory
   └── Adds .toggleAll() to the result

3️⃣ Replaces the factory on all globals:
   ├── ReactiveUtils.collection
   ├── ReactiveUtils.list
   ├── ReactiveUtils.createCollection
   ├── Collections.create
   ├── Collections.collection
   └── Collections.list

Usage

javascript
const todos = Collections.create([
  { id: 1, text: 'Task A', done: false },
  { id: 2, text: 'Task B', done: false },
  { id: 3, text: 'Task C', done: true }
]);

// Toggle all items that are not done
const count = todos.toggleAll(t => !t.done, 'done');
console.log(count);  // 2

// Toggle ALL items (pass a predicate that matches everything)
const allCount = todos.toggleAll(() => true, 'done');
console.log(allCount);  // 3

Key difference from .toggle()

.toggle(predicate, field)
   → Finds the FIRST matching item
   → Toggles its boolean field
   → Returns the collection (chainable)

.toggleAll(predicate, field)
   → Finds ALL matching items
   → Toggles their boolean field
   → Returns the count of toggled items (not chainable)

Complete API Reference

Factory Functions

FunctionAvailable onDescription
create(items)CollectionsCreate a collection
collection(items)Collections, ReactiveUtilsAlias for create
list(items)Collections, ReactiveUtilsAlias for create
createCollection(items)ReactiveUtilsAlias for create
createWithComputed(items, computed)CollectionsCollection + computed properties
createFiltered(source, predicate)CollectionsLive filtered view

Also available on

javascript
// Through ReactiveState (if loaded)
ReactiveState.collection(items);
ReactiveState.list(items);

CRUD Methods

MethodParametersReturnsDescription
.add(item)item: anyCollectionAdd one item
.push(...items)items: any[]CollectionAdd one or more items
.remove(predicate)predicate: fn or valueCollectionRemove first match
.removeWhere(predicate)predicate: fnCollectionRemove all matches
.update(predicate, updates)predicate: fn or value, updates: objectCollectionUpdate first match
.updateWhere(predicate, updates)predicate: fn, updates: objectCollectionUpdate all matches
.toggle(predicate, field?)predicate: fn or value, field: string (default: 'done')CollectionToggle boolean on first match
.toggleAll(predicate, field?)predicate: fn or value, field: string (default: 'done')NumberToggle boolean on all matches
.clear()CollectionRemove all items
.reset(newItems?)newItems: array (default: [])CollectionReplace all items

Query Methods

MethodParametersReturnsDescription
.find(predicate)predicate: fn or valueItem or undefinedFind first match
.filter(predicate)predicate: fnArrayGet all matches
.includes(item)item: anyBooleanCheck if item exists (by reference)
.indexOf(item)item: anyNumberGet index of item
.at(index)index: numberItemGet item by index
.isEmpty()BooleanCheck if collection is empty

Iteration & Transformation

MethodParametersReturnsDescription
.map(fn)fn: functionArrayTransform items into new array
.forEach(fn)fn: functionCollectionIterate over items
.sort(compareFn?)compareFn: functionCollectionSort items in place
.reverse()CollectionReverse item order
.slice(start?, end?)start: number, end: numberArrayGet a portion as new array
.splice(start, count, ...items)start: number, count: number, items: any[]CollectionRemove/insert at position
.pop()ItemRemove and return last item
.shift()ItemRemove and return first item
.unshift(...items)items: any[]CollectionAdd items to the beginning
.toArray()ArrayGet a plain array copy

Convenience Getters

GetterReturnsDescription
.lengthNumberNumber of items
.firstItem or undefinedFirst item
.lastItem or undefinedLast item

Inherited Reactive Functions

Since collections are built on reactive state, all global reactive functions work with them:

FunctionExample
computed(state, { key: fn })Add a computed property
watch(state, 'key', callback)Watch a property for changes
batch(fn)Batch multiple changes
notify(state, 'key')Manually trigger effects for a key
toRaw(state)Access the raw (unproxied) data

Instance methods (computed, watch, .batch, .update, .set, .bind, .notify, .raw) are also available — see instance methods.


Load Order

html
<!-- 1. DOMHelpers Core (optional) -->
<script type="module">
  import { load } from 'https://cdn.jsdelivr.net/npm/dom-helpers-js@2.10.0/dist/dom-helpers.loader.esm.min.js';
  await load('reactive');
</script>

<!-- 2. Reactive State (required) -->
<script type="module">
  import { load } from 'https://cdn.jsdelivr.net/npm/dom-helpers-js@2.10.0/dist/dom-helpers.loader.esm.min.js';
  await load('reactive');
</script>

<!-- 3. Array Patch (required — collections rely on patched arrays) -->
<script type="module">
  import { load } from 'https://cdn.jsdelivr.net/npm/dom-helpers-js@2.10.0/dist/dom-helpers.loader.esm.min.js';
  await load('reactive');
</script>

<!-- 4. Collections (must come after reactive state and array patch) -->
<script type="module">
  import { load } from 'https://cdn.jsdelivr.net/npm/dom-helpers-js@2.10.0/dist/dom-helpers.loader.esm.min.js';
  await load('reactive');
</script>

Order matters:

  1. The reactive module provides state()
  2. 02_dh-reactive-array-patch.js patches array methods to trigger effects
  3. 03_dh-reactive-collections.js uses both to create reactive collections

Module Structure

The collections file contains two IIFEs:

IIFE 1 — Main Collections Extension
├── createCollection(items)
├── createCollectionWithComputed(items, computed)
├── createFilteredCollection(collection, predicate)
└── Exports to Collections, ReactiveUtils, ReactiveState

IIFE 2 — toggleAll Extension
├── Wraps the original collection factory
├── Adds .toggleAll() method to every new collection
└── Replaces factory on all globals

Common Questions

Can I use reactive instance methods on a collection?

Yes. Collections are reactive state objects, so all instance methods work:

javascript
const todos = Collections.create([]);

// Add a computed property
computed(todos, {
  doneCount: function() {
  return this.items.filter(t => t.done).length;
}
});

// Watch for changes
watch(todos, 'items', (newItems, oldItems) => {
  console.log('Items changed:', newItems.length);
});

// Batch changes
batch(() => {
  todos.add({ id: 1, text: 'Task A', done: false });
  todos.add({ id: 2, text: 'Task B', done: false });
  todos.add({ id: 3, text: 'Task C', done: false });
});
// Only one effect re-run for all three adds

Can I nest collections?

Collections are designed for flat lists. For nested data, use reactive state directly:

javascript
// ✅ Flat list — use a collection
const todos = Collections.create([
  { id: 1, text: 'Task', done: false }
]);

// ✅ Nested or complex state — use reactive state
const app = state({
  user: { name: 'Alice' },
  settings: { theme: 'dark' },
  todos: []
});

What's the difference between .items and .toArray()?

javascript
const list = Collections.create([1, 2, 3]);

list.items;      // The reactive array (tracked by the proxy)
list.toArray();  // A plain copy (modifying it won't affect the collection)

Use .items when you need reactivity (inside effects). Use .toArray() when you need a snapshot to pass elsewhere.


Summary

ConceptKey Takeaway
createCollectionFactory that creates a reactive state with .items and 25+ methods
createWithComputedCollection + computed properties that auto-recalculate
createFilteredLive filtered view that stays in sync with the source
toggleAllExtension that toggles boolean fields on all matching items
ChainableMost methods return this for fluent APIs
PredicatesAccept a value (exact match) or function (conditional match)
ReactiveAll mutations trigger effects automatically
InheritedCollections have all reactive instance methods (computed, watch, etc.)

Congratulations!

You've completed the Reactive Collections learning path. You now understand:

  • ✅ What reactive collections are and why they exist
  • ✅ How to create and use collections with the basic API
  • ✅ Every CRUD, query, iteration, and convenience method in detail
  • ✅ Real-world patterns for todos, carts, chats, and more
  • ✅ Advanced features: computed properties, filtered views, and toggleAll
  • ✅ The complete API reference and load order

Your reactive collections are ready for production!