DocsRenderAPI ReferencesignalStateStore()

signalStateStore()

Creates a reactive state store backed by Angular Signals that implements the StateStore interface from @json-render/core.

Import

import { signalStateStore } from '@ngaf/render';

Signature

function signalStateStore(initialState?: StateModel): StateStore;

Parameters

ParameterTypeDefaultDescription
initialStateStateModel{}Initial state object. StateModel is Record<string, unknown>.

Returns

A StateStore object with the following interface:

interface StateStore {
  get(path: string): unknown;
  set(path: string, value: unknown): void;
  update(updates: Record<string, unknown>): void;
  getSnapshot(): StateModel;
  subscribe(listener: () => void): () => void;
}

Methods

get(path)

Reads a value from the store at the given JSON Pointer path.

const store = signalStateStore({ user: { name: 'Alice' }, items: ['a', 'b'] });
 
store.get('/user/name');  // 'Alice'
store.get('/user');       // { name: 'Alice' }
store.get('/items/0');    // 'a'
store.get('/missing');    // undefined
ParameterTypeDescription
pathstringA JSON Pointer path (e.g., /user/name)

Returns: The value at the path, or undefined if the path does not exist.

set(path, value)

Sets a single value at the given path. Performs an immutable update -- the path to the target is cloned, preserving unchanged branches. If the new value is referentially equal (===) to the current value, the update is skipped and no subscribers are notified.

store.set('/user/name', 'Bob');
store.get('/user/name'); // 'Bob'
 
// No-op if value hasn't changed
store.set('/user/name', 'Bob'); // no notification
ParameterTypeDescription
pathstringA JSON Pointer path
valueunknownThe new value to set

update(updates)

Batch-sets multiple values in a single operation. Only triggers one subscriber notification, regardless of how many values change. If no values actually change, no notification is triggered.

store.update({
  '/user/name': 'Charlie',
  '/user/age': 25,
});
ParameterTypeDescription
updatesRecord<string, unknown>A map of JSON Pointer paths to new values

getSnapshot()

Returns the entire state object.

const store = signalStateStore({ x: 1, y: 2 });
store.getSnapshot(); // { x: 1, y: 2 }

Returns: StateModel -- the current state object.

subscribe(listener)

Registers a callback that is invoked after every state mutation. Returns an unsubscribe function.

const unsubscribe = store.subscribe(() => {
  console.log('State changed');
});
 
store.set('/count', 1); // logs: State changed
 
unsubscribe();
store.set('/count', 2); // no log
ParameterTypeDescription
listener() => voidCallback invoked after state changes

Returns: () => void -- an unsubscribe function.

JSON Pointer Format

Paths follow the RFC 6901 JSON Pointer specification:

  • Paths start with /
  • Each /-separated segment traverses one level
  • Array elements are accessed by numeric index
  • Escape sequences: ~0 for ~, ~1 for /
PathDescription
/nameTop-level name property
/user/emailNested property
/items/0First array element
/items/2/namename property of third array element
/a~1bProperty named a/b

Reactive Behavior

The store wraps state in an Angular signal(). This means:

  • Reading state via get() accesses the signal, creating a reactive dependency in any computed() or template that calls it
  • Writing state via set() or update() triggers the signal, which propagates through Angular's change detection
  • RenderSpecComponent and RenderElementComponent use computed() signals that depend on the store, ensuring the UI updates automatically
// The library internally does this:
const resolvedInputs = computed(() => {
  const ctx = { stateModel: store.getSnapshot() };
  return resolveElementProps(el.props, ctx);
});
// When store.set() fires, the signal updates, resolvedInputs recomputes,
// and NgComponentOutlet receives new inputs.

Array Handling

The store preserves array types when updating elements by index:

const store = signalStateStore({ items: ['a', 'b', 'c'] });
 
store.set('/items/1', 'B');
store.get('/items');          // ['a', 'B', 'c']
Array.isArray(store.get('/items')); // true

Immutable Updates

Every set() and update() call produces a new state object. Unchanged branches of the state tree are shared (structural sharing):

const store = signalStateStore({ a: { x: 1 }, b: { y: 2 } });
const before = store.getSnapshot();
 
store.set('/a/x', 10);
const after = store.getSnapshot();
 
before !== after;       // true -- new root
before.b === after.b;   // true -- b branch unchanged, same reference