I implemented a ngrx solution and I am trying to display some objects named bookmarks in my project.
I don't know why my bookmarks are not beeing displayed on storybook even thought everything works perfectly :
These are my actions :
import {Action} from '@ngrx/store';
import { Bookmark } from '../models/bookmark';
export enum BookmarkActionsTypes {
GET_BOOKMARKS = 'GET_BOOKMARKS',
GET_BOOKMARKS_SUCCESS = 'GET_BOOKMARKS_SUCCESS',
GET_BOOKMARKS_ERROR = 'GET_BOOKMARKS_ERROR',
DELETE_BOOKMARK = 'DELETE_BOOKMARK',
DELETE_BOOKMARK_SUCCESS = 'DELETE_BOOMARK_SUCCESS',
DELETE_BOOKMARK_ERROR = 'DELETE_BOOKMARK_ERROR',
}
export class GetBookmarks implements Action{
readonly type = BookmarkActionsTypes.GET_BOOKMARKS;
}
export class GetBookmarksSuccess implements Action{
readonly type = BookmarkActionsTypes.GET_BOOKMARKS_SUCCESS;
constructor(public payload : Bookmark[]){}
}
export class GetBookmarksError implements Action{
readonly type = BookmarkActionsTypes.GET_BOOKMARKS_ERROR;
constructor(public payload : string){}
}
export class DeleteBookmark implements Action{
readonly type = BookmarkActionsTypes.DELETE_BOOKMARK;
constructor(public payload : number){}
}
export class DeleteBookmarksSuccess implements Action{
readonly type = BookmarkActionsTypes.DELETE_BOOKMARK_SUCCESS;
}
export class DeleteBookmarksError implements Action{
readonly type = BookmarkActionsTypes.DELETE_BOOKMARK_ERROR;
constructor(public payload : string){}
}
export type BookmarkActions = GetBookmarks | GetBookmarksSuccess | GetBookmarksError | DeleteBookmark | DeleteBookmarksSuccess | DeleteBookmarksError ;
These are my reducers :
import { createEntityAdapter, EntityAdapter, EntityState } from "@ngrx/entity";
import { Bookmark} from "../models/bookmark";
import { BookmarkActions, BookmarkActionsTypes } from "./actions";
export interface State extends EntityState<Bookmark>{
error:string;
}
export const bookmarkAdapter:EntityAdapter<Bookmark> = createEntityAdapter<Bookmark>();
const defaultBookmarks = {
error:''
}
const initialState : State = bookmarkAdapter.getInitialState(defaultBookmarks);
export function BookmarkReducer(state:State=initialState, action : BookmarkActions):State{
switch(action.type){
case BookmarkActionsTypes.GET_BOOKMARKS:
return {...state, error:''};
case BookmarkActionsTypes.GET_BOOKMARKS_SUCCESS:
return bookmarkAdapter.setAll(action.payload,{...state, error:''});
case BookmarkActionsTypes.GET_BOOKMARKS_ERROR:
return {...state, error:action.payload};
case BookmarkActionsTypes.DELETE_BOOKMARK:
return bookmarkAdapter.removeOne(action.payload,{...state, error:''});
case BookmarkActionsTypes.DELETE_BOOKMARK_SUCCESS:
return {...state, error:''};
case BookmarkActionsTypes.DELETE_BOOKMARK_ERROR:
return {...state, error:action.payload};
default:{return state;}
}
}
These are my selectors :
import { createFeatureSelector, createSelector } from "@ngrx/store";
import { Bookmark } from "../models/bookmark";
import { State, bookmarkAdapter } from "./reducers";
import {EntitySelectors, EntityState} from '@ngrx/entity/src/models';
export const getBookmarkState = createFeatureSelector<State>('angular');
export const {
selectAll: selectAllBookmarks,
}:EntitySelectors<Bookmark, EntityState<Bookmark>> = bookmarkAdapter.getSelectors();
export const selectAll = createSelector(getBookmarkState, selectAllBookmarks);
export const selectError = createSelector(getBookmarkState, (state:State):string=>state.error);
And these are my effects :
import { Actions, ofType } from "@ngrx/effects";
import { catchError, map, Observable, of, switchMap } from "rxjs";
import { BookmarksService } from "../services/bookmarks-service.service";
import {Action} from '@ngrx/store';
import * as fromTodoActions from './actions';
import { Bookmark } from "../models/bookmark";
import { Injectable } from "@angular/core";
@Injectable({
providedIn:'root'
})
export class BookmarksEffects {
getBookmarks$:Observable<Action>=this.actions$.pipe(
ofType(fromTodoActions.BookmarkActionsTypes.GET_BOOKMARKS),
switchMap(()=>this.todoService.getBookmarks().pipe(
map((bookmarks:Bookmark[]) => new fromTodoActions.GetBookmarksSuccess(bookmarks)),
catchError((err:string)=>of(new fromTodoActions.GetBookmarksError(err))
))
)
)
deleteTodo$:Observable<Action>=this.actions$.pipe(
ofType(fromTodoActions.BookmarkActionsTypes.DELETE_BOOKMARK),
switchMap((action)=>{return this.todoService.deleteBookmark(action.payload).pipe(
map((bookmark:Bookmark[]) => new fromTodoActions.DeleteBookmarksSuccess()),
catchError((err:string)=>of(new fromTodoActions.DeleteBookmarksError(err))
))}
)
)
constructor(private actions$ : Actions<fromTodoActions.BookmarkActions>, private todoService : BookmarksService){}
}
I've created a json server and I am trying to display the contents of this file named bookmarks.json :
{
"bookmarks": [
{
"id":123456,
"name":"Zeke",
"code":"93ac3Walk3r"
},
{
"id":242668,
"name":"Rex",
"code":"ShadowMoses"
},
{
"id":849761,
"name":"Ray",
"code":"Tanker"
}
]
}
Finally this is the component.ts where I am trying to display the bookmarks :
import { EventEmitter, Output, Input } from '@angular/core';
import { Component} from '@angular/core';
import { Bookmark } from '../../models/bookmark';
import { BookmarksService } from '../../services/bookmarks-service.service';
import {MatSnackBar} from '@angular/material/snack-bar';
import * as fromBookmarkReducers from '../../store/reducers';
import * as fromBookmarkActions from '../../store/actions';
import * as fromBookmarkSelectors from '../../store/selectors';
import { select, Store } from '@ngrx/store';
@Component({
selector: 'bookmark-bookmark-list',
templateUrl: './bookmark-list.component.html',
styleUrls: ['./bookmark-list.component.scss']
})
export class BookmarkListComponent{
bookmarks$ : Observable<Bookmark[]>;
state=false;
constructor(private bookmarksService : BookmarksService,private _snackBar: MatSnackBar, private store:Store<fromBookmarkReducers.State>) {
this.bookmarks$ = this.store.pipe(select(fromBookmarkSelectors.selectAll))
this.store.dispatch(new fromBookmarkActions.GetBookmarks());
}
@Output() click: EventEmitter<void> = new EventEmitter();
onDelete(bookmark:Bookmark) {
this.store.dispatch(new fromBookmarkActions.DeleteBookmark(bookmark.id));
}
onclick(){
this.click.emit();
}
@Input()
backgroundColor?:string;
@Input()
Color: 'white' | 'black';
}
And this is its template :
<div >
<div [ngStyle]="{'background-color':backgroundColor}">
<h3>Bookmarks</h3>
<mat-nav-list role="navigation">
<p *ngIf="state">No bookmarks...</p>
<mat-list-item *ngFor="let bookmark of bookmarks$ |async">
<a matLine [ngStyle]="{'color':Color}" (click)="onclick()">{{bookmark.name}} , {{bookmark.code}}, {{bookmark.id}}</a>
<button mat-icon-button (click)="onDelete(bookmark)">
<mat-icon>delete_outline</mat-icon>
</button>
</mat-list-item>
</mat-nav-list>
</div>
Oh yes ! And this is the service (almost forgot) :
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable} from 'rxjs';
import { Bookmark } from '../models/bookmark';
@Injectable({
providedIn: 'root'
})
export class BookmarksService {
apiURL = 'http://localhost:3000/bookmarks/';
constructor(private http : HttpClient) { }
getBookmarks():Observable<Bookmark[]>{
return this.http.get<Bookmark[]>(this.apiURL)
}
deleteBookmark(id:number){
return this.http.delete<any>(this.apiURL id)
}
}
And I had forgot the library module :
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {MatSidenavModule} from '@angular/material/sidenav';
import {MatIconModule} from '@angular/material/icon';
import { BookmarkListComponent } from './components/bookmark-list/bookmark-list.component';
import { RouterModule, Routes } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { BookmarksService } from './services/bookmarks-service.service';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {MatButtonModule} from '@angular/material/button';
import {MatListModule} from '@angular/material/list';
import {MatSnackBar, MatSnackBarModule} from '@angular/material/snack-bar';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { BookmarksEffects } from './store/effects';
import { BookmarkReducer } from './store/reducers';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
const routes : Routes = [
{path:'', component:BookmarkListComponent}
]
const material = [
MatSidenavModule,
MatIconModule,
MatButtonModule,
MatListModule,
MatSnackBarModule,
];
@NgModule({
imports: [
CommonModule,
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
...material,
RouterModule.forRoot(routes, {initialNavigation: 'enabled'}),
StoreModule.forRoot({angular:BookmarkReducer}),
EffectsModule.forRoot([BookmarksEffects]),
StoreDevtoolsModule.instrument({maxAge:10}),
],
declarations: [
BookmarkListComponent,
],
providers: [BookmarksService, HttpClientModule],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
exports: []
})
export class LibModule {}
CodePudding user response:
In you effect file the effects must be nested in the createEffect() function.
Like this :
getBookmarks$:Observable<Action> = createEffect(() =>
this.actions$.pipe(
ofType(fromTodoActions.BookmarkActionsTypes.GET_BOOKMARKS),
switchMap(()=>this.todoService.getBookmarks().pipe(
map((bookmarks:Bookmark[]) => new fromTodoActions.GetBookmarksSuccess(bookmarks)),
catchError((err:string)=>of(new fromTodoActions.GetBookmarksError(err))
))
)
)
);
This should do the trick. The rest of your code looks ok.