Search code examples
angulartypescriptsignalsngrx

@ngrx/signals Store methods in separate file


I'm just starting to learn ngrx signals and one thing that struck me is that a "store" file could become quite large, quite quickly so ideally I would like to break it into separate parts.

Below is an example of how I might split out the "withMethods" factory but I cannot work out what the type of "store" should be in the bookMethodsFactory function and how I can avoid a circular dependency?

import { inject } from '@angular/core';
import { patchState, signalStore, withMethods, withState } from '@ngrx/signals';

// book.model.ts
export interface Book {
    id: number;
    title: string;
}

// books.service.ts
export class BooksService {
    public getBooks(): Promise<Book[]> {
        return Promise.resolve([
            { id: 1, title: 'Book 1' },
            { id: 2, title: 'Book 2' },
            { id: 3, title: 'Book 3' }
        ]);
    }
}

type BooksState = {
    books: Book[];
};

// book
const initialState: BooksState = {
    books: []
};

export const BooksStore = signalStore(
    { protectedState: false },
    withState(initialState),
    withMethods(bookMethodsFactory)
);

// book.methods.ts
export const bookMethodsFactory = ((store: ???) => {
    const bookService = inject(BooksService);

    return {
        async loadBooks(): Promise<void> {
            const books = await bookService.getBooks();
            patchState(store, { books });
        }
    }
});

Solution

  • In ngrx signal store you can create an extension using signalStoreFeature. it can be used in your case if you have a long list of withMethods functions or you can use it to share code between signals store.

    So in your case you can create a separate file book.signalMethods.ts

    import { signalStoreFeature, withMethods } from '@ngrx/signals';
    
    export function withMySeparatedMethods() {
     return signalStoreFeature(
        withMethods((store, bookService = inject(BooksService)) => {
          //loadBooks
          // function2
          // function3
          return { loadBooks, function2, function3 };
        })
      )
    }
    

    then all what you need to do is to import it in your main signal store

    export const BooksStore = signalStore(
     { protectedState: false },
     withState(initialState),
     withMySeparatedMethods() // HERE
    );