├── .editorconfig
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
└── extensions.json
├── README.md
├── angular.json
├── apps
├── .gitkeep
├── api
│ ├── jest.config.js
│ ├── src
│ │ ├── app
│ │ │ ├── .gitkeep
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── cart.service.spec.ts
│ │ │ ├── cart.service.ts
│ │ │ ├── mocks
│ │ │ │ ├── products.ts
│ │ │ │ └── shipping-methods.ts
│ │ │ ├── product.service.spec.ts
│ │ │ ├── product.service.ts
│ │ │ ├── shipping.service.spec.ts
│ │ │ └── shipping.service.ts
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ └── main.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── ngrx-workshop-app-e2e
│ ├── cypress.json
│ ├── src
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── integration
│ │ │ └── app.spec.ts
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ ├── app.po.ts
│ │ │ ├── commands.ts
│ │ │ └── index.ts
│ ├── tsconfig.e2e.json
│ └── tsconfig.json
└── ngrx-workshop-app
│ ├── browserslist
│ ├── jest.config.js
│ ├── proxy.conf.json
│ ├── src
│ ├── app
│ │ ├── app-routing.module.ts
│ │ ├── app.component.css
│ │ ├── app.component.html
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── cart
│ │ │ ├── cart-routing.module.ts
│ │ │ ├── cart.module.ts
│ │ │ ├── cart
│ │ │ │ ├── cart.component.css
│ │ │ │ ├── cart.component.html
│ │ │ │ └── cart.component.ts
│ │ │ └── shipping-method-selection-dialog
│ │ │ │ ├── shipping-method-selection-dialog.component.css
│ │ │ │ ├── shipping-method-selection-dialog.component.html
│ │ │ │ ├── shipping-method-selection-dialog.component.spec.ts
│ │ │ │ └── shipping-method-selection-dialog.component.ts
│ │ ├── layout
│ │ │ ├── layout.module.ts
│ │ │ └── top-bar
│ │ │ │ ├── top-bar.component.css
│ │ │ │ ├── top-bar.component.html
│ │ │ │ └── top-bar.component.ts
│ │ ├── products
│ │ │ ├── product-details
│ │ │ │ ├── product-details.component.css
│ │ │ │ ├── product-details.component.html
│ │ │ │ └── product-details.component.ts
│ │ │ ├── product-list
│ │ │ │ ├── product-list.component.css
│ │ │ │ ├── product-list.component.html
│ │ │ │ └── product-list.component.ts
│ │ │ ├── products-routing.module.ts
│ │ │ └── products.module.ts
│ │ └── shared
│ │ │ └── state
│ │ │ ├── app
│ │ │ ├── app.actions.ts
│ │ │ └── index.ts
│ │ │ ├── cart
│ │ │ ├── cart.actions.ts
│ │ │ ├── cart.effects.spec.ts
│ │ │ ├── cart.effects.ts
│ │ │ ├── cart.module.ts
│ │ │ ├── cart.reducer.spec.ts
│ │ │ ├── cart.reducer.ts
│ │ │ ├── cart.selectors.spec.ts
│ │ │ ├── cart.selectors.ts
│ │ │ └── index.ts
│ │ │ ├── products
│ │ │ ├── index.ts
│ │ │ ├── products.actions.ts
│ │ │ ├── products.effects.spec.ts
│ │ │ ├── products.effects.ts
│ │ │ ├── products.module.ts
│ │ │ ├── products.reducer.spec.ts
│ │ │ ├── products.reducer.ts
│ │ │ ├── products.selectors.spec.ts
│ │ │ └── products.selectors.ts
│ │ │ └── shipping
│ │ │ ├── index.ts
│ │ │ ├── shipping.actions.ts
│ │ │ ├── shipping.effects.ts
│ │ │ ├── shipping.module.ts
│ │ │ ├── shipping.reducer.ts
│ │ │ └── shipping.selectors.ts
│ ├── assets
│ │ └── .gitkeep
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.css
│ └── test-setup.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── jest.config.js
├── libs
├── .gitkeep
├── api-interface
│ ├── README.md
│ ├── jest.config.js
│ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ └── interfaces.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── cart-data-access
│ ├── README.md
│ ├── jest.config.js
│ ├── src
│ │ ├── index.ts
│ │ ├── lib
│ │ │ ├── cart-data-access.module.spec.ts
│ │ │ ├── cart-data-access.module.ts
│ │ │ └── cart.service.ts
│ │ └── test-setup.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── product-data-access
│ ├── README.md
│ ├── jest.config.js
│ ├── src
│ │ ├── index.ts
│ │ ├── lib
│ │ │ ├── product-data-access.module.spec.ts
│ │ │ ├── product-data-access.module.ts
│ │ │ └── product.service.ts
│ │ └── test-setup.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ └── tslint.json
└── shipping-data-access
│ ├── README.md
│ ├── jest.config.js
│ ├── src
│ ├── index.ts
│ ├── lib
│ │ ├── shipping-data-access.module.spec.ts
│ │ ├── shipping-data-access.module.ts
│ │ └── shipping.service.ts
│ └── test-setup.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── nx.json
├── package.json
├── tools
├── schematics
│ └── .gitkeep
└── tsconfig.tools.json
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
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 |
--------------------------------------------------------------------------------
/.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 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 |
37 | # System Files
38 | .DS_Store
39 | Thumbs.db
40 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Add files here to ignore them from prettier formatting
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "nrwl.angular-console",
4 | "angular.ng-template",
5 | "ms-vscode.vscode-typescript-tslint-plugin",
6 | "esbenp.prettier-vscode"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NgrxWorkshopApp
2 |
3 | This project was generated using [Nx](https://nx.dev).
4 |
5 |

6 |
7 | 🔎 **Nx is a set of Angular CLI power-ups for modern development.**
8 |
9 | ## Quick Start & Documentation
10 |
11 | [Nx Documentation](https://nx.dev)
12 |
13 | [30-minute video showing all Nx features](https://nx.dev/getting-started/what-is-nx)
14 |
15 | [Interactive Tutorial](https://nx.dev/tutorial/01-create-application)
16 |
17 | ## Adding capabilities to your workspace
18 |
19 | Nx supports many plugins which add capabilities for developing different types of applications and different tools.
20 |
21 | These capabilities include generating applications, libraries, .etc as well as the devtools to test, and build projects as well.
22 |
23 | Below are some plugins which you can add to your workspace:
24 |
25 | - [Angular](https://angular.io)
26 | - `ng add @nrwl/angular`
27 | - [React](https://reactjs.org)
28 | - `ng add @nrwl/react`
29 | - Web (no framework frontends)
30 | - `ng add @nrwl/web`
31 | - [Nest](https://nestjs.com)
32 | - `ng add @nrwl/nest`
33 | - [Express](https://expressjs.com)
34 | - `ng add @nrwl/express`
35 | - [Node](https://nodejs.org)
36 | - `ng add @nrwl/node`
37 |
38 | ## Generate an application
39 |
40 | Run `ng g @nrwl/angular:app my-app` to generate an application.
41 |
42 | > You can use any of the plugins above to generate applications as well.
43 |
44 | When using Nx, you can create multiple applications and libraries in the same workspace.
45 |
46 | ## Generate a library
47 |
48 | Run `ng g @nrwl/angular:lib my-lib` to generate a library.
49 |
50 | > You can also use any of the plugins above to generate libraries as well.
51 |
52 | Libraries are sharable across libraries and applications. They can be imported from `@ngrx-workshop-app/mylib`.
53 |
54 | ## Development server
55 |
56 | Run `ng serve my-app` for a dev server. Navigate to http://localhost:4200/. The app will automatically reload if you change any of the source files.
57 |
58 | ## Code scaffolding
59 |
60 | Run `ng g component my-component --project=my-app` to generate a new component.
61 |
62 | ## Build
63 |
64 | Run `ng build my-app` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
65 |
66 | ## Running unit tests
67 |
68 | Run `ng test my-app` to execute the unit tests via [Jest](https://jestjs.io).
69 |
70 | Run `npm run affected:test` to execute the unit tests affected by a change.
71 |
72 | ## Running end-to-end tests
73 |
74 | Run `ng e2e my-app` to execute the end-to-end tests via [Cypress](https://www.cypress.io).
75 |
76 | Run `npm run affected:e2e` to execute the end-to-end tests affected by a change.
77 |
78 | ## Understand your workspace
79 |
80 | Run `npm run dep-graph` to see a diagram of the dependencies of your projects.
81 |
82 | ## Further help
83 |
84 | Visit the [Nx Documentation](https://nx.dev) to learn more.
85 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "",
5 | "projects": {
6 | "ngrx-workshop-app": {
7 | "projectType": "application",
8 | "schematics": {},
9 | "root": "apps/ngrx-workshop-app",
10 | "sourceRoot": "apps/ngrx-workshop-app/src",
11 | "prefix": "ngrx-workshop-app",
12 | "architect": {
13 | "build": {
14 | "builder": "@angular-devkit/build-angular:browser",
15 | "options": {
16 | "outputPath": "dist/apps/ngrx-workshop-app",
17 | "index": "apps/ngrx-workshop-app/src/index.html",
18 | "main": "apps/ngrx-workshop-app/src/main.ts",
19 | "polyfills": "apps/ngrx-workshop-app/src/polyfills.ts",
20 | "tsConfig": "apps/ngrx-workshop-app/tsconfig.app.json",
21 | "assets": [
22 | "apps/ngrx-workshop-app/src/favicon.ico",
23 | "apps/ngrx-workshop-app/src/assets"
24 | ],
25 | "styles": ["apps/ngrx-workshop-app/src/styles.css"],
26 | "scripts": []
27 | },
28 | "configurations": {
29 | "production": {
30 | "fileReplacements": [
31 | {
32 | "replace": "apps/ngrx-workshop-app/src/environments/environment.ts",
33 | "with": "apps/ngrx-workshop-app/src/environments/environment.prod.ts"
34 | }
35 | ],
36 | "optimization": true,
37 | "outputHashing": "all",
38 | "sourceMap": false,
39 | "extractCss": true,
40 | "namedChunks": false,
41 | "aot": true,
42 | "extractLicenses": true,
43 | "vendorChunk": false,
44 | "buildOptimizer": true,
45 | "budgets": [
46 | {
47 | "type": "initial",
48 | "maximumWarning": "2mb",
49 | "maximumError": "5mb"
50 | }
51 | ]
52 | }
53 | }
54 | },
55 | "serve": {
56 | "builder": "@angular-devkit/build-angular:dev-server",
57 | "options": {
58 | "browserTarget": "ngrx-workshop-app:build",
59 | "proxyConfig": "apps/ngrx-workshop-app/proxy.conf.json"
60 | },
61 | "configurations": {
62 | "production": {
63 | "browserTarget": "ngrx-workshop-app:build:production"
64 | }
65 | }
66 | },
67 | "serve-with-api": {
68 | "builder": "@angular-devkit/architect:allOf",
69 | "options": {
70 | "targets": [
71 | {
72 | "target": "ngrx-workshop-app:serve"
73 | },
74 | {
75 | "target": "api:serve"
76 | }
77 | ]
78 | }
79 | },
80 | "extract-i18n": {
81 | "builder": "@angular-devkit/build-angular:extract-i18n",
82 | "options": {
83 | "browserTarget": "ngrx-workshop-app:build"
84 | }
85 | },
86 | "lint": {
87 | "builder": "@angular-devkit/build-angular:tslint",
88 | "options": {
89 | "tsConfig": [
90 | "apps/ngrx-workshop-app/tsconfig.app.json",
91 | "apps/ngrx-workshop-app/tsconfig.spec.json"
92 | ],
93 | "exclude": ["**/node_modules/**", "!apps/ngrx-workshop-app/**"]
94 | }
95 | },
96 | "test": {
97 | "builder": "@nrwl/jest:jest",
98 | "options": {
99 | "jestConfig": "apps/ngrx-workshop-app/jest.config.js",
100 | "tsConfig": "apps/ngrx-workshop-app/tsconfig.spec.json",
101 | "setupFile": "apps/ngrx-workshop-app/src/test-setup.ts"
102 | }
103 | }
104 | }
105 | },
106 | "ngrx-workshop-app-e2e": {
107 | "root": "apps/ngrx-workshop-app-e2e",
108 | "sourceRoot": "apps/ngrx-workshop-app-e2e/src",
109 | "projectType": "application",
110 | "architect": {
111 | "e2e": {
112 | "builder": "@nrwl/cypress:cypress",
113 | "options": {
114 | "cypressConfig": "apps/ngrx-workshop-app-e2e/cypress.json",
115 | "tsConfig": "apps/ngrx-workshop-app-e2e/tsconfig.e2e.json",
116 | "devServerTarget": "ngrx-workshop-app:serve"
117 | },
118 | "configurations": {
119 | "production": {
120 | "devServerTarget": "ngrx-workshop-app:serve:production"
121 | }
122 | }
123 | },
124 | "lint": {
125 | "builder": "@angular-devkit/build-angular:tslint",
126 | "options": {
127 | "tsConfig": "apps/ngrx-workshop-app-e2e/tsconfig.e2e.json",
128 | "exclude": ["**/node_modules/**", "!apps/ngrx-workshop-app-e2e/**"]
129 | }
130 | }
131 | }
132 | },
133 | "api": {
134 | "root": "apps/api",
135 | "sourceRoot": "apps/api/src",
136 | "projectType": "application",
137 | "prefix": "api",
138 | "schematics": {},
139 | "architect": {
140 | "build": {
141 | "builder": "@nrwl/node:build",
142 | "options": {
143 | "outputPath": "dist/apps/api",
144 | "main": "apps/api/src/main.ts",
145 | "tsConfig": "apps/api/tsconfig.app.json",
146 | "assets": ["apps/api/src/assets"]
147 | },
148 | "configurations": {
149 | "production": {
150 | "optimization": true,
151 | "extractLicenses": true,
152 | "inspect": false,
153 | "fileReplacements": [
154 | {
155 | "replace": "apps/api/src/environments/environment.ts",
156 | "with": "apps/api/src/environments/environment.prod.ts"
157 | }
158 | ]
159 | }
160 | }
161 | },
162 | "serve": {
163 | "builder": "@nrwl/node:execute",
164 | "options": {
165 | "buildTarget": "api:build"
166 | }
167 | },
168 | "lint": {
169 | "builder": "@angular-devkit/build-angular:tslint",
170 | "options": {
171 | "tsConfig": [
172 | "apps/api/tsconfig.app.json",
173 | "apps/api/tsconfig.spec.json"
174 | ],
175 | "exclude": ["**/node_modules/**", "!apps/api/**"]
176 | }
177 | },
178 | "test": {
179 | "builder": "@nrwl/jest:jest",
180 | "options": {
181 | "jestConfig": "apps/api/jest.config.js",
182 | "tsConfig": "apps/api/tsconfig.spec.json"
183 | }
184 | }
185 | }
186 | },
187 | "api-interface": {
188 | "root": "libs/api-interface",
189 | "sourceRoot": "libs/api-interface/src",
190 | "projectType": "library",
191 | "schematics": {},
192 | "architect": {
193 | "lint": {
194 | "builder": "@angular-devkit/build-angular:tslint",
195 | "options": {
196 | "tsConfig": [
197 | "libs/api-interface/tsconfig.lib.json",
198 | "libs/api-interface/tsconfig.spec.json"
199 | ],
200 | "exclude": ["**/node_modules/**", "!libs/api-interface/**"]
201 | }
202 | },
203 | "test": {
204 | "builder": "@nrwl/jest:jest",
205 | "options": {
206 | "jestConfig": "libs/api-interface/jest.config.js",
207 | "tsConfig": "libs/api-interface/tsconfig.spec.json"
208 | }
209 | }
210 | }
211 | },
212 | "cart-data-access": {
213 | "projectType": "library",
214 | "root": "libs/cart-data-access",
215 | "sourceRoot": "libs/cart-data-access/src",
216 | "prefix": "ngrx-workshop-app",
217 | "architect": {
218 | "lint": {
219 | "builder": "@angular-devkit/build-angular:tslint",
220 | "options": {
221 | "tsConfig": [
222 | "libs/cart-data-access/tsconfig.lib.json",
223 | "libs/cart-data-access/tsconfig.spec.json"
224 | ],
225 | "exclude": ["**/node_modules/**", "!libs/cart-data-access/**"]
226 | }
227 | },
228 | "test": {
229 | "builder": "@nrwl/jest:jest",
230 | "options": {
231 | "jestConfig": "libs/cart-data-access/jest.config.js",
232 | "tsConfig": "libs/cart-data-access/tsconfig.spec.json",
233 | "setupFile": "libs/cart-data-access/src/test-setup.ts"
234 | }
235 | }
236 | },
237 | "schematics": {}
238 | },
239 | "product-data-access": {
240 | "projectType": "library",
241 | "root": "libs/product-data-access",
242 | "sourceRoot": "libs/product-data-access/src",
243 | "prefix": "ngrx-workshop-app",
244 | "architect": {
245 | "lint": {
246 | "builder": "@angular-devkit/build-angular:tslint",
247 | "options": {
248 | "tsConfig": [
249 | "libs/product-data-access/tsconfig.lib.json",
250 | "libs/product-data-access/tsconfig.spec.json"
251 | ],
252 | "exclude": ["**/node_modules/**", "!libs/product-data-access/**"]
253 | }
254 | },
255 | "test": {
256 | "builder": "@nrwl/jest:jest",
257 | "options": {
258 | "jestConfig": "libs/product-data-access/jest.config.js",
259 | "tsConfig": "libs/product-data-access/tsconfig.spec.json",
260 | "setupFile": "libs/product-data-access/src/test-setup.ts"
261 | }
262 | }
263 | },
264 | "schematics": {}
265 | },
266 | "shipping-data-access": {
267 | "projectType": "library",
268 | "root": "libs/shipping-data-access",
269 | "sourceRoot": "libs/shipping-data-access/src",
270 | "prefix": "ngrx-workshop-app",
271 | "architect": {
272 | "lint": {
273 | "builder": "@angular-devkit/build-angular:tslint",
274 | "options": {
275 | "tsConfig": [
276 | "libs/shipping-data-access/tsconfig.lib.json",
277 | "libs/shipping-data-access/tsconfig.spec.json"
278 | ],
279 | "exclude": ["**/node_modules/**", "!libs/shipping-data-access/**"]
280 | }
281 | },
282 | "test": {
283 | "builder": "@nrwl/jest:jest",
284 | "options": {
285 | "jestConfig": "libs/shipping-data-access/jest.config.js",
286 | "tsConfig": "libs/shipping-data-access/tsconfig.spec.json",
287 | "setupFile": "libs/shipping-data-access/src/test-setup.ts"
288 | }
289 | }
290 | },
291 | "schematics": {}
292 | }
293 | },
294 | "cli": {
295 | "warnings": {
296 | "typescriptMismatch": false,
297 | "versionMismatch": false
298 | },
299 | "defaultCollection": "@nrwl/angular"
300 | },
301 | "schematics": {
302 | "@nrwl/angular:application": {
303 | "unitTestRunner": "jest",
304 | "e2eTestRunner": "cypress"
305 | },
306 | "@nrwl/angular:library": {
307 | "unitTestRunner": "jest"
308 | }
309 | },
310 | "defaultProject": "ngrx-workshop-app"
311 | }
312 |
--------------------------------------------------------------------------------
/apps/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/apps/api/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'api',
3 | preset: '../../jest.config.js',
4 | coverageDirectory: '../../coverage/apps/api'
5 | };
6 |
--------------------------------------------------------------------------------
/apps/api/src/app/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nrwl/ngrx-workshop-app/a30affbee50c5d0959b8dda56ec74106ad6d67ee/apps/api/src/app/.gitkeep
--------------------------------------------------------------------------------
/apps/api/src/app/app.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { AppController } from './app.controller';
3 | import { CartService } from './cart.service';
4 | import { ProductService } from './product.service';
5 | import { ShippingService } from './shipping.service';
6 |
7 | describe('AppController', () => {
8 | let app: TestingModule;
9 |
10 | beforeAll(async () => {
11 | app = await Test.createTestingModule({
12 | controllers: [AppController],
13 | providers: [CartService, ProductService, ShippingService]
14 | }).compile();
15 | });
16 |
17 | describe('getShippingMethods', () => {
18 | it('should return expected list of methods', () => {});
19 | });
20 |
21 | describe('getProducts', () => {});
22 |
23 | describe('getProduct', () => {});
24 |
25 | describe('getCartItems', () => {});
26 |
27 | describe('addToCart', () => {});
28 |
29 | describe('checkout', () => {});
30 |
31 | // describe('getData', () => {
32 | // it('should return "Welcome to api!"', () => {
33 | // const appController = app.get(AppController);
34 | // expect(appController.getData()).toEqual({ message: 'Welcome to api!' });
35 | // });
36 | // });
37 | });
38 |
--------------------------------------------------------------------------------
/apps/api/src/app/app.controller.ts:
--------------------------------------------------------------------------------
1 | import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
2 | import {
3 | Product,
4 | ShippingMethod,
5 | Item
6 | } from '@ngrx-workshop-app/api-interface';
7 | import { CartService } from './cart.service';
8 | import { ProductService } from './product.service';
9 | import { ShippingService } from './shipping.service';
10 |
11 | @Controller()
12 | export class AppController {
13 | constructor(
14 | private readonly cartService: CartService,
15 | private readonly productService: ProductService,
16 | private readonly shippingService: ShippingService
17 | ) {}
18 |
19 | @Get('shipping')
20 | getShippingMethods(): ShippingMethod[] {
21 | return this.shippingService.getShippingMethods();
22 | }
23 |
24 | @Get('products')
25 | getProducts(): Product[] {
26 | return this.productService.getProducts();
27 | }
28 |
29 | @Get('products/:productId')
30 | getProduct(@Param('productId') productId: string): Product {
31 | return this.productService.getProduct(+productId);
32 | }
33 |
34 | @Get('cart')
35 | getCartItems(): Item[] {
36 | return this.cartService.getItems();
37 | }
38 |
39 | @Post('cart')
40 | addToCart(@Body() { productId }): Item[] {
41 | return this.cartService.addItem(productId);
42 | }
43 |
44 | @Delete('cart')
45 | checkout(): Item[] {
46 | return this.cartService.checkout();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/apps/api/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { AppController } from './app.controller';
3 | import { CartService } from './cart.service';
4 | import { ProductService } from './product.service';
5 | import { ShippingService } from './shipping.service';
6 |
7 | @Module({
8 | imports: [],
9 | controllers: [AppController],
10 | providers: [CartService, ProductService, ShippingService]
11 | })
12 | export class AppModule {}
13 |
--------------------------------------------------------------------------------
/apps/api/src/app/cart.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test } from '@nestjs/testing';
2 | import { CartService } from './cart.service';
3 |
4 | describe('CartService', () => {
5 | let service: CartService;
6 |
7 | beforeAll(async () => {
8 | const app = await Test.createTestingModule({
9 | providers: [CartService]
10 | }).compile();
11 |
12 | service = app.get(CartService);
13 | });
14 |
15 | describe('getItems', () => {
16 | it('should return expected items', () => {});
17 | });
18 |
19 | describe('addItem', () => {
20 | it('should add the item and return all items in cart', () => {});
21 | });
22 |
23 | describe('checkout', () => {
24 | it('should add remove all items from the cart and return an empty array', () => {});
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/apps/api/src/app/cart.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { Item } from '@ngrx-workshop-app/api-interface';
3 |
4 | @Injectable()
5 | export class CartService {
6 | private itemCount = 0;
7 | private items: Item[] = [];
8 |
9 | constructor() {}
10 |
11 | getItems(): Item[] {
12 | return this.items;
13 | }
14 |
15 | addItem(productId: string): Item[] {
16 | this.items.push({
17 | itemId: `${this.itemCount++}`,
18 | productId: productId
19 | });
20 | return this.items;
21 | }
22 |
23 | checkout(): Item[] {
24 | this.items = [];
25 | return this.items;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/apps/api/src/app/mocks/products.ts:
--------------------------------------------------------------------------------
1 | import { Product } from '@ngrx-workshop-app/api-interface';
2 |
3 | export const mockProducts: Product[] = [
4 | {
5 | productId: '1',
6 | name: 'Phone XL',
7 | price: 799,
8 | description: 'A large phone with one of the best screens'
9 | },
10 | {
11 | productId: '2',
12 | name: 'Phone Mini',
13 | price: 699,
14 | description: 'A great phone with one of the best cameras'
15 | },
16 | {
17 | productId: '3',
18 | name: 'Phone Standard',
19 | price: 299,
20 | description: ''
21 | }
22 | ];
23 |
--------------------------------------------------------------------------------
/apps/api/src/app/mocks/shipping-methods.ts:
--------------------------------------------------------------------------------
1 | import { ShippingMethod } from '@ngrx-workshop-app/api-interface';
2 |
3 | export const mockShippingMethods: ShippingMethod[] = [
4 | {
5 | type: 'Overnight',
6 | price: 25.99
7 | },
8 | {
9 | type: '2-Day',
10 | price: 9.99
11 | },
12 | {
13 | type: 'Postal',
14 | price: 2.99
15 | }
16 | ];
17 |
--------------------------------------------------------------------------------
/apps/api/src/app/product.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test } from '@nestjs/testing';
2 | import { Product } from '@ngrx-workshop-app/api-interface';
3 | import { ProductService } from './product.service';
4 |
5 | describe('ProductService', () => {
6 | let service: ProductService;
7 | let mockProducts: Product[];
8 |
9 | beforeAll(async () => {
10 | const app = await Test.createTestingModule({
11 | providers: [ProductService]
12 | }).compile();
13 |
14 | service = app.get(ProductService);
15 |
16 | mockProducts = [
17 | {
18 | productId: '1',
19 | name: 'Phone XL',
20 | price: 799,
21 | description: 'A large phone with one of the best screens'
22 | },
23 | {
24 | productId: '2',
25 | name: 'Phone Mini',
26 | price: 699,
27 | description: 'A great phone with one of the best cameras'
28 | },
29 | {
30 | productId: '3',
31 | name: 'Phone Standard',
32 | price: 299,
33 | description: ''
34 | }
35 | ];
36 | });
37 |
38 | describe('getProducts', () => {
39 | it('should return mock products', () => {
40 | expect(service.getProducts()).toEqual(mockProducts);
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/apps/api/src/app/product.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { Product } from '@ngrx-workshop-app/api-interface';
3 | import { mockProducts } from './mocks/products';
4 |
5 | @Injectable()
6 | export class ProductService {
7 | private products: Product[];
8 |
9 | constructor() {
10 | this.products = mockProducts;
11 | }
12 |
13 | getProducts(): Product[] {
14 | return this.products;
15 | }
16 |
17 | getProduct(productId: number): Product {
18 | return this.products.find(product => +product.productId === productId);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/apps/api/src/app/shipping.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test } from '@nestjs/testing';
2 | import { ShippingMethod } from '@ngrx-workshop-app/api-interface';
3 | import { ShippingService } from './shipping.service';
4 |
5 | describe('ShippingService', () => {
6 | let service: ShippingService;
7 | let mockShippingMethods: ShippingMethod[];
8 |
9 | beforeAll(async () => {
10 | const app = await Test.createTestingModule({
11 | providers: [ShippingService]
12 | }).compile();
13 |
14 | service = app.get(ShippingService);
15 |
16 | mockShippingMethods = [
17 | {
18 | type: 'Overnight',
19 | price: 25.99
20 | },
21 | {
22 | type: '2-Day',
23 | price: 9.99
24 | },
25 | {
26 | type: 'Postal',
27 | price: 2.99
28 | }
29 | ];
30 | });
31 |
32 | describe('getShippingMethods', () => {
33 | it('should return mock shipping methods', () => {
34 | expect(service.getShippingMethods()).toEqual(mockShippingMethods);
35 | });
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/apps/api/src/app/shipping.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { ShippingMethod } from '@ngrx-workshop-app/api-interface';
3 | import { mockShippingMethods } from './mocks/shipping-methods';
4 |
5 | @Injectable()
6 | export class ShippingService {
7 | private shippingMethods: ShippingMethod[];
8 |
9 | constructor() {
10 | this.shippingMethods = mockShippingMethods;
11 | }
12 |
13 | getShippingMethods(): ShippingMethod[] {
14 | return this.shippingMethods;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/apps/api/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nrwl/ngrx-workshop-app/a30affbee50c5d0959b8dda56ec74106ad6d67ee/apps/api/src/assets/.gitkeep
--------------------------------------------------------------------------------
/apps/api/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/apps/api/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
--------------------------------------------------------------------------------
/apps/api/src/main.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This is not a production server yet!
3 | * This is only a minimal backend to get started.
4 | **/
5 |
6 | import { NestFactory } from '@nestjs/core';
7 |
8 | import { AppModule } from './app/app.module';
9 |
10 | async function bootstrap() {
11 | const app = await NestFactory.create(AppModule);
12 | const globalPrefix = 'api';
13 | app.setGlobalPrefix(globalPrefix);
14 | const port = process.env.port || 3333;
15 | await app.listen(port, () => {
16 | console.log('Listening at http://localhost:' + port + '/' + globalPrefix);
17 | });
18 | }
19 |
20 | bootstrap();
21 |
--------------------------------------------------------------------------------
/apps/api/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "types": ["node"]
6 | },
7 | "exclude": ["**/*.spec.ts"],
8 | "include": ["**/*.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/apps/api/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["node", "jest"]
5 | },
6 | "include": ["**/*.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/apps/api/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": ["**/*.spec.ts", "**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/apps/api/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {}
4 | }
5 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app-e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileServerFolder": ".",
3 | "fixturesFolder": "./src/fixtures",
4 | "integrationFolder": "./src/integration",
5 | "pluginsFile": "./src/plugins/index",
6 | "supportFile": false,
7 | "video": true,
8 | "videosFolder": "../../dist/cypress/apps/ngrx-workshop-app-e2e/videos",
9 | "screenshotsFolder": "../../dist/cypress/apps/ngrx-workshop-app-e2e/screenshots",
10 | "chromeWebSecurity": false
11 | }
12 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app-e2e/src/integration/app.spec.ts:
--------------------------------------------------------------------------------
1 | import { getGreeting } from '../support/app.po';
2 |
3 | describe('ngrx-workshop-app', () => {
4 | beforeEach(() => cy.visit('/'));
5 |
6 | it('should display welcome message', () => {
7 | getGreeting().contains('Welcome to ngrx-workshop-app!');
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app-e2e/src/plugins/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example plugins/index.js can be used to load plugins
3 | //
4 | // You can change the location of this file or turn off loading
5 | // the plugins file with the 'pluginsFile' configuration option.
6 | //
7 | // You can read more here:
8 | // https://on.cypress.io/plugins-guide
9 | // ***********************************************************
10 |
11 | // This function is called when a project is opened or re-opened (e.g. due to
12 | // the project's config changing)
13 |
14 | const { preprocessTypescript } = require('@nrwl/cypress/plugins/preprocessor');
15 |
16 | module.exports = (on, config) => {
17 | // `on` is used to hook into various events Cypress emits
18 | // `config` is the resolved Cypress config
19 |
20 | // Preprocess Typescript
21 | on('file:preprocessor', preprocessTypescript(config));
22 | };
23 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app-e2e/src/support/commands.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add("login", (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This will overwrite an existing command --
25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app-e2e/src/support/index.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app-e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "sourceMap": false,
5 | "outDir": "../../dist/out-tsc"
6 | },
7 | "include": ["src/**/*.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["cypress", "node"]
5 | },
6 | "include": ["**/*.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/browserslist:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'ngrx-workshop-app',
3 | preset: '../../jest.config.js',
4 | coverageDirectory: '../../coverage/apps/ngrx-workshop-app',
5 | snapshotSerializers: [
6 | 'jest-preset-angular/AngularSnapshotSerializer.js',
7 | 'jest-preset-angular/HTMLCommentSerializer.js'
8 | ]
9 | };
10 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/proxy.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "/api": {
3 | "target": "http://localhost:3333",
4 | "secure": false
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | const routes: Routes = [];
5 |
6 | @NgModule({
7 | imports: [RouterModule.forRoot(routes)],
8 | exports: [RouterModule]
9 | })
10 | export class AppRoutingModule {}
11 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/app.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nrwl/ngrx-workshop-app/a30affbee50c5d0959b8dda56ec74106ad6d67ee/apps/ngrx-workshop-app/src/app/app.component.css
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { HttpClientModule } from '@angular/common/http';
2 | import { async, TestBed } from '@angular/core/testing';
3 | import { RouterTestingModule } from '@angular/router/testing';
4 | import { provideMockStore } from '@ngrx/store/testing';
5 | import { AppComponent } from './app.component';
6 | import { TopBarComponent } from './layout/top-bar/top-bar.component';
7 | import { MatDialog } from '@angular/material/dialog';
8 |
9 | describe('AppComponent', () => {
10 | beforeEach(async(() => {
11 | TestBed.configureTestingModule({
12 | declarations: [AppComponent, TopBarComponent],
13 | imports: [HttpClientModule, RouterTestingModule],
14 | providers: [
15 | provideMockStore({ initialState: {} }),
16 | { provide: MatDialog, useValue: { open: () => {} } }
17 | ]
18 | }).compileComponents();
19 | }));
20 |
21 | it('should create the app', () => {
22 | const fixture = TestBed.createComponent(AppComponent);
23 | const app = fixture.debugElement.componentInstance;
24 | expect(app).toBeTruthy();
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'ngrx-workshop-app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.css']
7 | })
8 | export class AppComponent {}
9 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { HttpClientModule } from '@angular/common/http';
2 | import { NgModule } from '@angular/core';
3 | import { ReactiveFormsModule } from '@angular/forms';
4 | import { BrowserModule } from '@angular/platform-browser';
5 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
6 |
7 | import { EffectsModule } from '@ngrx/effects';
8 | import { StoreRouterConnectingModule, RouterState } from '@ngrx/router-store';
9 | import { StoreModule } from '@ngrx/store';
10 | import { StoreDevtoolsModule } from '@ngrx/store-devtools';
11 |
12 | import { AppComponent } from './app.component';
13 | import { AppRoutingModule } from './app-routing.module';
14 | import { CartModule } from './cart/cart.module';
15 | import { environment } from '../environments/environment';
16 | import { LayoutModule } from './layout/layout.module';
17 | import { ProductsModule } from './products/products.module';
18 |
19 | @NgModule({
20 | declarations: [AppComponent],
21 | imports: [
22 | BrowserModule,
23 | BrowserAnimationsModule,
24 | HttpClientModule,
25 | ReactiveFormsModule,
26 | AppRoutingModule,
27 | ProductsModule,
28 | LayoutModule,
29 | CartModule,
30 | StoreModule.forRoot(
31 | {},
32 | {
33 | metaReducers: !environment.production ? [] : [],
34 | runtimeChecks: {
35 | strictStateImmutability: true,
36 | strictActionImmutability: true
37 | }
38 | }
39 | ),
40 | EffectsModule.forRoot([]),
41 | !environment.production ? StoreDevtoolsModule.instrument() : [],
42 | StoreRouterConnectingModule.forRoot({
43 | routerState: RouterState.Minimal
44 | })
45 | ],
46 | bootstrap: [AppComponent]
47 | })
48 | export class AppModule {}
49 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/cart/cart-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | import { CartComponent } from './cart/cart.component';
5 |
6 | const routes: Routes = [{ path: 'cart', component: CartComponent }];
7 |
8 | @NgModule({
9 | imports: [RouterModule.forChild(routes)],
10 | exports: [RouterModule]
11 | })
12 | export class CartRoutingModule {}
13 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/cart/cart.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { ReactiveFormsModule } from '@angular/forms';
4 |
5 | import { MatDialogModule } from '@angular/material/dialog';
6 | import { MatButtonModule } from '@angular/material/button';
7 |
8 | import { CartDataAccessModule } from '@ngrx-workshop-app/cart-data-access';
9 | import { ShippingDataAccessModule } from '@ngrx-workshop-app/shipping-data-access';
10 | import { CartStateModule } from '@ngrx-workshop-app/shared/state/cart';
11 | import { ShippingStateModule } from '@ngrx-workshop-app/shared/state/shipping';
12 |
13 | import { CartComponent } from './cart/cart.component';
14 | import { CartRoutingModule } from './cart-routing.module';
15 | import { ShippingMethodSelectionDialogComponent } from './shipping-method-selection-dialog/shipping-method-selection-dialog.component';
16 |
17 | @NgModule({
18 | declarations: [CartComponent, ShippingMethodSelectionDialogComponent],
19 | imports: [
20 | CommonModule,
21 | ReactiveFormsModule,
22 | MatDialogModule,
23 | MatButtonModule,
24 | CartRoutingModule,
25 | CartDataAccessModule,
26 | CartStateModule,
27 | ShippingDataAccessModule,
28 | ShippingStateModule
29 | ],
30 | entryComponents: [ShippingMethodSelectionDialogComponent]
31 | })
32 | export class CartModule {}
33 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/cart/cart/cart.component.css:
--------------------------------------------------------------------------------
1 | .side-by-side {
2 | display: grid;
3 | grid-template-columns: 1fr 1fr;
4 | grid-template-rows: 1fr;
5 | grid-column-gap: 20px;
6 | grid-row-gap: 0px;
7 | }
8 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/cart/cart/cart.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Cart - Subtotal: {{ cartSubtotal | currency }}
4 |
5 |
6 |
7 | {{ item.product.name }}
8 | {{ item.product.price | currency }}
9 |
10 |
11 |
12 |
13 |
14 |
20 | Shipping Options - Subtotal: {{ shippingSubtotal | currency }}
21 |
22 |
23 |
24 | Shipping Options - SHIPPING SELECTION REQUIRED
25 |
26 |
27 |
28 |
34 | {{ shipping.type }}
35 | {{ shipping.price | currency }}
36 |
37 |
38 |
39 |
40 |
41 | Shipping Info
42 |
43 |
68 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/cart/cart/cart.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { FormControl, FormGroup, Validators } from '@angular/forms';
3 | import { ItemWithProduct } from '@ngrx-workshop-app/api-interface';
4 | import * as fromShipping from '@ngrx-workshop-app/shared/state/shipping';
5 | import { select, Store } from '@ngrx/store';
6 | import { combineLatest, Observable } from 'rxjs';
7 | import { map, startWith } from 'rxjs/operators';
8 |
9 | @Component({
10 | selector: 'app-cart',
11 | templateUrl: './cart.component.html',
12 | styleUrls: ['./cart.component.css']
13 | })
14 | export class CartComponent implements OnInit {
15 | checkoutForm = new FormGroup({
16 | name: new FormControl('', [Validators.required]),
17 | address: new FormControl('', [Validators.required])
18 | });
19 |
20 | items = new Array();
21 | shippingOptions$ = this.store.pipe(
22 | select(fromShipping.selectAllShippingOptions)
23 | );
24 | selectedMethod$ = this.store.pipe(
25 | select(fromShipping.selectSelectedShippingOption)
26 | );
27 | cartSubtotal = 0;
28 | shippingSubtotal$ = this.store.pipe(select(fromShipping.selectShippingCost));
29 | grandTotal = 0;
30 | shippingInvalid$ = this.store.pipe(select(fromShipping.getShippingInvalid));
31 | cartInvalid = false;
32 |
33 | shippingInfoInvalid$ = this.checkoutForm.statusChanges.pipe(
34 | map(x => x !== 'VALID'),
35 | startWith(true)
36 | );
37 |
38 | checkoutDisabled$: Observable = combineLatest([
39 | this.shippingInvalid$,
40 | this.shippingInfoInvalid$
41 | ]).pipe(map(arr => arr.some(x => x === true)));
42 |
43 | constructor(private store: Store<{}>) {}
44 |
45 | ngOnInit() {}
46 |
47 | optionSelected(shippingMethod: string) {}
48 |
49 | onSubmit() {
50 | this.checkoutForm.reset();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/cart/shipping-method-selection-dialog/shipping-method-selection-dialog.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nrwl/ngrx-workshop-app/a30affbee50c5d0959b8dda56ec74106ad6d67ee/apps/ngrx-workshop-app/src/app/cart/shipping-method-selection-dialog/shipping-method-selection-dialog.component.css
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/cart/shipping-method-selection-dialog/shipping-method-selection-dialog.component.html:
--------------------------------------------------------------------------------
1 |
2 | Select a Shipping Method:
3 |
4 |
10 | {{ shipping.type }}
11 | {{ shipping.price | currency }}
12 |
13 |
14 |
15 |
18 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/cart/shipping-method-selection-dialog/shipping-method-selection-dialog.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ShippingMethodSelectionDialogComponent } from './shipping-method-selection-dialog.component';
4 | import { provideMockStore } from '@ngrx/store/testing';
5 | import { MatDialogRef } from '@angular/material/dialog';
6 |
7 | describe('ShippingMethodSelectionDialogComponent', () => {
8 | let component: ShippingMethodSelectionDialogComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(async(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [ShippingMethodSelectionDialogComponent],
14 | providers: [
15 | provideMockStore(),
16 | { provide: MatDialogRef, useValue: { close: () => {} } }
17 | ]
18 | }).compileComponents();
19 | }));
20 |
21 | beforeEach(() => {
22 | fixture = TestBed.createComponent(ShippingMethodSelectionDialogComponent);
23 | component = fixture.componentInstance;
24 | fixture.detectChanges();
25 | });
26 |
27 | it('should create', () => {
28 | expect(component).toBeTruthy();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/cart/shipping-method-selection-dialog/shipping-method-selection-dialog.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { MatDialogRef } from '@angular/material/dialog';
3 | import { select, Store } from '@ngrx/store';
4 | import { combineLatest, merge, Subject } from 'rxjs';
5 | import { map, publishReplay, refCount, startWith, take } from 'rxjs/operators';
6 |
7 | import * as fromShipping from '@ngrx-workshop-app/shared/state/shipping';
8 | import { ShippingActions } from '@ngrx-workshop-app/shared/state/shipping';
9 |
10 | @Component({
11 | selector: 'ngrx-workshop-app-shipping-method-selection-dialog',
12 | templateUrl: './shipping-method-selection-dialog.component.html',
13 | styleUrls: ['./shipping-method-selection-dialog.component.css']
14 | })
15 | export class ShippingMethodSelectionDialogComponent implements OnInit {
16 | private _userSelections$ = new Subject();
17 | private _originallySelectedOption$ = this.store.pipe(
18 | select(fromShipping.selectSelectedShippingOption),
19 | take(1),
20 | publishReplay(1),
21 | refCount()
22 | );
23 |
24 | shippingOptions$ = this.store.pipe(
25 | select(fromShipping.selectAllShippingOptions)
26 | );
27 |
28 | currentlySelected$ = merge(
29 | this._originallySelectedOption$,
30 | this._userSelections$
31 | ).pipe(
32 | publishReplay(1),
33 | refCount()
34 | );
35 |
36 | selectDisabled$ = combineLatest([
37 | this.currentlySelected$.pipe(
38 | startWith(fromShipping.NO_SHIPPING_METHOD_SELECTED_TOKEN)
39 | ),
40 | this._originallySelectedOption$
41 | ]).pipe(
42 | map(([current, original]) =>
43 | current === fromShipping.NO_SHIPPING_METHOD_SELECTED_TOKEN
44 | ? true
45 | : current === original
46 | )
47 | );
48 |
49 | constructor(
50 | private store: Store<{}>,
51 | private dialogRef: MatDialogRef
52 | ) {}
53 |
54 | ngOnInit() {}
55 |
56 | optionSelected(option: string) {
57 | this._userSelections$.next(option);
58 | }
59 |
60 | select(shippingMethod: string) {
61 | this.store.dispatch(
62 | ShippingActions.shippingDialogSelectShippingMethod({ shippingMethod })
63 | );
64 | this.dialogRef.close();
65 | }
66 |
67 | cancel() {
68 | this.dialogRef.close();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/layout/layout.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { RouterModule } from '@angular/router';
4 |
5 | import { TopBarComponent } from './top-bar/top-bar.component';
6 |
7 | @NgModule({
8 | declarations: [TopBarComponent],
9 | imports: [CommonModule, RouterModule],
10 | exports: [TopBarComponent]
11 | })
12 | export class LayoutModule {}
13 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/layout/top-bar/top-bar.component.css:
--------------------------------------------------------------------------------
1 | .status-buttons button,
2 | .status-buttons a {
3 | margin: 4px;
4 | }
5 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/layout/top-bar/top-bar.component.html:
--------------------------------------------------------------------------------
1 |
2 | My Store
3 |
4 |
41 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/layout/top-bar/top-bar.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { MatDialog } from '@angular/material/dialog';
3 | import { ShippingMethodSelectionDialogComponent } from '@ngrx-workshop-app/cart/shipping-method-selection-dialog/shipping-method-selection-dialog.component';
4 | import * as fromShipping from '@ngrx-workshop-app/shared/state/shipping';
5 | import { select, Store } from '@ngrx/store';
6 |
7 | @Component({
8 | selector: 'ngrx-workshop-app-top-bar',
9 | templateUrl: './top-bar.component.html',
10 | styleUrls: ['./top-bar.component.css']
11 | })
12 | export class TopBarComponent {
13 | total = 0;
14 | shippingMethod$ = this.store.pipe(
15 | select(fromShipping.selectSelectedShippingOption)
16 | );
17 | NO_SHIPPING_METHOD_SELECTED_TOKEN =
18 | fromShipping.NO_SHIPPING_METHOD_SELECTED_TOKEN;
19 |
20 | constructor(private dialog: MatDialog, private store: Store<{}>) {}
21 |
22 | openShippingOptions() {
23 | this.dialog.open(ShippingMethodSelectionDialogComponent, {
24 | width: '450px'
25 | });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/products/product-details/product-details.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nrwl/ngrx-workshop-app/a30affbee50c5d0959b8dda56ec74106ad6d67ee/apps/ngrx-workshop-app/src/app/products/product-details/product-details.component.css
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/products/product-details/product-details.component.html:
--------------------------------------------------------------------------------
1 | Product Details
2 |
3 |
4 |
{{ product.name }}
5 |
{{ product.price | currency }}
6 |
{{ product.description }}
7 |
8 |
9 |
10 |
11 |
12 | Loading ...
13 |
14 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/products/product-details/product-details.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { MatSnackBar } from '@angular/material';
3 | import { ActivatedRoute } from '@angular/router';
4 | import { Product } from '@ngrx-workshop-app/api-interface';
5 | import * as fromProducts from '@ngrx-workshop-app/shared/state/products';
6 | import { ProductActions } from '@ngrx-workshop-app/shared/state/products';
7 | import { select, Store } from '@ngrx/store';
8 | import { map } from 'rxjs/operators';
9 |
10 | @Component({
11 | selector: 'app-product-details',
12 | templateUrl: './product-details.component.html',
13 | styleUrls: ['./product-details.component.css']
14 | })
15 | export class ProductDetailsComponent implements OnInit {
16 | product$ = this.store.pipe(select(fromProducts.getSelectedProduct));
17 |
18 | constructor(
19 | private store: Store<{}>,
20 | private route: ActivatedRoute,
21 | private snack: MatSnackBar
22 | ) {}
23 |
24 | ngOnInit() {
25 | this.route.paramMap
26 | .pipe(
27 | map(params =>
28 | ProductActions.enterProductDetailsPage({ id: params.get('productId') })
29 | )
30 | )
31 | .subscribe(this.store);
32 | }
33 |
34 | addToCart(product: Product) {
35 | this.snack.open('Product added to cart successfully!', null, {
36 | duration: 1000
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/products/product-list/product-list.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nrwl/ngrx-workshop-app/a30affbee50c5d0959b8dda56ec74106ad6d67ee/apps/ngrx-workshop-app/src/app/products/product-list/product-list.component.css
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/products/product-list/product-list.component.html:
--------------------------------------------------------------------------------
1 | Products
2 |
3 |
4 |
12 |
Description: {{ product.description }}
13 |
14 |
17 |
18 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/products/product-list/product-list.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Store, select } from '@ngrx/store';
3 | import * as fromProducts from '@ngrx-workshop-app/shared/state/products';
4 | import { MatSnackBar } from '@angular/material';
5 | import { ProductActions } from '@ngrx-workshop-app/shared/state/products';
6 |
7 | @Component({
8 | selector: 'app-product-list',
9 | templateUrl: './product-list.component.html',
10 | styleUrls: ['./product-list.component.css']
11 | })
12 | export class ProductListComponent implements OnInit {
13 | products$ = this.store.pipe(select(fromProducts.getAllProducts));
14 |
15 | constructor(private store: Store<{}>, private snackBar: MatSnackBar) {}
16 |
17 | ngOnInit() {
18 | this.store.dispatch(ProductActions.enterProductsPage());
19 | }
20 |
21 | share() {
22 | this.snackBar.open('The product has been shared!', undefined, {
23 | duration: 1000
24 | });
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/products/products-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 | import { ProductDetailsComponent } from './product-details/product-details.component';
4 | import { ProductListComponent } from './product-list/product-list.component';
5 |
6 | const routes: Routes = [
7 | { path: '', component: ProductListComponent },
8 | { path: 'products/:productId', component: ProductDetailsComponent }
9 | ];
10 |
11 | @NgModule({
12 | imports: [RouterModule.forChild(routes)],
13 | exports: [RouterModule]
14 | })
15 | export class ProductsRoutingModule {}
16 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/products/products.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { ProductDataAccessModule } from '@ngrx-workshop-app/product-data-access';
4 |
5 | import { ProductsStateModule } from '@ngrx-workshop-app/shared/state/products';
6 |
7 | import { ProductDetailsComponent } from './product-details/product-details.component';
8 | import { ProductListComponent } from './product-list/product-list.component';
9 | import { ProductsRoutingModule } from './products-routing.module';
10 | import { MatSnackBarModule } from '@angular/material';
11 |
12 | @NgModule({
13 | declarations: [ProductListComponent, ProductDetailsComponent],
14 | imports: [
15 | CommonModule,
16 | ProductDataAccessModule,
17 | ProductsStateModule,
18 | ProductsRoutingModule,
19 | MatSnackBarModule
20 | ]
21 | })
22 | export class ProductsModule {}
23 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/app/app.actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction } from '@ngrx/store';
2 |
3 | export const init = createAction('[App] Init');
4 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/app/index.ts:
--------------------------------------------------------------------------------
1 | import * as AppActions from './app.actions';
2 |
3 | export { AppActions };
4 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/cart/cart.actions.ts:
--------------------------------------------------------------------------------
1 | // TODO: Implement Cart Actions
2 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/cart/cart.effects.spec.ts:
--------------------------------------------------------------------------------
1 | import { HttpClientTestingModule } from '@angular/common/http/testing';
2 | import { TestBed } from '@angular/core/testing';
3 | import { MatSnackBarModule } from '@angular/material';
4 | import { RouterTestingModule } from '@angular/router/testing';
5 | import { CartService } from '@ngrx-workshop-app/cart-data-access';
6 | import { provideMockActions } from '@ngrx/effects/testing';
7 | import { Observable } from 'rxjs';
8 | import { CartEffects } from './cart.effects';
9 |
10 | describe('CartEffects', () => {
11 | let actions$: Observable;
12 | let effects: CartEffects;
13 |
14 | beforeEach(() => {
15 | TestBed.configureTestingModule({
16 | imports: [
17 | HttpClientTestingModule,
18 | RouterTestingModule,
19 | MatSnackBarModule
20 | ],
21 | providers: [CartEffects, provideMockActions(() => actions$), CartService]
22 | });
23 |
24 | effects = TestBed.get(CartEffects);
25 | });
26 |
27 | it('should be created', () => {
28 | expect(effects).toBeTruthy();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/cart/cart.effects.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { MatSnackBar } from '@angular/material';
3 | import { Router } from '@angular/router';
4 | import { CartService } from '@ngrx-workshop-app/cart-data-access';
5 | import { Actions } from '@ngrx/effects';
6 |
7 | @Injectable()
8 | export class CartEffects {
9 | // TODO: Implement Cart Effects
10 |
11 | constructor(
12 | private actions$: Actions,
13 | private cartService: CartService,
14 | private router: Router,
15 | private snackBar: MatSnackBar
16 | ) {}
17 | }
18 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/cart/cart.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { EffectsModule } from '@ngrx/effects';
4 | import { StoreModule } from '@ngrx/store';
5 |
6 | import { CartEffects } from './cart.effects';
7 | import * as fromCart from './cart.reducer';
8 | import { MatSnackBarModule } from '@angular/material';
9 |
10 | @NgModule({
11 | declarations: [],
12 | imports: [
13 | StoreModule.forFeature(fromCart.cartFeatureKey, fromCart.reducer),
14 | EffectsModule.forFeature([CartEffects]),
15 | MatSnackBarModule
16 | ]
17 | })
18 | export class CartStateModule {}
19 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/cart/cart.reducer.spec.ts:
--------------------------------------------------------------------------------
1 | import { initialState, reducer } from './cart.reducer';
2 |
3 | describe('Products Reducer', () => {
4 | describe('an unknown action', () => {
5 | it('should return the previous state', () => {
6 | const action = {} as any;
7 |
8 | const result = reducer(initialState, action);
9 |
10 | expect(result).toBe(initialState);
11 | });
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/cart/cart.reducer.ts:
--------------------------------------------------------------------------------
1 | import { Action, createReducer } from '@ngrx/store';
2 |
3 | // TOOD: Implement Cart State Interface extending from EntityState
4 | export interface State {}
5 |
6 | export const cartFeatureKey = 'cart';
7 |
8 | // TODO: Implement Cart State Entity Adapter
9 |
10 | // TODO: Implement Cart Initial State
11 | export const initialState: State = {};
12 |
13 | // TODO: Implement On handlers for each action
14 | const cartReducer = createReducer(initialState);
15 |
16 | export function reducer(state: State | undefined, action: Action) {
17 | return cartReducer(state, action);
18 | }
19 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/cart/cart.selectors.spec.ts:
--------------------------------------------------------------------------------
1 | import { Item, Product } from '@ngrx-workshop-app/api-interface';
2 | import * as fromProducts from '../products';
3 | import * as fromCart from './cart.reducer';
4 |
5 | describe('Cart Selectors', () => {
6 | const ERROR_MSG = 'No Error Available';
7 |
8 | let storeState;
9 |
10 | beforeEach(() => {
11 | const createItem = (id: string): Item => ({
12 | itemId: `item-${id}`,
13 | productId: id
14 | });
15 | const createProducts = (id: string, name = ''): Product => ({
16 | productId: id,
17 | name: name || `name-${id}`,
18 | description: '',
19 | price: 4.99
20 | });
21 | storeState = {
22 | cart: fromCart.initialState,
23 | products: fromProducts.adapter.addAll(
24 | [
25 | createProducts('PRODUCT-AAA'),
26 | createProducts('PRODUCT-BBB'),
27 | createProducts('PRODUCT-CCC')
28 | ],
29 | fromProducts.initialState
30 | )
31 | };
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/cart/cart.selectors.ts:
--------------------------------------------------------------------------------
1 | // TODO: Implement Cart Selectors
2 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/cart/index.ts:
--------------------------------------------------------------------------------
1 | // import * as CartActions from './cart.actions';
2 |
3 | export * from './cart.module';
4 | export * from './cart.reducer';
5 | // export * from './cart.selectors';
6 |
7 | // export { CartActions };
8 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/products/index.ts:
--------------------------------------------------------------------------------
1 | import * as ProductActions from './products.actions';
2 | export * from './products.module';
3 | export * from './products.reducer';
4 | export * from './products.selectors';
5 | export { ProductActions };
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/products/products.actions.ts:
--------------------------------------------------------------------------------
1 | import { Product } from '@ngrx-workshop-app/api-interface';
2 | import { createAction, props } from '@ngrx/store';
3 |
4 | export const enterProductsPage = createAction('[Products Page] Enter');
5 |
6 | export const enterProductDetailsPage = createAction(
7 | '[Products Details Page] Enter',
8 | props<{ id: string }>()
9 | );
10 |
11 | // TODO: Implement addToCart action
12 |
13 | export const loadProductsSuccess = createAction(
14 | '[Products API] Load Products Success',
15 | props<{ products: Product[] }>()
16 | );
17 |
18 | export const loadProductsFailure = createAction(
19 | '[Products API] Load Products Failure',
20 | props<{ error: any }>()
21 | );
22 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/products/products.effects.spec.ts:
--------------------------------------------------------------------------------
1 | import { HttpClientTestingModule } from '@angular/common/http/testing';
2 | import { TestBed } from '@angular/core/testing';
3 | import { ProductService } from '@ngrx-workshop-app/product-data-access';
4 | import { provideMockActions } from '@ngrx/effects/testing';
5 | import { Observable } from 'rxjs';
6 | import { ProductsEffects } from './products.effects';
7 |
8 | describe('ProductsEffects', () => {
9 | let actions$: Observable;
10 | let effects: ProductsEffects;
11 |
12 | beforeEach(() => {
13 | TestBed.configureTestingModule({
14 | imports: [HttpClientTestingModule],
15 | providers: [
16 | ProductsEffects,
17 | provideMockActions(() => actions$),
18 | ProductService
19 | ]
20 | });
21 |
22 | effects = TestBed.get(ProductsEffects);
23 | });
24 |
25 | it('should be created', () => {
26 | expect(effects).toBeTruthy();
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/products/products.effects.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { ProductService } from '@ngrx-workshop-app/product-data-access';
3 | import { Actions, createEffect, ofType } from '@ngrx/effects';
4 | import { of } from 'rxjs';
5 | import { catchError, exhaustMap, map } from 'rxjs/operators';
6 |
7 | import * as ProductsActions from './products.actions';
8 | import * as AppActions from '../app/app.actions';
9 |
10 | @Injectable()
11 | export class ProductsEffects {
12 | loadProducts$ = createEffect(() =>
13 | this.actions$.pipe(
14 | ofType(
15 | ProductsActions.enterProductsPage,
16 | ProductsActions.enterProductDetailsPage,
17 | AppActions.init
18 | ),
19 | exhaustMap(() =>
20 | this.productsService.getProducts().pipe(
21 | map(products => ProductsActions.loadProductsSuccess({ products })),
22 | catchError(() =>
23 | of(
24 | ProductsActions.loadProductsFailure({
25 | error: 'Unable to load products'
26 | })
27 | )
28 | )
29 | )
30 | )
31 | )
32 | );
33 |
34 | constructor(
35 | private actions$: Actions,
36 | private productsService: ProductService
37 | ) {}
38 | }
39 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/products/products.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { StoreModule } from '@ngrx/store';
4 | import { EffectsModule } from '@ngrx/effects';
5 |
6 | import * as fromProducts from './products.reducer';
7 | import { ProductsEffects } from './products.effects';
8 |
9 | @NgModule({
10 | declarations: [],
11 | imports: [
12 | CommonModule,
13 | StoreModule.forFeature(
14 | fromProducts.productsFeatureKey,
15 | fromProducts.reducer
16 | ),
17 | EffectsModule.forFeature([ProductsEffects])
18 | ]
19 | })
20 | export class ProductsStateModule {}
21 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/products/products.reducer.spec.ts:
--------------------------------------------------------------------------------
1 | import { reducer, initialState } from './products.reducer';
2 |
3 | describe('Products Reducer', () => {
4 | describe('an unknown action', () => {
5 | it('should return the previous state', () => {
6 | const action = {} as any;
7 |
8 | const result = reducer(initialState, action);
9 |
10 | expect(result).toBe(initialState);
11 | });
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/products/products.reducer.ts:
--------------------------------------------------------------------------------
1 | import { Action, createReducer, on } from '@ngrx/store';
2 | import { EntityState, createEntityAdapter } from '@ngrx/entity';
3 | import { Product } from '@ngrx-workshop-app/api-interface';
4 |
5 | import * as ProductsActions from './products.actions';
6 |
7 | export interface State extends EntityState {
8 | selectedId: string | null;
9 | loaded: boolean;
10 | error: string | null;
11 | }
12 |
13 | export const productsFeatureKey = 'products';
14 |
15 | export const adapter = createEntityAdapter({
16 | selectId: (model: Product) => model.productId
17 | });
18 |
19 | export const initialState: State = adapter.getInitialState({
20 | selectedId: null,
21 | loaded: false,
22 | error: null
23 | });
24 |
25 | const productsReducer = createReducer(
26 | initialState,
27 | on(ProductsActions.enterProductsPage, state => ({
28 | ...state,
29 | loaded: false,
30 | error: null
31 | })),
32 | on(ProductsActions.enterProductDetailsPage, (state, { id }) => ({
33 | ...state,
34 | selectedId: id
35 | })),
36 | on(ProductsActions.loadProductsSuccess, (state, { products }) =>
37 | adapter.addAll(products, { ...state, loaded: true, error: null })
38 | ),
39 | on(ProductsActions.loadProductsFailure, (state, { error }) => ({
40 | ...state,
41 | error
42 | }))
43 | );
44 |
45 | export function reducer(state: State | undefined, action: Action) {
46 | return productsReducer(state, action);
47 | }
48 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/products/products.selectors.spec.ts:
--------------------------------------------------------------------------------
1 | import { Product } from '@ngrx-workshop-app/api-interface';
2 | import * as ProductsSelectors from './products.selectors';
3 |
4 | describe('Products Selectors', () => {
5 | const ERROR_MSG = 'No Error Available';
6 | const getProductsId = it => it['productId'];
7 |
8 | let storeState;
9 |
10 | beforeEach(() => {
11 | const createProducts = (id: string, name = ''): Product => ({
12 | productId: id,
13 | name: name || `name-${id}`,
14 | description: '',
15 | price: 4.99
16 | });
17 | storeState = {
18 | products: {
19 | ids: ['PRODUCT-AAA', 'PRODUCT-BBB', 'PRODUCT-CCC'],
20 | entities: {
21 | ['PRODUCT-AAA']: createProducts('PRODUCT-AAA'),
22 | ['PRODUCT-BBB']: createProducts('PRODUCT-BBB'),
23 | ['PRODUCT-CCC']: createProducts('PRODUCT-CCC')
24 | },
25 | selectedId: 'PRODUCT-BBB',
26 | error: ERROR_MSG,
27 | loaded: true
28 | }
29 | };
30 | });
31 |
32 | describe('Products Selectors', () => {
33 | it('getAllProducts() should return the list of Products', () => {
34 | const results = ProductsSelectors.getAllProducts(storeState);
35 | const selId = getProductsId(results[1]);
36 |
37 | expect(results.length).toBe(3);
38 | expect(selId).toBe('PRODUCT-BBB');
39 | });
40 |
41 | it('getSelectedProducts() should return the selected Product', () => {
42 | const result = ProductsSelectors.getSelectedProduct(storeState);
43 | const selId = getProductsId(result);
44 |
45 | expect(selId).toBe('PRODUCT-BBB');
46 | });
47 |
48 | it("getLoaded() should return the current 'loaded' status", () => {
49 | const result = ProductsSelectors.getProductsLoaded(storeState);
50 |
51 | expect(result).toBe(true);
52 | });
53 |
54 | it("getError() should return the current 'error' storeState", () => {
55 | const result = ProductsSelectors.getError(storeState);
56 |
57 | expect(result).toBe(ERROR_MSG);
58 | });
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/products/products.selectors.ts:
--------------------------------------------------------------------------------
1 | import { createFeatureSelector, createSelector } from '@ngrx/store';
2 | import * as fromProducts from './products.reducer';
3 |
4 | const { selectAll, selectEntities } = fromProducts.adapter.getSelectors();
5 |
6 | // Lookup the 'Products' feature state managed by NgRx
7 | const getProductsState = createFeatureSelector(
8 | fromProducts.productsFeatureKey
9 | );
10 |
11 | export const getProductsLoaded = createSelector(
12 | getProductsState,
13 | state => state.loaded
14 | );
15 | export const getError = createSelector(
16 | getProductsState,
17 | state => state.error
18 | );
19 |
20 | export const getAllProducts = createSelector(
21 | getProductsState,
22 | selectAll
23 | );
24 |
25 | export const getProductsEntities = createSelector(
26 | getProductsState,
27 | selectEntities
28 | );
29 |
30 | export const getSelectedProductId = createSelector(
31 | getProductsState,
32 | state => state.selectedId
33 | );
34 |
35 | export const getSelectedProduct = createSelector(
36 | getProductsEntities,
37 | getSelectedProductId,
38 | (entities, selectedId) => selectedId && entities[selectedId]
39 | );
40 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/shipping/index.ts:
--------------------------------------------------------------------------------
1 | import * as ShippingActions from './shipping.actions';
2 | export * from './shipping.module';
3 | export * from './shipping.reducer';
4 | export * from './shipping.selectors';
5 |
6 | export { ShippingActions };
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/shipping/shipping.actions.ts:
--------------------------------------------------------------------------------
1 | import { ShippingMethod } from '@ngrx-workshop-app/api-interface';
2 | import { createAction, props } from '@ngrx/store';
3 |
4 | export const shippingApiOptionsLoadedSuccess = createAction(
5 | '[Shipping API] Options Loaded Success',
6 | props<{ shippingMethods: ShippingMethod[] }>()
7 | );
8 |
9 | export const shippingApiOptionsLoadFailure = createAction(
10 | '[Shipping API] Options Load Failure',
11 | props<{ error: string }>()
12 | );
13 |
14 | export const shippingDialogSelectShippingMethod = createAction(
15 | '[Shipping Dialog] Select Shipping Method',
16 | props<{ shippingMethod: string }>()
17 | );
18 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/shipping/shipping.effects.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { ShippingService } from '@ngrx-workshop-app/shipping-data-access';
3 | import { createEffect, Actions, ofType } from '@ngrx/effects';
4 | import { switchMap, map, catchError } from 'rxjs/operators';
5 | import { of } from 'rxjs';
6 |
7 | import * as AppActions from '../app/app.actions';
8 | import * as ShippingActions from './shipping.actions';
9 |
10 | @Injectable()
11 | export class ShippingEffects {
12 | getShippingOptions$ = createEffect(() =>
13 | this.actions.pipe(
14 | ofType(AppActions.init),
15 | switchMap(() =>
16 | this.shippingService.getShippingPrices().pipe(
17 | map(shippingMethods =>
18 | ShippingActions.shippingApiOptionsLoadedSuccess({ shippingMethods })
19 | ),
20 | catchError((error: Error) =>
21 | of(
22 | ShippingActions.shippingApiOptionsLoadFailure({
23 | error: error.message
24 | })
25 | )
26 | )
27 | )
28 | )
29 | )
30 | );
31 |
32 | dispatchAppInit$ = createEffect(() => of(AppActions.init()));
33 |
34 | constructor(
35 | private actions: Actions,
36 | private shippingService: ShippingService
37 | ) {}
38 | }
39 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/shipping/shipping.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { EffectsModule } from '@ngrx/effects';
3 | import { StoreModule } from '@ngrx/store';
4 | import { ShippingEffects } from './shipping.effects';
5 | import { reducer, shippingFeatureKey } from './shipping.reducer';
6 |
7 | @NgModule({
8 | imports: [
9 | StoreModule.forFeature(shippingFeatureKey, reducer),
10 | EffectsModule.forFeature([ShippingEffects])
11 | ]
12 | })
13 | export class ShippingStateModule {}
14 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/shipping/shipping.reducer.ts:
--------------------------------------------------------------------------------
1 | import { ShippingMethod } from '@ngrx-workshop-app/api-interface';
2 | import { createEntityAdapter, EntityState } from '@ngrx/entity';
3 | import { Action, createReducer, on } from '@ngrx/store';
4 | import * as AppActions from '../app/app.actions';
5 | import * as ShippingActions from './shipping.actions';
6 |
7 | export interface ShippingState extends EntityState {
8 | selectedMethod: string | null;
9 | loading: boolean;
10 | error: string | null;
11 | }
12 |
13 | export const shippingFeatureKey = 'shipping';
14 |
15 | export const shippingAdapter = createEntityAdapter({
16 | selectId: shippingMethod => shippingMethod.type
17 | });
18 |
19 | export const initialState: ShippingState = shippingAdapter.getInitialState({
20 | selectedMethod: null,
21 | loading: false,
22 | error: null
23 | });
24 |
25 | const shippingReducer = createReducer(
26 | initialState,
27 | on(AppActions.init, state => ({ ...state, loading: true })),
28 | on(
29 | ShippingActions.shippingApiOptionsLoadedSuccess,
30 | (state, { shippingMethods }) => ({
31 | ...shippingAdapter.addAll(shippingMethods, state),
32 | loading: false
33 | })
34 | ),
35 | on(ShippingActions.shippingApiOptionsLoadFailure, (state, { error }) => ({
36 | ...state,
37 | error
38 | })),
39 | on(
40 | ShippingActions.shippingDialogSelectShippingMethod,
41 | (state, { shippingMethod }) => ({
42 | ...state,
43 | selectedMethod: shippingMethod
44 | })
45 | )
46 | );
47 |
48 | export function reducer(state: ShippingState | undefined, action: Action) {
49 | return shippingReducer(state, action);
50 | }
51 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/app/shared/state/shipping/shipping.selectors.ts:
--------------------------------------------------------------------------------
1 | import { createFeatureSelector, createSelector } from '@ngrx/store';
2 | import { shippingAdapter, ShippingState } from './shipping.reducer';
3 |
4 | export const NO_SHIPPING_METHOD_SELECTED_TOKEN = {};
5 | Object.freeze(NO_SHIPPING_METHOD_SELECTED_TOKEN);
6 |
7 | const { selectAll, selectEntities } = shippingAdapter.getSelectors();
8 |
9 | export const selectShippingFeatureSelector = createFeatureSelector<
10 | ShippingState
11 | >('shipping');
12 |
13 | export const selectAllShippingOptions = createSelector(
14 | selectShippingFeatureSelector,
15 | selectAll
16 | );
17 |
18 | export const selectSelectedShippingOption = createSelector(
19 | selectShippingFeatureSelector,
20 | state => state.selectedMethod || NO_SHIPPING_METHOD_SELECTED_TOKEN
21 | );
22 |
23 | export const getShippingInvalid = createSelector(
24 | selectSelectedShippingOption,
25 | selected => selected === NO_SHIPPING_METHOD_SELECTED_TOKEN
26 | );
27 |
28 | export const selectShippingEntities = createSelector(
29 | selectShippingFeatureSelector,
30 | selectEntities
31 | );
32 |
33 | export const selectShippingCost = createSelector(
34 | selectShippingEntities,
35 | selectSelectedShippingOption,
36 | (entities, selected) =>
37 | selected === NO_SHIPPING_METHOD_SELECTED_TOKEN
38 | ? 0
39 | : entities[selected as string].price
40 | );
41 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nrwl/ngrx-workshop-app/a30affbee50c5d0959b8dda56ec74106ad6d67ee/apps/ngrx-workshop-app/src/assets/.gitkeep
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nrwl/ngrx-workshop-app/a30affbee50c5d0959b8dda56ec74106ad6d67ee/apps/ngrx-workshop-app/src/favicon.ico
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NgrxWorkshopApp
6 |
7 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic()
12 | .bootstrapModule(AppModule)
13 | .catch(err => console.error(err));
14 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
22 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
23 |
24 | /**
25 | * Web Animations `@angular/platform-browser/animations`
26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
28 | */
29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
30 |
31 | /**
32 | * By default, zone.js will patch all possible macroTask and DomEvents
33 | * user can disable parts of macroTask/DomEvents patch by setting following flags
34 | * because those flags need to be set before `zone.js` being loaded, and webpack
35 | * will put import in the top of bundle, so user need to create a separate file
36 | * in this directory (for example: zone-flags.ts), and put the following flags
37 | * into that file, and then add the following code before importing zone.js.
38 | * import './zone-flags.ts';
39 | *
40 | * The flags allowed in zone-flags.ts are listed here.
41 | *
42 | * The following flags will work for all browsers.
43 | *
44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
47 | *
48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
50 | *
51 | * (window as any).__Zone_enable_cross_context_check = true;
52 | *
53 | */
54 |
55 | /***************************************************************************************************
56 | * Zone JS is required by default for Angular itself.
57 | */
58 | import 'zone.js/dist/zone'; // Included with Angular CLI.
59 |
60 | /***************************************************************************************************
61 | * APPLICATION IMPORTS
62 | */
63 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/styles.css:
--------------------------------------------------------------------------------
1 | @import '~@angular/material/prebuilt-themes/indigo-pink.css';
2 |
3 | /* Global Styles */
4 |
5 | * {
6 | font-family: 'Roboto', Arial, sans-serif;
7 | color: #616161;
8 | box-sizing: border-box;
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 |
13 | body {
14 | margin: 0;
15 | }
16 |
17 | .container {
18 | display: flex;
19 | flex-direction: row;
20 | }
21 |
22 | router-outlet + * {
23 | padding: 0 16px;
24 | }
25 |
26 | /* Text */
27 |
28 | h1 {
29 | font-size: 32px;
30 | }
31 |
32 | h2 {
33 | font-size: 20px;
34 | }
35 |
36 | h1,
37 | h2 {
38 | font-weight: lighter;
39 | }
40 |
41 | p {
42 | font-size: 14px;
43 | }
44 |
45 | /* Hyperlink */
46 |
47 | a {
48 | cursor: pointer;
49 | color: #1976d2;
50 | text-decoration: none;
51 | }
52 |
53 | a:hover {
54 | opacity: 0.8;
55 | }
56 |
57 | /* Input */
58 |
59 | input {
60 | font-size: 14px;
61 | border-radius: 2px;
62 | padding: 8px;
63 | margin-bottom: 16px;
64 | border: 1px solid #bdbdbd;
65 | }
66 |
67 | label {
68 | font-size: 12px;
69 | font-weight: bold;
70 | margin-bottom: 4px;
71 | display: block;
72 | text-transform: uppercase;
73 | }
74 |
75 | /* Button */
76 | .button,
77 | button {
78 | display: inline-flex;
79 | align-items: center;
80 | padding: 8px 16px;
81 | border-radius: 2px;
82 | font-size: 14px;
83 | cursor: pointer;
84 | background-color: #1976d2;
85 | color: white;
86 | border: none;
87 | }
88 |
89 | .button:hover,
90 | button:hover {
91 | opacity: 0.8;
92 | font-weight: normal;
93 | }
94 |
95 | .button:disabled,
96 | button:disabled {
97 | opacity: 0.5;
98 | cursor: auto;
99 | }
100 |
101 | /* Fancy Button */
102 |
103 | .fancy-button {
104 | background-color: white;
105 | color: #1976d2;
106 | }
107 |
108 | .fancy-button i.material-icons {
109 | color: #1976d2;
110 | padding-right: 4px;
111 | }
112 |
113 | .fancy-button svg {
114 | padding-right: 4px;
115 | }
116 |
117 | .fancy-button svg path {
118 | stroke: #1976d2;
119 | fill: #1976d2;
120 | }
121 |
122 | .fancy-button.invalid {
123 | color: rgb(255, 55, 55);
124 | }
125 |
126 | .fancy-button.invalid svg path {
127 | stroke: rgb(255, 55, 55);
128 | fill: rgb(255, 55, 55);
129 | }
130 |
131 | /* Top Bar */
132 |
133 | ngrx-workshop-app-top-bar {
134 | width: 100%;
135 | height: 68px;
136 | background-color: #1976d2;
137 | padding: 16px;
138 | display: flex;
139 | flex-direction: row;
140 | justify-content: space-between;
141 | align-items: center;
142 | color: white;
143 | margin: 0;
144 | }
145 |
146 | ngrx-workshop-app-top-bar h1 {
147 | color: white;
148 | margin: 0;
149 | }
150 |
151 | /* Checkout Cart, Shipping Prices */
152 |
153 | .cart-item,
154 | .shipping-item {
155 | width: 100%;
156 | min-width: 400px;
157 | max-width: 450px;
158 | display: flex;
159 | flex-direction: row;
160 | justify-content: space-between;
161 | padding: 16px 32px;
162 | margin-bottom: 8px;
163 | border-radius: 2px;
164 | background-color: #eeeeee;
165 | }
166 |
167 | .shipping-item {
168 | cursor: pointer;
169 | }
170 |
171 | .shipping-item.selected {
172 | background-color: #1976d2;
173 | }
174 |
175 | .shipping-item.selected span {
176 | color: white;
177 | }
178 |
179 | simple-snack-bar span {
180 | color: white;
181 | }
182 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
2 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "types": []
6 | },
7 | "include": ["**/*.ts"],
8 | "exclude": ["src/test-setup.ts", "**/*.spec.ts"],
9 | "angularCompilerOptions": {
10 | "enableIvy": false
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["node", "jest"]
5 | },
6 | "include": ["**/*.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "files": ["src/test-setup.ts"],
9 | "include": ["**/*.spec.ts", "**/*.d.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/ngrx-workshop-app/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "ngrxWorkshopApp", "camelCase"],
5 | "component-selector": [true, "element", "ngrx-workshop-app", "kebab-case"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
3 | transform: {
4 | '^.+\\.(ts|js|html)$': 'ts-jest'
5 | },
6 | resolver: '@nrwl/jest/plugins/resolver',
7 | moduleFileExtensions: ['ts', 'js', 'html'],
8 | collectCoverage: true,
9 | coverageReporters: ['html']
10 | };
11 |
--------------------------------------------------------------------------------
/libs/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nrwl/ngrx-workshop-app/a30affbee50c5d0959b8dda56ec74106ad6d67ee/libs/.gitkeep
--------------------------------------------------------------------------------
/libs/api-interface/README.md:
--------------------------------------------------------------------------------
1 | # api-interface
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `ng test api-interface` to execute the unit tests via [Jest](https://jestjs.io).
8 |
--------------------------------------------------------------------------------
/libs/api-interface/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'api-interface',
3 | preset: '../../jest.config.js',
4 | transform: {
5 | '^.+\\.[tj]sx?$': 'ts-jest'
6 | },
7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
8 | coverageDirectory: '../../coverage/libs/api-interface'
9 | };
10 |
--------------------------------------------------------------------------------
/libs/api-interface/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/interfaces';
2 |
--------------------------------------------------------------------------------
/libs/api-interface/src/lib/interfaces.ts:
--------------------------------------------------------------------------------
1 | export interface Message {
2 | message: string;
3 | }
4 |
5 | export interface ShippingMethod {
6 | type: string;
7 | price: number;
8 | }
9 |
10 | export interface Product {
11 | productId: string;
12 | name: string;
13 | price: number;
14 | description: string;
15 | }
16 |
17 | export interface Item {
18 | productId: string;
19 | itemId: string;
20 | }
21 |
22 | export type ItemWithProduct = Item & { product: Product };
23 |
--------------------------------------------------------------------------------
/libs/api-interface/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["node", "jest"]
5 | },
6 | "include": ["**/*.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/libs/api-interface/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "types": []
6 | },
7 | "exclude": ["**/*.spec.ts"],
8 | "include": ["**/*.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/libs/api-interface/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "include": [
9 | "**/*.spec.ts",
10 | "**/*.spec.tsx",
11 | "**/*.spec.js",
12 | "**/*.spec.jsx",
13 | "**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/libs/api-interface/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": []
4 | }
5 |
--------------------------------------------------------------------------------
/libs/cart-data-access/README.md:
--------------------------------------------------------------------------------
1 | # cart-data-access
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `ng test cart-data-access` to execute the unit tests.
8 |
--------------------------------------------------------------------------------
/libs/cart-data-access/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'cart-data-access',
3 | preset: '../../jest.config.js',
4 | coverageDirectory: '../../coverage/libs/cart-data-access',
5 | snapshotSerializers: [
6 | 'jest-preset-angular/AngularSnapshotSerializer.js',
7 | 'jest-preset-angular/HTMLCommentSerializer.js'
8 | ]
9 | };
10 |
--------------------------------------------------------------------------------
/libs/cart-data-access/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/cart-data-access.module';
2 | export { CartService } from './lib/cart.service';
3 |
--------------------------------------------------------------------------------
/libs/cart-data-access/src/lib/cart-data-access.module.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, TestBed } from '@angular/core/testing';
2 | import { CartDataAccessModule } from './cart-data-access.module';
3 |
4 | describe('CartDataAccessModule', () => {
5 | beforeEach(async(() => {
6 | TestBed.configureTestingModule({
7 | imports: [CartDataAccessModule]
8 | }).compileComponents();
9 | }));
10 |
11 | it('should create', () => {
12 | expect(CartDataAccessModule).toBeDefined();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/libs/cart-data-access/src/lib/cart-data-access.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | @NgModule({
5 | imports: [CommonModule]
6 | })
7 | export class CartDataAccessModule {}
8 |
--------------------------------------------------------------------------------
/libs/cart-data-access/src/lib/cart.service.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient } from '@angular/common/http';
2 | import { Injectable } from '@angular/core';
3 | import { Product, Item } from '@ngrx-workshop-app/api-interface';
4 | import { Observable } from 'rxjs';
5 |
6 | @Injectable({
7 | providedIn: 'root'
8 | })
9 | export class CartService {
10 | constructor(private http: HttpClient) {}
11 |
12 | getItems(): Observable- {
13 | return this.http.get
- ('api/cart');
14 | }
15 |
16 | addToCart(productId: string): Observable
- {
17 | return this.http.post
- ('api/cart', { productId });
18 | }
19 |
20 | checkout(): Observable
- {
21 | return this.http.delete
- ('api/cart');
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/libs/cart-data-access/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
2 |
--------------------------------------------------------------------------------
/libs/cart-data-access/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["node", "jest"]
5 | },
6 | "include": ["**/*.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/libs/cart-data-access/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": ["dom", "es2018"]
10 | },
11 | "angularCompilerOptions": {
12 | "annotateForClosureCompiler": true,
13 | "skipTemplateCodegen": true,
14 | "strictMetadataEmit": true,
15 | "fullTemplateTypeCheck": true,
16 | "strictInjectionParameters": true,
17 | "enableResourceInlining": true
18 | },
19 | "exclude": ["src/test.ts", "**/*.spec.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/libs/cart-data-access/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "files": ["src/test-setup.ts"],
9 | "include": ["**/*.spec.ts", "**/*.d.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/libs/cart-data-access/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "ngrxWorkshopApp", "camelCase"],
5 | "component-selector": [true, "element", "ngrx-workshop-app", "kebab-case"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/libs/product-data-access/README.md:
--------------------------------------------------------------------------------
1 | # product-data-access
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `ng test product-data-access` to execute the unit tests.
8 |
--------------------------------------------------------------------------------
/libs/product-data-access/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'product-data-access',
3 | preset: '../../jest.config.js',
4 | coverageDirectory: '../../coverage/libs/product-data-access',
5 | snapshotSerializers: [
6 | 'jest-preset-angular/AngularSnapshotSerializer.js',
7 | 'jest-preset-angular/HTMLCommentSerializer.js'
8 | ]
9 | };
10 |
--------------------------------------------------------------------------------
/libs/product-data-access/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/product-data-access.module';
2 | export { ProductService } from './lib/product.service';
3 |
--------------------------------------------------------------------------------
/libs/product-data-access/src/lib/product-data-access.module.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, TestBed } from '@angular/core/testing';
2 | import { ProductDataAccessModule } from './product-data-access.module';
3 |
4 | describe('ProductDataAccessModule', () => {
5 | beforeEach(async(() => {
6 | TestBed.configureTestingModule({
7 | imports: [ProductDataAccessModule]
8 | }).compileComponents();
9 | }));
10 |
11 | it('should create', () => {
12 | expect(ProductDataAccessModule).toBeDefined();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/libs/product-data-access/src/lib/product-data-access.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | @NgModule({
5 | imports: [CommonModule]
6 | })
7 | export class ProductDataAccessModule {}
8 |
--------------------------------------------------------------------------------
/libs/product-data-access/src/lib/product.service.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient } from '@angular/common/http';
2 | import { Injectable } from '@angular/core';
3 | import { Product } from '@ngrx-workshop-app/api-interface';
4 | import { Observable } from 'rxjs';
5 |
6 | @Injectable({
7 | providedIn: 'root'
8 | })
9 | export class ProductService {
10 | constructor(private http: HttpClient) {}
11 |
12 | getProducts(): Observable {
13 | return this.http.get('api/products');
14 | }
15 |
16 | getProduct(productId: number): Observable {
17 | return this.http.get(`api/products/${productId}`);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/libs/product-data-access/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
2 |
--------------------------------------------------------------------------------
/libs/product-data-access/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["node", "jest"]
5 | },
6 | "include": ["**/*.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/libs/product-data-access/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": ["dom", "es2018"]
10 | },
11 | "angularCompilerOptions": {
12 | "annotateForClosureCompiler": true,
13 | "skipTemplateCodegen": true,
14 | "strictMetadataEmit": true,
15 | "fullTemplateTypeCheck": true,
16 | "strictInjectionParameters": true,
17 | "enableResourceInlining": true
18 | },
19 | "exclude": ["src/test.ts", "**/*.spec.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/libs/product-data-access/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "files": ["src/test-setup.ts"],
9 | "include": ["**/*.spec.ts", "**/*.d.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/libs/product-data-access/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "ngrxWorkshopApp", "camelCase"],
5 | "component-selector": [true, "element", "ngrx-workshop-app", "kebab-case"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/libs/shipping-data-access/README.md:
--------------------------------------------------------------------------------
1 | # shipping-data-access
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `ng test shipping-data-access` to execute the unit tests.
8 |
--------------------------------------------------------------------------------
/libs/shipping-data-access/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'shipping-data-access',
3 | preset: '../../jest.config.js',
4 | coverageDirectory: '../../coverage/libs/shipping-data-access',
5 | snapshotSerializers: [
6 | 'jest-preset-angular/AngularSnapshotSerializer.js',
7 | 'jest-preset-angular/HTMLCommentSerializer.js'
8 | ]
9 | };
10 |
--------------------------------------------------------------------------------
/libs/shipping-data-access/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/shipping-data-access.module';
2 | export { ShippingService } from './lib/shipping.service';
3 |
--------------------------------------------------------------------------------
/libs/shipping-data-access/src/lib/shipping-data-access.module.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, TestBed } from '@angular/core/testing';
2 | import { ShippingDataAccessModule } from './shipping-data-access.module';
3 |
4 | describe('ShippingDataAccessModule', () => {
5 | beforeEach(async(() => {
6 | TestBed.configureTestingModule({
7 | imports: [ShippingDataAccessModule]
8 | }).compileComponents();
9 | }));
10 |
11 | it('should create', () => {
12 | expect(ShippingDataAccessModule).toBeDefined();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/libs/shipping-data-access/src/lib/shipping-data-access.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | @NgModule({
5 | imports: [CommonModule]
6 | })
7 | export class ShippingDataAccessModule {}
8 |
--------------------------------------------------------------------------------
/libs/shipping-data-access/src/lib/shipping.service.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient } from '@angular/common/http';
2 | import { Injectable } from '@angular/core';
3 | import { ShippingMethod } from '@ngrx-workshop-app/api-interface';
4 | import { Observable } from 'rxjs';
5 |
6 | @Injectable({
7 | providedIn: 'root'
8 | })
9 | export class ShippingService {
10 | constructor(private http: HttpClient) {}
11 |
12 | getShippingPrices(): Observable {
13 | return this.http.get('api/shipping');
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/libs/shipping-data-access/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
2 |
--------------------------------------------------------------------------------
/libs/shipping-data-access/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["node", "jest"]
5 | },
6 | "include": ["**/*.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/libs/shipping-data-access/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": ["dom", "es2018"]
10 | },
11 | "angularCompilerOptions": {
12 | "annotateForClosureCompiler": true,
13 | "skipTemplateCodegen": true,
14 | "strictMetadataEmit": true,
15 | "fullTemplateTypeCheck": true,
16 | "strictInjectionParameters": true,
17 | "enableResourceInlining": true
18 | },
19 | "exclude": ["src/test.ts", "**/*.spec.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/libs/shipping-data-access/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "files": ["src/test-setup.ts"],
9 | "include": ["**/*.spec.ts", "**/*.d.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/libs/shipping-data-access/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "ngrxWorkshopApp", "camelCase"],
5 | "component-selector": [true, "element", "ngrx-workshop-app", "kebab-case"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/nx.json:
--------------------------------------------------------------------------------
1 | {
2 | "npmScope": "ngrx-workshop-app",
3 | "implicitDependencies": {
4 | "angular.json": "*",
5 | "package.json": "*",
6 | "tsconfig.json": "*",
7 | "tslint.json": "*",
8 | "nx.json": "*"
9 | },
10 | "projects": {
11 | "ngrx-workshop-app-e2e": {
12 | "tags": []
13 | },
14 | "ngrx-workshop-app": {
15 | "tags": []
16 | },
17 | "api": {
18 | "tags": []
19 | },
20 | "api-interface": {
21 | "tags": []
22 | },
23 | "cart-data-access": {
24 | "tags": []
25 | },
26 | "product-data-access": {
27 | "tags": []
28 | },
29 | "shipping-data-access": {
30 | "tags": []
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngrx-workshop-app",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "ng run ngrx-workshop-app:serve-with-api",
8 | "build": "ng build",
9 | "test": "ng test",
10 | "lint": "nx lint && ng lint",
11 | "e2e": "ng e2e",
12 | "affected:apps": "nx affected:apps",
13 | "affected:libs": "nx affected:libs",
14 | "affected:build": "nx affected:build",
15 | "affected:e2e": "nx affected:e2e",
16 | "affected:test": "nx affected:test",
17 | "affected:lint": "nx affected:lint",
18 | "affected:dep-graph": "nx affected:dep-graph",
19 | "affected": "nx affected",
20 | "format": "nx format:write",
21 | "format:write": "nx format:write",
22 | "format:check": "nx format:check",
23 | "update": "ng update @nrwl/workspace",
24 | "update:check": "ng update",
25 | "workspace-schematic": "nx workspace-schematic",
26 | "dep-graph": "nx dep-graph",
27 | "help": "nx help"
28 | },
29 | "private": true,
30 | "dependencies": {
31 | "@angular/animations": "^8.0.0",
32 | "@angular/cdk": "^8.1.1",
33 | "@angular/common": "^8.0.0",
34 | "@angular/compiler": "^8.0.0",
35 | "@angular/core": "^8.0.0",
36 | "@angular/forms": "^8.0.0",
37 | "@angular/material": "^8.1.1",
38 | "@angular/platform-browser": "^8.0.0",
39 | "@angular/platform-browser-dynamic": "^8.0.0",
40 | "@angular/router": "^8.0.0",
41 | "@nestjs/common": "^6.2.4",
42 | "@nestjs/core": "^6.2.4",
43 | "@nestjs/platform-express": "^6.2.4",
44 | "@ngrx/effects": "8.1.0",
45 | "@ngrx/entity": "8.1.0",
46 | "@ngrx/router-store": "8.1.0",
47 | "@ngrx/store": "8.1.0",
48 | "@nrwl/angular": "8.2.0",
49 | "core-js": "^2.5.4",
50 | "reflect-metadata": "^0.1.12",
51 | "rxjs": "~6.5.2",
52 | "zone.js": "^0.9.1"
53 | },
54 | "devDependencies": {
55 | "@angular-devkit/build-angular": "^0.800.0",
56 | "@angular/cli": "8.0.0",
57 | "@angular/compiler-cli": "^8.0.0",
58 | "@angular/language-service": "^8.0.0",
59 | "rxjs": "~6.5.2",
60 | "zone.js": "^0.9.1",
61 | "@nestjs/schematics": "^6.3.0",
62 | "@nestjs/testing": "^6.2.4",
63 | "@ngrx/schematics": "^8.1.0",
64 | "@ngrx/store-devtools": "8.1.0",
65 | "@nrwl/angular": "8.2.0",
66 | "@nrwl/cypress": "8.2.0",
67 | "@nrwl/jest": "8.2.0",
68 | "@nrwl/nest": "8.2.0",
69 | "@nrwl/node": "8.2.0",
70 | "@nrwl/workspace": "8.2.0",
71 | "@types/jest": "24.0.9",
72 | "@types/node": "~8.9.4",
73 | "codelyzer": "~5.0.1",
74 | "cypress": "~3.3.1",
75 | "dotenv": "6.2.0",
76 | "jest": "24.1.0",
77 | "jest-preset-angular": "7.0.0",
78 | "prettier": "1.16.4",
79 | "ts-jest": "24.0.0",
80 | "ts-node": "~7.0.0",
81 | "tslint": "~5.11.0",
82 | "typescript": "~3.4.5"
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/tools/schematics/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nrwl/ngrx-workshop-app/a30affbee50c5d0959b8dda56ec74106ad6d67ee/tools/schematics/.gitkeep
--------------------------------------------------------------------------------
/tools/tsconfig.tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/out-tsc/tools",
5 | "rootDir": ".",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": ["node"]
9 | },
10 | "include": ["**/*.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "rootDir": ".",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "importHelpers": true,
11 | "target": "es2015",
12 | "module": "esnext",
13 | "typeRoots": ["node_modules/@types"],
14 | "lib": ["es2017", "dom"],
15 | "skipLibCheck": true,
16 | "skipDefaultLibCheck": true,
17 | "baseUrl": ".",
18 | "paths": {
19 | "@ngrx-workshop-app/api-interface": ["libs/api-interface/src/index.ts"],
20 | "@ngrx-workshop-app/cart-data-access": [
21 | "libs/cart-data-access/src/index.ts"
22 | ],
23 | "@ngrx-workshop-app/product-data-access": [
24 | "libs/product-data-access/src/index.ts"
25 | ],
26 | "@ngrx-workshop-app/shipping-data-access": [
27 | "libs/shipping-data-access/src/index.ts"
28 | ],
29 | "@ngrx-workshop-app/*": ["apps/ngrx-workshop-app/src/app/*"]
30 | }
31 | },
32 | "exclude": ["node_modules", "tmp"]
33 | }
34 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/@nrwl/workspace/src/tslint",
4 | "node_modules/codelyzer"
5 | ],
6 | "rules": {
7 | "arrow-return-shorthand": true,
8 | "callable-types": true,
9 | "class-name": true,
10 | "deprecation": {
11 | "severity": "warn"
12 | },
13 | "forin": true,
14 | "import-blacklist": [true, "rxjs/Rx"],
15 | "interface-over-type-literal": true,
16 | "member-access": false,
17 | "member-ordering": [
18 | true,
19 | {
20 | "order": [
21 | "static-field",
22 | "instance-field",
23 | "static-method",
24 | "instance-method"
25 | ]
26 | }
27 | ],
28 | "no-arg": true,
29 | "no-bitwise": true,
30 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"],
31 | "no-construct": true,
32 | "no-debugger": true,
33 | "no-duplicate-super": true,
34 | "no-empty": false,
35 | "no-empty-interface": true,
36 | "no-eval": true,
37 | "no-inferrable-types": [true, "ignore-params"],
38 | "no-misused-new": true,
39 | "no-non-null-assertion": true,
40 | "no-shadowed-variable": true,
41 | "no-string-literal": false,
42 | "no-string-throw": true,
43 | "no-switch-case-fall-through": true,
44 | "no-unnecessary-initializer": true,
45 | "no-unused-expression": true,
46 | "no-var-keyword": true,
47 | "object-literal-sort-keys": false,
48 | "prefer-const": true,
49 | "radix": true,
50 | "triple-equals": [true, "allow-null-check"],
51 | "unified-signatures": true,
52 | "variable-name": false,
53 | "nx-enforce-module-boundaries": [
54 | true,
55 | {
56 | "allow": [],
57 | "depConstraints": [
58 | {
59 | "sourceTag": "*",
60 | "onlyDependOnLibsWithTags": ["*"]
61 | }
62 | ]
63 | }
64 | ],
65 | "directive-selector": [true, "attribute", "app", "camelCase"],
66 | "component-selector": [true, "element", "app", "kebab-case"],
67 | "no-conflicting-lifecycle": true,
68 | "no-host-metadata-property": true,
69 | "no-input-rename": true,
70 | "no-inputs-metadata-property": true,
71 | "no-output-native": true,
72 | "no-output-on-prefix": true,
73 | "no-output-rename": true,
74 | "no-outputs-metadata-property": true,
75 | "template-banana-in-box": true,
76 | "template-no-negated-async": true,
77 | "use-lifecycle-interface": true,
78 | "use-pipe-transform-interface": true
79 | }
80 | }
81 |
--------------------------------------------------------------------------------