I am new to NgRX store. I am facing problem in getting tickets data into ticket component using NgRx store.
I used effects module to retrieve async tickets through http get method in ticketService
I am not sure how to configure my store and selector to get the ticket details. Providing stackblitz url to reproduce the issue. Thanks in advance.
https://stackblitz.com/edit/ngrx-demo-ticket
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule} from '@angular/common/http';
import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
import { TicketsComponent } from './tickets/tickets.component';
import { TicketsService } from './tickets.service';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { environment } from './environment/environment';
import {TicketReducer} from './store/ticket.reducers';
import {TicketEffects} from './store/ticket.effects';
@NgModule({
imports: [ BrowserModule, FormsModule, HttpClientModule,
StoreModule.forRoot(TicketReducer, {}),
EffectsModule.forRoot([TicketEffects]),
!environment.production ? StoreDevtoolsModule.instrument() : [],
],
declarations: [ AppComponent, HelloComponent, TicketsComponent ],
bootstrap: [ AppComponent ],
providers: [TicketsService]
})
export class AppModule { }
tickets.component.ts
import { Component, OnInit } from '@angular/core';
import { TicketsService } from '../tickets.service';
import { of, Observable, pipe } from 'rxjs';
import { Ticket } from '../models/ticket';
import { map, catchError, tap } from 'rxjs/operators';
import { Store, select } from '@ngrx/store';
import { AppState } from '../models/app.state';
import { selectTickets } from '../store/ticket.reducers';
import * as ticketActions from '../store/ticket.actions';
@Component({
selector: 'app-tickets',
templateUrl: './tickets.component.html',
styleUrls: ['./tickets.component.css']
})
export class TicketsComponent implements OnInit {
tickets$: Observable<Ticket[]>;
constructor(private store: Store<AppState>) {
this.tickets$ = this.store.select(pipe(selectTickets));
}
ngOnInit() {
this.store.dispatch(new ticketActions.loadTicketsAction());
this.tickets$.pipe(
// Getting tickets as undefined
tap(tickets => console.log('Store Tickets', tickets)),
map((data) => {
if (data && data !== null) {
data[6].assignee = '<a href="javascript:void(0);" class="btn btn-link">John Mike</a>';
return data;
}
}),
catchError(error => {
return of([]);
})
).subscribe(tickets => {
console.log('Store tickets', tickets)
});
}
}
app.state.ts
import { Ticket } from '../models/ticket';
export interface AppState {
readonly tickets: Ticket[];
}
ticket.ts
export interface Ticket {
id: string;
applicationName?: string;
category?: string;
subject: string;
description?: string;
status: string;
priority: string;
assignee: string;
platform?: string;
dueDate: string;
}
ticket.action.ts
import { Action } from '@ngrx/store';
import * as types from './action.types';
import { Ticket } from '../models/ticket';
export class loadTicketsAction implements Action {
readonly type = types.LOAD_TICKETS;
}
export class loadTicketsSuccessAction implements Action {
readonly type = types.LOAD_TICKETS_SUCCESS;
constructor(public payload: Ticket[]) {}
}
export type Actions =
loadTicketsAction |
loadTicketsSuccessAction;
ticket.reducers.ts
import * as ticketActions from './ticket.actions';
import * as types from './action.types';
import { AppState } from '../models/app.state';
export const initialState: AppState = {
tickets: []
}
export function TicketReducer(state = initialState, action: ticketActions.Actions):AppState {
switch(action.type) {
case types.LOAD_TICKETS_SUCCESS: {
return {...state, tickets: action.payload };
}
default:
return state;
}
}
export const selectTickets = (state: AppState) => state.tickets;
ticket.effects.ts
import { Injectable } from "@angular/core";
import { TicketsService } from "../tickets.service";
import { Effect, Actions, ofType } from '@ngrx/effects';
import { map, mergeMap } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { Action } from '@ngrx/store';
import * as ticketActions from './ticket.actions';
import * as types from './action.types';
@Injectable({
providedIn: 'root'
})
export class TicketEffects {
constructor(private ticketsService: TicketsService,
private actions$: Actions
) { }
@Effect() loadTickets$: Observable<Action> = this.actions$.pipe(
ofType<ticketActions.loadTicketsAction>(types.LOAD_TICKETS),
mergeMap(() => this.ticketsService.getAllTickets().pipe(
map(tickets => {
return (new ticketActions.loadTicketsSuccessAction(tickets))
})
))
)
}
The problem seems to be with your effects. You need to dispatch the action and not return it. So instead of:
map(tickets => {
return (new ticketActions.loadTicketsSuccessAction(tickets))
})
do this:
map(tickets => {
this.store.dispatch(new ticketActions.loadTicketsSuccessAction(tickets))
})
Also add this to your selector and make sure to import 'selectTickets$'. If you want to get data from the store, you can use the following pattern:
export const selectTicketsState = createFeatureSelector<TicketsState>('tickets');
export const getTickets = (state: AppState) => state.tickets;
export const selectTickets$ = createSelector(selectTicketsState, getTickets);
EDIT:
Ok I modified your stackblitz for it to work, honestly I made a lot of changes there so I can't fit them all in. A lot of things were broken/done the wrong way:
https://stackblitz.com/edit/ngrx-demo-ticket-uh4hnw
I recommend watching a tutorial, following him for the first time, then after getting the hang of it experimenting more.
I also recommend going through my Stackblitz then yours so you can compare and see what was wrong.
Good luck! I recommend