I am facing this error :
TypeError: entities is not iterable
I don't know where it is coming from since the project works fine with json web server but not with mockoon.
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);
These are my effects :
import { Actions, createEffect, 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>=createEffect(() =>
this.actions$.pipe(
ofType(fromTodoActions.BookmarkActionsTypes.GET_BOOKMARKS),
switchMap(()=>this.bookmarksService.getBookmarks().pipe(
map((bookmarks:Bookmark[]) => new fromTodoActions.GetBookmarksSuccess(bookmarks)),
catchError((err:string)=>of(new fromTodoActions.GetBookmarksError(err))
))
)
))
deleteTodo$:Observable<Action>=createEffect(() =>
this.actions$.pipe(
ofType(fromTodoActions.BookmarkActionsTypes.DELETE_BOOKMARK),
switchMap((action)=>{return this.bookmarksService.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 bookmarksService : BookmarksService){}
}
This is my service :
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:3002/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)
}
}
This is my 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';
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
const routes : Routes = [
{path:'', component:BookmarkListComponent}
]
const material = [
MatSidenavModule,
MatIconModule,
MatButtonModule,
MatListModule,
MatSnackBarModule,
MatSlideToggleModule
];
@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 {}
This is my component logic (.ts) :
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 { MatSlideToggleChange } from '@angular/material/slide-toggle';
import * as fromBookmarkReducers from '../../store/reducers';
import * as fromBookmarkActions from '../../store/actions';
import * as fromBookmarkSelectors from '../../store/selectors';
import { select, Store } from '@ngrx/store';
import { Observable } from 'rxjs';
@Component({
selector: 'bookmark-bookmark-list',
templateUrl: './bookmark-list.component.html',
styleUrls: ['./bookmark-list.component.scss']
})
export class BookmarkListComponent{
bookmarks$ : Observable<Bookmark[]>;
isDisplayed=true;
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));
this._snackBar.open("You have deleted the bookmark : " bookmark.SNS, "close", { duration: 3000});
}
onclick(){
this.click.emit();
}
toggleChanges($event: MatSlideToggleChange) {
this.isDisplayed = $event.checked;
}
}
And finally this is the template :
<div >
<div >
<mat-nav-list role="navigation">
<mat-slide-toggle [checked]="isDisplayed" (change)="toggleChanges($event)">Display DMC</mat-slide-toggle>
<div mat-subheader>Bookmarks</div>
<div mat-subheader *ngIf="(bookmarks$ | async).length ===0">No Bookmarks...</div>
<mat-list-item *ngFor="let bookmark of bookmarks$ | async" (click)="onclick()">
<a matLine>{{bookmark.SNS}}</a>
<a matLine >{{bookmark.title}}</a>
<a matLine *ngIf="isDisplayed">{{bookmark.DMC}}</a>
<button mat-icon-button (click)="onDelete(bookmark)">
<mat-icon>delete_outline</mat-icon>
</button>
</mat-list-item>
</mat-nav-list>
</div>
I am using Mockoon to create a fake rest API. This is the Get route :
And this is the json file I am using for testing :
"bookmarks": [
{
"id":123456,
"SNS":"Zeke",
"title":"93ac3Walk3r",
"DMC":"test"
},
{
"id":242668,
"SNS":"Rex",
"title":"ShadowMosesIsland",
"DMC":"blablablablablablablablabla"
},
{
"id":849761,
"SNS":"Ray",
"title":"Tanker",
"DMC":"beaucoup de mots"
}
]
}
CodePudding user response:
It seems that your service expects an array. Mockon provides an Object.
Either change the JSON to
[
{
"id":123456,
"SNS":"Zeke",
"title":"93ac3Walk3r",
"DMC":"test"
},
{
"id":242668,
"SNS":"Rex",
"title":"ShadowMosesIsland",
"DMC":"blablablablablablablablabla"
},
{
"id":849761,
"SNS":"Ray",
"title":"Tanker",
"DMC":"beaucoup de mots"
}
]
}
or use a map function in your http-service:
getBookmarks():Observable<Bookmark[]>{
return this.http.get<Bookmark[]>(this.apiURL).pipe(
map(res=>res=res['bookmarks'])
)
}