diff --git a/package-lock.json b/package-lock.json index 4b8b6aa..6ff572e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -577,6 +577,24 @@ } } }, + "@angular-redux/form": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@angular-redux/form/-/form-9.0.1.tgz", + "integrity": "sha512-qcrpwW05vG3UrjMNZTHICkgA4oeUbfImqeGLwjUAGca1xsnS11eYecGIZ+BrbaaveUWsUJPL9lYaJOn90eesRQ==", + "requires": { + "immutable": "^3.8.1" + } + }, + "@angular-redux/router": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@angular-redux/router/-/router-9.0.0.tgz", + "integrity": "sha512-8cfgoNMKZuTjLuEclzKpUURpSetQxo9tiuMWz8KnIFCFP6btvQHzYyDhfR2ROq+bwDpIXlUI6C4bWAORYD1DGw==" + }, + "@angular-redux/store": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@angular-redux/store/-/store-9.0.0.tgz", + "integrity": "sha512-0aWUktzTK88xDoDlGUDmDBnGW1ZB21W3H9dq0E52fuaN87cwtdca83ioi20/YT+M6EOecYPY7il9fSpy/Ewd1A==" + }, "@angular/animations": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-6.0.5.tgz", @@ -5968,6 +5986,11 @@ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", "dev": true }, + "immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=" + }, "import-local": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", @@ -6687,8 +6710,7 @@ "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" }, "js-yaml": { "version": "3.7.0", @@ -7681,7 +7703,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true, "requires": { "js-tokens": "^3.0.0" } @@ -8194,6 +8215,14 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "ngx-redux-state-props": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/ngx-redux-state-props/-/ngx-redux-state-props-0.0.3.tgz", + "integrity": "sha512-lTZroWvB664/yHK9sbxg48LSi46I9J3Q5Irw8GI0i0javvD7zf5txX4kjdkF52XRdkcd1z6VFj38ognPtnOD1A==", + "requires": { + "tslib": "^1.9.0" + } + }, "ngx-take-until-destroy": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ngx-take-until-destroy/-/ngx-take-until-destroy-3.0.0.tgz", @@ -9902,6 +9931,20 @@ "dev": true, "optional": true }, + "redux": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.0.tgz", + "integrity": "sha512-NnnHF0h0WVE/hXyrB6OlX67LYRuaf/rJcbWvnHHEPCF/Xa/AZpwhs/20WyqzQae5x4SD2F9nPObgBh2rxAgLiA==", + "requires": { + "loose-envify": "^1.1.0", + "symbol-observable": "^1.2.0" + } + }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, "reflect-metadata": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.12.tgz", @@ -11337,6 +11380,11 @@ "has-flag": "^1.0.0" } }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, "tapable": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.0.0.tgz", @@ -11597,8 +11645,7 @@ "tslib": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz", - "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw==", - "dev": true + "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw==" }, "tslint": { "version": "5.10.0", diff --git a/package.json b/package.json index c46b851..6cb1714 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,9 @@ }, "private": true, "dependencies": { + "@angular-redux/form": "^9.0.1", + "@angular-redux/router": "^9.0.0", + "@angular-redux/store": "^9.0.0", "@angular/animations": "^6.0.5", "@angular/common": "^6.0.5", "@angular/compiler": "^6.0.5", @@ -26,7 +29,10 @@ "@angular/router": "^6.0.5", "bootstrap": "^4.0.0-beta.2", "core-js": "^2.4.1", + "ngx-redux-state-props": "0.0.3", "ngx-take-until-destroy": "^3.0.0", + "redux": "^4.0.0", + "redux-thunk": "^2.3.0", "rxjs": "^6.2.1", "rxjs-compat": "^6.0.0-rc.0", "zone.js": "^0.8.26" diff --git a/src/app/about/about.component.html b/src/app/about/about.component.html index f3b046b..315489e 100644 --- a/src/app/about/about.component.html +++ b/src/app/about/about.component.html @@ -3,3 +3,10 @@ This project shows you how to do things in Angular with full diff examples in commits/pull requests, with instructions included.

+ + + +
+

App State

+
{{ state | json }}
+
diff --git a/src/app/about/about.component.spec.ts b/src/app/about/about.component.spec.ts index 190d37a..9cb2f9a 100644 --- a/src/app/about/about.component.spec.ts +++ b/src/app/about/about.component.spec.ts @@ -1,6 +1,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NgxReduxStatePropsService } from 'ngx-redux-state-props'; import { AboutComponent } from './about.component'; +import { AboutActions } from './services/about.actions'; describe('AboutComponent', () => { let component: AboutComponent; @@ -9,6 +11,7 @@ describe('AboutComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [AboutComponent], + providers: [AboutActions, NgxReduxStatePropsService], }).compileComponents(); })); diff --git a/src/app/about/about.component.ts b/src/app/about/about.component.ts index 1eab16f..c3ac19c 100644 --- a/src/app/about/about.component.ts +++ b/src/app/about/about.component.ts @@ -1,8 +1,21 @@ import { Component } from '@angular/core'; +import { NgxReduxStatePropsService } from 'ngx-redux-state-props'; + +import { AboutActions } from './services/about.actions'; @Component({ selector: 'app-about', templateUrl: './about.component.html', styleUrls: ['./about.component.scss'], }) -export class AboutComponent {} +export class AboutComponent { + constructor(private actions: AboutActions, private redux: NgxReduxStatePropsService) {} + + get state() { + return this.redux.appState; + } + + testRedux() { + this.actions.getApiValue(); + } +} diff --git a/src/app/about/about.module.ts b/src/app/about/about.module.ts index 90e5d05..1904eaa 100644 --- a/src/app/about/about.module.ts +++ b/src/app/about/about.module.ts @@ -3,9 +3,11 @@ import { NgModule } from '@angular/core'; import { AboutComponent } from './about.component'; import { RoutingModule } from './about.router'; +import { AboutActions } from './services/about.actions'; @NgModule({ imports: [RoutingModule, CommonModule], declarations: [AboutComponent], + providers: [AboutActions], }) export class AboutModule {} diff --git a/src/app/about/services/about.actions.ts b/src/app/about/services/about.actions.ts new file mode 100644 index 0000000..8fc6829 --- /dev/null +++ b/src/app/about/services/about.actions.ts @@ -0,0 +1,34 @@ +import { dispatch } from '@angular-redux/store'; +import { Injectable } from '@angular/core'; + +const wait = (ms) => { + return new Promise((r) => setTimeout(r, ms)); +}; + +@Injectable() +export class AboutActions { + static readonly getApiValueType = 'getApiValueType'; + static readonly apiSuccessType = 'apiSuccessType'; + + @dispatch() + getApiValue() { + console.log('ACTION: getApiValue'); + + const testTimeout = 1000; + + return async (dispatcher) => { + await wait(testTimeout); + console.log('after 1s'); + dispatcher(this.apiSuccess()); + }; + } + + @dispatch() + apiSuccess() { + console.log('ACTION: apiSuccess'); + + return { + type: AboutActions.apiSuccessType, + }; + } +} diff --git a/src/app/about/services/about.reducer.ts b/src/app/about/services/about.reducer.ts new file mode 100644 index 0000000..4be51b8 --- /dev/null +++ b/src/app/about/services/about.reducer.ts @@ -0,0 +1,25 @@ +import { AboutActions } from './about.actions'; + +export class AboutReducer { + [AboutActions.getApiValueType](state = {}, action) { + console.log('REDUCER: getApiValueType'); + + return { + ...state, + somethingFromReducer: 'foobar', + action, + }; + } + + [AboutActions.apiSuccessType](state = {}, action) { + console.log('REDUCER: apiSuccessType'); + const offset = 32; + + return { + ...state, + somethingFromReducer: 'foobar', + type: action.type, + action: Math.random().toString(offset), + }; + } +} diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index b21615c..1d0760a 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -1,13 +1,25 @@ +import { NgReduxRouterModule } from '@angular-redux/router'; +import { NgReduxModule } from '@angular-redux/store'; import { async, TestBed } from '@angular/core/testing'; +import { BrowserModule } from '@angular/platform-browser'; import { RouterTestingModule } from '@angular/router/testing'; +import { NgxReduxStatePropsModule } from 'ngx-redux-state-props'; import { AppComponent } from './app.component'; +import { SharedModule } from './shared/shared.module'; describe('AppComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [AppComponent], - imports: [RouterTestingModule], + imports: [ + RouterTestingModule, + BrowserModule, + SharedModule, + NgReduxModule, + NgReduxRouterModule.forRoot(), + NgxReduxStatePropsModule, + ], }).compileComponents(); })); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index a70f453..0ef9d42 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,10 @@ +import { NgReduxRouter, routerReducer } from '@angular-redux/router'; +import { DevToolsExtension, NgRedux } from '@angular-redux/store'; import { Component } from '@angular/core'; +import { combineReducers } from 'redux'; +import thunk from 'redux-thunk'; + +import { AboutReducer } from './about/services/about.reducer'; @Component({ selector: 'app-root', @@ -7,4 +13,31 @@ import { Component } from '@angular/core'; }) export class AppComponent { title = 'app'; + + constructor( + private ngRedux: NgRedux, + private ngReduxRouter: NgReduxRouter, + private devTools: DevToolsExtension, + ) { + const rootReducer = combineReducers({ + about: this.createReducer(AboutReducer), + router: routerReducer, + }); + this.ngRedux.configureStore( + rootReducer, + {}, + [thunk], + this.devTools.isEnabled() ? [this.devTools.enhancer()] : undefined, + ); + this.ngReduxRouter.initialize(); + } + + createReducer(ReducerService) { + const reducer = new ReducerService(); + const defaultCallback = (s = {}) => s; + + return (state = {}, action) => { + return (reducer[action.type] || defaultCallback)(state, action); + }; + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 32ce11f..556f9f8 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,5 +1,8 @@ +import { NgReduxRouterModule } from '@angular-redux/router'; +import { NgReduxModule } from '@angular-redux/store'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; +import { NgxReduxStatePropsModule } from 'ngx-redux-state-props'; import { AppComponent } from './app.component'; import { AppRoutingModule } from './app.router'; @@ -7,7 +10,14 @@ import { SharedModule } from './shared/shared.module'; @NgModule({ declarations: [AppComponent], - imports: [AppRoutingModule, BrowserModule, SharedModule], + imports: [ + AppRoutingModule, + BrowserModule, + SharedModule, + NgReduxModule, + NgReduxRouterModule.forRoot(), + NgxReduxStatePropsModule, + ], providers: [], bootstrap: [AppComponent], })