0
votes

I have a scenario where I want to display some array (eg: list of files)when component is loaded using NGRX.

So what I have done is created two actions as below

  1. GET_FILES => GetFiles as class

  2. SET_FILES => SetFiles as class

and an reducer maintaining the state like

export interface State {
  files: Files[];
}

const initialState: State = {
  files: []
};
 
export function filesReducer(
  state: State = initialState,
  action: FilesAction.FilesAction
) {
  switch (action.type) {
    case FilesAction.GET_FILES:
      return {
        ...state
      };
    case FilesAction.SET_FILES:
      const files: Files[] = action.payload;
      return {
        ...state,
        ...files
      };
    default:
      return state;
  }
}

Then there will be an effect that gets makes an rest api call to get the list of files and then effect will dispatch SET_FILES action.

@Effect()
getFiles = this.action$.pipe(
ofType(FilesAction.GET_FILES),
switchMap(() => {
  return this.http.get<Files[]>(environment.baseURI + 'api/get-files');
}),
map(filesState => {
  return new FilesAction.SetFiles(filesState);
})
);

And when component is loaded on ngOnInit we can dispatch an action of SET_FILES which inturn executes an effect which is then responsible to dispatch another action of SET_FILES.

export class FileListComponent implements OnInit {
  iFileId: number = 0;
  fileList: Files[] = [];
  constructor(private store: Store<fromApp.AppState>) {
 
    //Problem Area
    this.store.select('files').subscribe(files => {
      this.fileList = files.files;
      console.log('FileList:');
      console.log(this.fileList);
    });
  }
 
  ngOnInit(): void {
    this.store.dispatch(new FilesActions.GetFiles(this.iFileId));
  }
}

Here, the problem is i am successfully able to dispatch action on ngOnInit and if i console.log then i will get list of files but how do i store that in a variable like i am trying to do in constructor ?

1
dont subscribe in your component use async pipeenno.void
@enno.void - Thanks for your response. I tried to get values using async pipe fileList: Observable<{ files: Files[] }>; constructor(private store: Store<fromApp.AppState>) { this.fileList = this.store.select('files'); } <p *ngIf="fileList" *ngFor="let file of (fileList | async).files">{{ file }}</p> . But since there is no value in fileList i get compile time error.Ronak

1 Answers

0
votes

In your component ts store a reference to the selector result in an observable:

export class FileListComponent implements OnInit {
  iFileId: number = 0;
  fileList$ = this.store.select('files');  // <<<-- add this

  constructor(private store: Store<fromApp.AppState>) {}
 
  ngOnInit(): void {
    this.store.dispatch(new FilesActions.GetFiles(this.iFileId));
  }
}

In your template:

 <p *ngIf="fileList$ | async as fileList" *ngFor="let file of fileList.files">
   {{ file }}
 </p>

Note the ngIf.. as syntax to subscribe and check the emitted value is truthy - you can then safely use fileList in the ngFor.

Other recommendations

  1. You do not need the FilesAction.GET_FILES case in your reducer, as it does not alter the state.

  2. Name your reducers after the related events that triggered them (see https://2018.ng-conf.org/sessions/good-action-hygiene-ngrx/ 4:30):

   GET_FILES => FILES_REQUESTED  
   SET_FILES => FILES_LOADED