UNPKG

@angular-redux/store

Version:
825 lines (802 loc) 31.1 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('redux'), require('@angular/core'), require('rxjs'), require('rxjs/operators')) : typeof define === 'function' && define.amd ? define('@angular-redux/store', ['exports', 'redux', '@angular/core', 'rxjs', 'rxjs/operators'], factory) : (factory((global['angular-redux'] = global['angular-redux'] || {}, global['angular-redux'].store = {}),global.redux,global.ng.core,global.rxjs,global.rxjs.operators)); }(this, (function (exports,redux,core,rxjs,operators) { 'use strict'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * This is the public interface of \@angular-redux/store. It wraps the global * redux store and adds a few other add on methods. It's what you'll inject * into your Angular application as a service. * @abstract * @template RootState */ var NgRedux = /** @class */ (function () { function NgRedux() { } /** * \@hidden, \@deprecated */ NgRedux.instance = undefined; return NgRedux; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ var environment = ( /** @type {?} */((typeof window !== 'undefined' ? window : {}))); /** * An angular-2-ified version of the Redux DevTools chrome extension. */ var DevToolsExtension = /** @class */ (function () { /** @hidden */ function DevToolsExtension(appRef, ngRedux) { var _this = this; this.appRef = appRef; this.ngRedux = ngRedux; /** * A wrapper for the Chrome Extension Redux DevTools. * Makes sure state changes triggered by the extension * trigger Angular2's change detector. * * @argument options: dev tool options; same * format as described here: * [zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md] */ this.enhancer = function (options) { /** @type {?} */ var subscription; if (!_this.isEnabled()) { return null; } // Make sure changes from dev tools update angular's view. ( /** @type {?} */(_this.getDevTools())).listen(function (_a) { var type = _a.type; if (type === 'START') { subscription = _this.ngRedux.subscribe(function () { if (!core.NgZone.isInAngularZone()) { _this.appRef.tick(); } }); } else if (type === 'STOP') { subscription(); } }); return ( /** @type {?} */(_this.getDevTools()))(options || {}); }; /** * Returns true if the extension is installed and enabled. */ this.isEnabled = function () { return !!_this.getDevTools(); }; /** * Returns the redux devtools enhancer. */ this.getDevTools = function () { return environment && (environment.__REDUX_DEVTOOLS_EXTENSION__ || environment.devToolsExtension); }; } DevToolsExtension.decorators = [ { type: core.Injectable } ]; /** @nocollapse */ DevToolsExtension.ctorParameters = function () { return [ { type: core.ApplicationRef }, { type: NgRedux } ]; }; return DevToolsExtension; }()); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function () { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Gets a deeply-nested property value from an object, given a 'path' * of property names or array indices. * * @hidden * @param {?} v * @param {?} pathElems * @return {?} */ function getIn(v, pathElems) { if (!v) { return v; } // If this is an ImmutableJS structure, use existing getIn function if ('function' === typeof v.getIn) { return v.getIn(pathElems); } var _a = __read(pathElems), firstElem = _a[0], restElems = _a.slice(1); if (undefined === v[firstElem]) { return undefined; } if (restElems.length === 0) { return v[firstElem]; } return getIn(v[firstElem], restElems); } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Sets a deeply-nested property value from an object, given a 'path' * of property names or array indices. Path elements are created if * not there already. Does not mutate the given object. * * @hidden * @type {?} */ var setIn = function (obj, _a, value) { var _b = __read(_a), firstElem = _b[0], restElems = _b.slice(1); var _c, _d; return 'function' === typeof (obj[firstElem] || {}).setIn ? __assign({}, obj, (_c = {}, _c[firstElem] = obj[firstElem].setIn(restElems, value), _c)) : __assign({}, obj, (_d = {}, _d[firstElem] = restElems.length === 0 ? value : setIn(obj[firstElem] || {}, restElems, value), _d)); }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ var reducerMap = {}; /** @type {?} */ var composeReducers = function () { var reducers = []; for (var _i = 0; _i < arguments.length; _i++) { reducers[_i] = arguments[_i]; } return function (state, action) { return reducers.reduce(function (subState, reducer) { return reducer(subState, action); }, state); }; }; /** * @param {?} rootReducer Call this on your root reducer to enable SubStore * functionality for pre-configured stores (e.g. using NgRedux.provideStore()). * NgRedux.configureStore * does it for you under the hood. * @return {?} */ function enableFractalReducers(rootReducer) { reducerMap = {}; return composeReducers(rootFractalReducer, rootReducer); } /** * @hidden * @param {?} basePath * @param {?} localReducer * @return {?} */ function registerFractalReducer(basePath, localReducer) { /** @type {?} */ var existingFractalReducer = reducerMap[JSON.stringify(basePath)]; if (existingFractalReducer && existingFractalReducer !== localReducer) { throw new Error("attempt to overwrite fractal reducer for basePath " + basePath); } reducerMap[JSON.stringify(basePath)] = localReducer; } /** * @hidden * @param {?} basePath * @param {?} nextLocalReducer * @return {?} */ function replaceLocalReducer(basePath, nextLocalReducer) { reducerMap[JSON.stringify(basePath)] = nextLocalReducer; } /** * @param {?=} state * @param {?=} action * @return {?} */ function rootFractalReducer(state, action) { if (state === void 0) { state = {}; } /** @type {?} */ var fractalKey = action['@angular-redux::fractalkey']; /** @type {?} */ var fractalPath = fractalKey ? JSON.parse(fractalKey) : []; /** @type {?} */ var localReducer = reducerMap[fractalKey || '']; return fractalKey && localReducer ? setIn(state, fractalPath, localReducer(getIn(state, fractalPath), action)) : state; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * OPTIONS_KEY: this is per-class (static) and holds the config from the * \@SubStore decorator. * @type {?} */ var OPTIONS_KEY = '@angular-redux::substore::class::options'; /** * INSTANCE_SUBSTORE_KEY, INSTANCE_SELECTIONS_KEY: these are per-instance * (non-static) and holds references to the substores/selected observables * to be used by an instance of a decorated class. I'm not using * reflect-metadata here because I want * * 1. different instances to have different substores in the case where * `basePathMethodName` is dynamic. * 2. the instance substore to be garbage collected when the instance is no * longer reachable. * This is therefore an own-property on the actual instance of the decorated * class. * @type {?} */ var INSTANCE_SUBSTORE_KEY = '@angular-redux::substore::instance::store'; /** @type {?} */ var INSTANCE_SELECTIONS_KEY = '@angular-redux::substore::instance::selections'; /** * Used to detect when the base path changes - this allows components to * dynamically adjust their selections if necessary. * @type {?} */ var INSTANCE_BASE_PATH_KEY = '@angular-redux::substore::instance::basepath'; /** @type {?} */ var getClassOptions = function (decoratedInstance) { return decoratedInstance.constructor[OPTIONS_KEY]; }; /** * @hidden * @type {?} */ var setClassOptions = function (decoratedClassConstructor, options) { decoratedClassConstructor[OPTIONS_KEY] = options; }; // I want the store to be saved on the actual instance so // 1. different instances can have distinct substores if necessary // 2. the substore/selections will be marked for garbage collection when the // instance is destroyed. /** @type {?} */ var setInstanceStore = function (decoratedInstance, store) { return (decoratedInstance[INSTANCE_SUBSTORE_KEY] = store); }; /** @type {?} */ var getInstanceStore = function (decoratedInstance) { return decoratedInstance[INSTANCE_SUBSTORE_KEY]; }; /** @type {?} */ var getInstanceSelectionMap = function (decoratedInstance) { /** @type {?} */ var map = decoratedInstance[INSTANCE_SELECTIONS_KEY] || {}; decoratedInstance[INSTANCE_SELECTIONS_KEY] = map; return map; }; /** @type {?} */ var hasBasePathChanged = function (decoratedInstance, basePath) { return decoratedInstance[INSTANCE_BASE_PATH_KEY] !== (basePath || []).toString(); }; /** @type {?} */ var setInstanceBasePath = function (decoratedInstance, basePath) { decoratedInstance[INSTANCE_BASE_PATH_KEY] = (basePath || []).toString(); }; /** @type {?} */ var clearInstanceState = function (decoratedInstance) { decoratedInstance[INSTANCE_SELECTIONS_KEY] = null; decoratedInstance[INSTANCE_SUBSTORE_KEY] = null; decoratedInstance[INSTANCE_BASE_PATH_KEY] = null; }; /** * Gets the store associated with a decorated instance (e.g. a * component or service) * @hidden * @type {?} */ var getBaseStore = function (decoratedInstance) { // The root store hasn't been set up yet. if (!NgRedux.instance) { return undefined; } /** @type {?} */ var options = getClassOptions(decoratedInstance); // This is not decorated with `@WithSubStore`. Return the root store. if (!options) { return NgRedux.instance; } // Dynamic base path support: /** @type {?} */ var basePath = decoratedInstance[options.basePathMethodName](); if (hasBasePathChanged(decoratedInstance, basePath)) { clearInstanceState(decoratedInstance); setInstanceBasePath(decoratedInstance, basePath); } if (!basePath) { return NgRedux.instance; } /** @type {?} */ var store = getInstanceStore(decoratedInstance); if (!store) { setInstanceStore(decoratedInstance, NgRedux.instance.configureSubStore(basePath, options.localReducer)); } return getInstanceStore(decoratedInstance); }; /** * Creates an Observable from the given selection parameters, * rooted at decoratedInstance's store, and caches it on the * instance for future use. * @hidden * @type {?} */ var getInstanceSelection = function (decoratedInstance, key, selector, transformer, comparator) { /** @type {?} */ var store = getBaseStore(decoratedInstance); if (store) { /** @type {?} */ var selections = getInstanceSelectionMap(decoratedInstance); selections[key] = selections[key] || (!transformer ? store.select(selector, comparator) : store.select(selector).pipe(function (obs$) { return transformer(obs$, decoratedInstance); }, operators.distinctUntilChanged(comparator))); return selections[key]; } return undefined; }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Auto-dispatches the return value of the decorated function. * * Decorate a function creator method with \@dispatch and its return * value will automatically be passed to ngRedux.dispatch() for you. * @return {?} */ function dispatch() { return function decorate(target, key, descriptor) { /** @type {?} */ var originalMethod; /** @type {?} */ var wrapped = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } /** @type {?} */ var result = originalMethod.apply(this, args); if (result !== undefined) { /** @type {?} */ var store = getBaseStore(this) || NgRedux.instance; if (store) { store.dispatch(result); } } return result; }; descriptor = descriptor || Object.getOwnPropertyDescriptor(target, key); if (descriptor === undefined) { /** @type {?} */ var dispatchDescriptor = { get: function () { return wrapped; }, set: function (setMethod) { return (originalMethod = setMethod); }, }; Object.defineProperty(target, key, dispatchDescriptor); return dispatchDescriptor; } else { originalMethod = descriptor.value; descriptor.value = wrapped; return descriptor; } }; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Selects an observable from the store, and attaches it to the decorated * property. * * ```ts * import { select } from '\@angular-redux/store'; * * class SomeClass { * \@select(['foo','bar']) foo$: Observable<string> * } * ``` * * @template T * @param {?=} selector * A selector function, property name string, or property name path * (array of strings/array indices) that locates the store data to be * selected * * @param {?=} comparator Function used to determine if this selector has changed. * @return {?} */ function select(selector, comparator) { return function (target, key) { /** @type {?} */ var adjustedSelector = selector ? selector : String(key).lastIndexOf('$') === String(key).length - 1 ? String(key).substring(0, String(key).length - 1) : key; decorate(adjustedSelector, undefined, comparator)(target, key); }; } /** * Selects an observable using the given path selector, and runs it through the * given transformer function. A transformer function takes the store * observable as an input and returns a derived observable from it. That derived * observable is run through distinctUntilChanges with the given optional * comparator and attached to the store property. * * Think of a Transformer as a FunctionSelector that operates on observables * instead of values. * * ```ts * import { select$ } from 'angular-redux/store'; * * export const debounceAndTriple = obs$ => obs$ * .debounce(300) * .map(x => 3 * x); * * class Foo { * \@select$(['foo', 'bar'], debounceAndTriple) * readonly debouncedFooBar$: Observable<number>; * } * ``` * @template T * @param {?} selector * @param {?} transformer * @param {?=} comparator * @return {?} */ function select$(selector, transformer, comparator) { return decorate(selector, transformer, comparator); } /** * @param {?} selector * @param {?=} transformer * @param {?=} comparator * @return {?} */ function decorate(selector, transformer, comparator) { return function decorator(target, key) { /** * @this {?} * @return {?} */ function getter() { return getInstanceSelection(this, key, selector, transformer, comparator); } // Replace decorated property with a getter that returns the observable. if (delete target[key]) { Object.defineProperty(target, key, { get: getter, enumerable: true, configurable: true, }); } }; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Modifies the behaviour of any `\@select`, `\@select$`, or `\@dispatch` * decorators to operate on a substore defined by the IFractalStoreOptions. * * See: * https://github.com/angular-redux/platform/blob/master/packages/store/articles/fractal-store.md * for more information about SubStores. * @param {?} __0 * @return {?} */ function WithSubStore(_a) { var basePathMethodName = _a.basePathMethodName, localReducer = _a.localReducer; return function decorate(constructor) { setClassOptions(constructor, { basePathMethodName: basePathMethodName, localReducer: localReducer, }); }; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @hidden * @type {?} */ var assert = function (condition, message) { if (!condition) { throw new Error(message); } }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @hidden * @type {?} */ var sniffSelectorType = function (selector) { return !selector ? 'nil' : Array.isArray(selector) ? 'path' : 'function' === typeof selector ? 'function' : 'property'; }; /** * @hidden * @type {?} */ var resolver = function (selector) { return ({ property: function (state) { return state ? state[( /** @type {?} */(selector))] : undefined; }, path: function (state) { return getIn(state, ( /** @type {?} */(selector))); }, function: ( /** @type {?} */(selector)), nil: function (state) { return state; }, }); }; /** * @hidden * @type {?} */ var resolveToFunctionSelector = function (selector) { return resolver(selector)[sniffSelectorType(selector)]; }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @hidden * @template State */ var /** * @hidden * @template State */ SubStore = /** @class */ (function () { function SubStore(rootStore, basePath, localReducer) { var _this = this; this.rootStore = rootStore; this.basePath = basePath; this.dispatch = function (action) { return _this.rootStore.dispatch(__assign({}, (( /** @type {?} */(action))), { '@angular-redux::fractalkey': JSON.stringify(_this.basePath) })); }; this.getState = function () { return getIn(_this.rootStore.getState(), _this.basePath); }; this.configureSubStore = function (basePath, localReducer) { return new SubStore(_this.rootStore, __spread(_this.basePath, basePath), localReducer); }; this.select = function (selector, comparator) { return _this.rootStore.select(_this.basePath).pipe(operators.map(resolveToFunctionSelector(selector)), operators.distinctUntilChanged(comparator)); }; this.subscribe = function (listener) { /** @type {?} */ var subscription = _this.select().subscribe(listener); return function () { return subscription.unsubscribe(); }; }; this.replaceReducer = function (nextLocalReducer) { return replaceLocalReducer(_this.basePath, nextLocalReducer); }; registerFractalReducer(basePath, localReducer); } return SubStore; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @hidden * @template RootState */ var /** * @hidden * @template RootState */ RootStore = /** @class */ (function (_super) { __extends(RootStore, _super); function RootStore(ngZone) { var _this = _super.call(this) || this; _this.ngZone = ngZone; _this.store = undefined; _this.configureStore = function (rootReducer, initState, middleware, enhancers) { if (middleware === void 0) { middleware = []; } if (enhancers === void 0) { enhancers = []; } assert(!_this.store, 'Store already configured!'); // Variable-arity compose in typescript FTW. _this.setStore(redux.compose.apply(void 0, __spread([redux.applyMiddleware.apply(void 0, __spread(middleware))], enhancers))(redux.createStore)(enableFractalReducers(rootReducer), initState)); }; _this.provideStore = function (store) { assert(!_this.store, 'Store already configured!'); _this.setStore(store); }; _this.getState = function () { return ( /** @type {?} */(_this.store)).getState(); }; _this.subscribe = function (listener) { return ( /** @type {?} */(_this.store)).subscribe(listener); }; _this.replaceReducer = function (nextReducer) { ( /** @type {?} */(_this.store)).replaceReducer(nextReducer); }; _this.dispatch = function (action) { assert(!!_this.store, 'Dispatch failed: did you forget to configure your store? ' + 'https://github.com/angular-redux/platform/blob/master/packages/store/' + 'README.md#quick-start'); if (!core.NgZone.isInAngularZone()) { return _this.ngZone.run(function () { return ( /** @type {?} */(_this.store)).dispatch(action); }); } else { return ( /** @type {?} */(_this.store)).dispatch(action); } }; _this.select = function (selector, comparator) { return _this.store$.pipe(operators.distinctUntilChanged(), operators.map(resolveToFunctionSelector(selector)), operators.distinctUntilChanged(comparator)); }; _this.configureSubStore = function (basePath, localReducer) { return new SubStore(_this, basePath, localReducer); }; _this.storeToObservable = function (store) { return new rxjs.Observable(function (observer) { observer.next(store.getState()); /** @type {?} */ var unsubscribeFromRedux = store.subscribe(function () { return observer.next(store.getState()); }); return function () { unsubscribeFromRedux(); observer.complete(); }; }); }; NgRedux.instance = _this; _this.store$ = ( /** @type {?} */(new rxjs.BehaviorSubject(undefined).pipe(operators.filter(function (n) { return n !== undefined; }), operators.switchMap(function (observableStore) { return ( /** @type {?} */(observableStore)); })))); return _this; } /** * @private * @param {?} store * @return {?} */ RootStore.prototype.setStore = /** * @private * @param {?} store * @return {?} */ function (store) { this.store = store; /** @type {?} */ var storeServable = this.storeToObservable(store); this.store$.next(( /** @type {?} */(storeServable))); }; return RootStore; }(NgRedux)); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @hidden * @param {?} ngZone * @return {?} */ function _ngReduxFactory(ngZone) { return new RootStore(ngZone); } var NgReduxModule = /** @class */ (function () { function NgReduxModule() { } NgReduxModule.decorators = [ { type: core.NgModule, args: [{ providers: [ DevToolsExtension, { provide: NgRedux, useFactory: _ngReduxFactory, deps: [core.NgZone] }, ], },] } ]; return NgReduxModule; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ exports.NgRedux = NgRedux; exports.NgReduxModule = NgReduxModule; exports.DevToolsExtension = DevToolsExtension; exports.enableFractalReducers = enableFractalReducers; exports.select = select; exports.select$ = select$; exports.dispatch = dispatch; exports.WithSubStore = WithSubStore; exports.ɵb = RootStore; exports.ɵa = _ngReduxFactory; Object.defineProperty(exports, '__esModule', { value: true }); }))); //# sourceMappingURL=angular-redux-store.umd.js.map