4

I'm using ngrx/store in my app and I'm having trouble saving an array. My array is set to empty at startup with:

export interface AppState{
    customObjects: Array<CustomObject>;
};

export const initialState: AppState = {
    customObjects: []
};

and I am trying to manually feed it an array of two CustomObjects using the following function:

loadCustomObjects() {
    let initialItems: CustomObject[] = [
        {
            directoryName: "d_name_1",
            brokenLinks: 0,
        },
        {
            directoryName: "d_name_2",
            brokenLinks: 0,
        }
    ];

    this.appStore.dispatch({ type: LOAD_CUSTOM_OBJECTS, payload: initialItems });
}

Calling this function dispatches a call to my reducer function:

export const mainReducer: ActionReducer = (state: AppState, action: Action) => {

if (typeof state == 'undefined') {
    state = initialState;
}

switch (action.type) {
    case LOAD_CUSTOM_OBJECTS:
        return Object.assign({}, action.payload)
    default: {
        return state;
    }
}

}

This all seems fine, but my main app component's array does not get the two objects. If I manually set the default state to have those two CustomObjects, then it works (I can't do this since I need to be able to update the array whenever I want). My HomeComponent uses:

customObjects$ Observable<Array<CustomObject>>;

constructor(private appStore: Store<AppState>) {
            appstore.select('mainReducer')
            .subscribe((data: AppState) => {

                if (typeof data != 'undefined') {
                    this.customObjects$ = Observable.of(data.customObjects);
                }
            });
}

I've analyzed my objects and I expect to see 'this.customObjects$' end up being an array of two CustomObjects. Instead, it is a ScalarOservable:

ScalarObservable {_isScalar: true, value: undefined, scheduler: null}, etc. etc.

Note - I'm not sure if I need to, but I have included ChangeDetectionStrategy.onPush in my home component's @Component section. I've also made sure to use

StoreModule.provideStore({ mainReducer })

in my app.module class.

Does anyone know what I'm doing wrong and why 'this.customObjects$' is not a two object array? I'm new to ngrx/store - I get the feeling there is an issue with the way my reducer function is setting the array but I'm not sure.

1 Answer 1

1

I was able to get it to work with this code:

Store related code:

export interface CustomObject {
  directoryName: string;
  brokenLinks: number;
}

export interface AppState {
  Main: CustomObject[];
};

export function MainReducer(state: CustomObject[] = [], action: Action) {

  switch (action.type) {
    case LOAD_CUSTOM_OBJECTS:
      return Object.assign({}, action.payload);
    default: {
      return state;
    }
  }
}

const reducers = {
  Main: MainReducer
};

Component/Service to dispatch/listen to store

customObjects: CustomObject[];

constructor(private appStore: Store<AppState>) {
  appStore.select(store => store.Main)
    .subscribe(objs => {
      this.customObjects = objs;
      console.log(this.customObjects);
    })
}


loadCustomObjects() {
  let initialItems: CustomObject[] = [
    {
      directoryName: "d_name_1",
      brokenLinks: 0,
    },
    {
      directoryName: "d_name_2",
      brokenLinks: 0,
    }
  ];

  this.appStore.dispatch({ type: LOAD_CUSTOM_OBJECTS, payload: initialItems });
}

Async pipe version:

customObjects$: Observable<CustomObject[]>;

constructor(private appStore: Store<AppState>) {
  this.customObjects$ = appStore.select(store => store.Main);
}


loadCustomObjects() {
  let initialItems: CustomObject[] = [
    {
      directoryName: "d_name_1",
      brokenLinks: 0,
    },
    {
      directoryName: "d_name_2",
      brokenLinks: 0,
    }
  ];

  this.appStore.dispatch({ type: LOAD_CUSTOM_OBJECTS, payload: initialItems });
}

<div *ngFor="let customObj of customObjects$ | async"></div>
Sign up to request clarification or add additional context in comments.

7 Comments

Is there a reason your component's customObjects array is NOT an observable?
It depends on how you want to use it. If you plan on using the async pipe, you can use the observable directly instead of manually subscribing to it. angular.io/api/common/AsyncPipe
Whenever you do appStore.select(store => store.Main) it returns an observable. I just have it immediately subscribe to the observable. Anyway if you are binding the observable directly to the UI you can use the async pipe version, I updated my answer with that approach as well.
This video does a good job explaining the async pipe: egghead.io/lessons/angular-2-using-the-async-pipe-in-angular-2
I tested the code and it's own this branch as reference for you: github.com/seescode/budget/tree/feature/stack-overflow I'm using node 7.7.0 and typescript ~2.2.2. I generally just steal the package.json settings from the example app so I don't deal with the dependency headaches: github.com/ngrx/example-app
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.