Home > Mobile >  TypeError: entities is not iterable
TypeError: entities is not iterable

Time:06-13

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 :

Mockoon Screenshot

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'])
    )
  }
  • Related