ng2-redux
This is a fork of NgRedux that adds a provideStore
method
that will hopefully soon get merged into that project.
Angular 2 bindings for Redux.
For Angular 1 see ng-redux
ng2-redux lets you easily connect your Angular 2 components with Redux.
Table of Contents
Installation
npm install --save ng2-redux
Quick Start
Initialization
Import the NgRedux
class and add it to your application as an Angular 2
provider.
;; bootstrapApp, ;
Once you've done this, you'll be able to inject 'NgRedux' into your Angular 2 components. In your top-level app component, you can configure your Redux store with reducers, initial state, and optionally middlewares and enhancers as you would in Redux directly.
;;; ;
Or if you prefer to create the Redux store yourself you can do that and use the
provideStore()
function instead.
Create your store:
// store.ts ;;; ; ;
Create your App and call provideStore
with your newly created store:
// app.ts ;; ;
Now your Angular 2 app has been reduxified!
Usage
ng2-redux
has two main usage patterns: the select
pattern and the connect
pattern.
The Select Pattern
The select pattern allows you to get slices of your state as RxJS observables.
These plug in very efficiently to Angular 2's change detection mechanism and this is the preferred approach to accessing store data in Angular 2.
The @select decorator
The @select
decorator can be added to the property of any class or angular
component/injectable. It will turn the property into an observable which observes
the Redux Store value which is selected by the decorator's parameter.
The decorator expects to receive a string
, an array of string
s, a function
or no
parameter at all.
- If a
string
is passed the@select
decorator will attempt to observe a store property whose name matches thestring
. - If an array of strings is passed, the decorator will attempt to match that path
through the store (similar to
immutableJS
'sgetIn
). - If a
function
is passed the@select
decorator will attempt to use that function as a selector on the RxJs observable. - If nothing is passed then the
@select
decorator will attempt to use the name of the class property to find a matching value in the Redux store. Note that a utility is in place here where any $ characters will be ignored from the class property's name.
;;;;
Select Without Decorators
If you like RxJS, but aren't comfortable with decorators, you can also make
store selections using the ngRedux.select()
function.
;;;;;; ;
ngRedux.select
can take a property name or a function which transforms a property.
Since it's an observable, you can also transform data using observable operators like
.map
, .filter
, etc.
The Connect Pattern
Alternately you can use the 'ngRedux.connect' API, which will map your state and action creators to the component class directly.
This pattern is provided for backwards compatibility. It's worth noting that
Angular 2's view layer is more optimized for Observables and the select
pattern above.
;;;; ; // NB: 'import * as CounterActions' won't provide the right type// for bindActionCreators.;
A Note about Internet Explorer
This library relies on the presence of Object.assign
and Array.forEach
.
Internet Explorer requires polyfills for these; however these same functions
are also needed for Angular 2 itself to work. Just make sure you include
core-js or es6-shim
if you need to support IE.
Cookbooks
Using Angular 2 Services in your Action Creators
In order to use services in action creators, we need to integrate them into Angular 2's dependency injector.
We may as well adopt a more class-based approach to satisfy Angular 2's OOP idiom, and to allow us to
- make our actions
@Injectable()
, and - inject other services for our action creators to use.
Take a look at this example, which injects NgRedux to access
dispatch
and getState
(a replacement for redux-thunk
),
and a simple RandomNumberService
to show a side effect.
;;;;;
To use these action creators, we can just go ahead and inject them into our component:
;;;;
Using Angular 2 Services in your Middleware
Again, we just want to use Angular DI the way it was meant to be used.
Here's a contrived example that fetches a name from a remote API using Angular's
Http
service:
;;;
As with the action example above, we've attached our middleware function to
an @Injectable
class that can itself receive services from Angular's dependency
injector.
Note the arrow function called middleware
: this is what we can pass to the middlewares
parameter when we initialize ngRedux in our top-level component. We use an arrow function
to make sure that what we pass to ngRedux has a properly-bound function context.
;;
Using DevTools
Ng2Redux is fully compatible with the Chrome extension version of the Redux dev tools:
https://github.com/zalmoxisus/redux-devtools-extension
Here's how to enable them in your app (you probably only want to do this in development mode):
; // Add Whatever other enhancers you want. if __DEVMODE__ && window.devToolsExtension // Add the dev tools enhancer your ngRedux.configureStore called// when you initialize your root component:
API
configureStore()
Adds your ngRedux store to NgRedux. This should be called once, typically in your top-level app component's constructor.
Arguments:
rootReducer
(Reducer): Your top-level Redux reducer.initialState
(*Object): The desired initial state of your store.middleware
(Middleware[]): An optional array of Redux middleware functions.enhancers
(StoreEnhancer[StoreEnhancer]): An optional array of Redux store enhancer functions.
select(key | function,[comparer]) => Observable
Exposes a slice of state as an observable. Accepts either a property name or a selector function.
If using the async pipe, you do not need to subscribe to it explicitly, but can use the angular Async pipe to bind its values into your template.
Arguments:
key
(string): A key within the state that you want to subscribe to.selector
(Function): A function that accepts the application state, and returns the slice you want subscribe to for changes.
e.g:
this.counter$ = this.ngRedux.selectstate.counter;// orthis.counterSubscription = this.ngRedux .selectstate.counter .subscribethis.counter = count;// or this.counter$ = this.ngRedux.select'counter';
provideStore()
Initializes your ngRedux store. This should be called once, typically in your
top-level app component's constructor. If configureStore
has been used this cannot be used.
Arguments:
store
(Store): Your app's store.
select(key | function,[comparer]) => Observable
Exposes a slice of state as an observable. Accepts either a property name or a selector function.
If using the async pipe, you do not need to subscribe to it explicitly, but can use the angular Async pipe to bind its values into your template.
Arguments:
key
(string): A key within the state that you want to subscribe to.selector
(Function): A function that accepts the application state, and returns the slice you want subscribe to for changes.
e.g:
this.counter$ = this.ngRedux.selectstate.counter;// orthis.counterSubscription = this.ngRedux .selectstate.counter .subscribethis.counter = count;// or this.counter$ = this.ngRedux.select'counter';
@select(key | path | function)
Property decorator.
Attaches an observable to the property which will reflect the latest value in the Redux store.
Arguments:
key
(string): A key within the state that you want to subscribe to.path
(string[]): A path of nested keys within the state you want to subscribe to.selector
(Function): A function that accepts the application state, and returns the slice you want to subscribe to for changes.
e.g. see the @select decorator
connect(mapStateToTarget, mapDispatchToTarget)(target)
Connects an Angular component to Redux, and maps action creators and store properties onto the component instance.
Arguments:
mapStateToTarget
(Function): connect will subscribe to Redux store updates. Any time it updates, mapStateToTarget will be called. Its result must be a plain object, and it will be merged intotarget
. If you have a component which simply triggers actions without needing any state you can pass null tomapStateToTarget
.- [
mapDispatchToTarget
] (Object or Function): Optional. If an object is passed, each function inside it will be assumed to be a Redux action creator. An object with the same function names, but bound to a Redux store, will be merged ontotarget
. If a function is passed, it will be givendispatch
. It’s up to you to return an object that somehow usesdispatch
to bind action creators in your own way. (Tip: you may use thebindActionCreators()
helper from Redux.).
You then need to invoke the function a second time, with target
as parameter:
target
(Object or Function): If passed an object, the results ofmapStateToTarget
andmapDispatchToTarget
will be merged onto it. If passed a function, the function will receive the results ofmapStateToTarget
andmapDispatchToTarget
as parameters.
e.g:
connectthis.mapStateToThis, this.mapDispatchToThisthis;//Orconnectthis.mapState, this.mapDispatch;
Remarks:
- The
mapStateToTarget
function takes a single argument of the entire Redux store’s state and returns an object to be passed as props. It is often called a selector. Use reselect to efficiently compose selectors and compute derived data.
Store API
All of redux's store methods (i.e. dispatch
, subscribe
and getState
) are exposed by $ngRedux and can be accessed directly. For example:
ngRedux.subscribe
This means that you are free to use Redux basic API in advanced cases where connect
's API would not fill your needs.