diff --git a/modules/store/spec/integration.spec.ts b/modules/store/spec/integration.spec.ts index f9998590e0..d6fd04a6d6 100644 --- a/modules/store/spec/integration.spec.ts +++ b/modules/store/spec/integration.spec.ts @@ -243,6 +243,42 @@ describe('ngRx Integration spec', () => { payload: { id: 2 }, }); }); + + it('should use the props in the projector to get a todo', () => { + const getTodosState = createFeatureSelector<TodoAppSchema, Todo[]>( + 'todos' + ); + + const getTodosById = createSelector( + getTodosState, + (todos: Todo[], { id }: { id: number }) => + todos.find(todo => todo.id === id) + ); + + let testCase = 1; + const todo$ = store.pipe(select(getTodosById, { id: 2 })); + todo$.subscribe(todo => { + if (testCase === 1) { + expect(todo).toEqual(undefined); + } else if (testCase === 2) { + expect(todo).toEqual({ + id: 2, + text: 'second todo', + completed: false, + }); + } else if (testCase === 3) { + expect(todo).toEqual({ id: 2, text: 'second todo', completed: true }); + } + testCase++; + }); + + store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } }); + store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } }); + store.dispatch({ + type: COMPLETE_TODO, + payload: { id: 2 }, + }); + }); }); describe('feature state', () => { diff --git a/modules/store/spec/selector.spec.ts b/modules/store/spec/selector.spec.ts index b6f62e8ea2..75dd01acb0 100644 --- a/modules/store/spec/selector.spec.ts +++ b/modules/store/spec/selector.spec.ts @@ -137,7 +137,9 @@ describe('Selectors', () => { ); selector({}, { value: 47 }); - expect(projectFn).toHaveBeenCalledWith(countOne, countTwo, 47); + expect(projectFn).toHaveBeenCalledWith(countOne, countTwo, 47, { + value: 47, + }); }); it('should be possible to test a projector fn independent from the selectors it is composed of', () => { @@ -363,7 +365,9 @@ describe('Selectors', () => { projectFn )({}, { value: 47 }); - expect(projectFn).toHaveBeenCalledWith(countOne, countTwo, 47); + expect(projectFn).toHaveBeenCalledWith(countOne, countTwo, 47, { + value: 47, + }); }); it('should be possible to test a projector fn independent from the selectors it is composed of', () => { diff --git a/modules/store/src/selector.ts b/modules/store/src/selector.ts index 1f42136531..95c57200db 100644 --- a/modules/store/src/selector.ts +++ b/modules/store/src/selector.ts @@ -90,7 +90,7 @@ export function createSelector<State, S1, Result>( ): MemoizedSelector<State, Result>; export function createSelector<State, Props, S1, Result>( s1: SelectorWithProps<State, Props, S1>, - projector: (s1: S1) => Result + projector: (s1: S1, props: Props) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, Result>( selectors: [Selector<State, S1>], @@ -98,7 +98,7 @@ export function createSelector<State, S1, Result>( ): MemoizedSelector<State, Result>; export function createSelector<State, Props, S1, Result>( selectors: [SelectorWithProps<State, Props, S1>], - projector: (s1: S1) => Result + projector: (s1: S1, props: Props) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, Result>( @@ -109,7 +109,7 @@ export function createSelector<State, S1, S2, Result>( export function createSelector<State, Props, S1, S2, Result>( s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, - projector: (s1: S1, s2: S2) => Result + projector: (s1: S1, s2: S2, props: Props) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, Result>( selectors: [Selector<State, S1>, Selector<State, S2>], @@ -120,7 +120,7 @@ export function createSelector<State, Props, S1, S2, Result>( SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2> ], - projector: (s1: S1, s2: S2) => Result + projector: (s1: S1, s2: S2, props: Props) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, S3, Result>( @@ -133,7 +133,7 @@ export function createSelector<State, Props, S1, S2, S3, Result>( s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, - projector: (s1: S1, s2: S2, s3: S3) => Result + projector: (s1: S1, s2: S2, s3: S3, props: Props) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, S3, Result>( selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>], @@ -145,7 +145,7 @@ export function createSelector<State, Props, S1, S2, S3, Result>( SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3> ], - projector: (s1: S1, s2: S2, s3: S3) => Result + projector: (s1: S1, s2: S2, s3: S3, props: Props) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, S3, S4, Result>( @@ -160,7 +160,7 @@ export function createSelector<State, Props, S1, S2, S3, S4, Result>( s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, s4: SelectorWithProps<State, Props, S4>, - projector: (s1: S1, s2: S2, s3: S3, s4: S4) => Result + projector: (s1: S1, s2: S2, s3: S3, s4: S4, props: Props) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, S3, S4, Result>( selectors: [ @@ -178,7 +178,7 @@ export function createSelector<State, Props, S1, S2, S3, S4, Result>( SelectorWithProps<State, Props, S3>, SelectorWithProps<State, Props, S4> ], - projector: (s1: S1, s2: S2, s3: S3, s4: S4) => Result + projector: (s1: S1, s2: S2, s3: S3, s4: S4, props: Props) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, S3, S4, S5, Result>( @@ -195,7 +195,7 @@ export function createSelector<State, Props, S1, S2, S3, S4, S5, Result>( s3: SelectorWithProps<State, Props, S3>, s4: SelectorWithProps<State, Props, S4>, s5: SelectorWithProps<State, Props, S5>, - projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5) => Result + projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, props: Props) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, S3, S4, S5, Result>( selectors: [ @@ -215,7 +215,7 @@ export function createSelector<State, Props, S1, S2, S3, S4, S5, Result>( SelectorWithProps<State, Props, S4>, SelectorWithProps<State, Props, S5> ], - projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5) => Result + projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, props: Props) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, S3, S4, S5, S6, Result>( @@ -234,7 +234,15 @@ export function createSelector<State, Props, S1, S2, S3, S4, S5, S6, Result>( s4: SelectorWithProps<State, Props, S4>, s5: SelectorWithProps<State, Props, S5>, s6: SelectorWithProps<State, Props, S6>, - projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6) => Result + projector: ( + s1: S1, + s2: S2, + s3: S3, + s4: S4, + s5: S5, + s6: S6, + props: Props + ) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, S3, S4, S5, S6, Result>( selectors: [ @@ -256,7 +264,15 @@ export function createSelector<State, Props, S1, S2, S3, S4, S5, S6, Result>( SelectorWithProps<State, Props, S5>, SelectorWithProps<State, Props, S6> ], - projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6) => Result + projector: ( + s1: S1, + s2: S2, + s3: S3, + s4: S4, + s5: S5, + s6: S6, + props: Props + ) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, S3, S4, S5, S6, S7, Result>( @@ -288,7 +304,16 @@ export function createSelector< s5: SelectorWithProps<State, Props, S5>, s6: SelectorWithProps<State, Props, S6>, s7: SelectorWithProps<State, Props, S7>, - projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7) => Result + projector: ( + s1: S1, + s2: S2, + s3: S3, + s4: S4, + s5: S5, + s6: S6, + s7: S7, + props: Props + ) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, S3, S4, S5, S6, S7, Result>( selectors: [ @@ -323,7 +348,16 @@ export function createSelector< SelectorWithProps<State, Props, S6>, SelectorWithProps<State, Props, S7> ], - projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7) => Result + projector: ( + s1: S1, + s2: S2, + s3: S3, + s4: S4, + s5: S5, + s6: S6, + s7: S7, + props: Props + ) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, S3, S4, S5, S6, S7, S8, Result>( @@ -375,7 +409,8 @@ export function createSelector< s5: S5, s6: S6, s7: S7, - s8: S8 + s8: S8, + props: Props ) => Result ): MemoizedSelectorWithProps<State, Props, Result>; export function createSelector<State, S1, S2, S3, S4, S5, S6, S7, S8, Result>( @@ -431,7 +466,8 @@ export function createSelector< s5: S5, s6: S6, s7: S7, - s8: S8 + s8: S8, + props: Props ) => Result ): MemoizedSelectorWithProps<State, Props, Result>; @@ -447,12 +483,19 @@ export function createSelector( export function defaultStateFn( state: any, - selectors: any[], + selectors: Selector<any, any>[] | SelectorWithProps<any, any, any>[], props: any, memoizedProjector: MemoizedProjection ): any { - const args = selectors.map(fn => fn(state, props)); - return memoizedProjector.memoized.apply(null, args); + if (props === undefined) { + const args = (<Selector<any, any>[]>selectors).map(fn => fn(state)); + return memoizedProjector.memoized.apply(null, args); + } + + const args = (<SelectorWithProps<any, any, any>[]>selectors).map(fn => + fn(state, props) + ); + return memoizedProjector.memoized.apply(null, [...args, props]); } export type SelectorFactoryConfig<T = any, V = any> = {