Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Angular Signals - Revolution in Angular develop...

Angular Signals - Revolution in Angular development

Angular in the midst of change. Signals play a central role as a small revolution in the modern, new Angular development. This talk will dive into the world of signals, explain how they work and why they are revolutionizing Angular application development. We will explore the technical aspects and best practices for using Angular Signals effectively, and look at what an Angular application with Signals can look like. This talk is an essential resource for anyone looking to expand their knowledge of Angular development and fully utilize the power of Angular Signals.

Fabian Gosebrink

July 04, 2024
Tweet

More Decks by Fabian Gosebrink

Other Decks in Technology

Transcript

  1. todos = signal<Todo[]>([]); todos(); todos.set(...); todos.update((items) => { /* ...

    */ }); 1 2 3 4 5 6 7 todos = signal<Todo[]>([]); 1 2 todos(); 3 4 todos.set(...); 5 6 todos.update((items) => { /* ... */ }); 7
  2. todos = signal<Todo[]>([]); todos(); todos.set(...); todos.update((items) => { /* ...

    */ }); 1 2 3 4 5 6 7 todos = signal<Todo[]>([]); 1 2 todos(); 3 4 todos.set(...); 5 6 todos.update((items) => { /* ... */ }); 7 todos(); todos = signal<Todo[]>([]); 1 2 3 4 todos.set(...); 5 6 todos.update((items) => { /* ... */ }); 7
  3. todos = signal<Todo[]>([]); todos(); todos.set(...); todos.update((items) => { /* ...

    */ }); 1 2 3 4 5 6 7 todos = signal<Todo[]>([]); 1 2 todos(); 3 4 todos.set(...); 5 6 todos.update((items) => { /* ... */ }); 7 todos(); todos = signal<Todo[]>([]); 1 2 3 4 todos.set(...); 5 6 todos.update((items) => { /* ... */ }); 7 todos.set(...); todos = signal<Todo[]>([]); 1 2 todos(); 3 4 5 6 todos.update((items) => { /* ... */ }); 7
  4. todos = signal<Todo[]>([]); todos(); todos.set(...); todos.update((items) => { /* ...

    */ }); 1 2 3 4 5 6 7 todos = signal<Todo[]>([]); 1 2 todos(); 3 4 todos.set(...); 5 6 todos.update((items) => { /* ... */ }); 7 todos(); todos = signal<Todo[]>([]); 1 2 3 4 todos.set(...); 5 6 todos.update((items) => { /* ... */ }); 7 todos.set(...); todos = signal<Todo[]>([]); 1 2 todos(); 3 4 5 6 todos.update((items) => { /* ... */ }); 7 todos.update((items) => { /* ... */ }); todos = signal<Todo[]>([]); 1 2 todos(); 3 4 todos.set(...); 5 6 7
  5. todos = signal<Todo[]>([]); count = computed(() => todos().length); effect(() =>

    { console.log('Todos count changed to ', count()); }); 1 2 3 4 5 6 7
  6. todos = signal<Todo[]>([]); count = computed(() => todos().length); effect(() =>

    { console.log('Todos count changed to ', count()); }); 1 2 3 4 5 6 7 count = computed(() => todos().length); todos = signal<Todo[]>([]); 1 2 3 4 effect(() => { 5 console.log('Todos count changed to ', count()); 6 }); 7
  7. todos = signal<Todo[]>([]); count = computed(() => todos().length); effect(() =>

    { console.log('Todos count changed to ', count()); }); 1 2 3 4 5 6 7 count = computed(() => todos().length); todos = signal<Todo[]>([]); 1 2 3 4 effect(() => { 5 console.log('Todos count changed to ', count()); 6 }); 7 effect(() => { console.log('Todos count changed to ', count()); }); todos = signal<Todo[]>([]); 1 2 count = computed(() => todos().length); 3 4 5 6 7
  8. todos = signal<Todo[]>([]); console.log('Todos: ' + todos()); effect(() => {

    console.log('Todos', todos()); }); 1 2 3 4 5 6 7
  9. // Reactive in TS effect(() => { console.log('Todos', todos()); });

    // Reactive in HTML <div>{{ todos() }}</div> todos = signal<Todo[]>([]); 1 2 console.log('Todos: ' + todos()); 3 4 5 6 7 8 9 10 11
  10. /** * Propagate a dirty notification to live consumers of

    this producer. */ export function producerNotifyConsumers(node: ReactiveNode): void { if (node.liveConsumerNode === undefined) { return; } // Prevent signal reads when we're updating the graph const prev = inNotificationPhase; inNotificationPhase = true; try { for (const consumer of node.liveConsumerNode) { if (!consumer.dirty) { consumerMarkDirty(consumer); } } } finally { inNotificationPhase = prev; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
  11. /** * Propagate a dirty notification to live consumers of

    this producer. */ export function producerNotifyConsumers(node: ReactiveNode): void { if (node.liveConsumerNode === undefined) { return; } // Prevent signal reads when we're updating the graph const prev = inNotificationPhase; inNotificationPhase = true; try { for (const consumer of node.liveConsumerNode) { if (!consumer.dirty) { consumerMarkDirty(consumer); } } } finally { inNotificationPhase = prev; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 export function producerNotifyConsumers(node: ReactiveNode): void { /** 1 * Propagate a dirty notification to live consumers of this producer. 2 */ 3 4 if (node.liveConsumerNode === undefined) { 5 return; 6 } 7 8 // Prevent signal reads when we're updating the graph 9 const prev = inNotificationPhase; 10 inNotificationPhase = true; 11 try { 12 for (const consumer of node.liveConsumerNode) { 13 if (!consumer.dirty) { 14 consumerMarkDirty(consumer); 15 } 16 } 17 } finally { 18 inNotificationPhase = prev; 19 } 20 } 21
  12. /** * Propagate a dirty notification to live consumers of

    this producer. */ export function producerNotifyConsumers(node: ReactiveNode): void { if (node.liveConsumerNode === undefined) { return; } // Prevent signal reads when we're updating the graph const prev = inNotificationPhase; inNotificationPhase = true; try { for (const consumer of node.liveConsumerNode) { if (!consumer.dirty) { consumerMarkDirty(consumer); } } } finally { inNotificationPhase = prev; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 export function producerNotifyConsumers(node: ReactiveNode): void { /** 1 * Propagate a dirty notification to live consumers of this producer. 2 */ 3 4 if (node.liveConsumerNode === undefined) { 5 return; 6 } 7 8 // Prevent signal reads when we're updating the graph 9 const prev = inNotificationPhase; 10 inNotificationPhase = true; 11 try { 12 for (const consumer of node.liveConsumerNode) { 13 if (!consumer.dirty) { 14 consumerMarkDirty(consumer); 15 } 16 } 17 } finally { 18 inNotificationPhase = prev; 19 } 20 } 21 consumerMarkDirty(consumer); /** 1 * Propagate a dirty notification to live consumers of this producer. 2 */ 3 export function producerNotifyConsumers(node: ReactiveNode): void { 4 if (node.liveConsumerNode === undefined) { 5 return; 6 } 7 8 // Prevent signal reads when we're updating the graph 9 const prev = inNotificationPhase; 10 inNotificationPhase = true; 11 try { 12 for (const consumer of node.liveConsumerNode) { 13 if (!consumer.dirty) { 14 15 } 16 } 17 } finally { 18 inNotificationPhase = prev; 19 } 20 } 21
  13. count = computed(() => todos().length); effect(() => { console.log('Todos count

    changed to ', count()); }); todos = signal<Todo[]>([]); 1 2 3 4 5 6 7
  14. count = computed(() => todos().length); effect(() => { console.log('just a

    message'); }); todos = signal<Todo[]>([]); 1 2 3 4 5 6 7
  15. interface ConsumerNode extends ReactiveNode interface ProducerNode extends ReactiveNode export interface

    ReactiveNode { version: Version; lastCleanEpoch: Version; dirty: boolean; producerNode: ReactiveNode[] | undefined; producerLastReadVersion: Version[] | undefined; producerIndexOfThis: number[] | undefined; nextProducerIndex: number; liveConsumerNode: ReactiveNode[] | undefined; liveConsumerIndexOfThis: number[] | undefined; consumerAllowSignalWrites: boolean; readonly consumerIsAlwaysLive: boolean; producerMustRecompute(node: unknown): boolean; producerRecomputeValue(node: unknown): void; consumerMarkedDirty(node: unknown): void; consumerOnSignalRead(node: unknown): void; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
  16. interface ConsumerNode extends ReactiveNode interface ProducerNode extends ReactiveNode export interface

    ReactiveNode { version: Version; lastCleanEpoch: Version; dirty: boolean; producerNode: ReactiveNode[] | undefined; producerLastReadVersion: Version[] | undefined; producerIndexOfThis: number[] | undefined; nextProducerIndex: number; liveConsumerNode: ReactiveNode[] | undefined; liveConsumerIndexOfThis: number[] | undefined; consumerAllowSignalWrites: boolean; readonly consumerIsAlwaysLive: boolean; producerMustRecompute(node: unknown): boolean; producerRecomputeValue(node: unknown): void; consumerMarkedDirty(node: unknown): void; consumerOnSignalRead(node: unknown): void; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 dirty: boolean; interface ConsumerNode extends ReactiveNode 1 2 interface ProducerNode extends ReactiveNode 3 4 export interface ReactiveNode { 5 version: Version; 6 lastCleanEpoch: Version; 7 8 producerNode: ReactiveNode[] | undefined; 9 producerLastReadVersion: Version[] | undefined; 10 producerIndexOfThis: number[] | undefined; 11 nextProducerIndex: number; 12 liveConsumerNode: ReactiveNode[] | undefined; 13 liveConsumerIndexOfThis: number[] | undefined; 14 consumerAllowSignalWrites: boolean; 15 readonly consumerIsAlwaysLive: boolean; 16 producerMustRecompute(node: unknown): boolean; 17 producerRecomputeValue(node: unknown): void; 18 consumerMarkedDirty(node: unknown): void; 19 consumerOnSignalRead(node: unknown): void; 20 } 21
  17. interface ConsumerNode extends ReactiveNode interface ProducerNode extends ReactiveNode export interface

    ReactiveNode { version: Version; lastCleanEpoch: Version; dirty: boolean; producerNode: ReactiveNode[] | undefined; producerLastReadVersion: Version[] | undefined; producerIndexOfThis: number[] | undefined; nextProducerIndex: number; liveConsumerNode: ReactiveNode[] | undefined; liveConsumerIndexOfThis: number[] | undefined; consumerAllowSignalWrites: boolean; readonly consumerIsAlwaysLive: boolean; producerMustRecompute(node: unknown): boolean; producerRecomputeValue(node: unknown): void; consumerMarkedDirty(node: unknown): void; consumerOnSignalRead(node: unknown): void; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 dirty: boolean; interface ConsumerNode extends ReactiveNode 1 2 interface ProducerNode extends ReactiveNode 3 4 export interface ReactiveNode { 5 version: Version; 6 lastCleanEpoch: Version; 7 8 producerNode: ReactiveNode[] | undefined; 9 producerLastReadVersion: Version[] | undefined; 10 producerIndexOfThis: number[] | undefined; 11 nextProducerIndex: number; 12 liveConsumerNode: ReactiveNode[] | undefined; 13 liveConsumerIndexOfThis: number[] | undefined; 14 consumerAllowSignalWrites: boolean; 15 readonly consumerIsAlwaysLive: boolean; 16 producerMustRecompute(node: unknown): boolean; 17 producerRecomputeValue(node: unknown): void; 18 consumerMarkedDirty(node: unknown): void; 19 consumerOnSignalRead(node: unknown): void; 20 } 21 version: Version; dirty: boolean; interface ConsumerNode extends ReactiveNode 1 2 interface ProducerNode extends ReactiveNode 3 4 export interface ReactiveNode { 5 6 lastCleanEpoch: Version; 7 8 producerNode: ReactiveNode[] | undefined; 9 producerLastReadVersion: Version[] | undefined; 10 producerIndexOfThis: number[] | undefined; 11 nextProducerIndex: number; 12 liveConsumerNode: ReactiveNode[] | undefined; 13 liveConsumerIndexOfThis: number[] | undefined; 14 consumerAllowSignalWrites: boolean; 15 readonly consumerIsAlwaysLive: boolean; 16 producerMustRecompute(node: unknown): boolean; 17 producerRecomputeValue(node: unknown): void; 18 consumerMarkedDirty(node: unknown): void; 19 consumerOnSignalRead(node: unknown): void; 20 } 21
  18. export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { if (!producerUpdatesAllowed()) {

    throwInvalidWriteToSignalError(); } if (!node.equal(node.value, newValue)) { node.value = newValue; signalValueChanged(node); } } 1 2 3 4 5 6 7 8 9 10 11 // ... 12 13 function signalValueChanged<T>(node: SignalNode<T>): void { 14 node.version++; 15 producerIncrementEpoch(); 16 producerNotifyConsumers(node); 17 postSignalSetFn?.(); 18 } 19 https://github.com/angular/angular/blob/1872fcd8e09fefb52f9b36e8261702cd6fb03f85/packages/core/primitives/signals/src/signal.ts
  19. export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { if (!producerUpdatesAllowed()) {

    throwInvalidWriteToSignalError(); } if (!node.equal(node.value, newValue)) { node.value = newValue; signalValueChanged(node); } } 1 2 3 4 5 6 7 8 9 10 11 // ... 12 13 function signalValueChanged<T>(node: SignalNode<T>): void { 14 node.version++; 15 producerIncrementEpoch(); 16 producerNotifyConsumers(node); 17 postSignalSetFn?.(); 18 } 19 function signalValueChanged<T>(node: SignalNode<T>): void { node.version++; producerIncrementEpoch(); producerNotifyConsumers(node); postSignalSetFn?.(); } export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { 1 if (!producerUpdatesAllowed()) { 2 throwInvalidWriteToSignalError(); 3 } 4 5 if (!node.equal(node.value, newValue)) { 6 node.value = newValue; 7 signalValueChanged(node); 8 } 9 } 10 11 // ... 12 13 14 15 16 17 18 19 https://github.com/angular/angular/blob/1872fcd8e09fefb52f9b36e8261702cd6fb03f85/packages/core/primitives/signals/src/signal.ts
  20. export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { if (!producerUpdatesAllowed()) {

    throwInvalidWriteToSignalError(); } if (!node.equal(node.value, newValue)) { node.value = newValue; signalValueChanged(node); } } 1 2 3 4 5 6 7 8 9 10 11 // ... 12 13 function signalValueChanged<T>(node: SignalNode<T>): void { 14 node.version++; 15 producerIncrementEpoch(); 16 producerNotifyConsumers(node); 17 postSignalSetFn?.(); 18 } 19 function signalValueChanged<T>(node: SignalNode<T>): void { node.version++; producerIncrementEpoch(); producerNotifyConsumers(node); postSignalSetFn?.(); } export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { 1 if (!producerUpdatesAllowed()) { 2 throwInvalidWriteToSignalError(); 3 } 4 5 if (!node.equal(node.value, newValue)) { 6 node.value = newValue; 7 signalValueChanged(node); 8 } 9 } 10 11 // ... 12 13 14 15 16 17 18 19 if (!node.equal(node.value, newValue)) { node.value = newValue; signalValueChanged(node); } export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { 1 if (!producerUpdatesAllowed()) { 2 throwInvalidWriteToSignalError(); 3 } 4 5 6 7 8 9 } 10 11 // ... 12 13 function signalValueChanged<T>(node: SignalNode<T>): void { 14 node.version++; 15 producerIncrementEpoch(); 16 producerNotifyConsumers(node); 17 postSignalSetFn?.(); 18 } 19 https://github.com/angular/angular/blob/1872fcd8e09fefb52f9b36e8261702cd6fb03f85/packages/core/primitives/signals/src/signal.ts
  21. export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { if (!producerUpdatesAllowed()) {

    throwInvalidWriteToSignalError(); } if (!node.equal(node.value, newValue)) { node.value = newValue; signalValueChanged(node); } } 1 2 3 4 5 6 7 8 9 10 11 // ... 12 13 function signalValueChanged<T>(node: SignalNode<T>): void { 14 node.version++; 15 producerIncrementEpoch(); 16 producerNotifyConsumers(node); 17 postSignalSetFn?.(); 18 } 19 function signalValueChanged<T>(node: SignalNode<T>): void { node.version++; producerIncrementEpoch(); producerNotifyConsumers(node); postSignalSetFn?.(); } export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { 1 if (!producerUpdatesAllowed()) { 2 throwInvalidWriteToSignalError(); 3 } 4 5 if (!node.equal(node.value, newValue)) { 6 node.value = newValue; 7 signalValueChanged(node); 8 } 9 } 10 11 // ... 12 13 14 15 16 17 18 19 if (!node.equal(node.value, newValue)) { node.value = newValue; signalValueChanged(node); } export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { 1 if (!producerUpdatesAllowed()) { 2 throwInvalidWriteToSignalError(); 3 } 4 5 6 7 8 9 } 10 11 // ... 12 13 function signalValueChanged<T>(node: SignalNode<T>): void { 14 node.version++; 15 producerIncrementEpoch(); 16 producerNotifyConsumers(node); 17 postSignalSetFn?.(); 18 } 19 signalValueChanged(node); function signalValueChanged<T>(node: SignalNode<T>): void { node.version++; producerIncrementEpoch(); producerNotifyConsumers(node); postSignalSetFn?.(); } export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { 1 if (!producerUpdatesAllowed()) { 2 throwInvalidWriteToSignalError(); 3 } 4 5 if (!node.equal(node.value, newValue)) { 6 node.value = newValue; 7 8 } 9 } 10 11 // ... 12 13 14 15 16 17 18 19 https://github.com/angular/angular/blob/1872fcd8e09fefb52f9b36e8261702cd6fb03f85/packages/core/primitives/signals/src/signal.ts
  22. export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { if (!producerUpdatesAllowed()) {

    throwInvalidWriteToSignalError(); } if (!node.equal(node.value, newValue)) { node.value = newValue; signalValueChanged(node); } } 1 2 3 4 5 6 7 8 9 10 11 // ... 12 13 function signalValueChanged<T>(node: SignalNode<T>): void { 14 node.version++; 15 producerIncrementEpoch(); 16 producerNotifyConsumers(node); 17 postSignalSetFn?.(); 18 } 19 function signalValueChanged<T>(node: SignalNode<T>): void { node.version++; producerIncrementEpoch(); producerNotifyConsumers(node); postSignalSetFn?.(); } export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { 1 if (!producerUpdatesAllowed()) { 2 throwInvalidWriteToSignalError(); 3 } 4 5 if (!node.equal(node.value, newValue)) { 6 node.value = newValue; 7 signalValueChanged(node); 8 } 9 } 10 11 // ... 12 13 14 15 16 17 18 19 if (!node.equal(node.value, newValue)) { node.value = newValue; signalValueChanged(node); } export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { 1 if (!producerUpdatesAllowed()) { 2 throwInvalidWriteToSignalError(); 3 } 4 5 6 7 8 9 } 10 11 // ... 12 13 function signalValueChanged<T>(node: SignalNode<T>): void { 14 node.version++; 15 producerIncrementEpoch(); 16 producerNotifyConsumers(node); 17 postSignalSetFn?.(); 18 } 19 signalValueChanged(node); function signalValueChanged<T>(node: SignalNode<T>): void { node.version++; producerIncrementEpoch(); producerNotifyConsumers(node); postSignalSetFn?.(); } export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { 1 if (!producerUpdatesAllowed()) { 2 throwInvalidWriteToSignalError(); 3 } 4 5 if (!node.equal(node.value, newValue)) { 6 node.value = newValue; 7 8 } 9 } 10 11 // ... 12 13 14 15 16 17 18 19 signalValueChanged(node); node.version++; export function signalSetFn<T>(node: SignalNode<T>, newValue: T) { 1 if (!producerUpdatesAllowed()) { 2 throwInvalidWriteToSignalError(); 3 } 4 5 if (!node.equal(node.value, newValue)) { 6 node.value = newValue; 7 8 } 9 } 10 11 // ... 12 13 function signalValueChanged<T>(node: SignalNode<T>): void { 14 15 producerIncrementEpoch(); 16 producerNotifyConsumers(node); 17 postSignalSetFn?.(); 18 } 19 https://github.com/angular/angular/blob/1872fcd8e09fefb52f9b36e8261702cd6fb03f85/packages/core/primitives/signals/src/signal.ts
  23. todos = signal<Todo[]>([]); count = computed(() => todos().length); effect(() =>

    { console.log('Todos count changed to ', count()); }); todos.update((items) => [...items, { ... }]); // CHANGE 1 2 3 4 5 6 7 8 9
  24. todos = signal<Todo[]>([]); count = computed(() => todos().length); effect(() =>

    { console.log('Todos count changed to ', count()); }); todos.update((items) => [...items]); // "NO" CHANGE 1 2 3 4 5 6 7 8 9
  25. todos = signal<Todo[]>([]); count = computed(() => todos().length); effect(() =>

    { console.log('Todos count changed to ', count()); }); todos.update((items) => [...items]); // "NO" CHANGE 1 2 3 4 5 6 7 8 9 todos.update((items) => [...items]); // "NO" CHANGE todos = signal<Todo[]>([]); 1 2 count = computed(() => todos().length); 3 4 effect(() => { 5 console.log('Todos count changed to ', count()); 6 }); 7 8 9
  26. todos.update((items) => [...items]); // Only ref change todos = signal<Todo[]>([]);

    1 2 count = computed(() => todos().length); 3 4 effect(() => { 5 console.log('Todos count changed to ', count()); 6 }); 7 8 9 export function defaultEquals<T>(a: T, b: T) { return Object.is(a, b); } 1 2 3 https://github.com/angular/angular/blob/23eafb4aa20f45233b22a62fe032e47ac21eca20/packages/core/primitives/signals/src/equality.ts#L17
  27. computed(() => isEven() ? getDoneLength() : getUndoneLength() ); 1 2

    3 4 [isEven, getDoneLength] [isEven, getUndoneLength] 1 2 3 [isEven, getDoneLength] 1 2 [isEven, getUndoneLength] 3
  28. computed(() => isEven() ? getDoneLength() : getUndoneLength() ); 1 2

    3 4 [isEven, getDoneLength] [isEven, getUndoneLength] 1 2 3 [isEven, getDoneLength] 1 2 [isEven, getUndoneLength] 3 [isEven, getUndoneLength] [isEven, getDoneLength] 1 2 3
  29. export class TodoMainComponent implements OnInit { private url = `${environment.apiUrl}todos`;

    private http = inject(HttpClient); private todos = signal<Todo[]>([]); count = computed(() => this.todos().length); doneItems = computed(() => this.todos().filter((item) => item.done)?.length); openItems = computed(() => this.todos().filter((item) => !item.done)?.length); sortedTodos = computed(() => this.todos().sort((b, a) => +b.done - +a.done)); // Method logic with http... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  30. @Injectable({ providedIn: 'root' }) export class TodoService { private url

    = `${environment.apiUrl}todos`; private http = inject(HttpClient); private todos = signal<Todo[]>([]); count = computed(() => this.todos().length); doneItems = computed(() => this.todos().filter((item) => item.done)?.length); openItems = computed(() => this.todos().filter((item) => !item.done)?.length); sortedTodos = computed(() => this.todos().sort((b, a) => +b.done - +a.done)); getItems() { this.http.get<Todo[]>(this.url).subscribe((todos) => this.todos.set(todos)); } replaceItem() { this.todos.update((items) => [...items]); } addItem(value: string) { this.http.post<Todo>(this.url, { value }).subscribe((addedTodo) => { this.todos.update((items) => [addedTodo, ...items]); }); } updateItem(value: Todo) { // ... } removeItem(id: string) { this.http.delete(`${this.url}/${id}`).subscribe(() => { this.todos.update((items) => [...items.filter((item) => item.id !== id)]); }); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
  31. export class TodoMainComponent implements OnInit { private todoService = inject(TodoService);

    count = this.todoService.count; doneItems = this.todoService.doneItems; openItems = this.todoService.openItems; sortedTodos = this.todoService.sortedTodos; constructor() { effect(() => { console.log('Todos changed (effect):', this.count()); }); } ngOnInit(): void { this.todoService.getItems(); } addTodo(value: string) { this.todoService.addItem(value); } deleteTodo(item: Todo): void { this.todoService.removeitem(item.id); } markAsDone(item: Todo): void { this.todoService.updateItem(item); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
  32. @Component({ /* ... */ }) export class TodoMainComponent { private

    readonly store = inject(Store); items$: Observable<Todo[]>; ngOnInit(): void { this.items$ = this.store.pipe(select(getAllItems)); this.store.dispatch(TodoActions.loadAllTodos()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13
  33. @Component({ /* ... */ }) export class TodoMainComponent { private

    readonly store = inject(Store); items$: Observable<Todo[]>; ngOnInit(): void { this.items$ = this.store.pipe(select(getAllItems)); this.store.dispatch(TodoActions.loadAllTodos()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13
  34. @Component({ /* ... */ }) export class TodoMainComponent { private

    readonly store = inject(Store); items$: Observable<Todo[]>; ngOnInit(): void { this.items$ = this.store.pipe(select(getAllItems)); this.store.dispatch(TodoActions.loadAllTodos()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 <app-todo-list [items]="items$ | async" /> 1
  35. @Component({ /* ... */ }) export class TodoMainComponent { private

    readonly store = inject(Store); items$: Observable<Todo[]>; ngOnInit(): void { this.items$ = this.store.pipe(select(getAllItems)); this.store.dispatch(TodoActions.loadAllTodos()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 <app-todo-list [items]="items$ | async" /> 1
  36. items: Signal<Todo[]>; this.items$ = this.store.selectSignal(getAllItems); 1 @Component({ /* ... */

    }) 2 export class TodoMainComponent { 3 private readonly store = inject(Store); 4 5 6 7 ngOnInit(): void { 8 9 10 this.store.dispatch(TodoActions.loadAllTodos()); 11 } 12 } 13
  37. items: Signal<Todo[]>; this.items$ = this.store.selectSignal(getAllItems); 1 @Component({ /* ... */

    }) 2 export class TodoMainComponent { 3 private readonly store = inject(Store); 4 5 6 7 ngOnInit(): void { 8 9 10 this.store.dispatch(TodoActions.loadAllTodos()); 11 } 12 } 13 <app-todo-list [items]="items()" /> 1
  38. items: Signal<Todo[]>; this.items$ = this.store.selectSignal(getAllItems); 1 @Component({ /* ... */

    }) 2 export class TodoMainComponent { 3 private readonly store = inject(Store); 4 5 6 7 ngOnInit(): void { 8 9 10 this.store.dispatch(TodoActions.loadAllTodos()); 11 } 12 } 13 @Component({ /* ... */ }) export class TodoMainComponent { private readonly store = inject(Store); items: Signal<Todo[]>; ngOnInit(): void { this.items$ = this.store.selectSignal(getAllItems); this.store.dispatch(TodoActions.loadAllTodos()); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 <app-todo-list [items]="items()" /> 1
  39. export interface TodoState { items: Todo[]; loading: boolean; } export

    const initialState: TodoState = { items: [], loading: false, }; export const TodoStore = signalStore( withState(initialState), ); 1 2 3 4 5 6 7 8 9 10 11 12 13
  40. export interface TodoState { items: Todo[]; loading: boolean; } export

    const initialState: TodoState = { items: [], loading: false, }; export const TodoStore = signalStore( withState(initialState), ); 1 2 3 4 5 6 7 8 9 10 11 12 13 export interface TodoState { items: Todo[]; loading: boolean; } 1 2 3 4 5 export const initialState: TodoState = { 6 items: [], 7 loading: false, 8 }; 9 10 export const TodoStore = signalStore( 11 withState(initialState), 12 ); 13
  41. export interface TodoState { items: Todo[]; loading: boolean; } export

    const initialState: TodoState = { items: [], loading: false, }; export const TodoStore = signalStore( withState(initialState), ); 1 2 3 4 5 6 7 8 9 10 11 12 13 export interface TodoState { items: Todo[]; loading: boolean; } 1 2 3 4 5 export const initialState: TodoState = { 6 items: [], 7 loading: false, 8 }; 9 10 export const TodoStore = signalStore( 11 withState(initialState), 12 ); 13 export const initialState: TodoState = { items: [], loading: false, }; export interface TodoState { 1 items: Todo[]; 2 loading: boolean; 3 } 4 5 6 7 8 9 10 export const TodoStore = signalStore( 11 withState(initialState), 12 ); 13
  42. export interface TodoState { items: Todo[]; loading: boolean; } export

    const initialState: TodoState = { items: [], loading: false, }; export const TodoStore = signalStore( withState(initialState), ); 1 2 3 4 5 6 7 8 9 10 11 12 13 export interface TodoState { items: Todo[]; loading: boolean; } 1 2 3 4 5 export const initialState: TodoState = { 6 items: [], 7 loading: false, 8 }; 9 10 export const TodoStore = signalStore( 11 withState(initialState), 12 ); 13 export const initialState: TodoState = { items: [], loading: false, }; export interface TodoState { 1 items: Todo[]; 2 loading: boolean; 3 } 4 5 6 7 8 9 10 export const TodoStore = signalStore( 11 withState(initialState), 12 ); 13 export const TodoStore = signalStore( withState(initialState), ); export interface TodoState { 1 items: Todo[]; 2 loading: boolean; 3 } 4 5 export const initialState: TodoState = { 6 items: [], 7 loading: false, 8 }; 9 10 11 12 13
  43. export const TodoStore = signalStore( withState({ /* ... */ }),

    withMethods((...) => ({ // ... })) ); 1 2 3 4 5 6
  44. export const TodoStore = signalStore( withState({ /* ... */ }),

    withMethods((...) => ({ // ... })) ); 1 2 3 4 5 6 withMethods((...) => ({ // ... })) export const TodoStore = signalStore( 1 withState({ /* ... */ }), 2 3 4 5 ); 6
  45. export const TodoStore = signalStore( withState({ /* ... */ ),

    withMethods((store, todoService = inject(TodoService)) => ({ loadAllTodos() { // use Todoservice and then patchState(store, { items }); }, })) ); 1 2 3 4 5 6 7 8 9
  46. export const TodoStore = signalStore( withState({ /* ... */ ),

    withMethods((store, todoService = inject(TodoService)) => ({ loadAllTodos() { // use Todoservice and then patchState(store, { items }); }, })) ); 1 2 3 4 5 6 7 8 9 withMethods((store, todoService = inject(TodoService)) => ({ loadAllTodos() { // use Todoservice and then patchState(store, { items }); }, })) export const TodoStore = signalStore( 1 withState({ /* ... */ ), 2 3 4 5 6 7 8 ); 9
  47. export const TodoStore = signalStore( { providedIn: 'root' }, withState({

    /* state goes here */ ), withMethods((store, todoService = inject(TodoService)) => ({ loadAllTodos: rxMethod( switchMap(() => { patchState(store, {loading: true}); return todoService.getItems().pipe( tapResponse({ next: (items) => patchState(store, {items}), error: console.error, finalize: () => patchState(store, {loading: false}), }) ); }) ), })) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
  48. export const TodoStore = signalStore( { providedIn: 'root' }, withState({

    /* state goes here */ ), withMethods((store, todoService = inject(TodoService)) => ({ loadAllTodos: rxMethod( switchMap(() => { patchState(store, {loading: true}); return todoService.getItems().pipe( tapResponse({ next: (items) => patchState(store, {items}), error: console.error, finalize: () => patchState(store, {loading: false}), }) ); }) ), })) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 loadAllTodos: rxMethod( switchMap(() => { patchState(store, {loading: true}); return todoService.getItems().pipe( tapResponse({ next: (items) => patchState(store, {items}), error: console.error, finalize: () => patchState(store, {loading: false}), }) ); }) ), export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ /* state goes here */ ), 3 withMethods((store, todoService = inject(TodoService)) => ({ 4 5 6 7 8 9 10 11 12 13 14 15 16 17 })) 18 ); 19
  49. export const TodoStore = signalStore( { providedIn: 'root' }, withState({

    /* state goes here */ ), withMethods((store, todoService = inject(TodoService)) => ({ loadAllTodos: rxMethod( switchMap(() => { patchState(store, {loading: true}); return todoService.getItems().pipe( tapResponse({ next: (items) => patchState(store, {items}), error: console.error, finalize: () => patchState(store, {loading: false}), }) ); }) ), })) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 loadAllTodos: rxMethod( switchMap(() => { patchState(store, {loading: true}); return todoService.getItems().pipe( tapResponse({ next: (items) => patchState(store, {items}), error: console.error, finalize: () => patchState(store, {loading: false}), }) ); }) ), export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ /* state goes here */ ), 3 withMethods((store, todoService = inject(TodoService)) => ({ 4 5 6 7 8 9 10 11 12 13 14 15 16 17 })) 18 ); 19 patchState(store, {loading: true}); export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ /* state goes here */ ), 3 withMethods((store, todoService = inject(TodoService)) => ({ 4 loadAllTodos: rxMethod( 5 switchMap(() => { 6 7 8 return todoService.getItems().pipe( 9 tapResponse({ 10 next: (items) => patchState(store, {items}), 11 error: console.error, 12 finalize: () => patchState(store, {loading: false}), 13 }) 14 ); 15 }) 16 ), 17 })) 18 ); 19
  50. export const TodoStore = signalStore( { providedIn: 'root' }, withState({

    /* state goes here */ ), withMethods((store, todoService = inject(TodoService)) => ({ loadAllTodos: rxMethod( switchMap(() => { patchState(store, {loading: true}); return todoService.getItems().pipe( tapResponse({ next: (items) => patchState(store, {items}), error: console.error, finalize: () => patchState(store, {loading: false}), }) ); }) ), })) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 loadAllTodos: rxMethod( switchMap(() => { patchState(store, {loading: true}); return todoService.getItems().pipe( tapResponse({ next: (items) => patchState(store, {items}), error: console.error, finalize: () => patchState(store, {loading: false}), }) ); }) ), export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ /* state goes here */ ), 3 withMethods((store, todoService = inject(TodoService)) => ({ 4 5 6 7 8 9 10 11 12 13 14 15 16 17 })) 18 ); 19 patchState(store, {loading: true}); export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ /* state goes here */ ), 3 withMethods((store, todoService = inject(TodoService)) => ({ 4 loadAllTodos: rxMethod( 5 switchMap(() => { 6 7 8 return todoService.getItems().pipe( 9 tapResponse({ 10 next: (items) => patchState(store, {items}), 11 error: console.error, 12 finalize: () => patchState(store, {loading: false}), 13 }) 14 ); 15 }) 16 ), 17 })) 18 ); 19 return todoService.getItems().pipe( tapResponse({ next: (items) => patchState(store, {items}), error: console.error, finalize: () => patchState(store, {loading: false}), }) ); export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ /* state goes here */ ), 3 withMethods((store, todoService = inject(TodoService)) => ({ 4 loadAllTodos: rxMethod( 5 switchMap(() => { 6 patchState(store, {loading: true}); 7 8 9 10 11 12 13 14 15 }) 16 ), 17 })) 18 ); 19
  51. export const TodoStore = signalStore( { providedIn: 'root' }, withState({

    /* state goes here */ ), withMethods((store, todoService = inject(TodoService)) => ({ loadAllTodos: rxMethod( switchMap(() => { patchState(store, {loading: true}); return todoService.getItems().pipe( tapResponse({ next: (items) => patchState(store, {items}), error: console.error, finalize: () => patchState(store, {loading: false}), }) ); }) ), })) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 loadAllTodos: rxMethod( switchMap(() => { patchState(store, {loading: true}); return todoService.getItems().pipe( tapResponse({ next: (items) => patchState(store, {items}), error: console.error, finalize: () => patchState(store, {loading: false}), }) ); }) ), export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ /* state goes here */ ), 3 withMethods((store, todoService = inject(TodoService)) => ({ 4 5 6 7 8 9 10 11 12 13 14 15 16 17 })) 18 ); 19 patchState(store, {loading: true}); export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ /* state goes here */ ), 3 withMethods((store, todoService = inject(TodoService)) => ({ 4 loadAllTodos: rxMethod( 5 switchMap(() => { 6 7 8 return todoService.getItems().pipe( 9 tapResponse({ 10 next: (items) => patchState(store, {items}), 11 error: console.error, 12 finalize: () => patchState(store, {loading: false}), 13 }) 14 ); 15 }) 16 ), 17 })) 18 ); 19 return todoService.getItems().pipe( tapResponse({ next: (items) => patchState(store, {items}), error: console.error, finalize: () => patchState(store, {loading: false}), }) ); export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ /* state goes here */ ), 3 withMethods((store, todoService = inject(TodoService)) => ({ 4 loadAllTodos: rxMethod( 5 switchMap(() => { 6 patchState(store, {loading: true}); 7 8 9 10 11 12 13 14 15 }) 16 ), 17 })) 18 ); 19 tapResponse({ next: (items) => patchState(store, {items}), error: console.error, finalize: () => patchState(store, {loading: false}), }) export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ /* state goes here */ ), 3 withMethods((store, todoService = inject(TodoService)) => ({ 4 loadAllTodos: rxMethod( 5 switchMap(() => { 6 patchState(store, {loading: true}); 7 8 return todoService.getItems().pipe( 9 10 11 12 13 14 ); 15 }) 16 ), 17 })) 18 ); 19
  52. export const TodoStore = signalStore( { providedIn: 'root' }, withState({

    /* state goes here */ ), withMethods((store, todoService = inject(TodoService)) => ({ loadAllTodos(store, todoService: TodoService) { ... }, async loadAllTodosByPromise() { patchState(store, { loading: true }); const items = await todoService.getItemsAsPromise(); patchState(store, { items, loading: false }); }, })) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  53. export const TodoStore = signalStore( { providedIn: 'root' }, withState({

    /* state goes here */ ), withMethods((store, todoService = inject(TodoService)) => ({ loadAllTodos(store, todoService: TodoService) { ... }, async loadAllTodosByPromise() { patchState(store, { loading: true }); const items = await todoService.getItemsAsPromise(); patchState(store, { items, loading: false }); }, })) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 async loadAllTodosByPromise() { patchState(store, { loading: true }); const items = await todoService.getItemsAsPromise(); patchState(store, { items, loading: false }); }, export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState({ /* state goes here */ ), 3 withMethods((store, todoService = inject(TodoService)) => ({ 4 loadAllTodos(store, todoService: TodoService) { ... }, 5 6 7 8 9 10 11 12 13 })) 14 ); 15
  54. @Component({ // ... providers: [TodoStore], }) export class AppComponent implements

    OnInit { readonly store = inject(TodoStore); ngOnInit() { this.store.loadAllTodos(); } } 1 2 3 4 5 6 7 8 9 10 11
  55. @Component({ // ... providers: [TodoStore], }) export class AppComponent implements

    OnInit { readonly store = inject(TodoStore); ngOnInit() { this.store.loadAllTodos(); } } 1 2 3 4 5 6 7 8 9 10 11 providers: [TodoStore], @Component({ 1 // ... 2 3 }) 4 export class AppComponent implements OnInit { 5 readonly store = inject(TodoStore); 6 7 ngOnInit() { 8 this.store.loadAllTodos(); 9 } 10 } 11
  56. @Component({ // ... providers: [TodoStore], }) export class AppComponent implements

    OnInit { readonly store = inject(TodoStore); ngOnInit() { this.store.loadAllTodos(); } } 1 2 3 4 5 6 7 8 9 10 11 providers: [TodoStore], @Component({ 1 // ... 2 3 }) 4 export class AppComponent implements OnInit { 5 readonly store = inject(TodoStore); 6 7 ngOnInit() { 8 this.store.loadAllTodos(); 9 } 10 } 11 readonly store = inject(TodoStore); @Component({ 1 // ... 2 providers: [TodoStore], 3 }) 4 export class AppComponent implements OnInit { 5 6 7 ngOnInit() { 8 this.store.loadAllTodos(); 9 } 10 } 11
  57. @Component({ // ... providers: [TodoStore], }) export class AppComponent implements

    OnInit { readonly store = inject(TodoStore); ngOnInit() { this.store.loadAllTodos(); } } 1 2 3 4 5 6 7 8 9 10 11 providers: [TodoStore], @Component({ 1 // ... 2 3 }) 4 export class AppComponent implements OnInit { 5 readonly store = inject(TodoStore); 6 7 ngOnInit() { 8 this.store.loadAllTodos(); 9 } 10 } 11 readonly store = inject(TodoStore); @Component({ 1 // ... 2 providers: [TodoStore], 3 }) 4 export class AppComponent implements OnInit { 5 6 7 ngOnInit() { 8 this.store.loadAllTodos(); 9 } 10 } 11 this.store.loadAllTodos(); @Component({ 1 // ... 2 providers: [TodoStore], 3 }) 4 export class AppComponent implements OnInit { 5 readonly store = inject(TodoStore); 6 7 ngOnInit() { 8 9 } 10 } 11
  58. template: `{{ store.items() }} {{ store.loading() }}`, readonly store =

    inject(TodoStore); @Component({ 1 // ... 2 3 providers: [TodoStore], 4 }) 5 export class AppComponent implements OnInit { 6 7 8 ngOnInit() { 9 this.store.loadAllTodos(); 10 } 11 } 12
  59. export const TodoStore = signalStore( { providedIn: 'root' }, withState(initialState),

    withMethods(/* ... */), withHooks({ // Hooks }) ); 1 2 3 4 5 6 7 8
  60. export const TodoStore = signalStore( { providedIn: 'root' }, withState(initialState),

    withMethods(/* ... */), withHooks({ // Hooks }) ); 1 2 3 4 5 6 7 8 withHooks({ // Hooks }) ); export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState(initialState), 3 withMethods(/* ... */), 4 5 6 7 8
  61. export const TodoStore = signalStore( { providedIn: 'root' }, withState(initialState),

    withMethods(/* ... */), withHooks({ onInit({ loadAllTodos }) { loadAllTodos(); }, onDestroy() { console.log('on destroy'); }, }) ); 1 2 3 4 5 6 7 8 9 10 11 12 13
  62. export const TodoStore = signalStore( { providedIn: 'root' }, withState(initialState),

    withMethods(/* ... */), withHooks({ onInit({ loadAllTodos }) { loadAllTodos(); }, onDestroy() { console.log('on destroy'); }, }) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 withHooks({ onInit({ loadAllTodos }) { loadAllTodos(); }, onDestroy() { console.log('on destroy'); }, }) export const TodoStore = signalStore( 1 { providedIn: 'root' }, 2 withState(initialState), 3 withMethods(/* ... */), 4 5 6 7 8 9 10 11 12 ); 13
  63. @Component({ // ... providers: [TodoStore], }) export class AppComponent implements

    OnInit { readonly store = inject(TodoStore); ngOnInit() { this.store.loadAllTodos(); } } 1 2 3 4 5 6 7 8 9 10 11
  64. @Component({ // ... providers: [TodoStore], }) export class AppComponent {

    readonly store = inject(TodoStore); } 1 2 3 4 5 6 7
  65. withComputed((state) => ({ /* ... */ })), export const TodoStore

    = signalStore( 1 withState(initialState), 2 3 4 5 6 7 withMethods(/* ... */), 8 withHooks(/* ... */) 9 ); 10
  66. export const TodoStore = signalStore( withState(initialState), withComputed(({ items }) =>

    ({ doneCount: computed(() => items().filter((x) => x.done).length), undoneCount: computed(() => items().filter((x) => !x.done).length), percentageDone: computed(() => { const done = items().filter((x) => x.done).length; const total = items().length; if (total === 0) { return 0; } return (done / total) * 100; }), })), withMethods(/* ... */), withHooks(/* ... */) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
  67. export const TodoStore = signalStore( withState(initialState), withComputed(({ items }) =>

    ({ doneCount: computed(() => items().filter((x) => x.done).length), undoneCount: computed(() => items().filter((x) => !x.done).length), percentageDone: computed(() => { const done = items().filter((x) => x.done).length; const total = items().length; if (total === 0) { return 0; } return (done / total) * 100; }), })), withMethods(/* ... */), withHooks(/* ... */) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 withComputed(({ items }) => ({ doneCount: computed(() => items().filter((x) => x.done).length), undoneCount: computed(() => items().filter((x) => !x.done).length), percentageDone: computed(() => { const done = items().filter((x) => x.done).length; const total = items().length; if (total === 0) { return 0; } return (done / total) * 100; }), })), export const TodoStore = signalStore( 1 withState(initialState), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 withMethods(/* ... */), 19 withHooks(/* ... */) 20 ); 21
  68. export const TodoStore = signalStore( withState(initialState), withComputed(({ items }) =>

    ({ doneCount: computed(() => items().filter((x) => x.done).length), undoneCount: computed(() => items().filter((x) => !x.done).length), percentageDone: computed(() => { const done = items().filter((x) => x.done).length; const total = items().length; if (total === 0) { return 0; } return (done / total) * 100; }), })), withMethods(/* ... */), withHooks(/* ... */) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 withComputed(({ items }) => ({ doneCount: computed(() => items().filter((x) => x.done).length), undoneCount: computed(() => items().filter((x) => !x.done).length), percentageDone: computed(() => { const done = items().filter((x) => x.done).length; const total = items().length; if (total === 0) { return 0; } return (done / total) * 100; }), })), export const TodoStore = signalStore( 1 withState(initialState), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 withMethods(/* ... */), 19 withHooks(/* ... */) 20 ); 21 doneCount: computed(() => items().filter((x) => x.done).length), export const TodoStore = signalStore( 1 withState(initialState), 2 3 withComputed(({ items }) => ({ 4 5 undoneCount: computed(() => items().filter((x) => !x.done).length), 6 percentageDone: computed(() => { 7 const done = items().filter((x) => x.done).length; 8 const total = items().length; 9 10 if (total === 0) { 11 return 0; 12 } 13 14 return (done / total) * 100; 15 }), 16 })), 17 18 withMethods(/* ... */), 19 withHooks(/* ... */) 20 ); 21
  69. export const TodoStore = signalStore( withState(initialState), withComputed(({ items }) =>

    ({ doneCount: computed(() => items().filter((x) => x.done).length), undoneCount: computed(() => items().filter((x) => !x.done).length), percentageDone: computed(() => { const done = items().filter((x) => x.done).length; const total = items().length; if (total === 0) { return 0; } return (done / total) * 100; }), })), withMethods(/* ... */), withHooks(/* ... */) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 withComputed(({ items }) => ({ doneCount: computed(() => items().filter((x) => x.done).length), undoneCount: computed(() => items().filter((x) => !x.done).length), percentageDone: computed(() => { const done = items().filter((x) => x.done).length; const total = items().length; if (total === 0) { return 0; } return (done / total) * 100; }), })), export const TodoStore = signalStore( 1 withState(initialState), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 withMethods(/* ... */), 19 withHooks(/* ... */) 20 ); 21 doneCount: computed(() => items().filter((x) => x.done).length), export const TodoStore = signalStore( 1 withState(initialState), 2 3 withComputed(({ items }) => ({ 4 5 undoneCount: computed(() => items().filter((x) => !x.done).length), 6 percentageDone: computed(() => { 7 const done = items().filter((x) => x.done).length; 8 const total = items().length; 9 10 if (total === 0) { 11 return 0; 12 } 13 14 return (done / total) * 100; 15 }), 16 })), 17 18 withMethods(/* ... */), 19 withHooks(/* ... */) 20 ); 21 undoneCount: computed(() => items().filter((x) => !x.done).length), export const TodoStore = signalStore( 1 withState(initialState), 2 3 withComputed(({ items }) => ({ 4 doneCount: computed(() => items().filter((x) => x.done).length), 5 6 percentageDone: computed(() => { 7 const done = items().filter((x) => x.done).length; 8 const total = items().length; 9 10 if (total === 0) { 11 return 0; 12 } 13 14 return (done / total) * 100; 15 }), 16 })), 17 18 withMethods(/* ... */), 19 withHooks(/* ... */) 20 ); 21
  70. export const TodoStore = signalStore( withState(initialState), withComputed(({ items }) =>

    ({ doneCount: computed(() => items().filter((x) => x.done).length), undoneCount: computed(() => items().filter((x) => !x.done).length), percentageDone: computed(() => { const done = items().filter((x) => x.done).length; const total = items().length; if (total === 0) { return 0; } return (done / total) * 100; }), })), withMethods(/* ... */), withHooks(/* ... */) ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 withComputed(({ items }) => ({ doneCount: computed(() => items().filter((x) => x.done).length), undoneCount: computed(() => items().filter((x) => !x.done).length), percentageDone: computed(() => { const done = items().filter((x) => x.done).length; const total = items().length; if (total === 0) { return 0; } return (done / total) * 100; }), })), export const TodoStore = signalStore( 1 withState(initialState), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 withMethods(/* ... */), 19 withHooks(/* ... */) 20 ); 21 doneCount: computed(() => items().filter((x) => x.done).length), export const TodoStore = signalStore( 1 withState(initialState), 2 3 withComputed(({ items }) => ({ 4 5 undoneCount: computed(() => items().filter((x) => !x.done).length), 6 percentageDone: computed(() => { 7 const done = items().filter((x) => x.done).length; 8 const total = items().length; 9 10 if (total === 0) { 11 return 0; 12 } 13 14 return (done / total) * 100; 15 }), 16 })), 17 18 withMethods(/* ... */), 19 withHooks(/* ... */) 20 ); 21 undoneCount: computed(() => items().filter((x) => !x.done).length), export const TodoStore = signalStore( 1 withState(initialState), 2 3 withComputed(({ items }) => ({ 4 doneCount: computed(() => items().filter((x) => x.done).length), 5 6 percentageDone: computed(() => { 7 const done = items().filter((x) => x.done).length; 8 const total = items().length; 9 10 if (total === 0) { 11 return 0; 12 } 13 14 return (done / total) * 100; 15 }), 16 })), 17 18 withMethods(/* ... */), 19 withHooks(/* ... */) 20 ); 21 percentageDone: computed(() => { const done = items().filter((x) => x.done).length; const total = items().length; if (total === 0) { return 0; } return (done / total) * 100; }), export const TodoStore = signalStore( 1 withState(initialState), 2 3 withComputed(({ items }) => ({ 4 doneCount: computed(() => items().filter((x) => x.done).length), 5 undoneCount: computed(() => items().filter((x) => !x.done).length), 6 7 8 9 10 11 12 13 14 15 16 })), 17 18 withMethods(/* ... */), 19 withHooks(/* ... */) 20 ); 21
  71. @Component({ providers: [TodoStore], templates: ` <div> {{ store.doneCount() }} /

    {{ store.undoneCount() }} {{ store.percentageDone() }} </div> ` }) export class AppComponent implements OnInit { readonly store = inject(TodoStore); } 1 2 3 4 5 6 7 8 9 10 11
  72. @Component({ providers: [TodoStore], templates: ` <div> {{ store.doneCount() }} /

    {{ store.undoneCount() }} {{ store.percentageDone() }} </div> ` }) export class AppComponent implements OnInit { readonly store = inject(TodoStore); } 1 2 3 4 5 6 7 8 9 10 11 <div> {{ store.doneCount() }} / {{ store.undoneCount() }} {{ store.percentageDone() }} </div> ` @Component({ 1 providers: [TodoStore], 2 templates: ` 3 4 5 6 7 }) 8 export class AppComponent implements OnInit { 9 readonly store = inject(TodoStore); 10 } 11
  73. import { computed } from '@angular/core'; import { signalStoreFeature, withComputed,

    withState } from '@ngrx/signals'; export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; export type RequestStatusState = { requestStatus: RequestStatus }; export function withRequestStatus() { return signalStoreFeature( withState<RequestStatusState>({ requestStatus: 'idle' }), withComputed(({ requestStatus }) => ({ isPending: computed(() => requestStatus() === 'pending'), isFulfilled: computed(() => requestStatus() === 'fulfilled'), error: computed(() => { const status = requestStatus(); return typeof status === 'object' ? status.error : null; }), })) ); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
  74. import { computed } from '@angular/core'; import { signalStoreFeature, withComputed,

    withState } from '@ngrx/signals'; export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; export type RequestStatusState = { requestStatus: RequestStatus }; export function withRequestStatus() { return signalStoreFeature( withState<RequestStatusState>({ requestStatus: 'idle' }), withComputed(({ requestStatus }) => ({ isPending: computed(() => requestStatus() === 'pending'), isFulfilled: computed(() => requestStatus() === 'fulfilled'), error: computed(() => { const status = requestStatus(); return typeof status === 'object' ? status.error : null; }), })) ); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 export function withRequestStatus() { import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 7 return signalStoreFeature( 8 withState<RequestStatusState>({ requestStatus: 'idle' }), 9 withComputed(({ requestStatus }) => ({ 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22
  75. import { computed } from '@angular/core'; import { signalStoreFeature, withComputed,

    withState } from '@ngrx/signals'; export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; export type RequestStatusState = { requestStatus: RequestStatus }; export function withRequestStatus() { return signalStoreFeature( withState<RequestStatusState>({ requestStatus: 'idle' }), withComputed(({ requestStatus }) => ({ isPending: computed(() => requestStatus() === 'pending'), isFulfilled: computed(() => requestStatus() === 'fulfilled'), error: computed(() => { const status = requestStatus(); return typeof status === 'object' ? status.error : null; }), })) ); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 export function withRequestStatus() { import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 7 return signalStoreFeature( 8 withState<RequestStatusState>({ requestStatus: 'idle' }), 9 withComputed(({ requestStatus }) => ({ 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22 return signalStoreFeature( import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 export function withRequestStatus() { 7 8 withState<RequestStatusState>({ requestStatus: 'idle' }), 9 withComputed(({ requestStatus }) => ({ 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22
  76. import { computed } from '@angular/core'; import { signalStoreFeature, withComputed,

    withState } from '@ngrx/signals'; export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; export type RequestStatusState = { requestStatus: RequestStatus }; export function withRequestStatus() { return signalStoreFeature( withState<RequestStatusState>({ requestStatus: 'idle' }), withComputed(({ requestStatus }) => ({ isPending: computed(() => requestStatus() === 'pending'), isFulfilled: computed(() => requestStatus() === 'fulfilled'), error: computed(() => { const status = requestStatus(); return typeof status === 'object' ? status.error : null; }), })) ); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 export function withRequestStatus() { import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 7 return signalStoreFeature( 8 withState<RequestStatusState>({ requestStatus: 'idle' }), 9 withComputed(({ requestStatus }) => ({ 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22 return signalStoreFeature( import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 export function withRequestStatus() { 7 8 withState<RequestStatusState>({ requestStatus: 'idle' }), 9 withComputed(({ requestStatus }) => ({ 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22 withState<RequestStatusState>({ requestStatus: 'idle' }), import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 export function withRequestStatus() { 7 return signalStoreFeature( 8 9 withComputed(({ requestStatus }) => ({ 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22
  77. import { computed } from '@angular/core'; import { signalStoreFeature, withComputed,

    withState } from '@ngrx/signals'; export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; export type RequestStatusState = { requestStatus: RequestStatus }; export function withRequestStatus() { return signalStoreFeature( withState<RequestStatusState>({ requestStatus: 'idle' }), withComputed(({ requestStatus }) => ({ isPending: computed(() => requestStatus() === 'pending'), isFulfilled: computed(() => requestStatus() === 'fulfilled'), error: computed(() => { const status = requestStatus(); return typeof status === 'object' ? status.error : null; }), })) ); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 export function withRequestStatus() { import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 7 return signalStoreFeature( 8 withState<RequestStatusState>({ requestStatus: 'idle' }), 9 withComputed(({ requestStatus }) => ({ 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22 return signalStoreFeature( import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 export function withRequestStatus() { 7 8 withState<RequestStatusState>({ requestStatus: 'idle' }), 9 withComputed(({ requestStatus }) => ({ 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22 withState<RequestStatusState>({ requestStatus: 'idle' }), import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 export function withRequestStatus() { 7 return signalStoreFeature( 8 9 withComputed(({ requestStatus }) => ({ 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22 withComputed(({ requestStatus }) => ({ import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 export function withRequestStatus() { 7 return signalStoreFeature( 8 withState<RequestStatusState>({ requestStatus: 'idle' }), 9 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22
  78. import { computed } from '@angular/core'; import { signalStoreFeature, withComputed,

    withState } from '@ngrx/signals'; export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; export type RequestStatusState = { requestStatus: RequestStatus }; export function withRequestStatus() { return signalStoreFeature( withState<RequestStatusState>({ requestStatus: 'idle' }), withComputed(({ requestStatus }) => ({ isPending: computed(() => requestStatus() === 'pending'), isFulfilled: computed(() => requestStatus() === 'fulfilled'), error: computed(() => { const status = requestStatus(); return typeof status === 'object' ? status.error : null; }), })) ); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 export function withRequestStatus() { import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 7 return signalStoreFeature( 8 withState<RequestStatusState>({ requestStatus: 'idle' }), 9 withComputed(({ requestStatus }) => ({ 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22 return signalStoreFeature( import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 export function withRequestStatus() { 7 8 withState<RequestStatusState>({ requestStatus: 'idle' }), 9 withComputed(({ requestStatus }) => ({ 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22 withState<RequestStatusState>({ requestStatus: 'idle' }), import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 export function withRequestStatus() { 7 return signalStoreFeature( 8 9 withComputed(({ requestStatus }) => ({ 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22 withComputed(({ requestStatus }) => ({ import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 export function withRequestStatus() { 7 return signalStoreFeature( 8 withState<RequestStatusState>({ requestStatus: 'idle' }), 9 10 isPending: computed(() => requestStatus() === 'pending'), 11 12 isFulfilled: computed(() => requestStatus() === 'fulfilled'), 13 14 error: computed(() => { 15 const status = requestStatus(); 16 17 return typeof status === 'object' ? status.error : null; 18 }), 19 })) 20 ); 21 } 22 isPending: computed(() => requestStatus() === 'pending'), isFulfilled: computed(() => requestStatus() === 'fulfilled'), error: computed(() => { const status = requestStatus(); return typeof status === 'object' ? status.error : null; }), import { computed } from '@angular/core'; 1 import { signalStoreFeature, withComputed, withState } from '@ngrx/signals'; 2 3 export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string }; 4 export type RequestStatusState = { requestStatus: RequestStatus }; 5 6 export function withRequestStatus() { 7 return signalStoreFeature( 8 withState<RequestStatusState>({ requestStatus: 'idle' }), 9 withComputed(({ requestStatus }) => ({ 10 11 12 13 14 15 16 17 18 19 })) 20 ); 21 } 22
  79. export function setPending(): RequestStatusState { return { requestStatus: 'pending' };

    } export function setFulfilled(): RequestStatusState { return { requestStatus: 'fulfilled' }; } export function setError(error: string): RequestStatusState { return { requestStatus: { error } }; } 1 2 3 4 5 6 7 8 9 10 11
  80. export const TodoStore = signalStore( withState(initialState), withRequestStatus(), withComputed(/* ... */),

    withMethods(/* ... */), withHooks(/* ... */) ); readonly store = inject(TodoStore); store.requestStatus(); store.isPending(); store.isFulfilled(); 1 2 3 4 5 6 7 8 9 10 11 12 13
  81. export const TodoStore = signalStore( withState(initialState), withRequestStatus(), withComputed(/* ... */),

    withMethods(/* ... */), withHooks(/* ... */) ); readonly store = inject(TodoStore); store.requestStatus(); store.isPending(); store.isFulfilled(); 1 2 3 4 5 6 7 8 9 10 11 12 13 withRequestStatus(), export const TodoStore = signalStore( 1 withState(initialState), 2 3 withComputed(/* ... */), 4 withMethods(/* ... */), 5 withHooks(/* ... */) 6 ); 7 8 readonly store = inject(TodoStore); 9 10 store.requestStatus(); 11 store.isPending(); 12 store.isFulfilled(); 13
  82. export const TodoStore = signalStore( withState(initialState), withRequestStatus(), withComputed(/* ... */),

    withMethods(/* ... */), withHooks(/* ... */) ); readonly store = inject(TodoStore); store.requestStatus(); store.isPending(); store.isFulfilled(); 1 2 3 4 5 6 7 8 9 10 11 12 13 withRequestStatus(), export const TodoStore = signalStore( 1 withState(initialState), 2 3 withComputed(/* ... */), 4 withMethods(/* ... */), 5 withHooks(/* ... */) 6 ); 7 8 readonly store = inject(TodoStore); 9 10 store.requestStatus(); 11 store.isPending(); 12 store.isFulfilled(); 13 readonly store = inject(TodoStore); export const TodoStore = signalStore( 1 withState(initialState), 2 withRequestStatus(), 3 withComputed(/* ... */), 4 withMethods(/* ... */), 5 withHooks(/* ... */) 6 ); 7 8 9 10 store.requestStatus(); 11 store.isPending(); 12 store.isFulfilled(); 13
  83. export const TodoStore = signalStore( withState(initialState), withRequestStatus(), withComputed(/* ... */),

    withMethods(/* ... */), withHooks(/* ... */) ); readonly store = inject(TodoStore); store.requestStatus(); store.isPending(); store.isFulfilled(); 1 2 3 4 5 6 7 8 9 10 11 12 13 withRequestStatus(), export const TodoStore = signalStore( 1 withState(initialState), 2 3 withComputed(/* ... */), 4 withMethods(/* ... */), 5 withHooks(/* ... */) 6 ); 7 8 readonly store = inject(TodoStore); 9 10 store.requestStatus(); 11 store.isPending(); 12 store.isFulfilled(); 13 readonly store = inject(TodoStore); export const TodoStore = signalStore( 1 withState(initialState), 2 withRequestStatus(), 3 withComputed(/* ... */), 4 withMethods(/* ... */), 5 withHooks(/* ... */) 6 ); 7 8 9 10 store.requestStatus(); 11 store.isPending(); 12 store.isFulfilled(); 13 store.requestStatus(); store.isPending(); store.isFulfilled(); export const TodoStore = signalStore( 1 withState(initialState), 2 withRequestStatus(), 3 withComputed(/* ... */), 4 withMethods(/* ... */), 5 withHooks(/* ... */) 6 ); 7 8 readonly store = inject(TodoStore); 9 10 11 12 13