Saksham
Saksham

Reputation: 9380

Subscription to the NgRx store returns empty object

I am trying to develop a a simple angular app using NgRx to maintain state.

Unfortunately, the callback to the subscription to the store gets fired but always returns an empty object.

I am trying to dispatch the object to store as

this._store.dispatch({
  type: 'ShowPassword',
  payload: value
})

and a look at the reducer function

export function showPasswordReducer(state, action) {
  //console.log(state);
    switch (action.type) {

    case 'ShowPassword':
      return {
        ...state,
        'ShowPassword': action.payload
      }
    break;
    default:
        return state;
    }
}

I am adding the reference of StoreModule in the imports array of root module as

StoreModule.forRoot(showPasswordReducer)

and subscribing to the store as

this._store.subscribe(val => {
    if (val)
        this.showPassword = val.ShowPassword;
})

Stackblitz link: https://stackblitz.com/edit/angular-first-ngrx-demo

Upvotes: 1

Views: 2456

Answers (2)

AVJT82
AVJT82

Reputation: 73357

You are using ngrx 8, so you should embrace that syntax, which is also cleaner in my opinion. We now have access to createReducer and createAction. So I suggest the following:

import { createAction, createReducer, on, } from '@ngrx/store';

export const showPwd = createAction(
  'Show Password',
  ({ showPw }: { showPw: boolean }) => ({
    showPw,
  })
)

const initialState = {
  showPw: false
};

export const showPasswordReducer = createReducer(
  initialState.showPw,
    // here you would probably want to have the action(s) in a separate file
    on(this.showPwd, (state: any, action: any) => {
    return action.showPw;
  }),
)

export function reducer(state: any | undefined, action: any) {
  return showPasswordReducer(state, action);
}

Then remember to mark to app.module imports:

StoreModule.forRoot({showPwd: showPasswordReducer})

Then finally in the component where you dispatch the action and listen to store:

ngOnInit() {
  this._store.subscribe(val => {
    if (val && val.showPwd)
      this.showPassword = val.showPwd;
  })
}

ToggleCheckbox(value: boolean) {
  this._store.dispatch(showPwd({showPw: value}))
}

Your forked STACKBLITZ

Upvotes: 2

user2216584
user2216584

Reputation: 5602

There are a couple of basic NGRX pieces missing in your code -

Let's deal with them one by one:

a) You must have an initial state [I am assuming that you want to have a state which tracks a boolean called showPassword]. Define an initial state object like this:

export const initialState = {
  showPassword: false
};

b) Setup your reducer to make use of initial state like this:

export function showPasswordReducer(state = initialState, action) {
  //console.log(state);
  switch (action.type) {
    case 'ShowPassword':
      return {showPassword: action.payload};
      break;
    default:
      return state;
  }
}

Notice that in case of default action reducer will return the initial state.

c) Now inject the reducer in forRoot method with the state name like this:

@NgModule({
  imports: [BrowserModule, FormsModule, StoreModule.forRoot({ShowPassword: showPasswordReducer})],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})

d) Now subscribe to the store [Ideally You MUST have selectors to get the piece of information from the store but to keep things simple, simply subscribe to the store directly and look for the same property which was used in the forRoot like this:

ngOnInit() {
    this._store.subscribe(val => {
      console.log(val);
      if (val)
        this.showPassword = val.ShowPassword.showPassword;
    })
  }

Working stackblitz - https://stackblitz.com/edit/angular-first-ngrx-demo-7yrl2r?file=src/app/app.module.ts

Hope it helps.

Upvotes: 2

Related Questions