Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/queued-scalar-inputs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/angular-pacer': minor
---

Return the processed scalar from `injectQueuedValue` and defer source reads until Angular inputs are available. Support primitive initial values and explicit options for object or function initial values. Replace uses of the previous `queued()` pending-items array with `queued.queuer.state().items` or `injectQueuedSignal`.
34 changes: 34 additions & 0 deletions docs/framework/angular/guides/queuing.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,40 @@ The focused snippets later in this guide use `injectQueuer` and assume they run

Pass `initialItems` when work is already available at creation time. The queue applies its normal insertion and capacity rules, and automatic processing can begin immediately unless `started: false` is set.

## Queue a source signal

`injectQueuedValue` returns a `QueuedValueSignal`: `queued()` reads the most recently processed value, `queued.addItem(value)` enqueues a value, and `queued.queuer` exposes the queue controls. Its default selector exposes pending items at `queued.queuer.state().items`.

```ts
readonly source = input.required<string>()
readonly queued = injectQueuedValue(this.source, { wait: 500 })
```

Construction does not read the source. Angular's effect reads it after inputs are bound and enqueues the latest value. Each later effect run enqueues the latest observed source value. Several source writes before an effect runs are coalesced; use `queued.addItem` for every value that must enter the queue individually.

Before processing begins, `queued()` reads the initial source value. Reading it before a required input is bound throws Angular's required-input error. Supply an explicit initial value if the output must be readable earlier:

```ts
const text = injectQueuedValue(source, '')
const object = injectQueuedValue(objectSource, { label: 'Loading' }, {})
const callback = injectQueuedValue(callbackSource, () => 'Loading', {})
```

Two-argument primitive initial values, including `0`, `false`, `''`, and `null`, are supported. Object and function initial values require a third options object, such as `{}`. A function in the options position is an options factory.

Pass a fourth selector argument, which can be `undefined`, when an explicit initial value uses factory or undefined options:

```ts
const queued = injectQueuedValue(source, '', () => ({ wait: wait() }), undefined)
const initialObject = injectQueuedValue(objectSource, { label: 'Loading' }, undefined, undefined)
```

`injectQueuedValue(source, options, undefined)` keeps the options-only meaning. An explicit `undefined` initial value requires an options object or a fourth argument.

### Migrate from the previous return shape

Earlier versions returned the pending-items array from `queued()`. It now returns the processed scalar value. Replace `queued().length` with `queued.queuer.state().items.length` using the default selector, or use `injectQueuedSignal` when the queue contents are the desired signal value. Custom selectors must include `items` to access that list.

## Ordering items

Automatic processing uses `addItemsTo` to choose where new items enter and `getItemsFrom` to choose where items leave.
Expand Down
145 changes: 123 additions & 22 deletions docs/framework/angular/reference/functions/injectQueuedValue.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ title: injectQueuedValue
function injectQueuedValue<TValue, TSelected>(
value,
options?,
selector?): QueuedSignal<TValue, TSelected>;
selector?): QueuedValueSignal<TValue, TSelected>;
```

Defined in: [queuer/injectQueuedValue.ts:32](https://github.com/TanStack/pacer/blob/main/packages/angular-pacer/src/queuer/injectQueuedValue.ts#L32)
Defined in: [queuer/injectQueuedValue.ts:48](https://github.com/TanStack/pacer/blob/main/packages/angular-pacer/src/queuer/injectQueuedValue.ts#L48)

An Angular function that creates a queued value that processes state changes in order with an optional delay.
This function uses injectQueuedSignal internally to manage a queue of state changes and apply them sequentially.
Expand All @@ -21,9 +21,17 @@ The queued value will process changes in the order they are received, with optio
processing each change. This is useful for handling state updates that need to be processed
in a specific order, like animations or sequential UI updates.

The function returns a tuple containing:
- A Signal that provides the current queued value
- The queuer instance with control methods
The returned signal provides the most recently processed value. Before the queue processes
an item, it provides the explicit initial value or the source's initial value.
Source reads are deferred until the signal is read or Angular runs its effect, so required
inputs can be bound before their values are read.

Use `queued.addItem(...)` to add a value and `queued.queuer` to control the queue.
Pending items are available through `queued.queuer.state().items` with the default selector.

Pass an options object as the third argument to supply an object or function as the initial
value. When those options are undefined or a factory, pass a fourth selector argument
(undefined is allowed) to distinguish the initial-value form from options plus a selector.

### Type Parameters

Expand All @@ -43,15 +51,15 @@ The function returns a tuple containing:

#### options?

[`AngularPacerOptions`](../type-aliases/AngularPacerOptions.md)\<`QueuerOptions`\<`TValue`\>\>
[`AngularPacerOptions`](../type-aliases/AngularPacerOptions.md)\<[`AngularQueuerOptions`](../interfaces/AngularQueuerOptions.md)\<`TValue`, `TSelected`\>\>

#### selector?

(`state`) => `TSelected`

### Returns

[`QueuedSignal`](../interfaces/QueuedSignal.md)\<`TValue`, `TSelected`\>
[`QueuedValueSignal`](../type-aliases/QueuedValueSignal.md)\<`TValue`, `TSelected`\>

### Example

Expand All @@ -73,10 +81,10 @@ function injectQueuedValue<TValue, TSelected>(
value,
initialValue,
options?,
selector?): QueuedSignal<TValue, TSelected>;
selector?): QueuedValueSignal<TValue, TSelected>;
```

Defined in: [queuer/injectQueuedValue.ts:43](https://github.com/TanStack/pacer/blob/main/packages/angular-pacer/src/queuer/injectQueuedValue.ts#L43)
Defined in: [queuer/injectQueuedValue.ts:59](https://github.com/TanStack/pacer/blob/main/packages/angular-pacer/src/queuer/injectQueuedValue.ts#L59)

An Angular function that creates a queued value that processes state changes in order with an optional delay.
This function uses injectQueuedSignal internally to manage a queue of state changes and apply them sequentially.
Expand All @@ -85,9 +93,17 @@ The queued value will process changes in the order they are received, with optio
processing each change. This is useful for handling state updates that need to be processed
in a specific order, like animations or sequential UI updates.

The function returns a tuple containing:
- A Signal that provides the current queued value
- The queuer instance with control methods
The returned signal provides the most recently processed value. Before the queue processes
an item, it provides the explicit initial value or the source's initial value.
Source reads are deferred until the signal is read or Angular runs its effect, so required
inputs can be bound before their values are read.

Use `queued.addItem(...)` to add a value and `queued.queuer` to control the queue.
Pending items are available through `queued.queuer.state().items` with the default selector.

Pass an options object as the third argument to supply an object or function as the initial
value. When those options are undefined or a factory, pass a fourth selector argument
(undefined is allowed) to distinguish the initial-value form from options plus a selector.

### Type Parameters

Expand All @@ -107,19 +123,95 @@ The function returns a tuple containing:

#### initialValue

`TValue`
`Extract`\<`TValue`, `PrimitiveInitialValue`\>

#### options?

`QueuerOptions`\<`TValue`\>
[`AngularQueuerOptions`](../interfaces/AngularQueuerOptions.md)\<`TValue`, `TSelected`\>

#### selector?

(`state`) => `TSelected`

### Returns

[`QueuedValueSignal`](../type-aliases/QueuedValueSignal.md)\<`TValue`, `TSelected`\>

### Example

```ts
const initialValue = signal('initial')
const queued = injectQueuedValue(initialValue, {
wait: 500,
started: true,
})

// Add changes to the queue
queued.addItem('new value')
```

## Call Signature

```ts
function injectQueuedValue<TValue, TSelected>(
value,
initialValue,
options,
selector?): QueuedValueSignal<TValue, TSelected>;
```

Defined in: [queuer/injectQueuedValue.ts:71](https://github.com/TanStack/pacer/blob/main/packages/angular-pacer/src/queuer/injectQueuedValue.ts#L71)

An Angular function that creates a queued value that processes state changes in order with an optional delay.
This function uses injectQueuedSignal internally to manage a queue of state changes and apply them sequentially.

The queued value will process changes in the order they are received, with optional delays between
processing each change. This is useful for handling state updates that need to be processed
in a specific order, like animations or sequential UI updates.

The returned signal provides the most recently processed value. Before the queue processes
an item, it provides the explicit initial value or the source's initial value.
Source reads are deferred until the signal is read or Angular runs its effect, so required
inputs can be bound before their values are read.

Use `queued.addItem(...)` to add a value and `queued.queuer` to control the queue.
Pending items are available through `queued.queuer.state().items` with the default selector.

Pass an options object as the third argument to supply an object or function as the initial
value. When those options are undefined or a factory, pass a fourth selector argument
(undefined is allowed) to distinguish the initial-value form from options plus a selector.

### Type Parameters

#### TValue

`TValue`

#### TSelected

`TSelected` *extends* `Pick`\<`QueuerState`\<`TValue`\>, `"items"`\> = `Pick`\<`QueuerState`\<`TValue`\>, `"items"`\>

### Parameters

#### value

`Signal`\<`TValue`\>

#### initialValue

`TValue`

#### options

[`AngularQueuerOptions`](../interfaces/AngularQueuerOptions.md)\<`TValue`, `TSelected`\>

#### selector?

(`state`) => `TSelected`

### Returns

[`QueuedSignal`](../interfaces/QueuedSignal.md)\<`TValue`, `TSelected`\>
[`QueuedValueSignal`](../type-aliases/QueuedValueSignal.md)\<`TValue`, `TSelected`\>

### Example

Expand All @@ -141,10 +233,10 @@ function injectQueuedValue<TValue, TSelected>(
value,
initialValue,
options,
selector): QueuedSignal<TValue, TSelected>;
selector): QueuedValueSignal<TValue, TSelected>;
```

Defined in: [queuer/injectQueuedValue.ts:55](https://github.com/TanStack/pacer/blob/main/packages/angular-pacer/src/queuer/injectQueuedValue.ts#L55)
Defined in: [queuer/injectQueuedValue.ts:83](https://github.com/TanStack/pacer/blob/main/packages/angular-pacer/src/queuer/injectQueuedValue.ts#L83)

An Angular function that creates a queued value that processes state changes in order with an optional delay.
This function uses injectQueuedSignal internally to manage a queue of state changes and apply them sequentially.
Expand All @@ -153,9 +245,17 @@ The queued value will process changes in the order they are received, with optio
processing each change. This is useful for handling state updates that need to be processed
in a specific order, like animations or sequential UI updates.

The function returns a tuple containing:
- A Signal that provides the current queued value
- The queuer instance with control methods
The returned signal provides the most recently processed value. Before the queue processes
an item, it provides the explicit initial value or the source's initial value.
Source reads are deferred until the signal is read or Angular runs its effect, so required
inputs can be bound before their values are read.

Use `queued.addItem(...)` to add a value and `queued.queuer` to control the queue.
Pending items are available through `queued.queuer.state().items` with the default selector.

Pass an options object as the third argument to supply an object or function as the initial
value. When those options are undefined or a factory, pass a fourth selector argument
(undefined is allowed) to distinguish the initial-value form from options plus a selector.

### Type Parameters

Expand All @@ -179,15 +279,16 @@ The function returns a tuple containing:

#### options

() => `QueuerOptions`\<`TValue`\>
\| [`AngularPacerOptions`](../type-aliases/AngularPacerOptions.md)\<[`AngularQueuerOptions`](../interfaces/AngularQueuerOptions.md)\<`TValue`, `TSelected`\>\>
\| `undefined`

#### selector

((`state`) => `TSelected`) \| `undefined`

### Returns

[`QueuedSignal`](../interfaces/QueuedSignal.md)\<`TValue`, `TSelected`\>
[`QueuedValueSignal`](../type-aliases/QueuedValueSignal.md)\<`TValue`, `TSelected`\>

### Example

Expand Down
1 change: 1 addition & 0 deletions docs/framework/angular/reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ title: "@tanstack/angular-pacer"
## Type Aliases

- [AngularPacerOptions](type-aliases/AngularPacerOptions.md)
- [QueuedValueSignal](type-aliases/QueuedValueSignal.md)

## Functions

Expand Down
37 changes: 37 additions & 0 deletions docs/framework/angular/reference/type-aliases/QueuedValueSignal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
id: QueuedValueSignal
title: QueuedValueSignal
---

```ts
type QueuedValueSignal<TValue, TSelected> = Signal<TValue> & object;
```

Defined in: [queuer/injectQueuedValue.ts:9](https://github.com/TanStack/pacer/blob/main/packages/angular-pacer/src/queuer/injectQueuedValue.ts#L9)

A processed value signal with methods for adding and controlling queued values.

## Type Declaration

### addItem

```ts
addItem: AngularQueuer<TValue, TSelected>["addItem"];
```

### queuer

```ts
queuer: AngularQueuer<TValue, TSelected>;
```

## Type Parameters

### TValue

`TValue`

### TSelected

`TSelected` = \{
\}
2 changes: 1 addition & 1 deletion examples/angular/injectQueuedValue/src/app/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ <h1>injectQueuedValue</h1>

<p>Source (instant): {{ source() }}</p>
<p>Value (queued): {{ queued() }}</p>
<p>Queue length: {{ queued().length }}</p>
<p>Queue length: {{ queued.queuer.state().items.length }}</p>

<button (click)="enqueueRandom()">Enqueue random</button>
<button (click)="clear()">Clear</button>
Expand Down
2 changes: 1 addition & 1 deletion examples/angular/injectQueuedValue/src/app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class App {
protected readonly source = signal('')

// A queued value: changes are applied in-order, with an optional delay between items.
// `value()` is the current processed value, and `items()` exposes the pending queue.
// `queued()` is the processed value; `queued.queuer.state().items` contains pending values.
protected readonly queued = injectQueuedValue(this.source, { wait: 500 }, (state) => ({
items: state.items,
}))
Expand Down
6 changes: 3 additions & 3 deletions examples/angular/injectQueuedValue/src/app/inputapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import { injectQueuedValue } from '@tanstack/angular-pacer'
selector: 'app-input',
standalone: true,
template: `
<h1>NG0950</h1>
<h1>Required input</h1>
<p>value: {{ value() }}</p>
<p>Value (queued): {{ queued() }}</p>
<p>Queue length: {{ queued().length }}</p>
<p>Queue length: {{ queued.queuer.state().items.length }}</p>
<button (click)="enqueueRandom()">Enqueue random</button>
`,
})
export class InputApp {
readonly value = input.required<string>()

protected readonly queued = injectQueuedValue(this.value, null, { wait: 500 }, (state) => ({
protected readonly queued = injectQueuedValue(this.value, { wait: 500 }, (state) => ({
items: state.items,
}))

Expand Down
Loading
Loading