rxMethod must inject in a constructor context

Discussão em 'Angular' iniciado por Spring, Novembro 12, 2024.

  1. Spring

    Spring Guest

    Hi @all I have a Angular Ionic app and in this application my signalstore does make a lot of Problems:

    import {
    patchState,
    signalStore,
    signalStoreFeature,
    type,
    withComputed,
    withHooks,
    withMethods,
    withState,
    } from '@ngrx/signals';
    import { HttpShoppingListService } from '../service/http-shopping-list.service';
    import { computed, inject } from '@angular/core';
    import { rxMethod } from '@ngrx/signals/rxjs-interop';
    import { debounceTime, distinctUntilChanged, pipe, switchMap, tap } from 'rxjs';
    import { tapResponse } from '@ngrx/operators';
    import { ShoppingList } from '../domain/ShoppingList';
    import { ShoppingListToCreate } from '../domain/ShoppingListToCreate';
    import { Product } from '../domain/Product';
    import { ShoppingListToUpdate } from '../domain/ShoppingListToUpdate';

    export type Filter = {
    shoppingListId: string;
    order: 'asc' | 'desc';
    };

    export type ShoppingListState = {
    shoppingLists: ShoppingList[];
    isLoading: boolean;
    filter: Filter;
    };

    export const initialState: ShoppingListState = {
    shoppingLists: [],
    isLoading: false,
    filter: { shoppingListId: '', order: 'asc' },
    };

    export const ShoppingListStore = signalStore(
    { providedIn: 'root' },
    withState(initialState),
    // withComputed((store) => ({
    // shoppingListCount: computed(() => store.shoppingLists().length),
    // sortedShoppingLists: computed(() => store.shoppingLists().sort((a, b) => a.name.localeCompare( b.name ))),
    // isLoading: computed(() => store.isLoading()),
    // })),

    withMethods(
    (store, httpShoppingListService = inject(HttpShoppingListService)) => ({
    loadShoppingListsByGroupId: rxMethod<string>(
    pipe(
    debounceTime(1000),
    distinctUntilChanged(),
    tap(() => patchState(store, { isLoading: true })),
    switchMap((groupId: string) =>
    httpShoppingListService.getAllShoppingLists(groupId).pipe(
    tapResponse({
    next: (shoppingLists: ShoppingList[]) =>
    patchState(store, { shoppingLists }),
    error: (err) => {
    console.error(err);
    },
    finalize: () => patchState(store, { isLoading: false }),
    })
    )
    )
    )
    ),

    addShoppingList: rxMethod<ShoppingListToCreate>(
    pipe(
    debounceTime(300),
    tap(() => patchState(store, { isLoading: true })),
    switchMap(shoppingListToCreate =>
    httpShoppingListService.addShoppingList(shoppingListToCreate).pipe(
    tapResponse({
    next: (createdShoppingList: ShoppingList) => {
    const test:ShoppingList[] = store.shoppingLists();
    const updatedShoppingLists: ShoppingList[] = [
    ...test,
    createdShoppingList,
    ]
    patchState(store, { shoppingLists: updatedShoppingLists });
    },
    error: (err) => console.error(err),
    finalize: () => patchState(store, { isLoading: false }),
    })
    )
    )
    )
    ),

    addProductsToShoppingList: rxMethod<{
    shoppingListId: string;
    products: Product[];
    }>(
    pipe(
    distinctUntilChanged(),
    tap(() => patchState(store, { isLoading: true })),
    switchMap(({ shoppingListId, products }) =>
    httpShoppingListService
    .addProductsToShoppingList(shoppingListId, products)
    .pipe(
    tapResponse({
    next: (updatedShoppingList: ShoppingList) => {
    patchState(store, ({ shoppingLists }) => ({
    shoppingLists: shoppingLists.map((list) =>
    list.id === shoppingListId ? updatedShoppingList : list
    ),
    }));
    },
    error: (err) => console.error(err),
    finalize: () => patchState(store, { isLoading: false }),
    })
    )
    )
    )
    ),

    updateShoppingList: rxMethod<{
    shoppingListToUpdate: ShoppingListToUpdate;
    }>(
    pipe(
    distinctUntilChanged(),
    tap(() => patchState(store, { isLoading: true })),
    switchMap(({ shoppingListToUpdate }) =>
    httpShoppingListService
    .updateShoppingList(shoppingListToUpdate)
    .pipe(
    tapResponse({
    next: (updatedShoppingList: ShoppingList) => {
    patchState(store, ({ shoppingLists }) => ({
    shoppingLists: shoppingLists.map((list) =>
    list.id === shoppingListToUpdate.id
    ? updatedShoppingList
    : list
    ),
    }));
    },
    error: (err) => {
    console.error(err);
    },
    finalize: () => patchState(store, { isLoading: false }),
    })
    )
    )
    )
    ),
    })
    ),
    );


    It can not be inject in my component:

    import { Component, inject, OnInit, signal, Signal } from '@angular/core';
    import { IonicModule, NavController } from '@ionic/angular';
    import { ShoppingListStore } from '../../store/shoppingListStore';

    @Component({
    selector: 'app-group',
    templateUrl: './group.component.html',
    styleUrls: [ './group.component.scss' ],
    imports: [
    IonicModule
    ],
    standalone: true,
    })
    export class GroupComponent {

    readonly shoppingListStore = inject(ShoppingListStore);

    // activeIndex = 0;

    protected readonly groupId: Signal<string> = signal('1');

    // protected readonly shoppingLists: Signal<ShoppingList[]>;
    // protected readonly isLoading: Signal<boolean> = this.shoppingListStore.isLoading
    // protected readonly count: Signal<number> = this.shoppingListStore.shoppingListCount

    constructor() {
    this.shoppingListStore.loadShoppingListsByGroupId('1')
    // this.shoppingListStore.loadShoppingListsByGroupId('1');
    // this.shoppingLists = this.shoppingListStore.sortedShoppingLists;
    // @ts-ignore
    // this.a = at;
    // @ts-ignore

    }
    }


    In a normal angular Application, this store works but i dont know why its throw an error. Maybe because a module import ?:

    import { Component, inject, OnInit, signal } from '@angular/core';
    import { ActivatedRoute } from '@angular/router';
    import { IonButtons, IonContent, IonHeader, IonMenuButton, IonTitle, IonToolbar } from '@ionic/angular/standalone';
    import { NgComponentOutlet, NgIf } from '@angular/common';
    import { ComponentMapping } from '../domain/component-mapping';
    import { SideBarContentService } from '../service/side-bar-content.service';
    import { Folder } from '../domain/Folder';
    import { ShoppingListStore } from '../store/shoppingListStore';

    @Component({
    selector: 'app-folder',
    templateUrl: './folder.page.html',
    styleUrls: [ './folder.page.scss' ],
    standalone: true,
    providers: [ShoppingListStore],
    imports: [ IonHeader, IonToolbar, IonButtons, IonMenuButton, IonTitle, IonContent, NgComponentOutlet, NgIf ],
    })
    export class FolderPage implements OnInit {

    private activatedRoute = inject(ActivatedRoute);
    private sideBareContentService = inject(SideBarContentService);

    private allFolders = this.sideBareContentService.folders;
    currentFolderPage = signal<Folder | null>(null);
    component = signal<any | null>(null);

    private componentMapping: ComponentMapping = this.sideBareContentService.componentMapping;

    ngOnInit() {
    const folderId = this.activatedRoute.snapshot.paramMap.get('id');
    if (folderId) {
    const currentFolder = this.allFolders.find(folder => folder.id === folderId);
    if (currentFolder) {
    this.setFolder(currentFolder);
    }
    }
    }
    private setFolder(folder: Folder) {
    this.currentFolderPage.set(folder);
    this.loadComponent(folder);
    }

    private loadComponent(folder: Folder | null) {
    if( folder && this.componentMapping[ folder.id ] ) {
    this.componentMapping[ folder.id ]().then((component: any) => {
    this.component.set(component);
    });
    } else {
    this.component.set(null);
    }
    }
    }

    import { AppPage } from '../domain/AppPage';
    import { Observable, of } from 'rxjs';
    import { ComponentMapping } from '../domain/component-mapping';
    import { Folder } from '../domain/Folder';
    import { DashboardComponent } from '../components/dashboard/dashboard.component';
    import { GroupComponent } from '../components/group/group.component';

    export class SideBarContentService {

    constructor() { }

    private _appPages: AppPage[] = [
    { sideBarName: 'Dashboard', url: '/folder/dashboard', icon: 'home-outline', isVisibleForUser: true },
    { sideBarName: 'Group Shopping Lists', url: '/folder/group', icon: 'cart-outline', isVisibleForUser: true },
    { sideBarName: 'PasswordManager', url: '/folder/password-manager', icon: 'key-outline', isVisibleForUser: true },
    ];

    private _componentMapping: ComponentMapping = {
    'dashboard': () => import('src/app/components/dashboard/dashboard.component').then(m => m.DashboardComponent),
    'group': () => import('src/app/components/group/group.component').then(m => m.GroupComponent),
    };

    private _folders: Folder[] = [
    {
    id: 'dashboard',
    title: 'Home',
    component: DashboardComponent,
    },
    {
    id: 'group',
    title: 'Shopping Lists for Group',
    component: GroupComponent,
    }
    ];

    get appPages(): Observable<AppPage[]> {
    return of(this._appPages);
    }

    get folders(): Folder[] {
    return this._folders;
    }

    get componentMapping() : ComponentMapping {
    return this._componentMapping;
    }
    }


    Following Error message occours:

    ERROR RuntimeError: NG0203: rxMethod() can only be used within an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`. Find more at https://angular.io/errors/NG0203
    at assertInInjectionContext (core.mjs:3324:11)
    at rxMethod (ngrx-signals-rxjs-interop.mjs:5:5)
    at shoppingListStore.ts:50:35
    at ngrx-signals.mjs:211:21
    at ngrx-signals.mjs:87:62
    at Array.reduce (<anonymous>)
    at new _SignalStore (ngrx-signals.mjs:87:35)
    at Object.SignalStore_Factory [as factory] (ngrx-signals.mjs:116:14)
    at core.mjs:3136:35
    at runInInjectorProfilerContext (core.mjs:867:5)
    handleError @ core.mjs:7195
    next @ core.mjs:33183
    ConsumerObserver2.next @ Subscriber.js:90
    Subscriber2._next @ Subscriber.js:59
    Subscriber2.next @ Subscriber.js:32
    (anonym) @ Subject.js:41
    errorContext @ errorContext.js:23
    Subject2.next @ Subject.js:31
    emit @ core.mjs:6590
    (anonym) @ core.mjs:7082
    invoke @ zone.js:339
    run @ zone.js:110
    runOutsideAngular @ core.mjs:6944
    onHandleError @ core.mjs:7082
    handleError @ zone.js:342
    runGuarded @ zone.js:124
    api.microtaskDrainDone @ zone.js:2235
    drainMicroTaskQueue @ zone.js:541
    invokeTask @ zone.js:444
    invokeTask @ zone.js:1086
    globalCallback @ zone.js:1116
    globalZoneAwareCallback @ zone.js:1147
    22 weitere Frames anzeigen
    Weniger anzeigen


    I think the rxMethod is being implemented correctly... Does anyone have any ideas?

    Continue reading...

Compartilhe esta Página