1
votes

currently i am trying to integrate the awesome ngrx lib into my project. Everything works as intended expect for the @ngrx/effects lib. What i am trying to do is to get some of my data from a service which will make a http request later on. I know that i am missing something but i can`t find my misunderstanding. Maybe some of you can help.

So i have a lazy loaded module within my application where i want to use the ngrx lib.

My code so far:

app.module.ts

import [...]
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { EffectsModule } from '@ngrx/effects';

@NgModule({
    imports: [
        HttpModule,
        BrowserModule,
        BrowserAnimationsModule,
        ToastModule.forRoot(),
        FormsModule,
        SharedModule,
        ReactiveFormsModule,
        appRouting,
        StoreModule.forRoot({}),
        StoreDevtoolsModule.instrument({
            maxAge: 25
        }),
        EffectsModule.forRoot([])
    ],
    declarations: [
        AppComponent,
        NavigationComponent,
        HomeComponent,
        LoginComponent,
        SidebarComponent,
        PageNotFoundComponent
    ],
    providers: [
        AuthenticationService,
        AlertService,
        AuthGuard,
        mockBackendProvider,
        MockBackend,
        BaseRequestOptions,

        AppConfig,
        {
            provide: APP_INITIALIZER,
            useFactory: (config: AppConfig) => () => config.load(),
            deps: [AppConfig],
            multi: true
        },
        {
            provide: ToastOptions, useClass: NotificationOptions
        }
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

project.module

import [...]
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { projectReducer } from './store/project.reducer';
import { ProjectService } from './services/project.service';
import { ProjectEffects } from './store/project.effects';

@NgModule({
    imports: [
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        ProjectRoutingModule,
        SharedModule,
        EffectsModule.forFeature([ProjectEffects]),
        StoreModule.forFeature('projectModule', {
            projectReducer: projectReducer,
        }),

    ],
    exports: [],
    declarations: [
        HomeComponent,
        DashboardComponent
    ],
    providers: [
        ProjectService,
        mockBackendProvider,
        MockBackend,
        BaseRequestOptions
    ],
})
export class ProjectModule {
}

I created 4 files:

project.actions.ts

import { Action } from '@ngrx/store';
import { Project } from '../../../models/Project/Project';

export const SELECT_ALL_PROJECTS = '[projects] Select all';
export const ADD_PROJECT = '[projects] Add project';
export const DELETE_PROJECT = '[projects] Delete project';

export class GetAllProjectsAction implements Action {
    readonly type = SELECT_ALL_PROJECTS;

    constructor(public projects: Project[]) {
    }
}

export class AddProjectAction implements Action {
    readonly type = ADD_PROJECT;

    constructor(public project: Project) {
    }
}

export class DeleteProjectAction implements Action {
    readonly type = DELETE_PROJECT;

    constructor(public project: Project) {
    }
}

export type Actions = GetAllProjectsAction | AddProjectAction | DeleteProjectAction;

projects.effects.ts

import { Injectable } from '@angular/core';
import {
    Actions,
    Effect
} from '@ngrx/effects';
import { Observable } from 'rxjs/Observable';
import { Action } from '@ngrx/store';

import * as projectAction from './project.actions';
import { ProjectService } from '../services/project.service';
import { of } from 'rxjs/observable/of';

@Injectable()
export class ProjectEffects {

    @Effect() getAll$: Observable<Action> = this.actions$.ofType(projectAction.SELECT_ALL_PROJECTS)
        .mergeMap(() =>
            this._projectService.getAll()
                .map((data: any[]) => ({type: projectAction.SELECT_ALL_PROJECTS, projects: data}))
                .catch((error: any) => {
                    return of({type: 'getALL_Failed'})
                })
        );

    constructor(private _projectService: ProjectService, private actions$: Actions) {

    }
}

projects.reducer.ts

import * as projectAction from './project.actions';

import { ProjectState } from './project.state';

export const initialState: ProjectState = {
    projects: [
        {id: 1, title: 'Project Alpha'}, {id: 2, title: 'Project Beta'}]
};

export function projectReducer(state = initialState, action: projectAction.Actions): ProjectState {
    switch (action.type) {
        case projectAction.ADD_PROJECT:
            return Object.assign({}, state, {
                projects: state.projects.concat(Object.assign([], state, action.project))
            });
        case projectAction.SELECT_ALL_PROJECTS:
            return Object.assign({}, state, action.projects);
        case projectAction.DELETE_PROJECT:
            return Object.assign({}, state, {
                projects: state.projects.filter(p => p.id !== action.project.id)
            });
        default:
            return state;
    }
}

projects.state.ts

import { Project } from '../../../models/Project/Project';

export interface ProjectState {
    projects: Project[];
}

Finally my service:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import {Project} from "../../../models/Project/Project";

@Injectable()
export class ProjectService {

    projects: Project[] = [{
        id: 1,
        title: 'Project Alpha'
    }];

    constructor() {
    }

    getAll(): Observable<Project[]> {
        return Observable.of(this.projects);
    }
}

So what i see on my screen are the 2 initial Projects and not the one project given by the project service. It seems like i do something wrong either in the service or in the reducer or maybe i am completely missing something else.

// UPDATE: I managed to get the effect working. Well at least the effect is called but now i am stuck i an infinit loop. The app won`t response and shows white screen

Maybe someone can help me with that My code in the calling component ist:

    import {DELETE_PROJECT, SELECT_ALL_PROJECTS} from '../../store/project.actions';

@Component({
    selector: 'project-dashboard',
    templateUrl: 'dashboard.component.html',
    styleUrls: ['dashboard.component.scss']
})

export class DashboardComponent implements OnInit {

    public async: any;

    projectState$: Observable<ProjectState>;

    constructor(private store: Store<any>) {
        // this.store.dispatch({type: SELECT_ALL_PROJECTS});
        this.projectState$ = this.store.select<ProjectState>(state => state.projectModule.projectReducer);
    }

    ngOnInit() {
        this.store.dispatch({type: SELECT_ALL_PROJECTS, projects: []});
    }

    delete(project: Project) {
        this.store.dispatch({type: DELETE_PROJECT, project: project})
    }
}

Every help is appreciated

Regards

1
You have lots of code but it's difficult to see exactly what you're asking. Can you be more specific, what isn't working? - Daniel B

1 Answers

0
votes

Your infinite loop is due to the effect dispatching the same action as it is observing to. You need to dispatch a SELECT_ALL_PROJECTS_COMPLETE or similar, which is then handled in your reducer.

I realize this is an old question, but perhaps it can help someone else, if you managed to fix this on your own :)