Skip to content

Commit 499b49a

Browse files
author
Seth Davenport
committed
Remove lodash as a dependency
Apart from making the lib less complex, it also makes it easier to handle with SystemJS. This is important given that the Angular-CLI has embraced SystemJS instead of our usual webpack toolchain. Also rejigged the deps a bit: - typings should be a devdepedency - Angular and RxJS should be peer deps Connected to #113
1 parent 488559a commit 499b49a

File tree

10 files changed

+223
-24
lines changed

10 files changed

+223
-24
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ ng2-redux lets you easily connect your Angular 2 components with Redux.
1616
- [Installation](#installation)
1717
- [Quick Start](#quick-start)
1818
- [Usage](#usage)
19+
- [A Note about Internet Explorer](#a-note-about-internet-explorer)
1920
- [Cookbooks](#cookbooks)
2021
- [Using Angular 2 Services in your Action Creators](#using-angular-2-services-in-your-action-creators)
2122
- [Using Angular 2 Services in your Middleware](#using-angular-2-services-in-your-middleware)
@@ -195,6 +196,14 @@ export class App {
195196
}
196197
```
197198

199+
## A Note about Internet Explorer
200+
201+
This library relies on the presence of `Object.assign` and `Array.forEach`.
202+
Internet Explorer requires polyfills for these; however these same functions
203+
are also needed for Angular 2 itself to work. Just make sure you include
204+
[core-js](https://npmjs.com/package/core-js) or [es6-shim](https://npmjs.com/packages/es6-shim)
205+
if you need to support IE.
206+
198207
## Cookbooks
199208

200209
### Using Angular 2 Services in your Action Creators

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
},
4242
"homepage": "https://github.com/angular-redux/ng2-redux#readme",
4343
"devDependencies": {
44+
"@angular/core": "2.0.0-rc.1",
4445
"chai": "^3.5.0",
4546
"expect": "^1.8.0",
4647
"mocha": "^2.4.5",
@@ -50,15 +51,15 @@
5051
"ts-loader": "^0.8.1",
5152
"ts-node": "^0.5.5",
5253
"typescript": "^1.8.10",
53-
"@angular/core": "2.0.0-rc.1",
5454
"es6-shim": "^0.35.0",
5555
"redux": "^3.4.0",
5656
"reflect-metadata": "0.1.3",
5757
"rxjs": "5.0.0-beta.6",
58+
"typings": "^0.7.4",
5859
"zone.js": "0.6.12"
5960
},
60-
"dependencies": {
61-
"lodash": "^3.10.1",
62-
"typings": "^0.7.4"
61+
"peerDependencies": {
62+
"rxjs": "5.0.0-beta.6",
63+
"@angular/core": "2.0.0-rc.1"
6364
}
6465
}

src/___tests___/components/ng-redux.spec.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import 'reflect-metadata';
2-
import {expect, use} from 'chai';
2+
import 'es6-shim';
3+
import { expect, use } from 'chai';
34
import { createStore } from 'redux';
4-
import {NgRedux} from '../../components/ng-redux';
5+
import { NgRedux } from '../../components/ng-redux';
56
import * as sinon from 'sinon';
67
import * as sinonChai from 'sinon-chai';
7-
import * as _ from 'lodash';
8+
89
use(sinonChai);
910

1011
function returnPojo() {
@@ -88,8 +89,8 @@ describe('Connector', () => {
8889
it('Should extend target (object) with actionCreators', () => {
8990
connector.connect(returnPojo,
9091
{ ac1: returnPojo, ac2: () => { } })(targetObj);
91-
expect(_.isFunction(targetObj.ac1)).to.equal(true);
92-
expect(_.isFunction(targetObj.ac2)).to.equal(true);
92+
expect(targetObj.ac1).to.be.a('Function');
93+
expect(targetObj.ac2).to.be.a('Function');
9394
});
9495

9596
it('Should return an unsubscribing function', () => {

src/___tests___/tests.entry.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/___tests___/utils/omit.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import 'reflect-metadata';
2+
import { expect, use } from 'chai';
3+
4+
import { omit } from '../../utils/omit';
5+
6+
describe('omit', () => {
7+
it('should omit the specified properties', () => {
8+
const input = { a: 1, b: 2, c:3, d: 4 };
9+
const output = omit(input, [ 'b', 'd' ]);
10+
11+
expect(output.hasOwnProperty('b')).to.be.false;
12+
expect(output.hasOwnProperty('d')).to.be.false;
13+
expect(output).to.eql({ a: 1, c: 3 });
14+
});
15+
16+
it('should not mutate its input', () => {
17+
const input = { a: 1, b: 2, c:3, d: 4 };
18+
const output = omit(input, [ 'b', 'd' ]);
19+
20+
expect(input).to.eql({ a: 1, b: 2, c:3, d: 4 });
21+
});
22+
});
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import { expect, use } from 'chai';
2+
3+
import { isObject, isFunction, isPlainObject} from '../../utils/type-checks';
4+
5+
describe('typeChecks', () => {
6+
describe('isObject', () => {
7+
it('should return true for an object literal', () => {
8+
expect(isObject({})).to.be.true;
9+
});
10+
11+
it('should return true for a new Object', () => {
12+
expect(isObject(new Object())).to.be.true;
13+
});
14+
15+
it('should return true for an instance of a class', () => {
16+
class Foo {};
17+
expect(isObject(new Foo())).to.be.true;
18+
});
19+
20+
it('should return false for null', () => {
21+
expect(isObject(null)).to.be.false;
22+
});
23+
24+
it('should return false for undefined', () => {
25+
expect(isObject(undefined)).to.be.false;
26+
});
27+
28+
it('should return false for a function', () => {
29+
expect(isObject(function foo() {})).to.be.false;
30+
});
31+
32+
it('should return false for an arrow function', () => {
33+
expect(isObject(() => undefined)).to.be.false;
34+
});
35+
36+
it('should return false for a constructor function', () => {
37+
class Foo {};
38+
expect(isObject(Foo)).to.be.false;
39+
});
40+
41+
it('should return false for a string', () => {
42+
expect(isObject('foo')).to.be.false;
43+
});
44+
45+
it('should return false for a number', () => {
46+
expect(isObject(1)).to.be.false;
47+
});
48+
});
49+
50+
describe('isFunction', () => {
51+
it('should return true for a function', () => {
52+
expect(isFunction(function () {})).to.be.true;
53+
});
54+
55+
it('should return true for an arrow function', () => {
56+
expect(isFunction(() => undefined)).to.be.true;
57+
});
58+
59+
it('should return true for a constructor function', () => {
60+
class Foo {};
61+
expect(isFunction(Foo)).to.be.true;
62+
});
63+
64+
it('should return false for null', () => {
65+
expect(isFunction(null)).to.be.false;
66+
});
67+
68+
it('should return false for undefined', () => {
69+
expect(isFunction(undefined)).to.be.false;
70+
});
71+
72+
it('should return false for an object literal', () => {
73+
expect(isFunction({})).to.be.false;
74+
});
75+
76+
it('should return false for an instance of a class', () => {
77+
class Foo {};
78+
expect(isFunction(new Foo())).to.be.false;
79+
});
80+
81+
it('should return false for a string', () => {
82+
expect(isFunction('foo')).to.be.false;
83+
});
84+
85+
it('should return false for a number', () => {
86+
expect(isFunction(1)).to.be.false;
87+
});
88+
89+
it('should return false for a list', () => {
90+
expect(isFunction([])).to.be.false;
91+
});
92+
});
93+
94+
describe('isPlainObject', () => {
95+
it('should return true for an object literal', () => {
96+
expect(isPlainObject({})).to.be.true;
97+
});
98+
99+
it('should return true for a new Object', () => {
100+
expect(isPlainObject(new Object())).to.be.true;
101+
});
102+
103+
it('should return false for an instance of a class', () => {
104+
class Foo {};
105+
expect(isPlainObject(new Foo())).to.be.false;
106+
});
107+
108+
it('should return false for null', () => {
109+
expect(isPlainObject(null)).to.be.false;
110+
});
111+
112+
it('should return false for undefined', () => {
113+
expect(isPlainObject(undefined)).to.be.false;
114+
});
115+
116+
it('should return false for a function', () => {
117+
expect(isPlainObject(function foo() {})).to.be.false;
118+
});
119+
120+
it('should return false for an arrow function', () => {
121+
expect(isPlainObject(() => undefined)).to.be.false;
122+
});
123+
124+
it('should return false for a constructor function', () => {
125+
class Foo {};
126+
expect(isPlainObject(Foo)).to.be.false;
127+
});
128+
129+
it('should return false for a string', () => {
130+
expect(isPlainObject('foo')).to.be.false;
131+
});
132+
133+
it('should return false for a number', () => {
134+
expect(isObject(1)).to.be.false;
135+
});
136+
});
137+
});

src/components/ng-redux.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import shallowEqual from '../utils/shallowEqual';
22
import wrapActionCreators from '../utils/wrapActionCreators';
33
import * as Redux from 'redux';
4-
import * as _ from 'lodash';
54
import { BehaviorSubject, Observable } from 'rxjs';
65
import { Store, Action, ActionCreator, Reducer } from 'redux';
76
import { Injectable } from '@angular/core';
87
import { invariant } from '../utils/invariant';
8+
import { isObject, isFunction, isPlainObject} from '../utils/type-checks';
9+
import { omit } from '../utils/omit';
910

1011
const VALID_SELECTORS = ['string', 'number', 'symbol', 'function'];
1112
const ERROR_MESSAGE = `Expected selector to be one of:
@@ -31,13 +32,13 @@ export class NgRedux<RootState> {
3132
this._store.subscribe(() => this._store$.next(this._store.getState()));
3233
this._defaultMapStateToTarget = () => ({});
3334
this._defaultMapDispatchToTarget = dispatch => ({ dispatch });
34-
const cleanedStore = _.omit(store, ['dispatch', 'getState', 'subscribe', 'replaceReducer'])
35+
const cleanedStore = omit(store, ['dispatch', 'getState', 'subscribe', 'replaceReducer'])
3536
Object.assign(this, cleanedStore);
3637
}
3738

3839
/**
3940
* Create an observable from a Redux store.
40-
*
41+
*
4142
* @param {Store<RootState>} store Redux store to create an observable from
4243
* @returns {BehaviorSubject<RootState>}
4344
*/
@@ -96,7 +97,7 @@ export class NgRedux<RootState> {
9697
|| this._defaultMapStateToTarget;
9798

9899
invariant(
99-
_.isFunction(finalMapStateToTarget),
100+
isFunction(finalMapStateToTarget),
100101
'mapStateToTarget must be a Function. Instead received %s.',
101102
finalMapStateToTarget);
102103

@@ -108,7 +109,7 @@ export class NgRedux<RootState> {
108109
return (target) => {
109110

110111
invariant(
111-
_.isFunction(target) || _.isObject(target),
112+
isFunction(target) || isObject(target),
112113
'The target parameter passed to connect must be a Function or' +
113114
'a plain object.'
114115
);
@@ -174,18 +175,18 @@ export class NgRedux<RootState> {
174175
};
175176

176177
private updateTarget(target, StateSlice, dispatch) {
177-
if (_.isFunction(target)) {
178+
if (isFunction(target)) {
178179
target(StateSlice, dispatch);
179180
} else {
180-
_.assign(target, StateSlice, dispatch);
181+
Object.assign(target, StateSlice, dispatch);
181182
}
182183
}
183184

184185
private getStateSlice(state, mapStateToScope) {
185186
const slice = mapStateToScope(state);
186187

187188
invariant(
188-
_.isPlainObject(slice),
189+
isPlainObject(slice),
189190
'`mapStateToScope` must return an object. Instead received %s.',
190191
slice
191192
);
@@ -194,16 +195,16 @@ export class NgRedux<RootState> {
194195
}
195196

196197
private getBoundActions = (actions) => {
197-
const finalMapDispatchToTarget = _.isPlainObject(actions) ?
198+
const finalMapDispatchToTarget = isPlainObject(actions) ?
198199
wrapActionCreators(actions) :
199200
actions || this._defaultMapDispatchToTarget;
200201
invariant(
201-
_.isPlainObject(finalMapDispatchToTarget)
202-
|| _.isFunction(finalMapDispatchToTarget),
202+
isPlainObject(finalMapDispatchToTarget)
203+
|| isFunction(finalMapDispatchToTarget),
203204
'mapDispatchToTarget must be a plain Object or a Function. ' +
204205
'Instead received % s.',
205206
finalMapDispatchToTarget);
206207

207208
return finalMapDispatchToTarget(this._store.dispatch);
208209
};
209-
}
210+
}

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export {provider} from './components/provider';
2-
export { NgRedux } from './components/ng-redux';
1+
export { provider } from './components/provider';
2+
export { NgRedux } from './components/ng-redux';

src/utils/omit.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Creates a copy of object, but with properties matching 'props'
3+
* omitted.
4+
*
5+
* @param {Object} object: the object to be copied and filtered.
6+
* @param {string[]} props: a list of property names to be excluded
7+
* from the filtered copy.
8+
*/
9+
export function omit(object: Object, props: string[]): Object {
10+
const clone = Object.assign({}, object);
11+
props.forEach(prop => delete clone[prop]);
12+
return clone;
13+
};

src/utils/type-checks.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export function isFunction(thing: any): boolean {
2+
return !!(thing &&
3+
thing.constructor &&
4+
thing.call &&
5+
thing.apply);
6+
}
7+
8+
export function isObject(thing: any): boolean {
9+
return !!(thing &&
10+
typeof thing === 'object' &&
11+
!isFunction(thing));
12+
}
13+
14+
export function isPlainObject(thing: any): boolean {
15+
return isObject(thing) && thing.constructor === Object;
16+
}

0 commit comments

Comments
 (0)