Skip to content

Commit

Permalink
feat(Store): Add lettable select operator
Browse files Browse the repository at this point in the history
Introduces lettable operator for Store.select.

BREAKING CHANGE:

Updates minimum version of RxJS dependency.

BEFORE:

Minimum peer dependency of RxJS ^5.0.0

AFTER:

Minimum peer dependency of RxJS ^5.5.0
  • Loading branch information
brandonroberts authored and MikeRyanDev committed Dec 18, 2017
1 parent d5e1814 commit 77eed24
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 33 deletions.
4 changes: 2 additions & 2 deletions modules/router-store/src/router_store_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
RouterStateSnapshot,
RoutesRecognized,
} from '@angular/router';
import { Store } from '@ngrx/store';
import { Store, select } from '@ngrx/store';
import { of } from 'rxjs/observable/of';
import {
DefaultRouterStateSerializer,
Expand Down Expand Up @@ -255,7 +255,7 @@ export class StoreRouterConnectingModule {
this.store.subscribe(s => {
this.storeState = s;
});
this.store.select(this.stateKey).subscribe(() => {
this.store.pipe(select(this.stateKey)).subscribe(() => {
this.navigateIfNeeded();
});
}
Expand Down
6 changes: 3 additions & 3 deletions modules/store/spec/edge.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Observable } from 'rxjs/Observable';
import { todos, todoCount } from './fixtures/edge_todos';
import { createInjector } from './helpers/injector';
import { Store, StoreModule } from '../';
import { Store, StoreModule, select } from '../';

interface TestAppSchema {
counter1: number;
Expand Down Expand Up @@ -36,12 +36,12 @@ describe('ngRx Store', () => {
let todosNextCount = 0;
let todosCountNextCount = 0;

store.select('todos').subscribe((todos: any[]) => {
store.pipe(select('todos')).subscribe((todos: any[]) => {
todosNextCount++;
store.dispatch({ type: 'SET_COUNT', payload: todos.length });
});

store.select('todoCount').subscribe(count => {
store.pipe(select('todoCount')).subscribe(count => {
todosCountNextCount++;
});

Expand Down
7 changes: 4 additions & 3 deletions modules/store/spec/integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
combineReducers,
ActionReducer,
ActionReducerMap,
select,
} from '../';
import { ReducerManager, INITIAL_STATE, State } from '../src/private_export';
import {
Expand Down Expand Up @@ -142,8 +143,8 @@ describe('ngRx Integration spec', () => {
let currentlyVisibleTodos: Todo[] = [];

Observable.combineLatest(
store.select('visibilityFilter'),
store.select('todos'),
store.pipe(select('visibilityFilter')),
store.pipe(select('todos')),
filterVisibleTodos
).subscribe(visibleTodos => {
currentlyVisibleTodos = visibleTodos;
Expand Down Expand Up @@ -221,7 +222,7 @@ describe('ngRx Integration spec', () => {
},
];

store.select(state => state).subscribe(state => {
store.pipe(select(state => state)).subscribe(state => {
expect(state).toEqual(expected.shift());
});
});
Expand Down
4 changes: 2 additions & 2 deletions modules/store/spec/ngc/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NgModule, Component, InjectionToken } from '@angular/core';
import { platformDynamicServer } from '@angular/platform-server';
import { BrowserModule } from '@angular/platform-browser';
import { Store, StoreModule, combineReducers } from '../../';
import { Store, StoreModule, combineReducers, select } from '../../';
import { counterReducer, INCREMENT, DECREMENT } from '../fixtures/counter';
import { todos } from '../fixtures/todos';
import { Observable } from 'rxjs/Observable';
Expand Down Expand Up @@ -40,7 +40,7 @@ export const reducerToken = new InjectionToken('Reducers');
export class NgcSpecComponent {
count: Observable<number>;
constructor(public store: Store<AppState>) {
this.count = store.select(state => state.count);
this.count = store.pipe(select(state => state.count));
}
increment() {
this.store.dispatch({ type: INCREMENT });
Expand Down
22 changes: 14 additions & 8 deletions modules/store/spec/store.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import 'rxjs/add/operator/take';
import { ReflectiveInjector } from '@angular/core';
import { hot } from 'jasmine-marbles';
import { createInjector } from './helpers/injector';
import { ActionsSubject, ReducerManager, Store, StoreModule } from '../';
import {
ActionsSubject,
ReducerManager,
Store,
StoreModule,
select,
} from '../';
import {
counterReducer,
INCREMENT,
Expand Down Expand Up @@ -83,7 +89,7 @@ describe('ngRx Store', () => {

counterSteps.subscribe(action => store.dispatch(action));

const counterStateWithString = store.select('counter1');
const counterStateWithString = store.pipe(select('counter1'));

const stateSequence = 'i-v--w--x--y--z';
const counter1Values = { i: 0, v: 1, w: 2, x: 1, y: 0, z: 1 };
Expand All @@ -98,7 +104,7 @@ describe('ngRx Store', () => {

counterSteps.subscribe(action => store.dispatch(action));

const counterStateWithFunc = store.select(s => s.counter1);
const counterStateWithFunc = store.pipe(select(s => s.counter1));

const stateSequence = 'i-v--w--x--y--z';
const counter1Values = { i: 0, v: 1, w: 2, x: 1, y: 0, z: 1 };
Expand All @@ -109,7 +115,7 @@ describe('ngRx Store', () => {
});

it('should correctly lift itself', () => {
const result = store.select('counter1');
const result = store.pipe(select('counter1'));

expect(result instanceof Store).toBe(true);
});
Expand All @@ -119,7 +125,7 @@ describe('ngRx Store', () => {

counterSteps.subscribe(action => store.dispatch(action));

const counterState = store.select('counter1');
const counterState = store.pipe(select('counter1'));

const stateSequence = 'i-v--w--x--y--z';
const counter1Values = { i: 0, v: 1, w: 2, x: 1, y: 0, z: 1 };
Expand All @@ -132,7 +138,7 @@ describe('ngRx Store', () => {

counterSteps.subscribe(action => dispatcher.next(action));

const counterState = store.select('counter1');
const counterState = store.pipe(select('counter1'));

const stateSequence = 'i-v--w--x--y--z';
const counter1Values = { i: 0, v: 1, w: 2, x: 1, y: 0, z: 1 };
Expand All @@ -145,8 +151,8 @@ describe('ngRx Store', () => {

counterSteps.subscribe(action => store.dispatch(action));

const counter1State = store.select('counter1');
const counter2State = store.select('counter2');
const counter1State = store.pipe(select('counter1'));
const counter2State = store.pipe(select('counter2'));

const stateSequence = 'i-v--w--x--y--z';
const counter2Values = { i: 1, v: 2, w: 3, x: 2, y: 0, z: 1 };
Expand Down
2 changes: 1 addition & 1 deletion modules/store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export {
Selector,
} from './models';
export { StoreModule } from './store_module';
export { Store } from './store';
export { Store, select } from './store';
export { combineReducers, compose, createReducerFactory } from './utils';
export { ActionsSubject, INIT } from './actions_subject';
export {
Expand Down
95 changes: 81 additions & 14 deletions modules/store/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,7 @@ export class Store<T> extends Observable<T> implements Observer<Action> {
pathOrMapFn: ((state: T) => any) | string,
...paths: string[]
): Store<any> {
let mapped$: Store<any>;

if (typeof pathOrMapFn === 'string') {
mapped$ = pluck.call(this, pathOrMapFn, ...paths);
} else if (typeof pathOrMapFn === 'function') {
mapped$ = map.call(this, pathOrMapFn);
} else {
throw new TypeError(
`Unexpected type '${typeof pathOrMapFn}' in select operator,` +
` expected 'string' or 'function'`
);
}

return distinctUntilChanged.call(mapped$);
return select(pathOrMapFn, ...paths)(this);
}

lift<R>(operator: Operator<T, R>): Store<R> {
Expand Down Expand Up @@ -117,3 +104,83 @@ export class Store<T> extends Observable<T> implements Observer<Action> {
}

export const STORE_PROVIDERS: Provider[] = [Store];

export function select<T, K>(
mapFn: ((state: T) => K) | string
): (source$: Observable<T>) => Store<K>;
export function select<T, a extends keyof T>(
key: a
): (source$: Store<a>) => Store<T[a]>;
export function select<T, a extends keyof T, b extends keyof T[a]>(
key1: a,
key2: b
): (source$: Store<T>) => Store<T[a][b]>;
export function select<
T,
a extends keyof T,
b extends keyof T[a],
c extends keyof T[a][b]
>(key1: a, key2: b, key3: c): (source$: Store<a>) => Store<T[a][b][c]>;
export function select<
T,
a extends keyof T,
b extends keyof T[a],
c extends keyof T[a][b],
d extends keyof T[a][b][c]
>(
key1: a,
key2: b,
key3: c,
key4: d
): (source$: Store<a>) => Store<T[a][b][c][d]>;
export function select<
T,
a extends keyof T,
b extends keyof T[a],
c extends keyof T[a][b],
d extends keyof T[a][b][c],
e extends keyof T[a][b][c][d]
>(
key1: a,
key2: b,
key3: c,
key4: d,
key5: e
): (source$: Store<a>) => Store<T[a][b][c][d][e]>;
export function select<
T,
a extends keyof T,
b extends keyof T[a],
c extends keyof T[a][b],
d extends keyof T[a][b][c],
e extends keyof T[a][b][c][d],
f extends keyof T[a][b][c][d][e]
>(
key1: a,
key2: b,
key3: c,
key4: d,
key5: e,
key6: f
): (source$: Store<a>) => Store<T[a][b][c][d][e][f]>;
export function select<T, K>(
pathOrMapFn: ((state: T) => any) | string,
...paths: string[]
) {
return function selectOperator(source$: Store<T>): Store<K> {
let mapped$: Store<any>;

if (typeof pathOrMapFn === 'string') {
mapped$ = pluck.call(source$, pathOrMapFn, ...paths);
} else if (typeof pathOrMapFn === 'function') {
mapped$ = map.call(source$, pathOrMapFn);
} else {
throw new TypeError(
`Unexpected type '${typeof pathOrMapFn}' in select operator,` +
` expected 'string' or 'function'`
);
}

return distinctUntilChanged.call(mapped$);
};
}

0 comments on commit 77eed24

Please sign in to comment.