Skip to content

Commit 0372c0e

Browse files
committed
Digest middleware + devTools in example
1 parent 5256351 commit 0372c0e

File tree

9 files changed

+116
-29
lines changed

9 files changed

+116
-29
lines changed

examples/counter/components/counter.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
<button ng-click='vm.increment()'>+</button>
55
<button ng-click='vm.decrement()'>-</button>
66
<button ng-click='vm.incrementIfOdd()'>Increment if odd</button>
7+
<button ng-click='vm.incrementAsync()'>Increment Async</button>
78
</div>

examples/counter/components/counter.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,13 @@ export default function counter() {
1414
class CounterController {
1515

1616
constructor($ngRedux) {
17-
this.counter = 0;
18-
$ngRedux.connect(state => ({
19-
counter: state.counter
20-
}),
21-
({counter}) => this.counter = counter);
17+
$ngRedux.connect(state => ({counter: state.counter}), this);
2218

23-
let {increment, decrement, incrementIfOdd} = bindActionCreators(CounterActions, $ngRedux.dispatch);
19+
let {increment, decrement, incrementIfOdd, incrementAsync} = bindActionCreators(CounterActions, $ngRedux.dispatch);
2420
this.increment = increment;
2521
this.decrement = decrement;
2622
this.incrementIfOdd = incrementIfOdd;
23+
this.incrementAsync = incrementAsync;
2724
}
25+
2826
}

examples/counter/index.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
55
<title>{%= o.htmlWebpackPlugin.options.title %}</title>
66
</head>
7-
<body ng-app='counter'>
8-
<ngr-counter></ngr-counter>
7+
<body>
8+
<div ng-app='counter'>
9+
<ngr-counter></ngr-counter>
10+
</div>
11+
<div id="devTools"></div>
912
</body>
1013
</html>

examples/counter/index.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,39 @@ import 'ng-redux';
33
import rootReducer from './reducers';
44
import thunk from 'redux-thunk';
55
import counter from './components/counter';
6+
import { devTools, persistState } from 'redux-devtools';
7+
import { DevTools, DebugPanel, LogMonitor } from 'redux-devtools/lib/react';
8+
import React, { Component } from 'react';
9+
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
610

711
angular.module('counter', ['ngRedux'])
812
.config(($ngReduxProvider) => {
9-
$ngReduxProvider.createStoreWith(rootReducer, [thunk]);
13+
$ngReduxProvider.createStoreWith(rootReducer, [thunk], [devTools()]);
1014
})
11-
.directive('ngrCounter', counter);
15+
.directive('ngrCounter', counter)
16+
//------- DevTools specific code ----
17+
.run(($ngRedux, $rootScope) => {
18+
React.render(
19+
<App store={ $ngRedux }/>,
20+
document.getElementById('devTools')
21+
);
22+
//Hack to reflect state changes when disabling/enabling actions via the monitor
23+
$ngRedux.subscribe(_ => {
24+
setTimeout($rootScope.$apply, 100);
25+
});
26+
});
27+
28+
29+
class App extends Component {
30+
render() {
31+
return (
32+
<div>
33+
<DebugPanel top right bottom>
34+
<DevTools store={ this.props.store } monitor = { LogMonitor } />
35+
</DebugPanel>
36+
</div>
37+
);
38+
}
39+
}
40+
41+

examples/counter/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
"babel-loader": "^5.3.2",
2222
"html-loader": "^0.3.0",
2323
"html-webpack-plugin": "^1.6.1",
24+
"react": "^0.13.3",
25+
"redux-devtools": "^1.1.1",
2426
"webpack": "^1.11.0",
2527
"webpack-dev-server": "^1.10.1"
2628
},

src/components/connector.js

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,39 @@
11
import isFunction from '../utils/isFunction';
2+
import isPlainObject from '../utils/isPlainObject';
23
import shallowEqual from '../utils/shallowEqual';
34
import invariant from 'invariant';
45

56
export default function Connector(store) {
6-
return (selector, callback) => {
7-
invariant(
8-
isFunction(callback),
9-
'The callback parameter passed to connect must be a Function. Instead received %s.',
10-
typeof callback
11-
);
7+
return (selector, target) => {
128

139
//Initial update
14-
let params = selector(store.getState());
15-
callback(params);
10+
let params = getStateSlice(store.getState(), selector);
11+
target = angular.merge(target, params);
1612

17-
return store.subscribe(() => {
18-
let nextParams = selector(store.getState());
13+
let unsubscribe = store.subscribe(() => {
14+
let nextParams = getStateSlice(store.getState(), selector);
1915
if (!shallowEqual(params, nextParams)) {
20-
callback(nextParams);
16+
target = angular.merge(target, nextParams);
2117
params = nextParams;
2218
}
2319
});
20+
21+
if(isFunction(target.$destroy)) {
22+
target.$on('$destroy', () => {
23+
unsubscribe();
24+
});
25+
}
26+
27+
return unsubscribe;
2428
}
2529
}
30+
31+
function getStateSlice(state, selector) {
32+
let slice = selector(state);
33+
invariant(
34+
isPlainObject(slice),
35+
'`selector` must return an object. Instead received %s.',
36+
slice
37+
);
38+
return slice;
39+
}

src/components/digestMiddleware.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default function digestMiddleware($rootScope) {
2+
return store => next => action => {
3+
if(!$rootScope.$$phase) {
4+
$rootScope.$apply(next(action));
5+
} else {
6+
next(action);
7+
}
8+
};
9+
}

src/components/ngRedux.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
import Connector from './connector';
22
import invariant from 'invariant';
33
import isFunction from '../utils/isFunction';
4-
import {createStore, applyMiddleware} from 'redux';
4+
import {createStore, applyMiddleware, compose} from 'redux';
5+
import digestMiddleware from './digestMiddleware';
56

67
export default function ngReduxProvider() {
78
let _reducer = undefined;
89
let _middlewares = [];
9-
let _storeEnhancer = undefined;
10+
let _storeEnhancers = undefined;
1011

11-
this.createStoreWith = (reducer, middlewares, storeEnhancer) => {
12+
this.createStoreWith = (reducer, middlewares, storeEnhancers) => {
1213
invariant(
1314
isFunction(reducer),
1415
'The reducer parameter passed to createStoreWith must be a Function. Instead received %s.',
1516
typeof reducer
1617
);
1718

1819
invariant(
19-
!storeEnhancer || isFunction(storeEnhancer),
20-
'The storeEnhancer parameter passed to createStoreWith must be a Function. Instead received %s.',
21-
typeof storeEnhancer
20+
!storeEnhancers || Array.isArray(storeEnhancers),
21+
'The storeEnhancers parameter passed to createStoreWith must be an Array. Instead received %s.',
22+
typeof storeEnhancers
2223
);
2324

2425
_reducer = reducer;
25-
_storeEnhancer = storeEnhancer || createStore;
26+
_storeEnhancers = storeEnhancers
2627
_middlewares = middlewares;
2728
};
2829

@@ -37,7 +38,11 @@ export default function ngReduxProvider() {
3738
}
3839
}
3940

40-
store = applyMiddleware(...resolvedMiddleware)(_storeEnhancer)(_reducer);
41+
let finalCreateStore = _storeEnhancers ? compose(..._storeEnhancers, createStore) : createStore;
42+
43+
resolvedMiddleware.push(digestMiddleware($injector.get('$rootScope')));
44+
45+
store = applyMiddleware(...resolvedMiddleware)(finalCreateStore)(_reducer);
4146

4247
return {
4348
...store,

src/utils/isPlainObject.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const fnToString = (fn) => Function.prototype.toString.call(fn);
2+
3+
/**
4+
* @param {any} obj The object to inspect.
5+
* @returns {boolean} True if the argument appears to be a plain object.
6+
*/
7+
export default function isPlainObject(obj) {
8+
if (!obj || typeof obj !== 'object') {
9+
return false;
10+
}
11+
12+
const proto = typeof obj.constructor === 'function' ?
13+
Object.getPrototypeOf(obj) :
14+
Object.prototype;
15+
16+
if (proto === null) {
17+
return true;
18+
}
19+
20+
const constructor = proto.constructor;
21+
22+
return typeof constructor === 'function'
23+
&& constructor instanceof constructor
24+
&& fnToString(constructor) === fnToString(Object);
25+
}

0 commit comments

Comments
 (0)