├── docs ├── .nojekyll ├── assets │ ├── images │ │ ├── icons.png │ │ ├── widgets.png │ │ ├── icons@2x.png │ │ └── widgets@2x.png │ └── js │ │ └── search.js ├── modules │ └── _index_.html ├── globals.html └── interfaces │ ├── _reducers_normalize_.entitymap.html │ ├── _actions_normalize_.schemamap.html │ ├── _reducers_normalize_.normalizedstate.html │ ├── _reducers_normalize_.normalizedentitystate.html │ ├── _actions_normalize_.normalizeactionschemaconfig.html │ ├── _actions_normalize_.normalizechildactionschemaconfig.html │ ├── _actions_normalize_.normalizeactionpayload.html │ └── _actions_normalize_.normalizeactionconfig.html ├── .travis.yml ├── src ├── index.ts ├── actions │ └── normalize.spec.ts └── reducers │ ├── normalize.ts │ └── normalize.spec.ts ├── .editorconfig ├── tsconfig.json ├── .npmignore ├── .gitignore ├── karma-ci.conf.js ├── karma.conf.js ├── LICENSE ├── package.json ├── CHANGELOG.md ├── tslint.json └── README.md /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | cache: yarn 2 | addons: 3 | chrome: stable 4 | language: node_js 5 | node_js: 6 | - "8" 7 | -------------------------------------------------------------------------------- /docs/assets/images/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelkrone/ngrx-normalizr/HEAD/docs/assets/images/icons.png -------------------------------------------------------------------------------- /docs/assets/images/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelkrone/ngrx-normalizr/HEAD/docs/assets/images/widgets.png -------------------------------------------------------------------------------- /docs/assets/images/icons@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelkrone/ngrx-normalizr/HEAD/docs/assets/images/icons@2x.png -------------------------------------------------------------------------------- /docs/assets/images/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelkrone/ngrx-normalizr/HEAD/docs/assets/images/widgets@2x.png -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Exports actions, reducers and selectors of the ngrx-normalizr package. 3 | */ 4 | 5 | export * from './actions/normalize'; 6 | export * from './reducers/normalize'; 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = tabs 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "files": ["./src/index.ts"], 4 | "exclude": ["dist", "node_modules", "**/*/node_modules"], 5 | "compilerOptions": { 6 | "outDir": "./dist", 7 | "sourceMap": false, 8 | "declaration": true, 9 | "noImplicitAny": true, 10 | "noUnusedLocals": true, 11 | "removeComments": true, 12 | "moduleResolution": "node", 13 | "module": "commonjs", 14 | "emitDecoratorMetadata": true, 15 | "experimentalDecorators": true, 16 | "target": "es5", 17 | "typeRoots": ["node_modules/@types"], 18 | "lib": ["es2016", "dom", "es2017.object"] 19 | }, 20 | "angularCompilerOptions": { 21 | "genDir": "dist" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # sources 2 | /src 3 | 4 | # compiled output 5 | /tmp 6 | /out-tsc 7 | /docs 8 | 9 | # dependencies 10 | /node_modules 11 | 12 | # IDEs and editors 13 | /.idea 14 | .project 15 | .classpath 16 | .c9/ 17 | *.launch 18 | .settings/ 19 | *.sublime-workspace 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | testem.log 35 | /typings 36 | yarn-error.log 37 | .editorconfig 38 | tslint.json 39 | tsconfig.json 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | *.ngsummary.json 8 | *.ngfactory.ts 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # IDEs and editors 14 | /.idea 15 | .project 16 | .classpath 17 | .c9/ 18 | *.launch 19 | .settings/ 20 | *.sublime-workspace 21 | 22 | # IDE - VSCode 23 | .vscode/* 24 | !.vscode/settings.json 25 | !.vscode/tasks.json 26 | !.vscode/launch.json 27 | !.vscode/extensions.json 28 | 29 | # misc 30 | /.sass-cache 31 | /connect.lock 32 | /coverage 33 | /libpeerconnection.log 34 | npm-debug.log 35 | testem.log 36 | /typings 37 | yarn-error.log 38 | 39 | # System Files 40 | .DS_Store 41 | Thumbs.db 42 | -------------------------------------------------------------------------------- /karma-ci.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | basePath: './', 7 | frameworks: ['jasmine', 'karma-typescript'], 8 | files: [{ pattern: 'src/**/*.ts' }], 9 | preprocessors: { 10 | 'src/**/*.ts': ['karma-typescript'] 11 | }, 12 | plugins: [ 13 | require('karma-jasmine'), 14 | require('karma-typescript'), 15 | require('karma-chrome-launcher'), 16 | require('karma-coverage-istanbul-reporter') 17 | ], 18 | client: { 19 | clearContext: false // leave Jasmine Spec Runner output visible in browser 20 | }, 21 | coverageIstanbulReporter: { 22 | reports: ['html', 'lcovonly'], 23 | fixWebpackSourcePaths: true 24 | }, 25 | reporters: ['progress', 'karma-typescript'], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: false, 30 | browsers: ['ChromeHeadless'], 31 | singleRun: true 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | basePath: './', 7 | frameworks: ['jasmine', 'karma-typescript'], 8 | files: [{ pattern: 'src/**/*.ts' }], 9 | preprocessors: { 10 | 'src/**/*.ts': ['karma-typescript'] 11 | }, 12 | plugins: [ 13 | require('karma-jasmine'), 14 | require('karma-typescript'), 15 | require('karma-chrome-launcher'), 16 | require('karma-jasmine-html-reporter'), 17 | require('karma-coverage-istanbul-reporter') 18 | ], 19 | client: { 20 | clearContext: false // leave Jasmine Spec Runner output visible in browser 21 | }, 22 | coverageIstanbulReporter: { 23 | reports: ['html', 'lcovonly'], 24 | fixWebpackSourcePaths: true 25 | }, 26 | reporters: ['progress', 'karma-typescript', 'kjhtml'], 27 | port: 9876, 28 | colors: true, 29 | logLevel: config.LOG_INFO, 30 | autoWatch: true, 31 | browsers: ['Chrome'], 32 | singleRun: false 33 | }); 34 | }; 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Michael Krone 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngrx-normalizr", 3 | "version": "2.3.0", 4 | "description": "Normalizing state reducer for ngrx applications", 5 | "main": "./dist/index.js", 6 | "types": "./dist/index.d.ts", 7 | "scripts": { 8 | "watch": "./node_modules/.bin/karma start", 9 | "prepublishOnly": "yarn aot", 10 | "tsc": "rm -rf ./dist && tsc", 11 | "aot": "rm -rf ./dist && ./node_modules/.bin/ngc", 12 | "docs": 13 | "rm -rf ./docs && typedoc --out ./docs --name ngrx-normalizr --readme ./README.md --module commonjs --target ES5 --theme default && yarn docs-github", 14 | "docs-github": "touch ./docs/.nojekyll", 15 | "test": "./node_modules/.bin/karma start karma-ci.conf.js" 16 | }, 17 | "readme": "README.md", 18 | "author": "Michael Krone ", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/michaelkrone/ngrx-normalizr" 22 | }, 23 | "license": "MIT", 24 | "peerDependencies": { 25 | "@ngrx/store": "^4.0.3" 26 | }, 27 | "dependencies": { 28 | "normalizr": "^3.2.3" 29 | }, 30 | "devDependencies": { 31 | "@angular/compiler": "^4.3.5", 32 | "@angular/compiler-cli": "^4.3.5", 33 | "@angular/core": "^4.3.5", 34 | "@ngrx/store": "^4.0.3", 35 | "@types/jasmine": "^2.5.53", 36 | "@types/jasminewd2": "^2.0.2", 37 | "@types/lodash": "^4.14.82", 38 | "@types/node": "^8.0.24", 39 | "@types/should": "^8.3.0", 40 | "codelyzer": "^3.2.1", 41 | "jasmine-core": "^2.7.0", 42 | "jasmine-spec-reporter": "^4.2.1", 43 | "karma": "^1.7.0", 44 | "karma-chrome-launcher": "^2.2.0", 45 | "karma-cli": "^1.0.1", 46 | "karma-coverage-istanbul-reporter": "^1.3.0", 47 | "karma-firefox-launcher": "^1.0.1", 48 | "karma-jasmine": "^1.1.0", 49 | "karma-jasmine-html-reporter": "^0.2.2", 50 | "karma-typescript": "^3.0.5", 51 | "rxjs": "^5.4.3", 52 | "should": "^11.2.1", 53 | "ts-node": "^3.3.0", 54 | "tslint": "^5.6.0", 55 | "typescript": "^2.4.2", 56 | "zone.js": "^0.8.16" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /docs/assets/js/search.js: -------------------------------------------------------------------------------- 1 | var typedoc = typedoc || {}; 2 | typedoc.search = typedoc.search || {}; 3 | typedoc.search.data = {"kinds":{"1":"External module","65536":"Type literal"},"rows":[{"id":0,"kind":65536,"name":"__type","url":"interfaces/_reducers_normalize_.schemaselectors.html#entityprojector.__type-1","classes":"tsd-kind-type-literal tsd-parent-kind-property tsd-is-not-exported","parent":"\"reducers/normalize\".SchemaSelectors.entityProjector"},{"id":1,"kind":65536,"name":"__type","url":"interfaces/_reducers_normalize_.schemaselectors.html#entitiesprojector.__type","classes":"tsd-kind-type-literal tsd-parent-kind-property tsd-is-not-exported","parent":"\"reducers/normalize\".SchemaSelectors.entitiesProjector"},{"id":2,"kind":65536,"name":"__type","url":"modules/_reducers_normalize_.html#initialstate.entities.__type","classes":"tsd-kind-type-literal tsd-parent-kind-variable tsd-is-not-exported","parent":"\"reducers/normalize\".initialState.entities"},{"id":3,"kind":65536,"name":"__type","url":"interfaces/_actions_normalize_.normalizeactioncreators.html#setdata.__type-4","classes":"tsd-kind-type-literal tsd-parent-kind-property tsd-is-not-exported","parent":"\"actions/normalize\".NormalizeActionCreators.setData"},{"id":4,"kind":65536,"name":"__type","url":"interfaces/_actions_normalize_.normalizeactioncreators.html#adddata.__type-1","classes":"tsd-kind-type-literal tsd-parent-kind-property tsd-is-not-exported","parent":"\"actions/normalize\".NormalizeActionCreators.addData"},{"id":5,"kind":65536,"name":"__type","url":"interfaces/_actions_normalize_.normalizeactioncreators.html#addchilddata.__type","classes":"tsd-kind-type-literal tsd-parent-kind-property tsd-has-type-parameter tsd-is-not-exported","parent":"\"actions/normalize\".NormalizeActionCreators.addChildData"},{"id":6,"kind":65536,"name":"__type","url":"interfaces/_actions_normalize_.normalizeactioncreators.html#removedata.__type-3","classes":"tsd-kind-type-literal tsd-parent-kind-property tsd-is-not-exported","parent":"\"actions/normalize\".NormalizeActionCreators.removeData"},{"id":7,"kind":65536,"name":"__type","url":"interfaces/_actions_normalize_.normalizeactioncreators.html#removechilddata.__type-2","classes":"tsd-kind-type-literal tsd-parent-kind-property tsd-is-not-exported","parent":"\"actions/normalize\".NormalizeActionCreators.removeChildData"},{"id":8,"kind":1,"name":"\"index\"","url":"modules/_index_.html","classes":"tsd-kind-external-module"}]}; -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | * _2.3.0_ 2 | 3 | * Adding `UpdateData` action and `updateData` action creator. 4 | 5 | * _2.2.0_ 6 | 7 | * Support for adding and removing child data. Adding `AddChildData` and 8 | `RemoveChildData` actions and `addChildData` and `removeChildData` action 9 | creators. 10 | 11 | * _2.1.0_ 12 | 13 | * entitiesProjector takes an optional array of id strings 14 | ([@juanpmarin](https://github.com/juanpmarin)) Closes #18 15 | 16 | * _2.0.0_ 17 | 18 | * Serialization support for actions. _Details:_ The normalization of entities 19 | is now perfomed in the action constructor. Previously it was handled by the 20 | reducer. As ([@PachowStudios](https://github.com/PachowStudios)) pointed out 21 | in Issue #16, ngrx-normalizr actions were not serializable. This could raise 22 | issues with other redux/ngrx libraries. The normalizr `schema.Entity` is not 23 | part of the action payload anymore, hence the interfaces for describing the 24 | payload have changed and the action constructor does no longer take the 25 | payload itself as an argument. As long as you did not type any action 26 | parameters in your code or dispatched actions directly with a simle pojo by 27 | using the exported action type names, you should have no problem updating, 28 | since the arity/keys of the constructor API did not change - see Breaking 29 | Changes. Closes #16 30 | 31 | * **Breaking Changes** 32 | * Action constructor parameters changed from `NormalizeDataPayload` to 33 | `NormalizeActionConfig` and from `NormalizeRemovePayload` to 34 | `NormalizeRemoveActionConfig` 35 | * Action `payload` property types changed from `NormalizeDataPayload` to 36 | `NormalizeActionPayload` and from `NormalizeRemovePayload` to 37 | `NormalizeRemoveActionPayload` 38 | * (might break) Internal used interface `SchemaPayload` replaced by 39 | `NormalizeActionSchemaConfig` 40 | 41 | * _1.0.4_ 42 | 43 | * exporting `SchemaSelectors` interface 44 | ([@JSantha](https://github.com/JSantha)) 45 | 46 | * _1.0.3_ 47 | 48 | * fixed typos ([@hoisel](https://github.com/hoisel)) 49 | * exporting types for reuse in other libraries 50 | 51 | * _1.0.2_ 52 | 53 | * improved documentation 54 | * improved code coverage (100%) 55 | * `SetData` action for setting entity data instead of updating and adding data 56 | 57 | * _1.0.1_ 58 | 59 | * `actionCreators` for creating schema bound actions 60 | * improved code coverage 61 | 62 | * _1.0.0_ 63 | 64 | * first production version 65 | 66 | * _0.0.1_ 67 | * first development version 68 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["node_modules/codelyzer"], 3 | "rules": { 4 | "arrow-return-shorthand": true, 5 | "callable-types": true, 6 | "class-name": true, 7 | "comment-format": [true, "check-space"], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "import-blacklist": [true, "rxjs"], 12 | "import-spacing": true, 13 | "indent": [true, "tabs"], 14 | "interface-over-type-literal": true, 15 | "label-position": true, 16 | "max-line-length": [true, 140], 17 | "member-access": false, 18 | "member-ordering": [ 19 | true, 20 | { 21 | "order": [ 22 | "static-field", 23 | "instance-field", 24 | "static-method", 25 | "instance-method" 26 | ] 27 | } 28 | ], 29 | "no-arg": true, 30 | "no-bitwise": true, 31 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], 32 | "no-construct": true, 33 | "no-debugger": true, 34 | "no-duplicate-super": true, 35 | "no-empty": false, 36 | "no-empty-interface": true, 37 | "no-eval": true, 38 | "no-inferrable-types": [true, "ignore-params"], 39 | "no-misused-new": true, 40 | "no-non-null-assertion": true, 41 | "no-shadowed-variable": true, 42 | "no-string-literal": false, 43 | "no-string-throw": true, 44 | "no-switch-case-fall-through": true, 45 | "no-trailing-whitespace": true, 46 | "no-unnecessary-initializer": true, 47 | "no-unused-expression": true, 48 | "no-use-before-declare": true, 49 | "no-var-keyword": true, 50 | "object-literal-sort-keys": false, 51 | "one-line": [ 52 | true, 53 | "check-open-brace", 54 | "check-catch", 55 | "check-else", 56 | "check-whitespace" 57 | ], 58 | "prefer-const": true, 59 | "quotemark": [true, "single"], 60 | "radix": true, 61 | "semicolon": [true, "always"], 62 | "triple-equals": [true, "allow-null-check"], 63 | "typedef-whitespace": [ 64 | true, 65 | { 66 | "call-signature": "nospace", 67 | "index-signature": "nospace", 68 | "parameter": "nospace", 69 | "property-declaration": "nospace", 70 | "variable-declaration": "nospace" 71 | } 72 | ], 73 | "typeof-compare": true, 74 | "unified-signatures": true, 75 | "variable-name": false, 76 | "whitespace": [ 77 | true, 78 | "check-branch", 79 | "check-decl", 80 | "check-operator", 81 | "check-separator", 82 | "check-type" 83 | ], 84 | "directive-selector": [true, "attribute", "app", "camelCase"], 85 | "component-selector": [true, "element", "app", "kebab-case"], 86 | "use-input-property-decorator": true, 87 | "use-output-property-decorator": true, 88 | "use-host-property-decorator": true, 89 | "no-input-rename": true, 90 | "no-output-rename": true, 91 | "use-life-cycle-interface": true, 92 | "use-pipe-transform-interface": true, 93 | "component-class-suffix": true, 94 | "directive-class-suffix": true, 95 | "no-access-missing-member": true, 96 | "templates-use-public": true, 97 | "invoke-injectable": true 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/actions/normalize.spec.ts: -------------------------------------------------------------------------------- 1 | import 'should'; 2 | import { Action } from '@ngrx/store'; 3 | import { schema as normalizrSchema, normalize, schema } from 'normalizr'; 4 | import * as actions from './normalize'; 5 | 6 | describe('Normalize actions', () => { 7 | const childSchema = new normalizrSchema.Entity('child'); 8 | const schema = new normalizrSchema.Entity('parent', { 9 | childs: [childSchema] 10 | }); 11 | const removeChildren = { child: 'childs' }; 12 | const data = [ 13 | { 14 | id: '1', 15 | property: 'value', 16 | childs: [ 17 | { id: '1', property: 'child-value' }, 18 | { id: '2', property: 'child-value' } 19 | ] 20 | }, 21 | { 22 | id: '2', 23 | property: 'value', 24 | childs: [ 25 | { id: '3', property: 'child-value' }, 26 | { id: '4', property: 'child-value' } 27 | ] 28 | } 29 | ]; 30 | const childData = [ 31 | { id: '5', property: 'new-child-value' }, 32 | { id: '6', property: 'new-child-value' } 33 | ]; 34 | 35 | function checkSerialization(action: Action) { 36 | const deserialized = JSON.parse(JSON.stringify(action)); 37 | deserialized.should.have.properties(Object.keys(action)); 38 | Object.keys(action).forEach(k => { 39 | action[k].should.eql(deserialized[k]); 40 | }); 41 | } 42 | 43 | describe('SetData', () => { 44 | it('should be exported', () => { 45 | actions.SetData.should.be.a.Function(); 46 | }); 47 | 48 | it('should be serializable', () => { 49 | checkSerialization(new actions.SetData({ data, schema })); 50 | }); 51 | }); 52 | 53 | describe('AddData', () => { 54 | it('should be exported', () => { 55 | actions.AddData.should.be.a.Function(); 56 | }); 57 | 58 | it('should be serializable', () => { 59 | checkSerialization(new actions.AddData({ data, schema })); 60 | }); 61 | }); 62 | 63 | describe('AddChildData', () => { 64 | it('should be exported', () => { 65 | actions.AddChildData.should.be.a.Function(); 66 | }); 67 | 68 | it('should be serializable', () => { 69 | const parentId = data[0].id; 70 | checkSerialization( 71 | new actions.AddChildData({ 72 | data: childData, 73 | parentSchema: schema, 74 | parentId, 75 | childSchema 76 | }) 77 | ); 78 | }); 79 | }); 80 | 81 | describe('UpdateData', () => { 82 | it('should be exported', () => { 83 | actions.UpdateData.should.be.a.Function(); 84 | }); 85 | 86 | it('should be serializable', () => { 87 | checkSerialization( 88 | new actions.UpdateData({ id: data[0].id, changes: data, schema }) 89 | ); 90 | }); 91 | }); 92 | 93 | describe('RemoveData', () => { 94 | it('should be exported', () => { 95 | actions.RemoveData.should.be.a.Function(); 96 | }); 97 | 98 | it('should be serializable', () => { 99 | checkSerialization( 100 | new actions.RemoveData({ id: '1', schema, removeChildren: {} }) 101 | ); 102 | }); 103 | }); 104 | 105 | describe('RemoveChildData', () => { 106 | it('should be exported', () => { 107 | actions.RemoveChildData.should.be.a.Function(); 108 | }); 109 | 110 | it('should be serializable', () => { 111 | const parentId = data[0].id; 112 | checkSerialization( 113 | new actions.RemoveChildData({ 114 | id: data[0].id, 115 | parentSchema: schema, 116 | parentId, 117 | childSchema 118 | }) 119 | ); 120 | }); 121 | }); 122 | describe('creator', () => { 123 | const result = actions.actionCreators(schema); 124 | 125 | it('should be exported', () => { 126 | actions.actionCreators.should.be.a.Function(); 127 | }); 128 | 129 | it('should return action creator functions', () => { 130 | result.should.be.an 131 | .Object() 132 | .and.have.properties('setData', 'removeData', 'addData'); 133 | result.setData.should.be.a.Function(); 134 | result.removeData.should.be.a.Function(); 135 | result.addData.should.be.a.Function(); 136 | }); 137 | 138 | describe('SetData creator', () => { 139 | const action = result.setData(data); 140 | 141 | it('should create a SetData action', () => { 142 | action.should.be.an.Object(); 143 | action.should.have.properties('payload'); 144 | action.payload.should.have.properties('entities', 'result'); 145 | action.payload.entities.should.eql(normalize(data, [schema]).entities); 146 | }); 147 | }); 148 | 149 | describe('AddData creator', () => { 150 | const action = result.addData(data); 151 | 152 | it('should create an AddData action', () => { 153 | action.should.be.an.Object(); 154 | action.should.have.properties('payload'); 155 | action.payload.should.have.properties('entities', 'result'); 156 | action.payload.entities.should.eql(normalize(data, [schema]).entities); 157 | }); 158 | }); 159 | 160 | describe('AddChildData creator', () => { 161 | const action = result.addChildData(childData, childSchema, data[0].id); 162 | 163 | it('should create an AddChildData action', () => { 164 | action.should.be.an.Object(); 165 | action.should.have.properties('payload'); 166 | action.payload.should.have.properties('entities', 'result'); 167 | action.payload.entities.should.eql( 168 | normalize(childData, [childSchema]).entities 169 | ); 170 | action.payload.parentSchemaKey.should.eql('parent'); 171 | action.payload.parentProperty.should.eql('childs'); 172 | action.payload.parentId.should.eql(data[0].id); 173 | }); 174 | }); 175 | 176 | describe('UpdateData creator', () => { 177 | const updateData = { newProperty: 'newValue', childs: childData }; 178 | const action = result.updateData(data[0].id, updateData); 179 | 180 | it('should create an UpdateData action', () => { 181 | action.should.be.an.Object(); 182 | action.should.have.properties('payload'); 183 | action.payload.should.have.properties('key', 'id', 'changes', 'result'); 184 | 185 | const expectedParent = { 186 | id: data[0].id, 187 | newProperty: updateData.newProperty, 188 | childs: [...updateData.childs.map(c => c.id)] 189 | }; 190 | 191 | action.payload.changes[schema.key][data[0].id].should.eql( 192 | expectedParent 193 | ); 194 | action.payload.changes[childSchema.key][childData[0].id].should.eql( 195 | childData[0] 196 | ); 197 | action.payload.changes[childSchema.key][childData[1].id].should.eql( 198 | childData[1] 199 | ); 200 | }); 201 | }); 202 | 203 | describe('RemoveData creator', () => { 204 | const id = 'some'; 205 | const result = actions.actionCreators(schema); 206 | 207 | it('should create a RemoveData action', () => { 208 | const action = result.removeData(id); 209 | action.should.be.an.Object(); 210 | action.should.have.properties('payload'); 211 | action.payload.should.have.properties('id'); 212 | action.payload.id.should.eql(id); 213 | }); 214 | 215 | it('should create a RemoveData action with removeChildren option', () => { 216 | const action = result.removeData(id, removeChildren); 217 | action.should.be.an.Object(); 218 | action.should.have.properties('payload'); 219 | action.payload.should.have.properties('id', 'removeChildren'); 220 | action.payload.id.should.eql(id); 221 | action.payload.removeChildren.should.eql(removeChildren); 222 | }); 223 | 224 | it('should not add invalid entity keys to the removeChildren payload', () => { 225 | const action = result.removeData(id, { some: 'random' }); 226 | action.should.be.an.Object(); 227 | action.should.have.properties('payload'); 228 | action.payload.should.have.properties('id', 'removeChildren'); 229 | action.payload.id.should.eql(id); 230 | (action.payload.removeChildren === null).should.be.true(); 231 | }); 232 | }); 233 | 234 | describe('RemoveChildData creator', () => { 235 | const action = result.removeChildData( 236 | childData[0].id, 237 | childSchema, 238 | data[0].id 239 | ); 240 | 241 | it('should create an RemoveChildData action', () => { 242 | action.should.be.an.Object(); 243 | action.should.have.properties('payload'); 244 | action.payload.should.have.properties( 245 | 'parentProperty', 246 | 'parentSchemaKey', 247 | 'parentId', 248 | 'id' 249 | ); 250 | action.payload.id.should.eql(childData[0].id); 251 | action.payload.parentSchemaKey.should.eql('parent'); 252 | action.payload.parentProperty.should.eql('childs'); 253 | action.payload.parentId.should.eql(data[0].id); 254 | }); 255 | }); 256 | }); 257 | }); 258 | -------------------------------------------------------------------------------- /docs/modules/_index_.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | "index" | ngrx-normalizr 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 62 |

External module "index"

63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | 98 |
99 |
100 | 159 |
160 |

Generated using TypeDoc

161 |
162 |
163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /docs/globals.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ngrx-normalizr 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 59 |

ngrx-normalizr

60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |

Index

68 |
69 |
70 |
71 |

External modules

72 | 77 |
78 |
79 |
80 |
81 |
82 | 110 |
111 |
112 | 171 |
172 |

Generated using TypeDoc

173 |
174 |
175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /src/reducers/normalize.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Exports reducers and selectors of the ngrx-normalizr package. 3 | */ 4 | 5 | import { createSelector, MemoizedSelector } from '@ngrx/store'; 6 | import { denormalize, schema } from 'normalizr'; 7 | 8 | import { NormalizeActionTypes } from '../actions/normalize'; 9 | import { 10 | NormalizeChildActionPayload, 11 | NormalizeRemoveChildActionPayload 12 | } from '../index'; 13 | 14 | /** 15 | * The state key under which the normalized state will be stored 16 | */ 17 | const STATE_KEY = 'normalized'; 18 | 19 | /** 20 | * Interface describing the entities propery of a normalized state. 21 | * A map of schema keys wich map to a map of entity id's to entity data. 22 | * This corresponds to the `entities` property of a `normalizr.normalize` result. 23 | */ 24 | export interface EntityMap { 25 | [key: string]: { [id: string]: any }; 26 | } 27 | 28 | /** 29 | * The state interface from which the app state should extend. 30 | * Holds an instance of `NormalizedEntityState` itself. 31 | */ 32 | export interface NormalizedState { 33 | /** The normalized state property */ 34 | normalized: NormalizedEntityState; 35 | } 36 | 37 | /** 38 | * The normalized state, representing a `normalizr.normalize` result. 39 | * Can be selected by the provided `getNormalizedEntities` and `getResult` 40 | * selectors. 41 | */ 42 | export interface NormalizedEntityState { 43 | /** 44 | * The original sorting of the unnormalized data. 45 | * Holds all id's of the last set operation in original order. 46 | * Can be used to restore the original sorting of entities 47 | */ 48 | result: string[]; 49 | 50 | /** 51 | * The normalized entities. Should be passed to all projector functions 52 | * to enable access to all entities needed. 53 | */ 54 | entities: EntityMap; 55 | } 56 | 57 | /** 58 | * The initial state for the normalized entity state. 59 | */ 60 | const initialState: NormalizedEntityState = { 61 | result: [], 62 | entities: {} 63 | }; 64 | 65 | /** 66 | * The normalizing reducer function which will handle actions with the types 67 | * `NormalizeActionTypes.SET_DATA`, `NormalizeActionTypes.ADD_DATA` and `NormalizeActionTypes.REMOVE_DATA`. 68 | * 69 | * On an `NormalizeActionTypes.SET_DATA` action: 70 | * 71 | * All entities and childs of the given schema will be replaced with the new entities. 72 | * 73 | * On an `NormalizeActionTypes.ADD_DATA` action: 74 | * 75 | * Entities are identified by their id attribute set in the schema passed by the payload. 76 | * Existing entities will be overwritten by updated data, new entities will be added to the store. 77 | * 78 | * On an `NormalizeActionTypes.REMOVE_DATA` action: 79 | * 80 | * Entities are identified by their id attribute set in the schema passed by the payload. 81 | * The entity with the passed id will be removed. If a `removeChildren` option is set in the action 82 | * payload, it is assumed as a map of schema keys to object property names. All referenced children 83 | * of the entity will be read by the object propety name and removed by the schema key. 84 | * 85 | * @param state The current state 86 | * @param action The dispatched action, one of `NormalizeActionTypes.ADD_DATA` or `NormalizeActionTypes.REMOVE_DATA`. 87 | */ 88 | export function normalized( 89 | state: NormalizedEntityState = initialState, 90 | action: any 91 | ) { 92 | switch (action.type) { 93 | case NormalizeActionTypes.SET_DATA: { 94 | const { result, entities } = action.payload; 95 | 96 | return { 97 | result, 98 | entities: { 99 | ...state.entities, 100 | ...entities 101 | } 102 | }; 103 | } 104 | 105 | case NormalizeActionTypes.ADD_DATA: { 106 | const { result, entities } = action.payload; 107 | 108 | return { 109 | result, 110 | entities: Object.keys(entities).reduce( 111 | (p: any, c: string) => { 112 | p[c] = { ...p[c], ...entities[c] }; 113 | return p; 114 | }, 115 | { ...state.entities } 116 | ) 117 | }; 118 | } 119 | 120 | case NormalizeActionTypes.ADD_CHILD_DATA: { 121 | const { 122 | result, 123 | entities, 124 | parentSchemaKey, 125 | parentProperty, 126 | parentId 127 | } = action.payload as NormalizeChildActionPayload; 128 | const newEntities = { ...state.entities }; 129 | 130 | /* istanbul ignore else */ 131 | if (getParentReferences(newEntities, action.payload)) { 132 | newEntities[parentSchemaKey][parentId][parentProperty].push(...result); 133 | } 134 | 135 | return { 136 | result, 137 | entities: Object.keys(entities).reduce((p: any, c: string) => { 138 | p[c] = { ...p[c], ...entities[c] }; 139 | return p; 140 | }, newEntities) 141 | }; 142 | } 143 | 144 | case NormalizeActionTypes.UPDATE_DATA: { 145 | const { id, key, changes, result } = action.payload; 146 | 147 | if (!state.entities[key] || !state.entities[key][id]) { 148 | return state; 149 | } 150 | 151 | const newEntities = { ...state.entities }; 152 | Object.entries(changes).forEach(([key, value]: [string, any]) => { 153 | Object.entries(changes[key]).forEach(([id, obj]: [string, any]) => { 154 | newEntities[key][id] = newEntities[key][id] || {}; 155 | Object.entries(changes[key][id]).forEach( 156 | ([property, value]: [string, any]) => { 157 | if (Array.isArray(value)) { 158 | newEntities[key][id][property].push(...value); 159 | } else { 160 | newEntities[key][id][property] = value; 161 | } 162 | } 163 | ); 164 | }); 165 | }); 166 | 167 | return { 168 | result, 169 | entities: newEntities 170 | }; 171 | } 172 | 173 | case NormalizeActionTypes.REMOVE_DATA: { 174 | const { id, key, removeChildren } = action.payload; 175 | const entities = { ...state.entities }; 176 | const entity = entities[key][id]; 177 | 178 | if (!entity) { 179 | return state; 180 | } 181 | 182 | if (removeChildren) { 183 | Object.entries(removeChildren).map( 184 | ([key, entityProperty]: [string, string]) => { 185 | const child = entity[entityProperty]; 186 | /* istanbul ignore else */ 187 | if (child && entities[key]) { 188 | const ids = Array.isArray(child) ? child : [child]; 189 | ids.forEach((oldId: string) => delete entities[key][oldId]); 190 | } 191 | } 192 | ); 193 | } 194 | 195 | delete entities[key][id]; 196 | 197 | return { 198 | result: state.result, 199 | entities 200 | }; 201 | } 202 | 203 | case NormalizeActionTypes.REMOVE_CHILD_DATA: { 204 | const { 205 | id, 206 | childSchemaKey, 207 | parentProperty, 208 | parentSchemaKey, 209 | parentId 210 | } = action.payload as NormalizeRemoveChildActionPayload; 211 | const newEntities = { ...state.entities }; 212 | const entity = newEntities[childSchemaKey][id]; 213 | 214 | /* istanbul ignore if */ 215 | if (!entity) { 216 | return state; 217 | } 218 | 219 | const parentRefs = getParentReferences(newEntities, action.payload); 220 | /* istanbul ignore else */ 221 | if (parentRefs && parentRefs.indexOf(id) > -1) { 222 | newEntities[parentSchemaKey][parentId][parentProperty].splice( 223 | parentRefs.indexOf(id), 224 | 1 225 | ); 226 | } 227 | 228 | delete newEntities[childSchemaKey][id]; 229 | 230 | return { 231 | ...state, 232 | entities: newEntities 233 | }; 234 | } 235 | 236 | default: 237 | return state; 238 | } 239 | } 240 | 241 | /** 242 | * Default getter for the normalized state 243 | * @param state any state 244 | */ 245 | const getNormalizedState = (state: any): NormalizedEntityState => 246 | state[STATE_KEY]; 247 | 248 | /** 249 | * Selects all normalized entities of the state, regardless of their schema. 250 | * This selector should be used to enable denormalizing projector functions access 251 | * to all needed schema entities. 252 | */ 253 | export const getNormalizedEntities: MemoizedSelector< 254 | any, 255 | EntityMap 256 | > = createSelector( 257 | getNormalizedState, 258 | (state: NormalizedEntityState) => state.entities 259 | ); 260 | 261 | /** 262 | * Select the result order of the last set operation. 263 | */ 264 | export const getResult: MemoizedSelector = createSelector( 265 | getNormalizedState, 266 | (state: NormalizedEntityState) => state.result 267 | ); 268 | 269 | /** 270 | * Generic interface for `createSchemaSelectors` return type. 271 | */ 272 | export interface SchemaSelectors { 273 | getNormalizedEntities: MemoizedSelector; 274 | getEntities: MemoizedSelector<{}, T[]>; 275 | entityProjector: (entities: {}, id: string) => T; 276 | entitiesProjector: (entities: {}) => T[]; 277 | } 278 | 279 | /** 280 | * Creates an object of selectors and projector functions bound to the given schema. 281 | * @param schema The schema to bind the selectors and projectors to 282 | */ 283 | export function createSchemaSelectors( 284 | schema: schema.Entity 285 | ): SchemaSelectors { 286 | return { 287 | /** 288 | * Select all entities, regardless of their schema, exported for convenience. 289 | */ 290 | getNormalizedEntities, 291 | 292 | /** 293 | * Select all entities and perform a denormalization based on the given schema. 294 | */ 295 | getEntities: createEntitiesSelector(schema), 296 | 297 | /** 298 | * Uses the given schema to denormalize an entity by the given id 299 | */ 300 | entityProjector: createEntityProjector(schema), 301 | 302 | /** 303 | * Uses the given schema to denormalize all given entities 304 | */ 305 | entitiesProjector: createEntitiesProjector(schema) 306 | }; 307 | } 308 | 309 | /** 310 | * Create a schema bound selector which denormalizes all entities with the given schema. 311 | * @param schema The schema to bind this selector to 312 | */ 313 | function createEntitiesSelector( 314 | schema: schema.Entity 315 | ): MemoizedSelector<{}, T[]> { 316 | return createSelector( 317 | getNormalizedEntities, 318 | createEntitiesProjector(schema) 319 | ); 320 | } 321 | 322 | /** 323 | * Create a schema bound projector function to denormalize a single entity. 324 | * @param schema The schema to bind this selector to 325 | */ 326 | function createEntityProjector(schema: schema.Entity) { 327 | return (entities: {}, id: string) => 328 | createSingleDenormalizer(schema)(entities, id) as T; 329 | } 330 | 331 | /** 332 | * Create a schema bound projector function to denormalize an object of normalized entities 333 | * @param schema The schema to bind this selector to 334 | */ 335 | function createEntitiesProjector(schema: schema.Entity) { 336 | return (entities: {}, ids?: Array) => 337 | createMultipleDenormalizer(schema)(entities, ids) as T[]; 338 | } 339 | 340 | /** 341 | * Create a schema bound denormalizer. 342 | * @param schema The schema to bind this selector to 343 | */ 344 | function createSingleDenormalizer(schema: schema.Entity) { 345 | const key = schema.key; 346 | return (entities: { [key: string]: {} }, id: string) => { 347 | /* istanbul ignore if */ 348 | if (!entities || !entities[key]) { 349 | return; 350 | } 351 | 352 | const denormalized = denormalize( 353 | { [key]: [id] }, 354 | { [key]: [schema] }, 355 | entities 356 | ); 357 | return denormalized[key][0]; 358 | }; 359 | } 360 | 361 | /** 362 | * Create a schema bound denormalizer. 363 | * @param schema The schema to bind this selector to 364 | */ 365 | function createMultipleDenormalizer(schema: schema.Entity) { 366 | const key = schema.key; 367 | return (entities: { [key: string]: {} }, ids?: Array) => { 368 | /* istanbul ignore if */ 369 | if (!entities || !entities[key]) { 370 | return; 371 | } 372 | const data = ids ? { [key]: ids } : { [key]: Object.keys(entities[key]) }; 373 | const denormalized = denormalize(data, { [key]: [schema] }, entities); 374 | return denormalized[key]; 375 | }; 376 | } 377 | 378 | /** 379 | * @private 380 | * Get the reference array from the parent entity 381 | * @param entities normalized entity state object 382 | * @param payload NormalizeChildActionPayload 383 | */ 384 | function getParentReferences( 385 | entities: any, 386 | payload: NormalizeChildActionPayload 387 | ): string | undefined { 388 | const { parentSchemaKey, parentProperty, parentId } = payload; 389 | if ( 390 | entities[parentSchemaKey] && 391 | entities[parentSchemaKey][parentId] && 392 | entities[parentSchemaKey][parentId][parentProperty] && 393 | Array.isArray(entities[parentSchemaKey][parentId][parentProperty]) 394 | ) { 395 | return entities[parentSchemaKey][parentId][parentProperty]; 396 | } 397 | } 398 | -------------------------------------------------------------------------------- /src/reducers/normalize.spec.ts: -------------------------------------------------------------------------------- 1 | import { schema } from 'normalizr'; 2 | import * as should from 'should'; 3 | 4 | import * as actions from '../actions/normalize'; 5 | import * as reducer from './normalize'; 6 | 7 | const childSchema = new schema.Entity('child'); 8 | const mySchema = new schema.Entity('parent', { childs: [childSchema] }); 9 | 10 | interface Child { 11 | id: string; 12 | property: string; 13 | } 14 | 15 | interface Parent { 16 | id: string; 17 | property: string; 18 | newProperty?: string; 19 | childs: Child[]; 20 | } 21 | 22 | describe('reducers', () => { 23 | let data: Parent[]; 24 | let childData: Child[]; 25 | let changes: Partial; 26 | 27 | beforeEach(() => { 28 | data = [ 29 | { 30 | id: '1', 31 | property: 'value', 32 | childs: [ 33 | { id: '1', property: 'child-value' }, 34 | { id: '2', property: 'child-value' } 35 | ] 36 | }, 37 | { 38 | id: '2', 39 | property: 'value', 40 | childs: [ 41 | { id: '3', property: 'child-value' }, 42 | { id: '4', property: 'child-value' } 43 | ] 44 | } 45 | ]; 46 | 47 | childData = [ 48 | { id: '5', property: 'new-child-value-1' }, 49 | { id: '6', property: 'new-child-value-2' } 50 | ]; 51 | changes = { newProperty: 'newValue', childs: childData }; 52 | }); 53 | 54 | describe('reducer function', () => { 55 | it('should be exported', () => { 56 | reducer.normalized.should.be.a.Function(); 57 | reducer.normalized.length.should.eql(2); 58 | }); 59 | 60 | it('should return the default state', () => { 61 | const state = reducer.normalized(undefined, { type: 'some' }); 62 | state.should.have.properties('entities', 'result'); 63 | state.entities.should.be.an.Object(); 64 | state.entities.should.eql({}); 65 | state.result.should.eql([]); 66 | }); 67 | 68 | describe('handling action', () => { 69 | let addAction1, 70 | addAction2, 71 | setAction1, 72 | addChildAction1, 73 | removeChildAction1, 74 | updateAction1; 75 | 76 | beforeEach(() => { 77 | addAction1 = new actions.AddData({ 78 | data: [data[0]], 79 | schema: mySchema 80 | }); 81 | addAction2 = new actions.AddData({ 82 | data: [data[1]], 83 | schema: mySchema 84 | }); 85 | setAction1 = new actions.SetData({ 86 | data: [data[1]], 87 | schema: mySchema 88 | }); 89 | addChildAction1 = new actions.AddChildData({ 90 | data: childData, 91 | parentSchema: mySchema, 92 | childSchema, 93 | parentId: data[0].id 94 | }); 95 | removeChildAction1 = new actions.RemoveChildData({ 96 | id: data[0].childs[0].id, 97 | childSchema, 98 | parentSchema: mySchema, 99 | parentId: data[0].id 100 | }); 101 | updateAction1 = new actions.UpdateData({ 102 | id: data[0].id, 103 | changes, 104 | schema: mySchema 105 | }); 106 | }); 107 | 108 | describe('SetData', () => { 109 | it('should set data in the store', () => { 110 | const state = reducer.normalized(undefined, setAction1); 111 | state.entities.should.have.properties('parent', 'child'); 112 | Object.keys(state.entities.parent).should.eql([data[1].id]); 113 | Object.keys(state.entities.child).should.eql( 114 | data[1].childs.map(c => c.id) 115 | ); 116 | state.result.should.eql([data[1].id]); 117 | }); 118 | 119 | it('should overwrite data in the store', () => { 120 | let state = reducer.normalized(undefined, addAction1); 121 | state = reducer.normalized(state, setAction1); 122 | state.entities.should.have.properties('parent', 'child'); 123 | Object.keys(state.entities.parent).should.eql([data[1].id]); 124 | Object.keys(state.entities.child).should.eql( 125 | data[1].childs.map(c => c.id) 126 | ); 127 | state.result.should.eql([data[1].id]); 128 | }); 129 | }); 130 | 131 | describe('AddData', () => { 132 | it('should add data to the store', () => { 133 | const state = reducer.normalized(undefined, addAction1); 134 | state.entities.should.have.properties('parent', 'child'); 135 | Object.keys(state.entities.parent).should.eql([data[0].id]); 136 | Object.keys(state.entities.child).should.eql( 137 | data[0].childs.map(c => c.id) 138 | ); 139 | state.result.should.eql([data[0].id]); 140 | }); 141 | 142 | it('should update data in the store', () => { 143 | let state = reducer.normalized(undefined, addAction1); 144 | state = reducer.normalized(state, addAction2); 145 | state.entities.should.have.properties('parent', 'child'); 146 | state.entities.parent.should.have.properties(data.map(d => d.id)); 147 | state.result.should.eql([data[1].id]); 148 | }); 149 | }); 150 | 151 | describe('AddChildData', () => { 152 | it('should add child data to the store', () => { 153 | let state = reducer.normalized(undefined, addAction1); 154 | state = reducer.normalized(state, addChildAction1); 155 | state.entities.should.have.properties('parent', 'child'); 156 | Object.keys(state.entities.parent).should.eql([data[0].id]); 157 | state.entities.parent[data[0].id].childs.should.containDeep( 158 | [...childData, ...data[0].childs].map(c => c.id) 159 | ); 160 | Object.keys(state.entities.child).should.containDeep( 161 | [...childData, ...data[0].childs].map(c => c.id) 162 | ); 163 | state.result.should.eql(childData.map(c => c.id)); 164 | }); 165 | }); 166 | 167 | describe('UpdateData', () => { 168 | it('should update an entity in the store', () => { 169 | let state = reducer.normalized(undefined, addAction1); 170 | state = reducer.normalized(state, updateAction1); 171 | state.entities.should.have.properties('parent', 'child'); 172 | state.entities.parent.should.have.properties(data[0].id); 173 | state.entities.parent[data[0].id].should.eql({ 174 | newProperty: changes.newProperty, 175 | id: data[0].id, 176 | property: data[0].property, 177 | childs: [...data[0].childs, ...childData].map(c => c.id) 178 | }); 179 | }); 180 | 181 | it('should update added child data in the store', () => { 182 | let state = reducer.normalized(undefined, addAction1); 183 | state = reducer.normalized(state, updateAction1); 184 | state.entities.should.have.properties('parent', 'child'); 185 | state.result.should.eql([data[0].id]); 186 | state.entities.parent[data[0].id].childs.should.eql( 187 | [...data[0].childs, ...childData].map(c => c.id) 188 | ); 189 | 190 | Object.keys(state.entities.child).should.eql( 191 | [...data[0].childs, ...childData].map(c => c.id) 192 | ); 193 | }); 194 | }); 195 | 196 | describe('RemoveData', () => { 197 | it('should remove data from the store', () => { 198 | let state = reducer.normalized(undefined, addAction1); 199 | state = reducer.normalized( 200 | state, 201 | new actions.RemoveData({ id: data[0].id, schema: mySchema }) 202 | ); 203 | should(state.entities.parent[data[0].id]).be.undefined(); 204 | data[0].childs 205 | .map(c => c.id) 206 | .forEach(id => should.exist(state.entities.child[id])); 207 | }); 208 | 209 | it('should not remove any data if an invalid id is passed', () => { 210 | let state = reducer.normalized(undefined, addAction1); 211 | state = reducer.normalized( 212 | state, 213 | new actions.RemoveData({ id: 'ZOMG', schema: mySchema }) 214 | ); 215 | state.entities.parent.should.have.properties(data[0].id); 216 | }); 217 | 218 | it('should remove data and its childs', () => { 219 | let state = reducer.normalized(undefined, addAction1); 220 | state = reducer.normalized( 221 | state, 222 | new actions.RemoveData({ 223 | id: data[0].id, 224 | schema: mySchema, 225 | removeChildren: { child: 'childs' } 226 | }) 227 | ); 228 | should(state.entities.parent[data[0].id]).be.undefined(); 229 | data[0].childs 230 | .map(c => c.id) 231 | .forEach(id => should.not.exist(state.entities.child[id])); 232 | }); 233 | 234 | it('should remove child data if its not an array', () => { 235 | const childSchema2 = new schema.Entity('child'); 236 | const mySchema2 = new schema.Entity('parent', { 237 | child: childSchema 238 | }); 239 | const denormalizedData = [ 240 | { 241 | id: '1', 242 | child: { id: '2' } 243 | } 244 | ]; 245 | const action = new actions.SetData({ 246 | data: denormalizedData, 247 | schema: mySchema2 248 | }); 249 | let state = reducer.normalized(undefined, action); 250 | state = reducer.normalized( 251 | state, 252 | new actions.RemoveData({ 253 | id: denormalizedData[0].id, 254 | schema: mySchema2, 255 | removeChildren: { child: 'child' } 256 | }) 257 | ); 258 | should(state.entities.parent[denormalizedData[0].id]).be.undefined(); 259 | should( 260 | state.entities.child[denormalizedData[0].child.id] 261 | ).be.undefined(); 262 | }); 263 | }); 264 | 265 | describe('RemoveChildData', () => { 266 | it('should remove child data from the store', () => { 267 | let state = reducer.normalized(undefined, addAction1); 268 | state = reducer.normalized(state, removeChildAction1); 269 | should(state.entities.parent[data[0].id]).be.ok(); 270 | should(state.entities.child[data[0].childs[0].id]).not.be.ok(); 271 | state.entities.parent[data[0].id].childs.should.not.containDeep( 272 | data[0].childs[0].id 273 | ); 274 | }); 275 | }); 276 | }); 277 | }); 278 | 279 | describe('exported selectors', () => { 280 | let add; 281 | 282 | beforeEach(() => { 283 | add = new actions.AddData({ data: [data[0]], schema: mySchema }); 284 | }); 285 | 286 | describe('getResult', () => { 287 | it('should be exported', () => { 288 | should(reducer.getResult).not.be.undefined(); 289 | reducer.getResult.should.be.a.Function(); 290 | }); 291 | 292 | it('should return the result', () => { 293 | const add = new actions.AddData({ data: [data[0]], schema: mySchema }); 294 | const state = reducer.normalized(undefined, add); 295 | const result = reducer.getResult({ normalized: state }); 296 | result.should.eql(state.result); 297 | }); 298 | }); 299 | 300 | describe('getNormalizedEntities', () => { 301 | it('should be exported', () => { 302 | should(reducer.getNormalizedEntities).not.be.undefined(); 303 | reducer.getNormalizedEntities.should.be.a.Function(); 304 | }); 305 | 306 | it('should return the normalized entity state', () => { 307 | const normalized = reducer.normalized(undefined, add); 308 | const denormalized = reducer.getNormalizedEntities({ normalized }); 309 | denormalized.should.be.an.Object(); 310 | denormalized.should.have.properties('parent', 'child'); 311 | }); 312 | }); 313 | }); 314 | 315 | describe('create schema selectors', () => { 316 | let add, addAll, selectors; 317 | 318 | beforeEach(() => { 319 | selectors = reducer.createSchemaSelectors(mySchema); 320 | add = new actions.AddData({ data: [data[0]], schema: mySchema }); 321 | addAll = new actions.AddData({ data, schema: mySchema }); 322 | }); 323 | 324 | it('should exist as a function', () => { 325 | reducer.createSchemaSelectors.should.be.a.Function(); 326 | reducer.createSchemaSelectors.should.have.lengthOf(1); 327 | }); 328 | 329 | describe('schema selectors', () => { 330 | it('should return schema selectors', () => { 331 | selectors.should.have.properties( 332 | 'getNormalizedEntities', 333 | 'getEntities' 334 | ); 335 | selectors.getNormalizedEntities.should.be.a.Function(); 336 | selectors.getEntities.should.be.a.Function(); 337 | }); 338 | 339 | describe('getEntities', () => { 340 | it('should return the denormalized entities', () => { 341 | const normalized = reducer.normalized(undefined, add); 342 | const denormalized = selectors.getEntities({ normalized }); 343 | denormalized.should.be.an.Array(); 344 | denormalized[0].should.eql(data[0]); 345 | }); 346 | }); 347 | }); 348 | 349 | describe('projector functions', () => { 350 | let projectors; 351 | 352 | beforeEach(() => { 353 | projectors = reducer.createSchemaSelectors(mySchema); 354 | }); 355 | 356 | it('should return schema projectors', () => { 357 | projectors.should.have.properties( 358 | 'entityProjector', 359 | 'entitiesProjector' 360 | ); 361 | projectors.entityProjector.should.be.a.Function(); 362 | projectors.entitiesProjector.should.be.a.Function(); 363 | }); 364 | 365 | describe('entitiesProjector', () => { 366 | it('should take entities and optionally ids as arguments', () => { 367 | projectors.entitiesProjector.should.have.lengthOf(2); 368 | }); 369 | 370 | it('should project the given schema to denormalized entities', () => { 371 | const normalized = reducer.normalized(undefined, addAll); 372 | const denormalized = projectors.entitiesProjector( 373 | normalized.entities 374 | ); 375 | denormalized.should.eql(data); 376 | }); 377 | }); 378 | 379 | describe('entityProjector', () => { 380 | it('should take entities and an id as an argument', () => { 381 | projectors.entityProjector.should.have.lengthOf(2); 382 | }); 383 | 384 | it('should project the given schema to one denormalized entitiy', () => { 385 | const normalized = reducer.normalized(undefined, addAll); 386 | const denormalized = projectors.entityProjector( 387 | normalized.entities, 388 | data[1].id 389 | ); 390 | denormalized.should.eql(data[1]); 391 | }); 392 | }); 393 | }); 394 | }); 395 | }); 396 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ngrx-normalizr 2 | 3 | [![Build Status](https://travis-ci.org/michaelkrone/ngrx-normalizr.svg?branch=master)](https://travis-ci.org/michaelkrone/ngrx-normalizr) 4 | ![AOT compatible](https://img.shields.io/badge/aot-compatible-blue.svg) 5 | 6 | > Managing [normalized state](https://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html) in [ngrx](https://github.com/ngrx/platform) applications, transparently. 7 | 8 | This package provides a set of actions, reducers and selectors for handling normalization and denormalization of state data **transparently**. 9 | *ngrx-normalizr* uses [normalizr](https://github.com/paularmstrong/normalizr) for [normalizing](https://github.com/paularmstrong/normalizr/blob/master/docs/api.md#normalizedata-schema) and [denormalizing](https://github.com/paularmstrong/normalizr/blob/master/docs/api.md#denormalizeinput-schema-entities) data. All normalization and denormalization 10 | is defined by the use of [normalizr schemas](https://github.com/paularmstrong/normalizr/blob/master/docs/api.md#schema), since that's the way normalizr works. This enables selectors to use a transparent and powerful projection of state data. 11 | 12 | > Releases will be published from the [`master`](https://github.com/michaelkrone/ngrx-normalizr/tree/master) branch. [Go there](https://github.com/michaelkrone/ngrx-normalizr/tree/master) for documentation that aligns with the npm repo version. 13 | 14 | ## Installation 15 | To install this package: 16 | ```sh 17 | yarn add ngrx-normalizr 18 | npm i ngrx-normalizr 19 | ``` 20 | 21 | ### Peer dependencies 22 | *ngrx-normalizr* [@ngrx-store](https://github.com/ngrx/platform/blob/master/docs/store/README.md) as its peer dependencies, so you need to install them if not present already: 23 | 24 | > *ngrx-normalizr* itself does not rely on any [Angular](https://angular.io) feature. 25 | 26 | ```sh 27 | yarn add @ngrx/store 28 | npm i @ngrx/store 29 | ``` 30 | 31 | ## Usage 32 | Also refer to the [Typedoc documentation](https://michaelkrone.github.io/ngrx-normalizr/). 33 | To enable the normalizing reducer to store normalized data, you have to add it to your state. The best place for this might be the root state of your application, but feature states may use their own normalized state as well. Extend your state interface with the `NormalizedState` interface. The`ActionReducerMap` has to implement a reducer which reduces the state to a `NormalizedState`. 34 | 35 | ```javascript 36 | import { ActionReducerMap } from '@ngrx/store'; 37 | import { NormalizedState, normalized } from 'ngrx-normalizr'; 38 | 39 | export interface State extends NormalizedState { 40 | /* ... other state properties */ 41 | } 42 | 43 | export const reducers: ActionReducerMap = { 44 | normalized, 45 | /* ... other state reducers */ 46 | }; 47 | ``` 48 | 49 | If there are no other state properties, it is sufficient to add the *ngrx-normalizr* reducer to your state reducers or simply pass it to `StoreModule.forRoot`. 50 | ```javascript 51 | export const reducers: ActionReducerMap = { normalized }; 52 | ``` 53 | 54 | Now you have a `normalized` state property which will hold the normalized data. Do not worry about the weird name, 55 | you will not have to deal with it. 56 | 57 | ### Schemas 58 | [Schemas](https://github.com/paularmstrong/normalizr/blob/master/docs/api.md#schema) define the relations of your data. 59 | In order to normalize and denormalize data, normalizr needs to be feed with a schema. In this example, a user might have 60 | an array of pets: 61 | ```javascript 62 | import { schema } from 'normalizr'; 63 | 64 | export class Pet { 65 | id: string; 66 | name: string; 67 | type: 'cat' | 'dog'; 68 | } 69 | 70 | export class User { 71 | id: string; 72 | name: string; 73 | pets: Pet[]; 74 | } 75 | 76 | export const petSchema = new schema.Entity('pets'); 77 | export const userSchema = new schema.Entity('users', { pets: [petSchema] }); 78 | ``` 79 | 80 | ## Add, set and remove data 81 | Actions are used to set data in - and remove data from - the normalized store. 82 | 83 | ### Adding data 84 | To add data and automatically normalize it, *ngrx-normalizr* provides a `AddData` action. This action takes an object with `data` and `schema` as an argument. Entities are identified by their id attribute set in the passed schema. 85 | Existing entities will be overwritten by updated data, new entities will be added to the store. For adding related childs, an `AddChildData` action is provided. 86 | 87 | ###### Using `AddData` in an effect 88 | ```javascript 89 | @Effect() 90 | loadEffect$ = this.actions$ 91 | .ofType(LOAD) 92 | .switchMap(action => this.http.get('https://example.com/api/user')) 93 | .mergeMap((data: User[]) => [ 94 | // dispatch to add data to the store 95 | new AddData({ data, schema }), 96 | // dispatch to inform feature reducer 97 | new LoadSuccess(data) 98 | ]) 99 | .catch(err => Observable.of(new LoadFail(err))); 100 | ``` 101 | 102 | #### Adding child data 103 | Adding a related child data to a parent entity can be done with the `AddChildData` action. Note that for this to work, the relation has to be defined in the schema. The action takes a couple of arguments which need to be given in an object: 104 | 105 | * `data`: Array of child entities to add 106 | * `childSchema`The `schema.Entity` of the child entity 107 | * `parentSchema`: The `schema.Entity` of the parent entity 108 | * `parentId`: The id of the entity to add child references to 109 | 110 | ###### Using `AddChildData` in an effect 111 | ```javascript 112 | @Effect() 113 | addPetEffect$ = this.actions$ 114 | .ofType(ADD_PET) 115 | .switchMap(action => this.http.post('https://example.com/api/pets')) 116 | .mergeMap((data: Pet[]) => [ 117 | // dispatch to add data to the store 118 | new AddChildData({ data, childSchema, parentSchema, parentId }), 119 | // dispatch to inform feature reducer 120 | new AddPetSuccess(data) 121 | ]) 122 | .catch(err => Observable.of(new LoadFail(err))); 123 | ``` 124 | 125 | ### Setting data 126 | The `SetData` action will overwrite all entities for a given schema with the normalized entities of the `data` property of the action constructor argument. This action can 127 | be used for resetting entity state data instead of adding and updating existing entities. 128 | 129 | ### Removing data 130 | To remove data, *ngrx-normalizr* provides a `RemoveData` action. 131 | This action takes an object with `id`, `schema` and an optional `removeChildren` property as constructor argument. The schema entity with the given id will be removed. If `removeChildren` is a map of the schema key mapped to an object property, all referenced child entities will also be removed from the store. This is handy for 1:1 relations, since only removing the parent entity may leave unused child entities in the store. 132 | 133 | ###### Using `RemoveData` in an effect 134 | ```javascript 135 | @Effect() 136 | removeEffect$ = this.actions$ 137 | .ofType(REMOVE) 138 | .switchMap((action: Remove) => this.http.delete(`https://example.com/api/user/${action.payload.id}`)) 139 | .mergeMap(result => [ 140 | // dispatch to remove data from the store 141 | new RemoveData({ id: result.id, schema, removeChildren: { pets: 'pets' } }), 142 | // dispatch to inform feature reducer 143 | new RemoveSuccess() 144 | ]) 145 | .catch(err => Observable.of(new RemoveFail(err))); 146 | ``` 147 | #### Removing child data 148 | Removing a child entity which is 1:1 related to a parent entity can be done with the `RemoveChildData` action. Note that for this to work, the relation has to be defined in the schema. The action takes a couple of arguments which need to be given in an object: 149 | 150 | * `id`: Id of the child entity that should be removed 151 | * `childSchema`The `schema.Entity` of the child entity 152 | * `parentSchema`: The `schema.Entity` of the parent entity 153 | * `parentId`: The id of the entity to remove child references from 154 | 155 | ###### Using `AddChildData` in an effect 156 | ```javascript 157 | @Effect() 158 | removePetEffect$ = this.actions$ 159 | .ofType(REMOVE_PET) 160 | .switchMap(action => this.http.remove(`https://example.com/api/pets/${action.payload.id}`)) 161 | .mergeMap((data: Pet) => [ 162 | // dispatch to add data to the store 163 | new RemoveChildData({ id: data.id, childSchema, parentSchema, parentId }), 164 | // dispatch to inform feature reducer 165 | new RemovePetSuccess(data) 166 | ]) 167 | .catch(err => Observable.of(new LoadFail(err))); 168 | ``` 169 | 170 | ### Action creators 171 | For convenience, *ngrx-normalizr* provides an `actionCreators` function which will return an object with following schema bound action creators: 172 | * `setData` - `(data: T[]) => SetData` 173 | * `addData` - `(data: T[]) => AddData` 174 | * `addChildData` - `(data: C[], childSchema: schema.Entity, parentId: string) => AddChildData` 175 | * `removeData` - `(id: string, removeChildren?: SchemaMap) => RemoveData` 176 | * `removeChildData` - `(id: string, childSchema: schema.Entity, parentId: string) => RemoveChildData` 177 | 178 | Action creators could be exported along whith other feature actions: 179 | ```javascript 180 | import { actionCreators } from 'ngrx-normalizr'; 181 | 182 | const creators = actionCreators(userSchema); 183 | export const setUserData = creators.setData; 184 | export const addUserData = creators.addData; 185 | export const removeUserData = creators.removeData; 186 | ``` 187 | 188 | Using the action creator in an Effect class: 189 | 190 | ###### Using the `removeUserData` action creator in an effect 191 | ```javascript 192 | import { removeUserData } from '../actions'; 193 | 194 | @Effect() 195 | removeEffect$ = this.actions$ 196 | .ofType(REMOVE) 197 | .switchMap((action: Remove) => this.http.delete(`https://example.com/api/user/${action.payload.id}`)) 198 | .mergeMap(result => [ 199 | // dispatch to remove data from the store 200 | removeUserData(id: result.id, { pets: 'pets' }), 201 | // dispatch to inform feature reducer 202 | new RemoveSuccess() 203 | ]) 204 | .catch(err => Observable.of(new RemoveFail(err))); 205 | ``` 206 | 207 | ## Query state data 208 | *ngrx-normalizr* provides two simple selectors and two simple projector functions to query the state and project/denormalize the result. 209 | 210 | ### Creating Schema selectors 211 | To transparently query data from the store from a feature module, selectors are provided by the `createSchemaSelectors` function. 212 | It takes an entity schema to create schema bound selectors: 213 | 214 | ```javascript 215 | import { createSchemaSelectors } from 'ngrx-normalizr'; 216 | import { User } from '../classes/user'; 217 | 218 | const schemaSelectors = createSchemaSelectors(userSchema); 219 | ``` 220 | 221 | `createSchemaSelectors` will return schema bound selectors (instance of `SchemaSelectors`): 222 | * `getEntities` - ` MemoizedSelector<{}, T[]>` Returns all denormalized entities for the schema 223 | * `getNormalizedEntities` - `MemoizedSelector` Returns all normalized (raw) state entities of every schema (the whole entities state) 224 | * `entitiesProjector` - `(entities: {}, ids?: Array) => T[]` Projector function for denormalizing a the set of normalized entities to an denormalized entity array. If no `ids` are given, all entities will be denormalized. 225 | * `entityProjector` - `(entities: {}, id: string) => T` Projector function for denormalizing a single normalized entity with the given id 226 | 227 | You might create several selectors with several schemas, i.e. a *listView* schema, which only denormalizes the data used in the list 228 | view, and a *detailView* schema, to completely denormalize a given entity. 229 | 230 | ### Using schema selectors 231 | Feature selectors can use the schema bound selectors and projector functions to query entity data from the store. To get all denormalized 232 | entities, you might simply use the `getEntities` selector like this: 233 | 234 | ```javascript 235 | // store.select(getUsers) will give all denormalized user entities 236 | const getUsers = schemaSelectors.getEntities; 237 | ``` 238 | Under the hood this does something similar to this: 239 | ```javascript 240 | // equivalent alternative 241 | const getUsers = createSelector( 242 | schemaSelectors.getNormalizedEntities, 243 | schemaSelectors.entitiesProjector 244 | ); 245 | 246 | ``` 247 | The `entitiesProjector` simply takes an object of normalized entity data and applies the denormalization with the bound schema. Optionally an array of id strings can be passed as a second parameter to perform denormalization for the given id's only. 248 | 249 | #### Composing schema selectors 250 | To query and denormalize specific data you can use the *@ngrx/store* [`createSelectors`](https://github.com/ngrx/platform/blob/master/docs/store/selectors.md#createselector) function and compose them with the schema bound 251 | selectors: 252 | 253 | ```javascript 254 | import { createSelector } from '@ngrx/store'; 255 | 256 | export const getSelectedUserId = createSelector( 257 | userFeatureSelector, 258 | user.getSelectedId 259 | ); 260 | 261 | // store.select(getSelectedUser) will give the denormalized selected user 262 | const getSelectedUser = createSelector( 263 | schemaSelectors.getNormalizedEntities, 264 | getSelectedUserId, 265 | schemaSelectors.entityProjector 266 | ); 267 | ``` 268 | `entityProjector` will simply take an object of denormalized entities and apply the denormalization with the bound schema only for the given id. Note that you might also select data from the denormalized result and providing your own selector: 269 | ```javascript 270 | const getSelectedUserWithPetsOnly = createSelector( 271 | getUsers, 272 | getSelectedId, 273 | (entities, id) => entities.find(e => e.id === id && e.pets.length > 0) 274 | ); 275 | ``` 276 | 277 | ## Meta 278 | 279 | Michael Krone – [@DevDig](https://twitter.com/DevDig) – michael.krone@outlook.com and all [CONTRIBUTORS](https://github.com/michaelkrone/ngrx-normalizr/graphs/contributors) 280 | 281 | Distributed under the MIT license. See [``LICENSE``](https://github.com/michaelkrone/ngrx-normalizr/blob/master/LICENSE) for more information. 282 | 283 | [https://github.com/michaelkrone/ngrx-normalizr](https://github.com/michaelkrone/ngrx-normalizr) 284 | 285 | ## Contributing 286 | 287 | 1. Fork it () 288 | 2. Create your feature branch (`git checkout -b feature/fooBar`) 289 | 3. Commit your changes (`git commit -am 'Add some fooBar'`) 290 | 4. Push to the branch (`git push origin feature/fooBar`) 291 | 5. Create a new [Pull Request](https://github.com/michaelkrone/ngrx-normalizr/compare?expand=1) 292 | -------------------------------------------------------------------------------- /docs/interfaces/_reducers_normalize_.entitymap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | EntityMap | ngrx-normalizr 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 65 |

Interface EntityMap

66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |

Interface describing the entities propery of a normalized state. 76 | A map of schema keys wich map to a map of entity id's to entity data. 77 | This corresponds to the entities property of a normalizr.normalize result.

78 |
79 |
80 |
81 |
82 |

Hierarchy

83 |
    84 |
  • 85 | EntityMap 86 |
  • 87 |
88 |
89 |
90 |

Indexable

91 |
[key: string]: object
92 |
93 |
94 |

Interface describing the entities propery of a normalized state. 95 | A map of schema keys wich map to a map of entity id's to entity data. 96 | This corresponds to the entities property of a normalizr.normalize result.

97 |
98 |
99 |
    100 |
  • 101 |
    [id: string]: any
    102 |
  • 103 |
104 |
105 |
106 | 189 |
190 |
191 |
192 |
193 |

Legend

194 |
195 |
    196 |
  • Module
  • 197 |
  • Object literal
  • 198 |
  • Variable
  • 199 |
  • Function
  • 200 |
  • Function with type parameter
  • 201 |
  • Index signature
  • 202 |
  • Type alias
  • 203 |
204 |
    205 |
  • Enumeration
  • 206 |
  • Enumeration member
  • 207 |
  • Property
  • 208 |
  • Method
  • 209 |
210 |
    211 |
  • Interface
  • 212 |
  • Interface with type parameter
  • 213 |
  • Constructor
  • 214 |
  • Property
  • 215 |
  • Method
  • 216 |
  • Index signature
  • 217 |
218 |
    219 |
  • Class
  • 220 |
  • Class with type parameter
  • 221 |
  • Constructor
  • 222 |
  • Property
  • 223 |
  • Method
  • 224 |
  • Accessor
  • 225 |
  • Index signature
  • 226 |
227 |
    228 |
  • Inherited constructor
  • 229 |
  • Inherited property
  • 230 |
  • Inherited method
  • 231 |
  • Inherited accessor
  • 232 |
233 |
    234 |
  • Protected property
  • 235 |
  • Protected method
  • 236 |
  • Protected accessor
  • 237 |
238 |
    239 |
  • Private property
  • 240 |
  • Private method
  • 241 |
  • Private accessor
  • 242 |
243 |
    244 |
  • Static property
  • 245 |
  • Static method
  • 246 |
247 |
248 |
249 |
250 |
251 |

Generated using TypeDoc

252 |
253 |
254 | 255 | 256 | 257 | -------------------------------------------------------------------------------- /docs/interfaces/_actions_normalize_.schemamap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SchemaMap | ngrx-normalizr 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 65 |

Interface SchemaMap

66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |

A map of schema names to object property names. 76 | Used for removing child properties of an entity.

77 |
78 |
79 |
80 |
81 |

Hierarchy

82 |
    83 |
  • 84 | SchemaMap 85 |
  • 86 |
87 |
88 |
89 |

Indexable

90 |
[schemaKey: string]: string
91 |
92 |
93 |

A map of schema names to object property names. 94 | Used for removing child properties of an entity.

95 |
96 |
97 |
98 |
99 | 197 |
198 |
199 |
200 |
201 |

Legend

202 |
203 |
    204 |
  • Module
  • 205 |
  • Object literal
  • 206 |
  • Variable
  • 207 |
  • Function
  • 208 |
  • Function with type parameter
  • 209 |
  • Index signature
  • 210 |
  • Type alias
  • 211 |
212 |
    213 |
  • Enumeration
  • 214 |
  • Enumeration member
  • 215 |
  • Property
  • 216 |
  • Method
  • 217 |
218 |
    219 |
  • Interface
  • 220 |
  • Interface with type parameter
  • 221 |
  • Constructor
  • 222 |
  • Property
  • 223 |
  • Method
  • 224 |
  • Index signature
  • 225 |
226 |
    227 |
  • Class
  • 228 |
  • Class with type parameter
  • 229 |
  • Constructor
  • 230 |
  • Property
  • 231 |
  • Method
  • 232 |
  • Accessor
  • 233 |
  • Index signature
  • 234 |
235 |
    236 |
  • Inherited constructor
  • 237 |
  • Inherited property
  • 238 |
  • Inherited method
  • 239 |
  • Inherited accessor
  • 240 |
241 |
    242 |
  • Protected property
  • 243 |
  • Protected method
  • 244 |
  • Protected accessor
  • 245 |
246 |
    247 |
  • Private property
  • 248 |
  • Private method
  • 249 |
  • Private accessor
  • 250 |
251 |
    252 |
  • Static property
  • 253 |
  • Static method
  • 254 |
255 |
256 |
257 |
258 |
259 |

Generated using TypeDoc

260 |
261 |
262 | 263 | 264 | 265 | -------------------------------------------------------------------------------- /docs/interfaces/_reducers_normalize_.normalizedstate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | NormalizedState | ngrx-normalizr 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 65 |

Interface NormalizedState

66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |

The state interface from which the app state should extend. 76 | Holds an instance of NormalizedEntityState itself.

77 |
78 |
79 |
80 |
81 |

Hierarchy

82 |
    83 |
  • 84 | NormalizedState 85 |
  • 86 |
87 |
88 |
89 |

Index

90 |
91 |
92 |
93 |

Properties

94 | 97 |
98 |
99 |
100 |
101 |
102 |

Properties

103 |
104 | 105 |

normalized

106 | 107 | 112 |
113 |
114 |

The normalized state property

115 |
116 |
117 |
118 |
119 |
120 | 208 |
209 |
210 |
211 |
212 |

Legend

213 |
214 |
    215 |
  • Module
  • 216 |
  • Object literal
  • 217 |
  • Variable
  • 218 |
  • Function
  • 219 |
  • Function with type parameter
  • 220 |
  • Index signature
  • 221 |
  • Type alias
  • 222 |
223 |
    224 |
  • Enumeration
  • 225 |
  • Enumeration member
  • 226 |
  • Property
  • 227 |
  • Method
  • 228 |
229 |
    230 |
  • Interface
  • 231 |
  • Interface with type parameter
  • 232 |
  • Constructor
  • 233 |
  • Property
  • 234 |
  • Method
  • 235 |
  • Index signature
  • 236 |
237 |
    238 |
  • Class
  • 239 |
  • Class with type parameter
  • 240 |
  • Constructor
  • 241 |
  • Property
  • 242 |
  • Method
  • 243 |
  • Accessor
  • 244 |
  • Index signature
  • 245 |
246 |
    247 |
  • Inherited constructor
  • 248 |
  • Inherited property
  • 249 |
  • Inherited method
  • 250 |
  • Inherited accessor
  • 251 |
252 |
    253 |
  • Protected property
  • 254 |
  • Protected method
  • 255 |
  • Protected accessor
  • 256 |
257 |
    258 |
  • Private property
  • 259 |
  • Private method
  • 260 |
  • Private accessor
  • 261 |
262 |
    263 |
  • Static property
  • 264 |
  • Static method
  • 265 |
266 |
267 |
268 |
269 |
270 |

Generated using TypeDoc

271 |
272 |
273 | 274 | 275 | 276 | -------------------------------------------------------------------------------- /docs/interfaces/_reducers_normalize_.normalizedentitystate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | NormalizedEntityState | ngrx-normalizr 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 65 |

Interface NormalizedEntityState

66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |

The normalized state, representing a normalizr.normalize result. 76 | Can be selected by the provided getNormalizedEntities and getResult 77 | selectors.

78 |
79 |
80 |
81 |
82 |

Hierarchy

83 |
    84 |
  • 85 | NormalizedEntityState 86 |
  • 87 |
88 |
89 |
90 |

Index

91 |
92 |
93 |
94 |

Properties

95 | 99 |
100 |
101 |
102 |
103 |
104 |

Properties

105 |
106 | 107 |

entities

108 |
entities: EntityMap
109 | 114 |
115 |
116 |

The normalized entities. Should be passed to all projector functions 117 | to enable access to all entities needed.

118 |
119 |
120 |
121 |
122 | 123 |

result

124 |
result: string[]
125 | 130 |
131 |
132 |

The original sorting of the unnormalized data. 133 | Holds all id's of the last set operation in original order. 134 | Can be used to restore the original sorting of entities

135 |
136 |
137 |
138 |
139 |
140 | 231 |
232 |
233 |
234 |
235 |

Legend

236 |
237 |
    238 |
  • Module
  • 239 |
  • Object literal
  • 240 |
  • Variable
  • 241 |
  • Function
  • 242 |
  • Function with type parameter
  • 243 |
  • Index signature
  • 244 |
  • Type alias
  • 245 |
246 |
    247 |
  • Enumeration
  • 248 |
  • Enumeration member
  • 249 |
  • Property
  • 250 |
  • Method
  • 251 |
252 |
    253 |
  • Interface
  • 254 |
  • Interface with type parameter
  • 255 |
  • Constructor
  • 256 |
  • Property
  • 257 |
  • Method
  • 258 |
  • Index signature
  • 259 |
260 |
    261 |
  • Class
  • 262 |
  • Class with type parameter
  • 263 |
  • Constructor
  • 264 |
  • Property
  • 265 |
  • Method
  • 266 |
  • Accessor
  • 267 |
  • Index signature
  • 268 |
269 |
    270 |
  • Inherited constructor
  • 271 |
  • Inherited property
  • 272 |
  • Inherited method
  • 273 |
  • Inherited accessor
  • 274 |
275 |
    276 |
  • Protected property
  • 277 |
  • Protected method
  • 278 |
  • Protected accessor
  • 279 |
280 |
    281 |
  • Private property
  • 282 |
  • Private method
  • 283 |
  • Private accessor
  • 284 |
285 |
    286 |
  • Static property
  • 287 |
  • Static method
  • 288 |
289 |
290 |
291 |
292 |
293 |

Generated using TypeDoc

294 |
295 |
296 | 297 | 298 | 299 | -------------------------------------------------------------------------------- /docs/interfaces/_actions_normalize_.normalizeactionschemaconfig.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | NormalizeActionSchemaConfig | ngrx-normalizr 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 65 |

Interface NormalizeActionSchemaConfig

66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |

Base interface for AddData, and RemoveData action payload.

76 |
77 |
78 |
79 |
80 |

Hierarchy

81 | 94 |
95 |
96 |

Index

97 |
98 |
99 |
100 |

Properties

101 | 104 |
105 |
106 |
107 |
108 |
109 |

Properties

110 |
111 | 112 |

schema

113 |
schema: Entity
114 | 119 |
120 |
121 |

Schema definition of the entity. Used for de-/ and normalizing given entities.

122 |
123 |
124 |
125 |
126 |
127 | 230 |
231 |
232 |
233 |
234 |

Legend

235 |
236 |
    237 |
  • Module
  • 238 |
  • Object literal
  • 239 |
  • Variable
  • 240 |
  • Function
  • 241 |
  • Function with type parameter
  • 242 |
  • Index signature
  • 243 |
  • Type alias
  • 244 |
245 |
    246 |
  • Enumeration
  • 247 |
  • Enumeration member
  • 248 |
  • Property
  • 249 |
  • Method
  • 250 |
251 |
    252 |
  • Interface
  • 253 |
  • Interface with type parameter
  • 254 |
  • Constructor
  • 255 |
  • Property
  • 256 |
  • Method
  • 257 |
  • Index signature
  • 258 |
259 |
    260 |
  • Class
  • 261 |
  • Class with type parameter
  • 262 |
  • Constructor
  • 263 |
  • Property
  • 264 |
  • Method
  • 265 |
  • Accessor
  • 266 |
  • Index signature
  • 267 |
268 |
    269 |
  • Inherited constructor
  • 270 |
  • Inherited property
  • 271 |
  • Inherited method
  • 272 |
  • Inherited accessor
  • 273 |
274 |
    275 |
  • Protected property
  • 276 |
  • Protected method
  • 277 |
  • Protected accessor
  • 278 |
279 |
    280 |
  • Private property
  • 281 |
  • Private method
  • 282 |
  • Private accessor
  • 283 |
284 |
    285 |
  • Static property
  • 286 |
  • Static method
  • 287 |
288 |
289 |
290 |
291 |
292 |

Generated using TypeDoc

293 |
294 |
295 | 296 | 297 | 298 | -------------------------------------------------------------------------------- /docs/interfaces/_actions_normalize_.normalizechildactionschemaconfig.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | NormalizeChildActionSchemaConfig | ngrx-normalizr 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 65 |

Interface NormalizeChildActionSchemaConfig

66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |

Base interface for AddChildDataandRemoveChildData` action payload.

76 |
77 |
78 |
79 |
80 |

Hierarchy

81 | 94 |
95 |
96 |

Index

97 |
98 |
99 |
100 |

Properties

101 | 104 |
105 |
106 |
107 |
108 |
109 |

Properties

110 |
111 | 112 |

parentSchema

113 |
parentSchema: Entity
114 | 119 |
120 |
121 |

Schema definition of the entity. Used for de-/ and normalizing given entities.

122 |
123 |
124 |
125 |
126 |
127 | 230 |
231 |
232 |
233 |
234 |

Legend

235 |
236 |
    237 |
  • Module
  • 238 |
  • Object literal
  • 239 |
  • Variable
  • 240 |
  • Function
  • 241 |
  • Function with type parameter
  • 242 |
  • Index signature
  • 243 |
  • Type alias
  • 244 |
245 |
    246 |
  • Enumeration
  • 247 |
  • Enumeration member
  • 248 |
  • Property
  • 249 |
  • Method
  • 250 |
251 |
    252 |
  • Interface
  • 253 |
  • Interface with type parameter
  • 254 |
  • Constructor
  • 255 |
  • Property
  • 256 |
  • Method
  • 257 |
  • Index signature
  • 258 |
259 |
    260 |
  • Class
  • 261 |
  • Class with type parameter
  • 262 |
  • Constructor
  • 263 |
  • Property
  • 264 |
  • Method
  • 265 |
  • Accessor
  • 266 |
  • Index signature
  • 267 |
268 |
    269 |
  • Inherited constructor
  • 270 |
  • Inherited property
  • 271 |
  • Inherited method
  • 272 |
  • Inherited accessor
  • 273 |
274 |
    275 |
  • Protected property
  • 276 |
  • Protected method
  • 277 |
  • Protected accessor
  • 278 |
279 |
    280 |
  • Private property
  • 281 |
  • Private method
  • 282 |
  • Private accessor
  • 283 |
284 |
    285 |
  • Static property
  • 286 |
  • Static method
  • 287 |
288 |
289 |
290 |
291 |
292 |

Generated using TypeDoc

293 |
294 |
295 | 296 | 297 | 298 | -------------------------------------------------------------------------------- /docs/interfaces/_actions_normalize_.normalizeactionpayload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | NormalizeActionPayload | ngrx-normalizr 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 65 |

Interface NormalizeActionPayload

66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |

Interface for a normalize action payload

76 |
77 |
78 |
79 |
80 |

Hierarchy

81 | 91 |
92 |
93 |

Index

94 |
95 |
96 |
97 |

Properties

98 | 102 |
103 |
104 |
105 |
106 |
107 |

Properties

108 |
109 | 110 |

entities

111 |
entities: EntityMap
112 | 117 |
118 |
119 |

The normalized entities mapped to their schema keys

120 |
121 |
122 |
123 |
124 | 125 |

result

126 |
result: string[]
127 | 132 |
133 |
134 |

The original sorted id's as an array

135 |
136 |
137 |
138 |
139 |
140 | 246 |
247 |
248 |
249 |
250 |

Legend

251 |
252 |
    253 |
  • Module
  • 254 |
  • Object literal
  • 255 |
  • Variable
  • 256 |
  • Function
  • 257 |
  • Function with type parameter
  • 258 |
  • Index signature
  • 259 |
  • Type alias
  • 260 |
261 |
    262 |
  • Enumeration
  • 263 |
  • Enumeration member
  • 264 |
  • Property
  • 265 |
  • Method
  • 266 |
267 |
    268 |
  • Interface
  • 269 |
  • Interface with type parameter
  • 270 |
  • Constructor
  • 271 |
  • Property
  • 272 |
  • Method
  • 273 |
  • Index signature
  • 274 |
275 |
    276 |
  • Class
  • 277 |
  • Class with type parameter
  • 278 |
  • Constructor
  • 279 |
  • Property
  • 280 |
  • Method
  • 281 |
  • Accessor
  • 282 |
  • Index signature
  • 283 |
284 |
    285 |
  • Inherited constructor
  • 286 |
  • Inherited property
  • 287 |
  • Inherited method
  • 288 |
  • Inherited accessor
  • 289 |
290 |
    291 |
  • Protected property
  • 292 |
  • Protected method
  • 293 |
  • Protected accessor
  • 294 |
295 |
    296 |
  • Private property
  • 297 |
  • Private method
  • 298 |
  • Private accessor
  • 299 |
300 |
    301 |
  • Static property
  • 302 |
  • Static method
  • 303 |
304 |
305 |
306 |
307 |
308 |

Generated using TypeDoc

309 |
310 |
311 | 312 | 313 | 314 | -------------------------------------------------------------------------------- /docs/interfaces/_actions_normalize_.normalizeactionconfig.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | NormalizeActionConfig | ngrx-normalizr 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 65 |

Interface NormalizeActionConfig<T>

66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |

Typed Interface for the config of the AddData and SetData action. 76 | Holds an typed array of entities to be added to the store.

77 |
78 |
79 |
80 |
81 |

Type parameters

82 |
    83 |
  • 84 |

    T

    85 |
  • 86 |
87 |
88 |
89 |

Hierarchy

90 | 100 |
101 |
102 |

Index

103 |
104 |
105 |
106 |

Properties

107 | 111 |
112 |
113 |
114 |
115 |
116 |

Properties

117 |
118 | 119 |

data

120 |
data: T[]
121 | 126 |
127 |
128 |

The array of entities which should be normalized and added to the store.

129 |
130 |
131 |
132 |
133 | 134 |

schema

135 |
schema: Entity
136 | 142 |
143 |
144 |

Schema definition of the entity. Used for de-/ and normalizing given entities.

145 |
146 |
147 |
148 |
149 |
150 | 256 |
257 |
258 |
259 |
260 |

Legend

261 |
262 |
    263 |
  • Module
  • 264 |
  • Object literal
  • 265 |
  • Variable
  • 266 |
  • Function
  • 267 |
  • Function with type parameter
  • 268 |
  • Index signature
  • 269 |
  • Type alias
  • 270 |
271 |
    272 |
  • Enumeration
  • 273 |
  • Enumeration member
  • 274 |
  • Property
  • 275 |
  • Method
  • 276 |
277 |
    278 |
  • Interface
  • 279 |
  • Interface with type parameter
  • 280 |
  • Constructor
  • 281 |
  • Property
  • 282 |
  • Method
  • 283 |
  • Index signature
  • 284 |
285 |
    286 |
  • Class
  • 287 |
  • Class with type parameter
  • 288 |
  • Constructor
  • 289 |
  • Property
  • 290 |
  • Method
  • 291 |
  • Accessor
  • 292 |
  • Index signature
  • 293 |
294 |
    295 |
  • Inherited constructor
  • 296 |
  • Inherited property
  • 297 |
  • Inherited method
  • 298 |
  • Inherited accessor
  • 299 |
300 |
    301 |
  • Protected property
  • 302 |
  • Protected method
  • 303 |
  • Protected accessor
  • 304 |
305 |
    306 |
  • Private property
  • 307 |
  • Private method
  • 308 |
  • Private accessor
  • 309 |
310 |
    311 |
  • Static property
  • 312 |
  • Static method
  • 313 |
314 |
315 |
316 |
317 |
318 |

Generated using TypeDoc

319 |
320 |
321 | 322 | 323 | 324 | --------------------------------------------------------------------------------