ngrx / platform

Reactive State for Angular
https://ngrx.io
Other
7.96k stars 1.95k forks source link

Cannot read property from Map that is a signal #4333

Closed dreamstar-enterprises closed 2 months ago

dreamstar-enterprises commented 2 months ago

Which @ngrx/* package(s) are the source of the bug?

signals, store

Minimal reproduction of the bug/regression with instructions

This is my Signal Store

When in the component I call this.filterModalStore.entireMap(), I can see the full map When in the component I call this.filterModalStore.selectedUnits(), it keeps returning undefined When in the component I call this.filterModalStore.filterModalMap()["contract-filter-units"], I see the property

I really cannot see where I'm going wrong...

/**********************************************************************************************************************/
/******************************************************* STORE ********************************************************/
/**********************************************************************************************************************/

/**********************************************************************************************************************/
/* State Type definitions */
/**********************************************************************************************************************/

type FilterModalsState = {
  filterModalMap: { [modalType: string]: any };
};

/**********************************************************************************************************************/
/* Initial State */
/**********************************************************************************************************************/

const initialState: FilterModalsState = {
  filterModalMap: <{ [modalType: string]: {} }>{},
};

/**********************************************************************************************************************/
/* NGRX Signal Store */
/**********************************************************************************************************************/

export const FilterModalStore = signalStore(
  {providedIn: 'root'},
  withState(initialState),

  withComputed((store) => {
    return {
      selectedUnits: computed(() => {
        const filterModalMap = store.filterModalMap()
        return filterModalMap["contract-filter-units"]
      }),
      selectedDepartments: computed(() => {
        return store.filterModalMap()["contract-filter-departments"]
      }),
      entireMap: computed(() => {
        const filterModalMap = store.filterModalMap()
        return filterModalMap
      }),
    }
  }),

  withMethods((store) => {
    return {
      updateFilterModalMap(modalType: string, modalData: any) {
        const updatedFilterModalMap = store.filterModalMap();
        updatedFilterModalMap[modalType] = modalData;
        patchState( store, { filterModalMap: updatedFilterModalMap})
      }
    }
  }),

  withHooks((store) => {
    return {
      onInit() {},
      onDestroy() {},
    };
  }),
);

/**********************************************************************************************************************/
/*************************************************** END OF ANGULAR ***************************************************/
/**********************************************************************************************************************/

Expected behavior

See above

Versions of NgRx, Angular, Node, affected browser(s) and operating system(s)

"@ngrx/operators": "^17.2.0", "@ngrx/signals": "^17.2.0",

Other information

No response

I would be willing to submit a PR to fix this issue

rainerhahnekamp commented 2 months ago

@dreamstar-enterprises, here's a link for a Stackblitz that is set up with the SignalStore. Please use that and paste the link here.

https://stackblitz.com/edit/ngrx-signal-store-starter

Would help a lot. Thanks!

dreamstar-enterprises commented 2 months ago

Thank you! I will try it out.

This works (it triggers when the nested array, selectedItemsState, changes)

selectedUnits: computed( () => {
     return filterModalStore.filterModalMap()
}),

I get, as expected this object, with a new state for the nested property, selectedItemsState.

{ "contract-filter-units": { "modalType": "contract-filter-units", "selectedItemsState": [ "Sound Unit" ], "cancelOrResetLabel": "Reset" } }

But the below doesn't work, when the nested array, selectedItemsState, changes...

selectedUnits: computed( () => {
   return filterModalStore.filterModalMap()["contract-filter-units"]
}),

And I'm not sure why... I want to try and get selectedItemsState, when this map changes,

type FilterModalState = {
  filterModalMap: { [modalType: string]: any };
};
dreamstar-enterprises commented 2 months ago

I gave up, and ultimately resorted to doing this, instead of trying to create a dynamic map (since right now deep signals are not supported)

const initialState: FilterModalState = {
  filterModalUnits: {
    selectedItemsState: [],
    cancelOrResetLabel: ''
  },
  filterModalDepartments: {
    selectedItemsState: [],
    cancelOrResetLabel: ''
  }
}
rainerhahnekamp commented 2 months ago

Could you please create a StackBlitz for it? Then we have something where we can collaborate and don't have to share code snippets. Btw. I think this shouldn't be an issue but should be moved to the Discussions panel.

dreamstar-enterprises commented 2 months ago

Somebody has already done a great job of answering why

store.Map()[key] wasn't triggering in a compute, when a nested item (like a nested array) changed. (My manual way, I'm still not sure why it didn't work)

But, after giving up, the next day (this morning) I then ungave up, and found this documentation: https://ngrx.io/guide/signals/signal-store/entity-management

It worked great, when you have data in Maps. (somehow the library takes care of any deep signalling changes)