Skip to content

Commit

Permalink
feat(signals): add deepComputed function (#4539)
Browse files Browse the repository at this point in the history
  • Loading branch information
markostanimirovic authored Oct 1, 2024
1 parent ffc1d87 commit 269bd32
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 0 deletions.
32 changes: 32 additions & 0 deletions modules/signals/spec/deep-computed.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { isSignal, signal } from '@angular/core';
import { deepComputed } from '../src';

describe('deepComputed', () => {
it('creates a deep computed signal when computation result is an object literal', () => {
const source = signal(0);
const result = deepComputed(() => ({ count: { value: source() + 1 } }));

expect(isSignal(result)).toBe(true);
expect(isSignal(result.count)).toBe(true);
expect(isSignal(result.count.value)).toBe(true);

expect(result()).toEqual({ count: { value: 1 } });
expect(result.count()).toEqual({ value: 1 });
expect(result.count.value()).toBe(1);

source.set(1);

expect(result()).toEqual({ count: { value: 2 } });
expect(result.count()).toEqual({ value: 2 });
expect(result.count.value()).toBe(2);
});

it('does not create a deep computed signal when computation result is an array', () => {
const source = signal(0);
const result = deepComputed(() => [{ value: source() + 1 }]);

expect(isSignal(result)).toBe(true);
expect(result()).toEqual([{ value: 1 }]);
expect((result as any)[0]).toBe(undefined);
});
});
8 changes: 8 additions & 0 deletions modules/signals/src/deep-computed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { computed } from '@angular/core';
import { DeepSignal, toDeepSignal } from './deep-signal';

export function deepComputed<T extends object>(
computation: () => T
): DeepSignal<T> {
return toDeepSignal(computed(computation));
}
1 change: 1 addition & 0 deletions modules/signals/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { deepComputed } from './deep-computed';
export { DeepSignal } from './deep-signal';
export { signalState, SignalState } from './signal-state';
export { signalStore } from './signal-store';
Expand Down
30 changes: 30 additions & 0 deletions projects/ngrx.io/content/guide/signals/deep-computed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# DeepComputed

The `deepComputed` function creates a `DeepSignal` when a computation result is an object literal.
It can be used as a regular computed signal, but it also contains computed signals for each nested property.

```ts
import { signal } from '@angular/core';
import { deepComputed } from '@ngrx/signals';

const limit = signal(25);
const offset = signal(0);
const totalItems = signal(100);

const pagination = deepComputed(() => ({
currentPage: Math.floor(offset() / limit()) + 1,
pageSize: limit(),
totalPages: Math.ceil(totalItems() / limit()),
}));

console.log(pagination()); // logs: { currentPage: 1, pageSize: 25, totalPages: 4 }
console.log(pagination.currentPage()); // logs: 1
console.log(pagination.pageSize()); // logs: 25
console.log(pagination.totalPages()); // logs: 4
```

<div class="alert is-helpful">

For enhanced performance, deeply nested signals are generated lazily and initialized only upon first access.

</div>
4 changes: 4 additions & 0 deletions projects/ngrx.io/content/navigation.json
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,10 @@
"title": "SignalState",
"url": "guide/signals/signal-state"
},
{
"title": "DeepComputed",
"url": "guide/signals/deep-computed"
},
{
"title": "RxJS Integration",
"url": "guide/signals/rxjs-integration"
Expand Down

0 comments on commit 269bd32

Please sign in to comment.