Search code examples
angulartypescriptngrxtypescript2.0ngrx-store

Why the change in state of store is not broadcasted to my component


When onMetaSignalChanged() is called in the component, it causes an effect which in turn calls the reducer case rangeSchemasLoadCompletedSuccess. I have debugged the App and its working fine till that point. As you can see rangeSchemasLoadCompletedSuccess updating the rangeSchemas of store state, so I think my rangeSchemas$ observable should get the updated value after that? But its not being updated, Am I missing something?

Model Class:

export interface RangeSchemaViewModel {
 id: number;
 rangeSchemaName: string;
}

In my component I'm selecting the store state as:

export class MyComponent implements OnInit, OnDestroy {
  rangeSchemas$: Observable<RangeSchemaViewModel[]>;

  ngOnInit() {
    this.rangeSchemas$ = this.store.pipe(
      select(RawSignalsStoreSelectors.selectRangeSchemasViewModel)
    );
  }

   onMetaSignalChanged(event: any) {
     this.store.dispatch(
       RawSignalsStoreActions.changeMetaSignal({ id: event.meta_Signal_Id })
     );
   }

  //onmetaSignalChanged, Action is called which is updating the state of rangeSchemas$
}

Here is my selector code:

const getRangeSchemas = (state: State): RangeSchema[] => state.rangeSchemas;

export const selectRangeSchemasState: MemoizedSelector<
  object,
  State
> = createFeatureSelector<State>('raw-signals');

export const selectRangeSchemasViewModel: MemoizedSelector< 
  object,
  RangeSchemaViewModel[]
> = createSelector(
    selectRangeSchemasState,
    getRangeSchemas,
    (state, rangeSchemas) => {
        return rangeSchemas.map((item) =>{
            let rangeSchemaViewModel: RangeSchemaViewModel;
            rangeSchemaViewModel.id= item.mapping.id;
            rangeSchemaViewModel.rangeSchemaName= item.mapping.rangeSchemaName;
            return rangeSchemaViewModel;
        })
    });

Reducer Code:

on(rawSignalsActions.changeMetaSignal, (state, { id }) => ({
...state,
linkedMetaSignal: state.metaSignals.filter(item => item.id === id)[0]
 })),
on(rawSignalsActions.rangeSchemasLoadCompletedError, state => ({
    ...state,
    loading: { ...state.loading, rangeSchemas: false }
 })),
on(rawSignalsActions.rangeSchemasLoadCompletedSuccess,
    // tslint:disable-next-line: no-shadowed-variable
    (state, { rangeSchemas }) => ({
      ...state,
      rangeSchemas,
      loading: { ...state.loading, rangeSchemas: false }
    })
  )

Effects Code:

  getRangeSchemas$ = createEffect(() =>
  this.actions$.pipe(
      ofType(RawSignalsActions.changeMetaSignal),
      concatMap(action => this.rawSignalsDataService.getRangeSchemas(action.id).pipe(
        map(rs1 => this.constructSignalCounts(rs1)),
        map(rs2 => RawSignalsActions.rangeSchemasLoadCompletedSuccess( { rangeSchemas: rs2 })),
          catchError(err => of(RawSignalsActions.rangeSchemasLoadCompletedError({ error: err })))
      )),
  ));

  constructSignalCounts = ( rangeSchemas: RangeSchema[]) => {
    for (var rangeSchema of rangeSchemas){
        let signalCounts: SignalCount[] = [];
        for (var abstractSignal of rangeSchema.abstractSignals) {
            let signalCount: SignalCount = { range: "0", populationSize: 0, populationPerc: 0, lowRange: 0, highRange:0 };
            signalCount.range = abstractSignal.abstractSignal.name;
            signalCount.populationSize = 20;
            signalCount.populationPerc = 40;
            if (rangeSchema.mapping.type == 2) {    // 2 -> numeric, 3 -> Text
              // actually type will come from Signal Range Schema
              signalCount.lowRange = abstractSignal.numericMappings[0].minValue;
              signalCount.highRange = abstractSignal.numericMappings[0].maxValue;
            }
            signalCounts.push(signalCount);
          }
          rangeSchema.signalCounts = signalCounts;

    }
    return rangeSchemas;
  };

Solution

  • After a struggle of almost 2 days I found the answer! Here is the selector code which fixed the issue:

    export const selectRangeSchemasState: MemoizedSelector<
      object,
      State
    > = createFeatureSelector<State>('raw-signals');
    
    const getRangeSchemas = (state: State): RangeSchema[] => state.rangeSchemas;
    
    export const selectRangeSchemasViewModel: MemoizedSelector<
    object,
      RangeSchemaViewModel[]
    > = createSelector(
      selectRangeSchemasState,
      getRangeSchemas,
      (state, rangeSchemas) => {
        return state.rangeSchemas.map(item => {
          let rangeSchemaViewModel: RangeSchemaViewModel = {
            id: 0,
            rangeSchemaName: ''
          };
          rangeSchemaViewModel.id = item.mapping.id;
          rangeSchemaViewModel.rangeSchemaName = item.mapping.rangeSchemaName;
          return rangeSchemaViewModel;
        });
      }
    );
    

    The reasons of problem are following:

    Its the global state from which I should construct the view Model i.e.,

    return state.rangeSchemas.map(item => {...
    

    rangeSchemaViewModel variable was undefined so I instantiated it with default values.

    let rangeSchemaViewModel: RangeSchemaViewModel = {
        id: 0,
        rangeSchemaName: ''
      };