Search code examples
angulartypescriptngrx

Can't get initial state, error: Type '{ workouts: Workout[]; } | null' is not assignable to type 'NgIterable<any> | null | undefined'


I am trying to display the initial state of my application on the page. But when I do a for loop over the state data, I am getting an error Object is possibly 'null'.

Several steps forward: the *ngFor looks like this now *ngFor="let workout of (workouts | async).workouts" . And when I delete .workouts the error turns to be Type '{ workouts: Workout[]; } | null' is not assignable to type 'NgIterable<any> | null | undefined'

So I assume that my application can't fetch the initial state for some reason. I checked the models, all types of data are correct. I also Googled for this: Type 'object' is not assignable to type 'NgIterable<any> | null | undefined' But it seems like I am doing everything right.

Now the code:

workout.model.ts

import { Exercise } from "./exercise.model"

export interface Workout {
  name: string,
  exercises: Exercise[]
}

exercise.model.ts

export interface Exercise {
  name: string,
  completed: boolean
}

actions.ts

import { Action } from "@ngrx/store"
import { Workout } from "../models/workout.model"

export const ADD_WORKOUT       = 'ADD_WORKOUT'
export const REMOVE_WORKOUT    = 'REMOVE_WORKOUT'
export const UPDATE_WORKOUT    = 'UPDATE_WORKOUT'

export class AddWorkout implements Action {
  readonly type = ADD_WORKOUT

  constructor(public payload: Workout) {}
}

export type Actions = AddWorkout

reducer.ts

import * as WorkoutActions from "../actions/tasks.actions"

const initialState = {
  workouts: [{
    name: "Day 1",
    exercises: [{
      name: "push-up",
      completed: false
    }]
  }]
}

export function workoutsReducer(state = initialState, action: WorkoutActions.Actions) {
  switch(action.type) {
    case WorkoutActions.ADD_WORKOUT:
      return {
        ...state,
        workouts: [...state.workouts, action.payload]
       };
    default:
      return state;
  }
}

app.module.ts

  imports: [
    BrowserModule,
    StoreModule.forRoot({
      workoutsList: workoutsReducer
    } as ActionReducerMap<any,any>)
  ],

workouts-list.component.ts

import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { Workout } from '../models/workout.model';

@Component({
  selector: 'app-workouts-list',
  templateUrl: './workouts-list.component.html',
  styleUrls: ['./workouts-list.component.scss']
})
export class WorkoutsListComponent implements OnInit {

  workouts!: Observable<{workouts: Workout[]}>;

  constructor(
    private store: Store<{workoutsList: {workouts: Workout[]}}>
  ) { }

  ngOnInit(): void {
    this.workouts = this.store.select('workoutsList')
  }

}

workouts-list.component.html

<div *ngFor="let workout of (workouts | async).workouts">
  <div>{{workout.name}}</div>
</div>

Solution

  • I avoid getting the Object is possibly 'null'. error by using the optional chaining operator.

    Try using it after workouts in the for loop.