├── .gitattributes
├── .github
├── ISSUE_TEMPLATE.md
└── workflows
│ └── main.yml
├── .gitignore
├── .husky
├── post-commit
└── pre-commit
├── .npmignore
├── .nvmrc
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── angular.json
├── jest.config.js
├── package-lock.json
├── package.json
├── projects
├── bank-v11
│ ├── e2e
│ │ ├── protractor.conf.js
│ │ ├── src
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── app.po.ts
│ │ └── tsconfig.json
│ ├── karma.conf.js
│ ├── src
│ │ ├── app
│ │ │ ├── actions
│ │ │ │ ├── actions.component.html
│ │ │ │ ├── actions.component.scss
│ │ │ │ └── actions.component.ts
│ │ │ ├── app-routing.module.ts
│ │ │ ├── app.component.html
│ │ │ ├── app.component.scss
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── balance
│ │ │ │ ├── balance.component.html
│ │ │ │ ├── balance.component.scss
│ │ │ │ └── balance.component.ts
│ │ │ ├── stores
│ │ │ │ └── account.store.ts
│ │ │ └── transactions
│ │ │ │ ├── transactions.component.html
│ │ │ │ ├── transactions.component.scss
│ │ │ │ └── transactions.component.ts
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.scss
│ │ └── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── bank-v13
│ ├── karma.conf.js
│ ├── src
│ │ ├── app
│ │ │ ├── actions
│ │ │ │ ├── actions.component.html
│ │ │ │ ├── actions.component.scss
│ │ │ │ └── actions.component.ts
│ │ │ ├── app.component.html
│ │ │ ├── app.component.scss
│ │ │ ├── app.component.spec.ts
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── balance
│ │ │ │ ├── balance.component.html
│ │ │ │ ├── balance.component.scss
│ │ │ │ └── balance.component.ts
│ │ │ ├── stores
│ │ │ │ └── account.store.ts
│ │ │ └── transactions
│ │ │ │ ├── transactions.component.html
│ │ │ │ ├── transactions.component.scss
│ │ │ │ └── transactions.component.ts
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.scss
│ │ └── test.ts
│ ├── tsconfig.app.json
│ └── tsconfig.spec.json
├── bank
│ ├── browserslist
│ ├── e2e
│ │ ├── protractor.conf.js
│ │ ├── src
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── app.po.ts
│ │ └── tsconfig.json
│ ├── karma.conf.js
│ ├── src
│ │ ├── app
│ │ │ ├── actions
│ │ │ │ └── actions.component.ts
│ │ │ ├── app.component.html
│ │ │ ├── app.component.scss
│ │ │ ├── app.component.spec.ts
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── balance
│ │ │ │ └── balance.component.ts
│ │ │ ├── stores
│ │ │ │ └── account.store.ts
│ │ │ └── transactions
│ │ │ │ └── transactions.component.ts
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.scss
│ │ └── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── mobx-angular
│ ├── README.md
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── setup-tests.ts
│ ├── src
│ │ ├── lib
│ │ │ ├── mobx-angular.module.ts
│ │ │ ├── mobx-angular.spec.ts
│ │ │ ├── mobx-autorun.directive.ts
│ │ │ ├── mobx-reaction.directive.ts
│ │ │ └── router-store.service.ts
│ │ ├── public-api.ts
│ │ └── test.ts
│ ├── tsconfig.lib.json
│ ├── tsconfig.lib.prod.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── tictactoe
│ ├── browserslist
│ ├── e2e
│ │ ├── protractor.conf.js
│ │ ├── src
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── app.po.ts
│ │ └── tsconfig.json
│ ├── karma.conf.js
│ ├── src
│ │ ├── app
│ │ │ ├── app.component.html
│ │ │ ├── app.component.scss
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── components
│ │ │ │ ├── board
│ │ │ │ │ └── board.component.ts
│ │ │ │ ├── cell
│ │ │ │ │ └── cell.component.ts
│ │ │ │ ├── controls
│ │ │ │ │ └── controls.component.ts
│ │ │ │ ├── game
│ │ │ │ │ └── game.component.ts
│ │ │ │ └── score
│ │ │ │ │ └── score.component.ts
│ │ │ ├── index.ts
│ │ │ └── services
│ │ │ │ └── game.store.ts
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.scss
│ │ └── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── todo-v6
│ ├── browserslist
│ ├── e2e
│ │ ├── protractor.conf.js
│ │ ├── src
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── app.po.ts
│ │ └── tsconfig.json
│ ├── karma.conf.js
│ ├── src
│ │ ├── app
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── components
│ │ │ │ ├── count
│ │ │ │ │ └── count.component.ts
│ │ │ │ ├── footer
│ │ │ │ │ └── footer.component.ts
│ │ │ │ └── section
│ │ │ │ │ └── section.component.ts
│ │ │ └── stores
│ │ │ │ └── todos.store.ts
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.scss
│ │ └── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ └── tslint.json
└── todo
│ ├── browserslist
│ ├── e2e
│ ├── protractor.conf.js
│ ├── src
│ │ ├── app.e2e-spec.ts
│ │ └── app.po.ts
│ └── tsconfig.json
│ ├── karma.conf.js
│ ├── src
│ ├── app
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── components
│ │ │ ├── count
│ │ │ │ └── count.component.ts
│ │ │ ├── footer
│ │ │ │ └── footer.component.ts
│ │ │ └── section
│ │ │ │ └── section.component.ts
│ │ └── stores
│ │ │ └── todos.store.ts
│ ├── assets
│ │ └── .gitkeep
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.scss
│ └── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── tsconfig.json
└── tslint.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | projects/bank/* linguist-generated=true
2 | projects/bank/* linguist-vendored
3 | projects/tictactoe/* linguist-generated=true
4 | projects/tictactoe/* linguist-vendored
5 | projects/todo/* linguist-generated=true
6 | projects/todo/* linguist-vendored
7 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Important!
2 |
3 | In case you are reporting a bug, or requesting help, please reproduce it in a GIT repo or fork this online template:
4 | https://stackblitz.com/edit/angular-usygae?file=app%2Fapp.component.html
5 |
6 | If you are unable to reproduce, please:
7 | - paste your package.json
8 | - say which browser you are using
9 | - paste your nodes and options and whatever code your feel will help reproduce the error
10 |
11 | Thank you and happy programming
12 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | build:
7 | name: "Units Test & Verify Bundle Size"
8 |
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - name: "Clone Repository"
13 | uses: actions/checkout@v3
14 | with:
15 | fetch-depth: 1
16 |
17 | - name: "Install Node"
18 | uses: actions/setup-node@v3.6.0
19 | with:
20 | node-version: '18'
21 |
22 | - name: "Install Project Dependencies"
23 | shell: bash
24 | run: npm install
25 |
26 | - name: "Unit tests"
27 | shell: bash
28 | run: |
29 | Xvfb -ac :99 -screen 0 1280x1024x16 &
30 | export DISPLAY=:99
31 | npm run testci:mobxangular
32 |
33 | - name: "Compare bundle sizes"
34 | uses: preactjs/compressed-size-action@2.5.0
35 | with:
36 | repo-token: "${{ secrets.GITHUB_TOKEN }}"
37 | build-script: "build:mobx-angular"
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.angular/cache
2 | node_modules
3 | dist
4 | coverage
5 | .DS_Store
6 | npm-debug.log
7 | compiled
8 | .idea
9 |
--------------------------------------------------------------------------------
/.husky/post-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | echo [Husky] pre-commit running Prettier on staged files
5 | npx git reset
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | echo [Husky] pre-commit running Prettier on staged files
5 | npx lint-staged
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | example
3 | *.ts
4 | !*.d.ts
5 | spec
6 | lib
7 | compiled
8 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 6.2.2
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - '10'
5 |
6 | before_script:
7 | - npm install
8 |
9 | script:
10 | - npm run lint
11 | - npm run build
12 | - npm run test
13 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # 4.7.1 (2023-01-26)
3 | * Fix npm publish
4 |
5 |
6 | # 4.7.0 (2022-12-05)
7 | * Added support for Angular 15
8 |
9 |
10 |
11 | # 4.6.0 (2022-06-27)
12 | * Added support for Angular 14
13 |
14 |
15 | # 4.5.0 (2022-01-05)
16 | * Update mobx-angular peerDependencies for Angular 13
17 |
18 |
19 | # 4.4.0 (2021-11-06)
20 | * Added support for Angular 13
21 | * Added an example project in Angular 13
22 | * Updated example project in Angular 11
23 |
24 |
25 | # 4.3.0 (2021-03-05)
26 | * Added support for Angular 11
27 | * Added an example project in Angular 11
28 |
29 |
30 | # 4.2.0 (2020-12-17)
31 | * Allow to pass MobX options to autorun and reaction directive
32 |
33 |
34 | # 4.1.0 (2020-02-19)
35 | * Removed mobxAutorunSync Directive
36 | * Added examples in Angular 9.0.0
37 |
38 |
39 | # 4.0.0 (2020-02-14)
40 | * Refactor to CLI packager
41 |
42 |
43 | # 3.1.1 (2020-02-11)
44 | * Added support for Angular 9 (removed spread operator)
45 |
46 |
47 | # 3.1.0 (2019-12-08)
48 | * Added observable/computed/action modifiers to proxy methods
49 |
50 |
51 | # 3.0.3 (2019-11-05)
52 | * Removed deprecated _view usage
53 |
54 |
55 | # 3.0.2 (2019-10-12)
56 | * Removed deprecated Renderer
57 |
58 |
59 | # 3.0.1 (2018-03-13)
60 | * Remove Debug
61 |
62 |
63 | # 3.0.0 (2018-03-13)
64 | * Support MobX 4
65 |
66 |
67 | # 2.1.1 (2018-01-21)
68 | * Fixing build problem
69 |
70 |
71 | # 2.1.0 (2018-01-19)
72 | * Not detaching from CD by default
73 |
74 |
75 | # 2.0.2 (2018-1-18)
76 | * Not collapsing log in debug
77 |
78 |
79 | # 2.0.1 (2017-12-05)
80 | * Fixed console.log
81 |
82 |
83 | # 2.0.0 (2017-11-30)
84 | * Detaching from CD by default in mobxAutorun (allow to override with dontDetach=true)
85 |
86 |
87 | # 1.9.0 (2017-10-05)
88 | * Changed MobXDebug element to right-click
89 |
90 |
91 | # 1.8.1 (2017-10-05)
92 | * Fixed MobxDebug on browsers where localStorage === null
93 |
94 |
95 | # 1.8.0 (2017-10-03)
96 | * Fixed MobxDebug on browsers where localStorage === null
97 |
98 |
99 | # 1.7.0 (2017-09-03)
100 | * Fixed MobX dependency
101 |
102 |
103 | # 1.6.0 (2017-08-17)
104 | * Upgraded to MobX 3.0.0
105 |
106 |
107 | # 1.5.0 (2017-04-19)
108 | * Added @action to proxy decorator
109 |
110 |
111 | # 1.4.2 (2017-04-12)
112 | * Updated bug with angular 4 debug name
113 |
114 |
115 | # 1.4.1 (2017-04-04)
116 | * Updated dependencies to support Angular 4
117 |
118 |
119 | # 1.3.0 (2017-03-07)
120 | * Renamed to mobx-angular
121 |
122 |
123 | # 1.2.9 (2017-02-25)
124 | * Added computed wrapper
125 |
126 |
127 | # 1.2.8 (2017-02-22)
128 | * Solved @computed twice issue
129 | * Deprecate mobxAutorunSync
130 |
131 |
132 | # 1.2.7 (2017-02-22)
133 | * Fixed using pipe - createEmbeddedView inside ngOnInit
134 |
135 |
136 | # 1.2.6 (2017-02-09)
137 | * Fixed exports / declarations in module
138 |
139 |
140 | # 1.2.5 (2017-02-06)
141 | * Added observable proxy for test
142 |
143 |
144 | # 1.2.4 (2017-02-04)
145 | * Replaced main / jsnext:main / module mismatch
146 |
147 |
148 | # 1.2.3 (2017-02-04)
149 | * Added AoT metadata
150 | * Added UMD bundle
151 | * Checking for localStorage / console in debug
152 |
153 |
154 | # 1.2.2 (2017-01-21)
155 | * Fixed change detection errors by creating element in constructor, and auto detecting in afterViewInit
156 | * Fixed ng2MobxDebug element positioning
157 |
158 |
159 | # 1.2.1 (2017-01-21)
160 | * Added ng2MobxDebug
161 |
162 |
163 | # 1.2.0 (2017-01-21)
164 | * Update example to be like TodoMVC
165 | * Update target compilation to ES5
166 | * Fixed dependencies
167 | * Added AoT
168 | * mobxReaction fires immediately
169 |
170 |
171 | # 1.1.3 (2016-11-12)
172 | * Using autorunAsync instead of sync
173 |
174 |
175 | # 1.1.2 (2016-11-10)
176 | * Relaxed Angular peer dependency requirement
177 | * Fix typo in readme
178 |
179 |
180 | # 1.1.1 (2016-10-27)
181 | * Added mobxReaction directive
182 |
183 |
184 | # 1.1.0 (2016-10-27)
185 | * Changed approach to use mobxAutorun directive
186 |
187 |
188 | # 1.0.1 (2016-10-24)
189 | * Initial version
190 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 500Tech LTD
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/mobxjs/mobx-angular/actions/workflows/main.yml)
2 | [](https://badge.fury.io/js/mobx-angular)
3 |
4 | # MobX Angular
5 |
6 | ## Why MobX?
7 |
8 | Angular's change detection is great, but it updates the entire UI on every change, and doesn't have any knowledge of how our components use our services.
9 |
10 | MobX automatically knows what properties your components use from the stores and listens to changes. It allows you to automatically react to changes and update only the parts of the UI that need to be updated, making your app performant.
11 |
12 | With MobX you manage your app's state using mutable objects and classes. It also helps you create computed values and side-effects.
13 |
14 | [Learn more about MobX](https://mobx.js.org/README.html)!
15 |
16 | ## This library
17 |
18 | 1. Automatically observe all the observables that your component uses
19 | 2. Automatically runs change detection - works great with OnPush strategy
20 | 3. Disposes of all the observers when the component is destroyed
21 |
22 | ## Usage
23 |
24 | Install:
25 |
26 | ```shell
27 | npm install --save mobx-angular mobx
28 | ```
29 |
30 | Import the MobxAngularModule:
31 |
32 | ```ts
33 | import { MobxAngularModule } from 'mobx-angular';
34 |
35 | @NgModule({
36 | imports: [..., MobxAngularModule]
37 | })
38 | export class MyModule {}
39 | ```
40 |
41 | ## Autorun
42 |
43 | Use `*mobxAutorun` directive in your template:
44 |
45 | ```ts
46 | import { Component, ChangeDetectionStrategy } from '@angular/core';
47 | import { store } from './store/counter';
48 |
49 | @Component({
50 | changeDetection: ChangeDetectionStrategy.OnPush,
51 | template: `
52 |
53 | {{ store.value }} - {{ store.computedValue }}
54 | Action
55 |
56 | `
57 | })
58 | export class AppComponent {
59 | store = store;
60 | }
61 | ```
62 |
63 | The directive will do the following:
64 |
65 | - Observe all the observables / computed values that your component uses
66 | - Automatically run the `detectChanges` method whenever there's a relevant change
67 |
68 | Under the hood, this magic happens by running `autorun(() => view.detectChanges())`
69 |
70 | ## Why directive and not decorator?
71 |
72 | In order to inject the change detector, and implement lifecycle hooks like ngOnDestroy, this library uses a directive, which is the most elegant solution in Angular.
73 |
74 | It also has the benefit of allowing you to easily have multiple observed sections of your component's template, in case it is required.
75 |
76 | ## Detach
77 |
78 | You can improve your component's performance by detaching it from CD (Change Detection), by supplying `*mobxAutorun="{ detach: true }"`.
79 |
80 | This might cause things to stop updating. You have 3 options to manage that:
81 |
82 | - Define local component properties as observables or computed values
83 | - Surround with \*mobxAutorun only the parts that actually use observable / computed values from the store
84 | - Instead of detaching, use OnPush CD strategy on the component
85 |
86 | ## Reaction
87 |
88 | Aside from autorun, MobX allows you to react to specific data changes.
89 |
90 | Usage:
91 |
92 | ```ts
93 | import { Component, ChangeDetectionStrategy } from '@angular/core';
94 | import { store } from './store/counter';
95 |
96 | @Component({
97 | changeDetection: ChangeDetectionStrategy.OnPush,
98 | template: `
99 |
100 | {{ parity }}
101 |
102 | `
103 | })
104 | class AppComponent {
105 | getParity() {
106 | return (this.parity = store.counter % 2 ? 'Odd' : 'Even');
107 | }
108 | }
109 | ```
110 |
111 | The `callback` function will automatically re-run whenever any observable that it uses changes.
112 | Change Detection will run automatically whenever the return value of `callback` changes.
113 | If you don't return anything, change detection will not run.
114 |
115 | In this example, the `parity` property will be updated according to `counter`,
116 | and change detection will run only when the `parity` changes.
117 |
118 | ## Options
119 |
120 | It is possible to pass an options object to `*mobxAutorun` and `*mobxReaction` directives. For a list of possible options visit the official [docs](https://mobx.js.org/reactions.html).
121 |
122 | Usage:
123 |
124 | ```ts
125 | import { Component, ChangeDetectionStrategy } from '@angular/core';
126 | import { store } from './store/counter';
127 | import { comparer } from 'mobx';
128 |
129 | @Component({
130 | changeDetection: ChangeDetectionStrategy.OnPush,
131 | template: `
132 |
133 | {{ store.value }} - {{ store.computedValue }}
134 | Action
135 |
136 |
137 | {{ parity }}
138 |
139 | `
140 | })
141 | export class AppComponent {
142 | store = store;
143 | comparer = comparer;
144 |
145 | getParity() {
146 | return (this.parity = store.counter % 2 ? 'Odd' : 'Even');
147 | }
148 | }
149 | ```
150 |
151 | ## Injectable stores
152 |
153 | You can easily make your stores injectable:
154 |
155 | ```ts
156 | import { observable, action } from 'mobx-angular';
157 |
158 | @Injectable()
159 | class Store {
160 | @observable value;
161 | @action doSomething() { ... }
162 | }
163 | ```
164 |
165 | ## Router store
166 |
167 | Using the `RouterStore`, you can observe route changes.
168 | By injecting this store to a component you will get access to the url as a MobX observable, and the entire activated route snapshot.
169 |
170 | Usage:
171 |
172 | ```ts
173 | import { Component, ChangeDetectionStrategy } from '@angular/core';
174 | import { RouterStore } from 'mobx-angular';
175 |
176 | @Component({
177 | changeDetection: ChangeDetectionStrategy.OnPush,
178 | template: `
`
179 | })
180 | export class AppComponent {
181 | constructor(public routerStore: RouterStore) {
182 | // You get access to the url as a mobx observable through routerStore.url
183 | // And to the activated route snapshot through routerStore.routeSnapshot
184 | }
185 | }
186 | ```
187 |
188 | ## MobX v6
189 |
190 | In order to become compatible with modern ES standards, decorators are not used by default in MobX v6. It still supports decorators, but they are not recommended for greenfield projects.
191 | [Read More](https://michel.codes/blogs/mobx6)
192 |
193 | - In order to use MobX 6 with decorators `makeObservable(this)` should be added to the constructor, and "useDefineForClassFields": true should be added to tsconfig.json.
194 | - For the full migration guide, click [here](https://mobx.js.org/migrating-from-4-or-5.html).
195 |
196 | Check out `projects/todo-v6` for a working example.
197 |
198 | ## Using with OnPush or ngZone: 'noop'
199 |
200 | To achieve great performance, you can set `OnPush` change detection strategy on your components (this can be configured as default in `.angular-cli.json`).
201 | MobX will run change detection manually for you on the components that need to be updated.
202 |
203 | - In Angular 5 there's a new option, which is to disable Zone completely when bootstrapping the app (ngZone: 'noop').
204 | - Please note that this means that all 3rd-party components will stop working (because they rely on change detection to work via Zone).
205 |
206 | ## AoT
207 |
208 | Some people complained about AoT when using mobx decorators inside components.
209 | In case you do that - we export a proxy to the decorators from mobx-angular, which should be AoT compatible, e.g.:
210 | `import { observable, computed } from 'mobx-angular'`
211 |
212 | ## DevTools
213 |
214 | Check out `projects/todo` for an example of how to use `mobx-remotedev` with Angular:
215 |
216 | ```shell
217 | npm install mobx-remotedev
218 | ```
219 |
220 | ```ts
221 | // app.module.ts
222 | import remotedev from 'mobx-remotedev';
223 | import { Todos } from './stores/todos.store';
224 |
225 | @NgModule({
226 | ...
227 | providers: [
228 | { provide: Todos, useClass: remotedev(Todos, { global: true }), deps: [] }
229 | ],
230 | })
231 |
232 | ```
233 |
234 | ## Examples
235 |
236 | See the `projects` folder, specifically these files:
237 | `/projects/todo/src/app/stores/todos.store.ts`
238 | `/projects/todo/src/app/app.component.ts`
239 |
240 | To run the examples, clone this repo and run:
241 |
242 | ```shell
243 | npm install -g @angular/cli
244 | npm install
245 | npm run build
246 | npm run start # for example `npm run start todo`
247 | ```
248 |
249 | ## Debugging MobX (only for mobx-angular versions 2.X and below)
250 |
251 | mobx-angular comes with a handy debug tool.
252 | To turn on / off the debug tool, open developer tools' console, and run:
253 |
254 | ```ts
255 | mobxAngularDebug(true); // turn on
256 | mobxAngularDebug(false); // turn off
257 | ```
258 |
259 | Then you can right-click on the components that use mobx directives, and you will see a console log of the components' dependencies.
260 | Also, every action that happens in mobx will be console.logged in a nice way.
261 |
262 | TBD - support debugging for MobX 4
263 |
264 | ## Legacy Versions
265 |
266 | If you're looking for the Angular 1 version version, it's [here](https://github.com/mobxjs/ng1-mobx).
267 |
268 | ## Contributing
269 |
270 | Important things to always consider when changing code in this library:
271 |
272 | - Make it readable, add comments when necessary.
273 | - Add unit tests for the new functionality. Think about edge cases. Make sure tests pass before merging.
274 | - Keep backwards compatibility. Don't force users to refactor their code, even if it means adding a new API instead of changing an existing one.
275 | - Keep SEMVER. If breaking changes is unavoidable - increase a major version. New features, however small should increase a minor version, and patch is for bugfixes/performance/refactoring.
276 | - Think about bundle size and speed.
277 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "mobx-angular": {
7 | "projectType": "library",
8 | "root": "projects/mobx-angular",
9 | "sourceRoot": "projects/mobx-angular/src",
10 | "prefix": "mobx",
11 | "architect": {
12 | "build": {
13 | "builder": "@angular-devkit/build-angular:ng-packagr",
14 | "options": {
15 | "tsConfig": "projects/mobx-angular/tsconfig.lib.json",
16 | "project": "projects/mobx-angular/ng-package.json"
17 | },
18 | "configurations": {
19 | "production": {
20 | "tsConfig": "projects/mobx-angular/tsconfig.lib.prod.json"
21 | }
22 | }
23 | },
24 | "test": {
25 | "builder": "@angular-devkit/build-angular:karma",
26 | "options": {
27 | "main": "projects/mobx-angular/src/test.ts",
28 | "tsConfig": "projects/mobx-angular/tsconfig.spec.json",
29 | "karmaConfig": "projects/mobx-angular/karma.conf.js"
30 | }
31 | }
32 | }
33 | },
34 | "todo": {
35 | "projectType": "application",
36 | "schematics": {
37 | "@schematics/angular:component": {
38 | "style": "scss"
39 | }
40 | },
41 | "root": "projects/todo",
42 | "sourceRoot": "projects/todo/src",
43 | "prefix": "app",
44 | "architect": {
45 | "build": {
46 | "builder": "@angular-devkit/build-angular:browser",
47 | "options": {
48 | "outputPath": "dist/todo",
49 | "index": "projects/todo/src/index.html",
50 | "main": "projects/todo/src/main.ts",
51 | "polyfills": "projects/todo/src/polyfills.ts",
52 | "tsConfig": "projects/todo/tsconfig.app.json",
53 | "assets": [
54 | "projects/todo/src/favicon.ico",
55 | "projects/todo/src/assets"
56 | ],
57 | "styles": ["projects/todo/src/styles.scss"],
58 | "scripts": [],
59 | "vendorChunk": true,
60 | "extractLicenses": false,
61 | "buildOptimizer": false,
62 | "sourceMap": true,
63 | "optimization": false,
64 | "namedChunks": true
65 | },
66 | "configurations": {
67 | "production": {
68 | "fileReplacements": [
69 | {
70 | "replace": "projects/todo/src/environments/environment.ts",
71 | "with": "projects/todo/src/environments/environment.prod.ts"
72 | }
73 | ],
74 | "optimization": true,
75 | "outputHashing": "all",
76 | "sourceMap": false,
77 | "namedChunks": false,
78 | "extractLicenses": true,
79 | "vendorChunk": false,
80 | "buildOptimizer": true,
81 | "budgets": [
82 | {
83 | "type": "initial",
84 | "maximumWarning": "2mb",
85 | "maximumError": "5mb"
86 | },
87 | {
88 | "type": "anyComponentStyle",
89 | "maximumWarning": "6kb",
90 | "maximumError": "10kb"
91 | }
92 | ]
93 | }
94 | },
95 | "defaultConfiguration": ""
96 | },
97 | "serve": {
98 | "builder": "@angular-devkit/build-angular:dev-server",
99 | "options": {
100 | "buildTarget": "todo:build"
101 | },
102 | "configurations": {
103 | "production": {
104 | "buildTarget": "todo:build:production"
105 | }
106 | }
107 | },
108 | "extract-i18n": {
109 | "builder": "@angular-devkit/build-angular:extract-i18n",
110 | "options": {
111 | "buildTarget": "todo:build"
112 | }
113 | },
114 | "test": {
115 | "builder": "@angular-devkit/build-angular:karma",
116 | "options": {
117 | "main": "projects/todo/src/test.ts",
118 | "polyfills": "projects/todo/src/polyfills.ts",
119 | "tsConfig": "projects/todo/tsconfig.spec.json",
120 | "karmaConfig": "projects/todo/karma.conf.js",
121 | "assets": [
122 | "projects/todo/src/favicon.ico",
123 | "projects/todo/src/assets"
124 | ],
125 | "styles": ["projects/todo/src/styles.scss"],
126 | "scripts": []
127 | }
128 | },
129 | "e2e": {
130 | "builder": "@angular-devkit/build-angular:protractor",
131 | "options": {
132 | "protractorConfig": "projects/todo/e2e/protractor.conf.js",
133 | "devServerTarget": "todo:serve"
134 | },
135 | "configurations": {
136 | "production": {
137 | "devServerTarget": "todo:serve:production"
138 | }
139 | }
140 | }
141 | }
142 | },
143 | "tictactoe": {
144 | "projectType": "application",
145 | "schematics": {
146 | "@schematics/angular:component": {
147 | "style": "scss"
148 | }
149 | },
150 | "root": "projects/tictactoe",
151 | "sourceRoot": "projects/tictactoe/src",
152 | "prefix": "ttt",
153 | "architect": {
154 | "build": {
155 | "builder": "@angular-devkit/build-angular:browser",
156 | "options": {
157 | "outputPath": "dist/tictactoe",
158 | "index": "projects/tictactoe/src/index.html",
159 | "main": "projects/tictactoe/src/main.ts",
160 | "polyfills": "projects/tictactoe/src/polyfills.ts",
161 | "tsConfig": "projects/tictactoe/tsconfig.app.json",
162 | "assets": [
163 | "projects/tictactoe/src/favicon.ico",
164 | "projects/tictactoe/src/assets"
165 | ],
166 | "styles": ["projects/tictactoe/src/styles.scss"],
167 | "scripts": [],
168 | "vendorChunk": true,
169 | "extractLicenses": false,
170 | "buildOptimizer": false,
171 | "sourceMap": true,
172 | "optimization": false,
173 | "namedChunks": true
174 | },
175 | "configurations": {
176 | "production": {
177 | "fileReplacements": [
178 | {
179 | "replace": "projects/tictactoe/src/environments/environment.ts",
180 | "with": "projects/tictactoe/src/environments/environment.prod.ts"
181 | }
182 | ],
183 | "optimization": true,
184 | "outputHashing": "all",
185 | "sourceMap": false,
186 | "namedChunks": false,
187 | "extractLicenses": true,
188 | "vendorChunk": false,
189 | "buildOptimizer": true,
190 | "budgets": [
191 | {
192 | "type": "initial",
193 | "maximumWarning": "2mb",
194 | "maximumError": "5mb"
195 | },
196 | {
197 | "type": "anyComponentStyle",
198 | "maximumWarning": "6kb",
199 | "maximumError": "10kb"
200 | }
201 | ]
202 | }
203 | },
204 | "defaultConfiguration": ""
205 | },
206 | "serve": {
207 | "builder": "@angular-devkit/build-angular:dev-server",
208 | "options": {
209 | "buildTarget": "tictactoe:build"
210 | },
211 | "configurations": {
212 | "production": {
213 | "buildTarget": "tictactoe:build:production"
214 | }
215 | }
216 | },
217 | "extract-i18n": {
218 | "builder": "@angular-devkit/build-angular:extract-i18n",
219 | "options": {
220 | "buildTarget": "tictactoe:build"
221 | }
222 | },
223 | "test": {
224 | "builder": "@angular-devkit/build-angular:karma",
225 | "options": {
226 | "main": "projects/tictactoe/src/test.ts",
227 | "polyfills": "projects/tictactoe/src/polyfills.ts",
228 | "tsConfig": "projects/tictactoe/tsconfig.spec.json",
229 | "karmaConfig": "projects/tictactoe/karma.conf.js",
230 | "assets": [
231 | "projects/tictactoe/src/favicon.ico",
232 | "projects/tictactoe/src/assets"
233 | ],
234 | "styles": ["projects/tictactoe/src/styles.scss"],
235 | "scripts": []
236 | }
237 | },
238 | "e2e": {
239 | "builder": "@angular-devkit/build-angular:protractor",
240 | "options": {
241 | "protractorConfig": "projects/tictactoe/e2e/protractor.conf.js",
242 | "devServerTarget": "tictactoe:serve"
243 | },
244 | "configurations": {
245 | "production": {
246 | "devServerTarget": "tictactoe:serve:production"
247 | }
248 | }
249 | }
250 | }
251 | },
252 | "bank": {
253 | "projectType": "application",
254 | "schematics": {
255 | "@schematics/angular:component": {
256 | "style": "scss"
257 | }
258 | },
259 | "root": "projects/bank",
260 | "sourceRoot": "projects/bank/src",
261 | "prefix": "app",
262 | "architect": {
263 | "build": {
264 | "builder": "@angular-devkit/build-angular:browser",
265 | "options": {
266 | "outputPath": "dist/bank",
267 | "index": "projects/bank/src/index.html",
268 | "main": "projects/bank/src/main.ts",
269 | "polyfills": "projects/bank/src/polyfills.ts",
270 | "tsConfig": "projects/bank/tsconfig.app.json",
271 | "assets": [
272 | "projects/bank/src/favicon.ico",
273 | "projects/bank/src/assets"
274 | ],
275 | "styles": ["projects/bank/src/styles.scss"],
276 | "scripts": [],
277 | "vendorChunk": true,
278 | "extractLicenses": false,
279 | "buildOptimizer": false,
280 | "sourceMap": true,
281 | "optimization": false,
282 | "namedChunks": true
283 | },
284 | "configurations": {
285 | "production": {
286 | "fileReplacements": [
287 | {
288 | "replace": "projects/bank/src/environments/environment.ts",
289 | "with": "projects/bank/src/environments/environment.prod.ts"
290 | }
291 | ],
292 | "optimization": true,
293 | "outputHashing": "all",
294 | "sourceMap": false,
295 | "namedChunks": false,
296 | "extractLicenses": true,
297 | "vendorChunk": false,
298 | "buildOptimizer": true,
299 | "budgets": [
300 | {
301 | "type": "initial",
302 | "maximumWarning": "2mb",
303 | "maximumError": "5mb"
304 | },
305 | {
306 | "type": "anyComponentStyle",
307 | "maximumWarning": "6kb",
308 | "maximumError": "10kb"
309 | }
310 | ]
311 | }
312 | },
313 | "defaultConfiguration": ""
314 | },
315 | "serve": {
316 | "builder": "@angular-devkit/build-angular:dev-server",
317 | "options": {
318 | "buildTarget": "bank:build"
319 | },
320 | "configurations": {
321 | "production": {
322 | "buildTarget": "bank:build:production"
323 | }
324 | }
325 | },
326 | "extract-i18n": {
327 | "builder": "@angular-devkit/build-angular:extract-i18n",
328 | "options": {
329 | "buildTarget": "bank:build"
330 | }
331 | },
332 | "test": {
333 | "builder": "@angular-devkit/build-angular:karma",
334 | "options": {
335 | "main": "projects/bank/src/test.ts",
336 | "polyfills": "projects/bank/src/polyfills.ts",
337 | "tsConfig": "projects/bank/tsconfig.spec.json",
338 | "karmaConfig": "projects/bank/karma.conf.js",
339 | "assets": [
340 | "projects/bank/src/favicon.ico",
341 | "projects/bank/src/assets"
342 | ],
343 | "styles": ["projects/bank/src/styles.scss"],
344 | "scripts": []
345 | }
346 | },
347 | "e2e": {
348 | "builder": "@angular-devkit/build-angular:protractor",
349 | "options": {
350 | "protractorConfig": "projects/bank/e2e/protractor.conf.js",
351 | "devServerTarget": "bank:serve"
352 | },
353 | "configurations": {
354 | "production": {
355 | "devServerTarget": "bank:serve:production"
356 | }
357 | }
358 | }
359 | }
360 | },
361 | "todo-v6": {
362 | "projectType": "application",
363 | "schematics": {
364 | "@schematics/angular:component": {
365 | "inlineTemplate": true,
366 | "inlineStyle": true,
367 | "style": "scss"
368 | }
369 | },
370 | "root": "projects/todo-v6",
371 | "sourceRoot": "projects/todo-v6/src",
372 | "prefix": "app",
373 | "architect": {
374 | "build": {
375 | "builder": "@angular-devkit/build-angular:browser",
376 | "options": {
377 | "outputPath": "dist/todo-v6",
378 | "index": "projects/todo-v6/src/index.html",
379 | "main": "projects/todo-v6/src/main.ts",
380 | "polyfills": "projects/todo-v6/src/polyfills.ts",
381 | "tsConfig": "projects/todo-v6/tsconfig.app.json",
382 | "assets": [
383 | "projects/todo-v6/src/favicon.ico",
384 | "projects/todo-v6/src/assets"
385 | ],
386 | "styles": ["projects/todo-v6/src/styles.scss"],
387 | "scripts": [],
388 | "vendorChunk": true,
389 | "extractLicenses": false,
390 | "buildOptimizer": false,
391 | "sourceMap": true,
392 | "optimization": false,
393 | "namedChunks": true
394 | },
395 | "configurations": {
396 | "production": {
397 | "fileReplacements": [
398 | {
399 | "replace": "projects/todo-v6/src/environments/environment.ts",
400 | "with": "projects/todo-v6/src/environments/environment.prod.ts"
401 | }
402 | ],
403 | "optimization": true,
404 | "outputHashing": "all",
405 | "sourceMap": false,
406 | "namedChunks": false,
407 | "extractLicenses": true,
408 | "vendorChunk": false,
409 | "buildOptimizer": true,
410 | "budgets": [
411 | {
412 | "type": "initial",
413 | "maximumWarning": "2mb",
414 | "maximumError": "5mb"
415 | },
416 | {
417 | "type": "anyComponentStyle",
418 | "maximumWarning": "6kb",
419 | "maximumError": "10kb"
420 | }
421 | ]
422 | }
423 | },
424 | "defaultConfiguration": ""
425 | },
426 | "serve": {
427 | "builder": "@angular-devkit/build-angular:dev-server",
428 | "options": {
429 | "buildTarget": "todo-v6:build"
430 | },
431 | "configurations": {
432 | "production": {
433 | "buildTarget": "todo-v6:build:production"
434 | }
435 | }
436 | },
437 | "extract-i18n": {
438 | "builder": "@angular-devkit/build-angular:extract-i18n",
439 | "options": {
440 | "buildTarget": "todo-v6:build"
441 | }
442 | },
443 | "test": {
444 | "builder": "@angular-devkit/build-angular:karma",
445 | "options": {
446 | "main": "projects/todo-v6/src/test.ts",
447 | "polyfills": "projects/todo-v6/src/polyfills.ts",
448 | "tsConfig": "projects/todo-v6/tsconfig.spec.json",
449 | "karmaConfig": "projects/todo-v6/karma.conf.js",
450 | "assets": [
451 | "projects/todo-v6/src/favicon.ico",
452 | "projects/todo-v6/src/assets"
453 | ],
454 | "styles": ["projects/todo-v6/src/styles.scss"],
455 | "scripts": []
456 | }
457 | },
458 | "e2e": {
459 | "builder": "@angular-devkit/build-angular:protractor",
460 | "options": {
461 | "protractorConfig": "projects/todo-v6/e2e/protractor.conf.js",
462 | "devServerTarget": "todo-v6:serve"
463 | },
464 | "configurations": {
465 | "production": {
466 | "devServerTarget": "todo-v6:serve:production"
467 | }
468 | }
469 | }
470 | }
471 | },
472 | "bank-v11": {
473 | "projectType": "application",
474 | "schematics": {
475 | "@schematics/angular:component": {
476 | "style": "scss"
477 | }
478 | },
479 | "root": "projects/bank-v11",
480 | "sourceRoot": "projects/bank-v11/src",
481 | "prefix": "app",
482 | "architect": {
483 | "build": {
484 | "builder": "@angular-devkit/build-angular:browser",
485 | "options": {
486 | "outputPath": "dist/bank-v11",
487 | "index": "projects/bank-v11/src/index.html",
488 | "main": "projects/bank-v11/src/main.ts",
489 | "polyfills": "projects/bank-v11/src/polyfills.ts",
490 | "tsConfig": "projects/bank-v11/tsconfig.app.json",
491 | "assets": [
492 | "projects/bank-v11/src/favicon.ico",
493 | "projects/bank-v11/src/assets"
494 | ],
495 | "styles": ["projects/bank-v11/src/styles.scss"],
496 | "scripts": [],
497 | "vendorChunk": true,
498 | "extractLicenses": false,
499 | "buildOptimizer": false,
500 | "sourceMap": true,
501 | "optimization": false,
502 | "namedChunks": true
503 | },
504 | "configurations": {
505 | "production": {
506 | "fileReplacements": [
507 | {
508 | "replace": "projects/bank-v11/src/environments/environment.ts",
509 | "with": "projects/bank-v11/src/environments/environment.prod.ts"
510 | }
511 | ],
512 | "optimization": true,
513 | "outputHashing": "all",
514 | "sourceMap": false,
515 | "namedChunks": false,
516 | "extractLicenses": true,
517 | "vendorChunk": false,
518 | "buildOptimizer": true,
519 | "budgets": [
520 | {
521 | "type": "initial",
522 | "maximumWarning": "2mb",
523 | "maximumError": "5mb"
524 | },
525 | {
526 | "type": "anyComponentStyle",
527 | "maximumWarning": "6kb",
528 | "maximumError": "10kb"
529 | }
530 | ]
531 | }
532 | },
533 | "defaultConfiguration": ""
534 | },
535 | "serve": {
536 | "builder": "@angular-devkit/build-angular:dev-server",
537 | "options": {
538 | "buildTarget": "bank-v11:build"
539 | },
540 | "configurations": {
541 | "production": {
542 | "buildTarget": "bank-v11:build:production"
543 | }
544 | }
545 | },
546 | "extract-i18n": {
547 | "builder": "@angular-devkit/build-angular:extract-i18n",
548 | "options": {
549 | "buildTarget": "bank-v11:build"
550 | }
551 | },
552 | "test": {
553 | "builder": "@angular-devkit/build-angular:karma",
554 | "options": {
555 | "main": "projects/bank-v11/src/test.ts",
556 | "polyfills": "projects/bank-v11/src/polyfills.ts",
557 | "tsConfig": "projects/bank-v11/tsconfig.spec.json",
558 | "karmaConfig": "projects/bank-v11/karma.conf.js",
559 | "assets": [
560 | "projects/bank-v11/src/favicon.ico",
561 | "projects/bank-v11/src/assets"
562 | ],
563 | "styles": ["projects/bank-v11/src/styles.scss"],
564 | "scripts": []
565 | }
566 | },
567 | "e2e": {
568 | "builder": "@angular-devkit/build-angular:protractor",
569 | "options": {
570 | "protractorConfig": "projects/bank-v11/e2e/protractor.conf.js",
571 | "devServerTarget": "bank-v11:serve"
572 | },
573 | "configurations": {
574 | "production": {
575 | "devServerTarget": "bank-v11:serve:production"
576 | }
577 | }
578 | }
579 | }
580 | },
581 | "bank-v13": {
582 | "projectType": "application",
583 | "schematics": {
584 | "@schematics/angular:component": {
585 | "style": "scss"
586 | },
587 | "@schematics/angular:application": {
588 | "strict": true
589 | }
590 | },
591 | "root": "projects/bank-v13",
592 | "sourceRoot": "projects/bank-v13/src",
593 | "prefix": "app",
594 | "architect": {
595 | "build": {
596 | "builder": "@angular-devkit/build-angular:browser",
597 | "options": {
598 | "outputPath": "dist/bank-v13",
599 | "index": "projects/bank-v13/src/index.html",
600 | "main": "projects/bank-v13/src/main.ts",
601 | "polyfills": "projects/bank-v13/src/polyfills.ts",
602 | "tsConfig": "projects/bank-v13/tsconfig.app.json",
603 | "inlineStyleLanguage": "scss",
604 | "assets": [
605 | "projects/bank-v13/src/favicon.ico",
606 | "projects/bank-v13/src/assets"
607 | ],
608 | "styles": ["projects/bank-v13/src/styles.scss"],
609 | "scripts": []
610 | },
611 | "configurations": {
612 | "production": {
613 | "budgets": [
614 | {
615 | "type": "initial",
616 | "maximumWarning": "500kb",
617 | "maximumError": "1mb"
618 | },
619 | {
620 | "type": "anyComponentStyle",
621 | "maximumWarning": "2kb",
622 | "maximumError": "4kb"
623 | }
624 | ],
625 | "fileReplacements": [
626 | {
627 | "replace": "projects/bank-v13/src/environments/environment.ts",
628 | "with": "projects/bank-v13/src/environments/environment.prod.ts"
629 | }
630 | ],
631 | "outputHashing": "all"
632 | },
633 | "development": {
634 | "buildOptimizer": false,
635 | "optimization": false,
636 | "vendorChunk": true,
637 | "extractLicenses": false,
638 | "sourceMap": true,
639 | "namedChunks": true
640 | }
641 | },
642 | "defaultConfiguration": "production"
643 | },
644 | "serve": {
645 | "builder": "@angular-devkit/build-angular:dev-server",
646 | "configurations": {
647 | "production": {
648 | "buildTarget": "bank-v13:build:production"
649 | },
650 | "development": {
651 | "buildTarget": "bank-v13:build:development"
652 | }
653 | },
654 | "defaultConfiguration": "development"
655 | },
656 | "extract-i18n": {
657 | "builder": "@angular-devkit/build-angular:extract-i18n",
658 | "options": {
659 | "buildTarget": "bank-v13:build"
660 | }
661 | },
662 | "test": {
663 | "builder": "@angular-devkit/build-angular:karma",
664 | "options": {
665 | "main": "projects/bank-v13/src/test.ts",
666 | "polyfills": "projects/bank-v13/src/polyfills.ts",
667 | "tsConfig": "projects/bank-v13/tsconfig.spec.json",
668 | "karmaConfig": "projects/bank-v13/karma.conf.js",
669 | "inlineStyleLanguage": "scss",
670 | "assets": [
671 | "projects/bank-v13/src/favicon.ico",
672 | "projects/bank-v13/src/assets"
673 | ],
674 | "styles": ["projects/bank-v13/src/styles.scss"],
675 | "scripts": []
676 | }
677 | }
678 | }
679 | }
680 | },
681 | "cli": {
682 | "analytics": false
683 | }
684 | }
685 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'jest-preset-angular',
3 | setupFilesAfterEnv: ['./projects/mobx-angular/setup-tests.ts'],
4 | globals: {
5 | 'ts-jest': {
6 | tsConfig: './projects/mobx-angular/tsconfig.spec.json',
7 | diagnostics: false
8 | }
9 | },
10 | testRegex: '/.*\\.spec\\.ts$',
11 | moduleFileExtensions: ['ts', 'js', 'node']
12 | };
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mobx-angular-cli",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "build:mobx-angular": "ng build mobx-angular",
9 | "test": "ng test --code-coverage",
10 | "testci:mobxangular": "ng test mobx-angular --watch=false --code-coverage",
11 | "lint": "ng lint",
12 | "e2e": "ng e2e",
13 | "prepare": "husky install"
14 | },
15 | "private": true,
16 | "dependencies": {
17 | "@angular/animations": "^18.1.3",
18 | "@angular/cdk": "^18.1.3",
19 | "@angular/common": "^18.1.3",
20 | "@angular/compiler": "^18.1.3",
21 | "@angular/core": "^18.1.3",
22 | "@angular/forms": "^18.1.3",
23 | "@angular/localize": "^18.1.3",
24 | "@angular/material": "^18.1.3",
25 | "@angular/platform-browser": "^18.1.3",
26 | "@angular/platform-browser-dynamic": "^18.1.3",
27 | "@angular/router": "^18.1.3",
28 | "@types/jest": "^25.1.3",
29 | "husky": "^8.0.3",
30 | "jest": "^29.7.0",
31 | "jest-preset-angular": "^14.2.2",
32 | "lodash": "^4.17.21",
33 | "mobx": "^6.8.0",
34 | "mobx-remotedev": "^0.3.6",
35 | "rxjs": "^7.8.0",
36 | "todomvc-app-css": "^2.4.2",
37 | "todomvc-common": "^1.0.5",
38 | "tslib": "^2.5.0",
39 | "zone.js": "~0.14.10"
40 | },
41 | "devDependencies": {
42 | "@angular-devkit/build-angular": "^18.1.3",
43 | "@angular/cli": "^18.1.3",
44 | "@angular/compiler-cli": "^18.1.3",
45 | "@angular/language-service": "^18.1.3",
46 | "@types/jasmine": "^4.3.0",
47 | "@types/jasminewd2": "^2.0.10",
48 | "@types/node": "^18.11.9",
49 | "codelyzer": "^6.0.2",
50 | "jasmine-core": "^4.5.0",
51 | "jasmine-spec-reporter": "^7.0.0",
52 | "karma": "^6.4.1",
53 | "karma-chrome-launcher": "^3.1.1",
54 | "karma-coverage": "^2.2.0",
55 | "karma-jasmine": "^5.1.0",
56 | "karma-jasmine-html-reporter": "^2.0.0",
57 | "karma-spec-reporter": "^0.0.36",
58 | "lint-staged": "^13.1.2",
59 | "ng-packagr": "^18.1.0",
60 | "prettier": "^2.8.4",
61 | "protractor": "~7.0.0",
62 | "ts-node": "^10.9.1",
63 | "tslint": "~6.1.3",
64 | "typescript": "^5.4.5"
65 | },
66 | "prettier": {
67 | "printWidth": 80,
68 | "tabWidth": 2,
69 | "useTabs": false,
70 | "semi": true,
71 | "singleQuote": true,
72 | "trailingComma": "none",
73 | "bracketSpacing": true,
74 | "jsxBracketSameLine": true,
75 | "requirePragma": false
76 | },
77 | "lint-staged": {
78 | "*.{js,ts,json}": [
79 | "prettier --write"
80 | ],
81 | "*.ts": [
82 | "tslint"
83 | ]
84 | }
85 | }
--------------------------------------------------------------------------------
/projects/bank-v11/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: ['./src/**/*.e2e-spec.ts'],
13 | capabilities: {
14 | browserName: 'chrome'
15 | },
16 | directConnect: true,
17 | SELENIUM_PROMISE_MANAGER: false,
18 | baseUrl: 'http://localhost:4200/',
19 | framework: 'jasmine',
20 | jasmineNodeOpts: {
21 | showColors: true,
22 | defaultTimeoutInterval: 30000,
23 | print: function() {}
24 | },
25 | onPrepare() {
26 | require('ts-node').register({
27 | project: require('path').join(__dirname, './tsconfig.json')
28 | });
29 | jasmine.getEnv().addReporter(
30 | new SpecReporter({
31 | spec: {
32 | displayStacktrace: StacktraceOption.PRETTY
33 | }
34 | })
35 | );
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/projects/bank-v11/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { browser, logging } from 'protractor';
2 | import { AppPage } from './app.po';
3 |
4 | describe('workspace-project App', () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it('should display welcome message', async () => {
12 | await page.navigateTo();
13 | expect(await page.getTitleText()).toEqual('bank-v11 app is running!');
14 | });
15 |
16 | afterEach(async () => {
17 | // Assert that there are no errors emitted from the browser
18 | const logs = await browser
19 | .manage()
20 | .logs()
21 | .get(logging.Type.BROWSER);
22 | expect(logs).not.toContain(
23 | jasmine.objectContaining({
24 | level: logging.Level.SEVERE
25 | } as logging.Entry)
26 | );
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/projects/bank-v11/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | async navigateTo(): Promise {
5 | return browser.get(browser.baseUrl);
6 | }
7 |
8 | async getTitleText(): Promise {
9 | return element(by.css('app-root .content span')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/projects/bank-v11/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../../out-tsc/e2e",
6 | "module": "commonjs",
7 | "target": "es2018",
8 | "types": ["jasmine", "node"]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/projects/bank-v11/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-spec-reporter'),
13 | require('karma-coverage'),
14 | require('@angular-devkit/build-angular/plugins/karma')
15 | ],
16 | client: {
17 | clearContext: false, // leave Jasmine Spec Runner output visible in browser
18 | jasmine: {
19 | random: false,
20 | }
21 | },
22 | coverageReporter: {
23 | reporters: [{ type: 'html' }, { type: 'text' }, { type: 'json-summary' }, { type: 'lcov' }],
24 | dir: require('path').join(__dirname, '../../coverage/bank-v11'),
25 | subdir: '.',
26 | fixWebpackSourcePaths: true
27 | },
28 | reporters: ['progress', 'kjhtml'],
29 | port: 9876,
30 | colors: true,
31 | logLevel: config.LOG_INFO,
32 | autoWatch: true,
33 | browsers: ['Chrome'],
34 | singleRun: false,
35 | restartOnFileChange: true
36 | });
37 | };
38 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/actions/actions.component.html:
--------------------------------------------------------------------------------
1 | deposit
2 | withdraw
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/actions/actions.component.scss:
--------------------------------------------------------------------------------
1 | button {
2 | width: 152px;
3 | margin-right: 20px;
4 | }
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/actions/actions.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Account } from '../stores/account.store';
3 |
4 | @Component({
5 | selector: 'app-actions',
6 | templateUrl: './actions.component.html',
7 | styleUrls: ['./actions.component.scss']
8 | })
9 | export class ActionsComponent implements OnInit {
10 | constructor(private account: Account) {}
11 |
12 | deposit() {
13 | this.account.deposit(parseInt(window.prompt('select amount'), 10));
14 | }
15 | withdraw() {
16 | this.account.withdraw(parseInt(window.prompt('select amount'), 10));
17 | }
18 | ngOnInit() {}
19 | }
20 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } 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 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
Welcome to your bank account manager!
3 |
8 |
9 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | .content {
2 | padding-left: 20px;
3 | }
4 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.scss']
7 | })
8 | export class AppComponent {
9 | title = 'bank-v11';
10 | }
11 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 |
4 | import { AppRoutingModule } from './app-routing.module';
5 | import { AppComponent } from './app.component';
6 | import { ActionsComponent } from './actions/actions.component';
7 | import { BalanceComponent } from './balance/balance.component';
8 | import { TransactionsComponent } from './transactions/transactions.component';
9 |
10 | import { MatFormFieldModule } from '@angular/material/form-field';
11 | import { MatInputModule } from '@angular/material/input';
12 | import { MatCardModule } from '@angular/material/card';
13 | import { MatButtonModule } from '@angular/material/button';
14 | import { MobxAngularModule } from 'mobx-angular';
15 | import { FormsModule } from '@angular/forms';
16 | import { CommonModule } from '@angular/common';
17 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
18 |
19 | @NgModule({
20 | declarations: [
21 | AppComponent,
22 | ActionsComponent,
23 | BalanceComponent,
24 | TransactionsComponent
25 | ],
26 | imports: [
27 | BrowserModule,
28 | AppRoutingModule,
29 | MobxAngularModule,
30 | FormsModule,
31 | CommonModule,
32 | MatButtonModule,
33 | MatFormFieldModule,
34 | MatInputModule,
35 | MatCardModule,
36 | BrowserAnimationsModule
37 | ],
38 | providers: [],
39 | bootstrap: [AppComponent],
40 | schemas: [CUSTOM_ELEMENTS_SCHEMA]
41 | })
42 | export class AppModule { }
43 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/balance/balance.component.html:
--------------------------------------------------------------------------------
1 |
2 | Your current balance:
3 | $ {{ account.balance }}
4 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/balance/balance.component.scss:
--------------------------------------------------------------------------------
1 | span {
2 | color: green;
3 | }
4 | .negative {
5 | color: red;
6 | }
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/balance/balance.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Account } from '../stores/account.store';
3 |
4 | @Component({
5 | selector: 'app-balance',
6 | templateUrl: './balance.component.html',
7 | styleUrls: ['./balance.component.scss']
8 | })
9 | export class BalanceComponent implements OnInit {
10 | constructor(public account: Account) {}
11 |
12 | ngOnInit(): void {}
13 | }
14 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/stores/account.store.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { observable, autorun, computed, action, makeObservable } from 'mobx';
3 | import { sum } from 'lodash';
4 |
5 | @Injectable({ providedIn: 'root' })
6 | export class Account {
7 | @observable transactions: number[] = [];
8 |
9 | constructor() {
10 | makeObservable(this);
11 | if (localStorage.savedTransactions) {
12 | this.transactions = JSON.parse(localStorage.savedTransactions);
13 | }
14 | autorun(() => {
15 | localStorage.savedTransactions = JSON.stringify(this.transactions);
16 | });
17 | }
18 |
19 | @computed get balance(): number {
20 | return sum(this.transactions);
21 | }
22 |
23 | @computed get isNegative(): boolean {
24 | return this.balance < 0;
25 | }
26 |
27 | @computed get deposits(): number[] {
28 | return this.transactions.filter((t) => t >= 0);
29 | }
30 |
31 | @computed get withdrawls(): number[] {
32 | return this.transactions.filter((t) => t < 0);
33 | }
34 |
35 | @action deposit(money: number) {
36 | if (money) {
37 | this.transactions = [...this.transactions, money];
38 | }
39 | }
40 | @action withdraw(money: number) {
41 | if (money) {
42 | this.transactions = [...this.transactions, -money];
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/transactions/transactions.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Deposits
5 |
6 |
7 | {{ deposit }}
8 |
9 |
10 |
11 |
12 | Withdrawls
13 |
14 |
15 |
16 | {{ withdrawl }}
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/transactions/transactions.component.scss:
--------------------------------------------------------------------------------
1 | .transactions {
2 | display: flex;
3 | flex-direction: row;
4 | }
5 |
6 | .column {
7 | width: 120px;
8 | margin-top: 20px;
9 | margin-right: 20px;
10 | }
--------------------------------------------------------------------------------
/projects/bank-v11/src/app/transactions/transactions.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Account } from '../stores/account.store';
3 |
4 | @Component({
5 | selector: 'app-transactions',
6 | templateUrl: './transactions.component.html',
7 | styleUrls: ['./transactions.component.scss']
8 | })
9 | export class TransactionsComponent implements OnInit {
10 | constructor(public account: Account) {}
11 |
12 | ngOnInit(): void {}
13 | }
14 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/bank-v11/src/assets/.gitkeep
--------------------------------------------------------------------------------
/projects/bank-v11/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/projects/bank-v11/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/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/bank-v11/src/favicon.ico
--------------------------------------------------------------------------------
/projects/bank-v11/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BankV11
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/projects/bank-v11/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 |
--------------------------------------------------------------------------------
/projects/bank-v11/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 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 | /***************************************************************************************************
51 | * APPLICATION IMPORTS
52 | */
53 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/projects/bank-v11/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | // First, initialize the Angular testing environment.
11 | getTestBed().initTestEnvironment(
12 | BrowserDynamicTestingModule,
13 | platformBrowserDynamicTesting(),
14 | {
15 | teardown: { destroyAfterEach: false }
16 | }
17 | );
18 |
--------------------------------------------------------------------------------
/projects/bank-v11/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/app",
6 | "types": []
7 | },
8 | "files": ["src/main.ts", "src/polyfills.ts"],
9 | "include": ["src/**/*.d.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/projects/bank-v11/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/spec",
6 | "types": ["jasmine"]
7 | },
8 | "files": ["src/test.ts", "src/polyfills.ts"],
9 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/projects/bank-v11/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "app", "camelCase"],
5 | "component-selector": [true, "element", "app", "kebab-case"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/bank-v13/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-spec-reporter'),
13 | require('karma-coverage'),
14 | require('@angular-devkit/build-angular/plugins/karma')
15 | ],
16 | client: {
17 | clearContext: false, // leave Jasmine Spec Runner output visible in browser
18 | jasmine: {
19 | random: false,
20 | }
21 | },
22 | coverageReporter: {
23 | reporters: [{ type: 'html' }, { type: 'text' }, { type: 'json-summary' }, { type: 'lcov' }],
24 | dir: require('path').join(__dirname, '../../coverage/bank-v13'),
25 | subdir: '.',
26 | fixWebpackSourcePaths: true
27 | },
28 | reporters: ['progress', 'kjhtml'],
29 | port: 9876,
30 | colors: true,
31 | logLevel: config.LOG_INFO,
32 | autoWatch: true,
33 | browsers: ['Chrome'],
34 | singleRun: false,
35 | restartOnFileChange: true
36 | });
37 | };
38 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/actions/actions.component.html:
--------------------------------------------------------------------------------
1 | deposit
2 | withdraw
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/actions/actions.component.scss:
--------------------------------------------------------------------------------
1 | button {
2 | width: 152px;
3 | margin-right: 20px;
4 | }
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/actions/actions.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Account } from '../stores/account.store';
3 |
4 | @Component({
5 | selector: 'app-actions',
6 | templateUrl: './actions.component.html',
7 | styleUrls: ['./actions.component.scss']
8 | })
9 | export class ActionsComponent implements OnInit {
10 | constructor(private account: Account) {}
11 |
12 | deposit() {
13 | this.account.deposit(parseInt(window.prompt('select amount'), 10));
14 | }
15 | withdraw() {
16 | this.account.withdraw(parseInt(window.prompt('select amount'), 10));
17 | }
18 | ngOnInit() {}
19 | }
20 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
Welcome to your bank account manager!
3 |
8 |
9 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | .content {
2 | padding-left: 20px;
3 | }
4 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(async () => {
7 | await TestBed.configureTestingModule({
8 | declarations: [AppComponent],
9 | schemas: [CUSTOM_ELEMENTS_SCHEMA]
10 | }).compileComponents();
11 | });
12 |
13 | it('should create the app', () => {
14 | const fixture = TestBed.createComponent(AppComponent);
15 | const app = fixture.componentInstance;
16 | expect(app).toBeTruthy();
17 | });
18 |
19 | it(`should have as title 'bank-v13'`, () => {
20 | const fixture = TestBed.createComponent(AppComponent);
21 | const app = fixture.componentInstance;
22 | expect(app.title).toEqual('bank-v13');
23 | });
24 |
25 | it('should render title in h1 tag', () => {
26 | const fixture = TestBed.createComponent(AppComponent);
27 | fixture.detectChanges();
28 | const compiled = fixture.debugElement.nativeElement;
29 | expect(compiled.querySelector('h1').textContent).toContain(
30 | 'Welcome to your bank account manager!'
31 | );
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.scss']
7 | })
8 | export class AppComponent {
9 | title = 'bank-v13';
10 | }
11 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 |
4 | import { AppComponent } from './app.component';
5 | import { ActionsComponent } from './actions/actions.component';
6 | import { BalanceComponent } from './balance/balance.component';
7 | import { TransactionsComponent } from './transactions/transactions.component';
8 |
9 | import { MatFormFieldModule } from '@angular/material/form-field';
10 | import { MatInputModule } from '@angular/material/input';
11 | import { MatCardModule } from '@angular/material/card';
12 | import { MatButtonModule } from '@angular/material/button';
13 | import { FormsModule } from '@angular/forms';
14 | import { CommonModule } from '@angular/common';
15 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
16 |
17 | import { MobxAngularModule } from 'projects/mobx-angular/src/public-api';
18 | // import { MobxAngularModule } from 'mobx-angular';
19 |
20 | @NgModule({
21 | declarations: [
22 | AppComponent,
23 | ActionsComponent,
24 | BalanceComponent,
25 | TransactionsComponent
26 | ],
27 | imports: [
28 | BrowserModule,
29 | MobxAngularModule,
30 | FormsModule,
31 | CommonModule,
32 | MatButtonModule,
33 | MatFormFieldModule,
34 | MatInputModule,
35 | MatCardModule,
36 | BrowserAnimationsModule
37 | ],
38 | providers: [],
39 | bootstrap: [AppComponent],
40 | schemas: [CUSTOM_ELEMENTS_SCHEMA]
41 | })
42 | export class AppModule { }
43 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/balance/balance.component.html:
--------------------------------------------------------------------------------
1 |
2 | Your current balance:
3 | $ {{ account.balance }}
4 |
5 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/balance/balance.component.scss:
--------------------------------------------------------------------------------
1 | span {
2 | color: green;
3 | }
4 | .negative {
5 | color: red;
6 | }
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/balance/balance.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Account } from '../stores/account.store';
3 |
4 | @Component({
5 | selector: 'app-balance',
6 | templateUrl: './balance.component.html',
7 | styleUrls: ['./balance.component.scss']
8 | })
9 | export class BalanceComponent implements OnInit {
10 | constructor(public account: Account) {}
11 |
12 | ngOnInit(): void {}
13 | }
14 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/stores/account.store.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import {
3 | observable,
4 | autorun,
5 | computed,
6 | action,
7 | makeAutoObservable
8 | } from 'mobx';
9 | import { sum } from 'lodash';
10 |
11 | @Injectable({ providedIn: 'root' })
12 | export class Account {
13 | @observable transactions: number[] = [];
14 |
15 | constructor() {
16 | if (localStorage.savedTransactions) {
17 | this.transactions = JSON.parse(localStorage.savedTransactions);
18 | }
19 |
20 | makeAutoObservable(this);
21 |
22 | autorun(() => {
23 | localStorage.savedTransactions = JSON.stringify(this.transactions);
24 | });
25 | }
26 |
27 | @computed get balance(): number {
28 | return sum(this.transactions);
29 | }
30 |
31 | @computed get isNegative(): boolean {
32 | return this.balance < 0;
33 | }
34 |
35 | @computed get deposits(): number[] {
36 | return this.transactions.filter((t) => t >= 0);
37 | }
38 |
39 | @computed get withdrawls(): number[] {
40 | return this.transactions.filter((t) => t < 0);
41 | }
42 |
43 | @action deposit(money: number) {
44 | if (money) {
45 | this.transactions = [...this.transactions, money];
46 | }
47 | }
48 | @action withdraw(money: number) {
49 | if (money) {
50 | this.transactions = [...this.transactions, -money];
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/transactions/transactions.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Deposits
5 |
6 |
7 | {{ deposit }}
8 |
9 |
10 |
11 |
12 | Withdrawls
13 |
14 |
15 |
16 | {{ withdrawl }}
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/transactions/transactions.component.scss:
--------------------------------------------------------------------------------
1 | .transactions {
2 | display: flex;
3 | flex-direction: row;
4 | }
5 |
6 | .column {
7 | width: 120px;
8 | margin-top: 20px;
9 | margin-right: 20px;
10 | }
--------------------------------------------------------------------------------
/projects/bank-v13/src/app/transactions/transactions.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Account } from '../stores/account.store';
3 |
4 | @Component({
5 | selector: 'app-transactions',
6 | templateUrl: './transactions.component.html',
7 | styleUrls: ['./transactions.component.scss']
8 | })
9 | export class TransactionsComponent implements OnInit {
10 | constructor(public account: Account) {}
11 |
12 | ngOnInit(): void {}
13 | }
14 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/bank-v13/src/assets/.gitkeep
--------------------------------------------------------------------------------
/projects/bank-v13/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build` 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/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/bank-v13/src/favicon.ico
--------------------------------------------------------------------------------
/projects/bank-v13/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BankV13
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/projects/bank-v13/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 |
--------------------------------------------------------------------------------
/projects/bank-v13/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 recent versions of Safari, Chrome (including
12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 | /***************************************************************************************************
51 | * APPLICATION IMPORTS
52 | */
53 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/projects/bank-v13/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | // First, initialize the Angular testing environment.
11 | getTestBed().initTestEnvironment(
12 | BrowserDynamicTestingModule,
13 | platformBrowserDynamicTesting()
14 | );
15 |
--------------------------------------------------------------------------------
/projects/bank-v13/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/app",
6 | "types": []
7 | },
8 | "files": ["src/main.ts", "src/polyfills.ts"],
9 | "include": ["src/**/*.d.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/projects/bank-v13/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/spec",
6 | "types": ["jasmine"]
7 | },
8 | "files": ["src/test.ts", "src/polyfills.ts"],
9 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/projects/bank/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'.
--------------------------------------------------------------------------------
/projects/bank/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter } = require('jasmine-spec-reporter');
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: ['./src/**/*.e2e-spec.ts'],
13 | capabilities: {
14 | browserName: 'chrome'
15 | },
16 | directConnect: true,
17 | baseUrl: 'http://localhost:4200/',
18 | framework: 'jasmine',
19 | jasmineNodeOpts: {
20 | showColors: true,
21 | defaultTimeoutInterval: 30000,
22 | print: function() {}
23 | },
24 | onPrepare() {
25 | require('ts-node').register({
26 | project: require('path').join(__dirname, './tsconfig.json')
27 | });
28 | jasmine
29 | .getEnv()
30 | .addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/projects/bank/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 | import { browser, logging } from 'protractor';
3 |
4 | describe('workspace-project App', () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it('should display welcome message', () => {
12 | page.navigateTo();
13 | expect(page.getTitleText()).toEqual('bank app is running!');
14 | });
15 |
16 | afterEach(async () => {
17 | // Assert that there are no errors emitted from the browser
18 | const logs = await browser
19 | .manage()
20 | .logs()
21 | .get(logging.Type.BROWSER);
22 | expect(logs).not.toContain(
23 | jasmine.objectContaining({
24 | level: logging.Level.SEVERE
25 | } as logging.Entry)
26 | );
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/projects/bank/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo(): Promise {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | getTitleText(): Promise {
9 | return element(by.css('app-root .content span')).getText() as Promise<
10 | string
11 | >;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/projects/bank/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": ["jasmine", "jasminewd2", "node"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/projects/bank/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-spec-reporter'),
13 | require('karma-coverage'),
14 | require('@angular-devkit/build-angular/plugins/karma')
15 | ],
16 | client: {
17 | clearContext: false, // leave Jasmine Spec Runner output visible in browser
18 | jasmine: {
19 | random: false,
20 | }
21 | },
22 | coverageReporter: {
23 | reporters: [{ type: 'html' }, { type: 'text' }, { type: 'json-summary' }, { type: 'lcov' }],
24 | dir: require('path').join(__dirname, '../../coverage/bank'),
25 | fixWebpackSourcePaths: true
26 | },
27 | reporters: ['progress', 'kjhtml'],
28 | port: 9876,
29 | colors: true,
30 | logLevel: config.LOG_INFO,
31 | autoWatch: true,
32 | browsers: ['Chrome'],
33 | singleRun: false,
34 | restartOnFileChange: true
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/projects/bank/src/app/actions/actions.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
2 | import { Account } from '../stores/account.store';
3 |
4 | @Component({
5 | selector: 'app-actions',
6 | changeDetection: ChangeDetectionStrategy.OnPush,
7 | template: `
8 | deposit
9 | withdraw
10 | `,
11 | styles: [
12 | `
13 | button {
14 | width: 152px;
15 | margin-right: 20px;
16 | }
17 | `
18 | ]
19 | })
20 | export class ActionsComponent implements OnInit {
21 | constructor(private account: Account) {}
22 |
23 | deposit() {
24 | this.account.deposit(parseInt(window.prompt('select amount'), 10));
25 | }
26 | withdraw() {
27 | this.account.withdraw(parseInt(window.prompt('select amount'), 10));
28 | }
29 | ngOnInit() {}
30 | }
31 |
--------------------------------------------------------------------------------
/projects/bank/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
297 |
298 |
299 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
{{ title }} app is running!
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
Resources
340 |
Here are some links to help you get started:
341 |
342 |
367 |
368 |
369 |
Next Steps
370 |
What do you want to do next with your app?
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
New Component
379 |
380 |
381 |
382 |
383 |
384 |
Angular Material
385 |
386 |
387 |
388 |
389 |
390 |
Add PWA Support
391 |
392 |
393 |
394 |
395 |
396 |
Add Dependency
397 |
398 |
399 |
400 |
401 |
402 |
Run and Watch Tests
403 |
404 |
405 |
406 |
407 |
408 |
Build for Production
409 |
410 |
411 |
412 |
413 |
414 |
ng generate component xyz
415 |
ng add @angular/material
416 |
ng add @angular/pwa
417 |
ng add _____
418 |
ng test
419 |
ng build --prod
420 |
421 |
422 |
423 |
500 |
501 |
502 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
--------------------------------------------------------------------------------
/projects/bank/src/app/app.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/bank/src/app/app.component.scss
--------------------------------------------------------------------------------
/projects/bank/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(async () => {
7 | TestBed.configureTestingModule({
8 | declarations: [AppComponent],
9 | schemas: [CUSTOM_ELEMENTS_SCHEMA]
10 | }).compileComponents();
11 | });
12 | it('should create the app', () => {
13 | const fixture = TestBed.createComponent(AppComponent);
14 | const app = fixture.debugElement.componentInstance;
15 | expect(app).toBeTruthy();
16 | });
17 | it('should render title in a h1 tag', () => {
18 | const fixture = TestBed.createComponent(AppComponent);
19 | fixture.detectChanges();
20 | const compiled = fixture.debugElement.nativeElement;
21 | expect(compiled.querySelector('h1').textContent).toContain(
22 | 'Welcome to your bank account manager!'
23 | );
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/projects/bank/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | template: `
6 |
7 |
8 | Welcome to your bank account manager!
9 |
10 |
15 |
16 | `,
17 | styles: [
18 | `
19 | .content {
20 | padding-left: 20px;
21 | }
22 | `
23 | ]
24 | })
25 | export class AppComponent {}
26 |
--------------------------------------------------------------------------------
/projects/bank/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { FormsModule } from '@angular/forms';
3 | import { CommonModule } from '@angular/common';
4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
5 | import { MobxAngularModule } from 'mobx-angular';
6 | import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
7 | import { Account } from './stores/account.store';
8 | import { AppComponent } from './app.component';
9 | import { BalanceComponent } from './balance/balance.component';
10 | import { TransactionsComponent } from './transactions/transactions.component';
11 | import { ActionsComponent } from './actions/actions.component';
12 | import { MatFormFieldModule } from '@angular/material/form-field';
13 | import { MatInputModule } from '@angular/material/input';
14 | import { MatCardModule } from '@angular/material/card';
15 | import { MatButtonModule } from '@angular/material/button';
16 |
17 | @NgModule({
18 | declarations: [
19 | AppComponent,
20 | BalanceComponent,
21 | TransactionsComponent,
22 | ActionsComponent
23 | ],
24 | imports: [
25 | BrowserModule,
26 | MobxAngularModule,
27 | FormsModule,
28 | CommonModule,
29 | MatButtonModule,
30 | MatFormFieldModule,
31 | MatInputModule,
32 | MatCardModule,
33 | BrowserAnimationsModule
34 | ],
35 | providers: [Account],
36 | bootstrap: [AppComponent],
37 | schemas: [CUSTOM_ELEMENTS_SCHEMA]
38 | })
39 | export class AppModule { }
40 |
--------------------------------------------------------------------------------
/projects/bank/src/app/balance/balance.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
2 | import { Account } from '../stores/account.store';
3 |
4 | @Component({
5 | selector: 'app-balance',
6 | changeDetection: ChangeDetectionStrategy.OnPush,
7 | template: `
8 |
9 | Your current balance:
10 | $ {{ account.balance }}
11 |
12 | `,
13 | styles: [
14 | `
15 | span {
16 | color: green;
17 | }
18 | `,
19 | `
20 | .negative {
21 | color: red;
22 | }
23 | `
24 | ]
25 | })
26 | export class BalanceComponent implements OnInit {
27 | constructor(public account: Account) {}
28 |
29 | ngOnInit() {}
30 | }
31 |
--------------------------------------------------------------------------------
/projects/bank/src/app/stores/account.store.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { observable, autorun, computed, action, makeObservable } from 'mobx';
3 | import { sum } from 'lodash';
4 |
5 | @Injectable()
6 | export class Account {
7 | @observable transactions: number[] = [];
8 |
9 | constructor() {
10 | makeObservable(this);
11 | if (localStorage.savedTransactions) {
12 | this.transactions = JSON.parse(localStorage.savedTransactions);
13 | }
14 | autorun(() => {
15 | localStorage.savedTransactions = JSON.stringify(this.transactions);
16 | });
17 | }
18 |
19 | @computed get balance(): number {
20 | return sum(this.transactions);
21 | }
22 |
23 | @computed get isNegative(): boolean {
24 | return this.balance < 0;
25 | }
26 |
27 | @computed get deposits(): number[] {
28 | return this.transactions.filter(t => t >= 0);
29 | }
30 |
31 | @computed get withdrawls(): number[] {
32 | return this.transactions.filter(t => t < 0);
33 | }
34 |
35 | @action deposit(money: number) {
36 | if (money) {
37 | this.transactions = [...this.transactions, money];
38 | }
39 | }
40 | @action withdraw(money: number) {
41 | if (money) {
42 | this.transactions = [...this.transactions, -money];
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/projects/bank/src/app/transactions/transactions.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
2 | import { Account } from '../stores/account.store';
3 |
4 | @Component({
5 | selector: 'app-transactions',
6 | changeDetection: ChangeDetectionStrategy.OnPush,
7 | template: `
8 |
9 |
10 |
11 | Deposits
12 |
13 |
14 | {{ deposit }}
15 |
16 |
17 |
18 |
19 | Withdrawls
20 |
21 |
22 |
23 | {{ withdrawl }}
24 |
25 |
26 |
27 |
28 | `,
29 | styles: [
30 | `
31 | .transactions {
32 | display: flex;
33 | flex-direction: row;
34 | }
35 | `,
36 | `
37 | .column {
38 | width: 120px;
39 | margin-top: 20px;
40 | margin-right: 20px;
41 | }
42 | `
43 | ]
44 | })
45 | export class TransactionsComponent implements OnInit {
46 | constructor(public account: Account) {}
47 |
48 | ngOnInit() {}
49 | }
50 |
--------------------------------------------------------------------------------
/projects/bank/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/bank/src/assets/.gitkeep
--------------------------------------------------------------------------------
/projects/bank/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/projects/bank/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/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/projects/bank/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/bank/src/favicon.ico
--------------------------------------------------------------------------------
/projects/bank/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Bank
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/projects/bank/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 |
--------------------------------------------------------------------------------
/projects/bank/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 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 | /***************************************************************************************************
51 | * APPLICATION IMPORTS
52 | */
53 |
--------------------------------------------------------------------------------
/projects/bank/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 | @import "@angular/material/prebuilt-themes/indigo-pink.css";
3 |
4 | * {
5 | font-family: sans-serif;
6 | }
7 |
8 | h1 {
9 | background-color: #00bcd4;
10 | color: white;
11 | padding: 20px 0 20px 20px;
12 | text-align: left;
13 | font-size: 36px;
14 | }
15 |
--------------------------------------------------------------------------------
/projects/bank/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | // First, initialize the Angular testing environment.
11 | getTestBed().initTestEnvironment(
12 | BrowserDynamicTestingModule,
13 | platformBrowserDynamicTesting(),
14 | {
15 | teardown: { destroyAfterEach: false }
16 | }
17 | );
18 |
--------------------------------------------------------------------------------
/projects/bank/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/app",
5 | "types": []
6 | },
7 | "files": ["src/main.ts", "src/polyfills.ts"],
8 | "include": ["src/**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/projects/bank/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.app.json"
3 | }
4 |
--------------------------------------------------------------------------------
/projects/bank/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": ["jasmine", "node"]
6 | },
7 | "files": ["src/test.ts", "src/polyfills.ts"],
8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/projects/bank/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "app", "camelCase"],
5 | "component-selector": [true, "element", "app", "kebab-case"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/mobx-angular/README.md:
--------------------------------------------------------------------------------
1 | # MobxAngular
2 |
3 | This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.0.1.
4 |
5 | ## Code scaffolding
6 |
7 | Run `ng generate component component-name --project mobx-angular` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project mobx-angular`.
8 | > Note: Don't forget to add `--project mobx-angular` or else it will be added to the default project in your `angular.json` file.
9 |
10 | ## Build
11 |
12 | Run `ng build mobx-angular` to build the project. The build artifacts will be stored in the `dist/` directory.
13 |
14 | ## Publishing
15 |
16 | After building your library with `ng build mobx-angular`, go to the dist folder `cd dist/mobx-angular` and run `npm publish`.
17 |
18 | ## Running unit tests
19 |
20 | Run `ng test mobx-angular` to execute the unit tests via [Karma](https://karma-runner.github.io).
21 |
22 | ## Further help
23 |
24 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
25 |
--------------------------------------------------------------------------------
/projects/mobx-angular/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-spec-reporter'),
13 | require('karma-coverage'),
14 | require('@angular-devkit/build-angular/plugins/karma')
15 | ],
16 | client: {
17 | clearContext: false, // leave Jasmine Spec Runner output visible in browser
18 | jasmine: {
19 | random: false,
20 | }
21 | },
22 | coverageReporter: {
23 | reporters: [{ type: 'html' }, { type: 'text' }, { type: 'json-summary' }, { type: 'lcov' }],
24 | dir: require('path').join(__dirname, '../../coverage/mobx-angular'),
25 | fixWebpackSourcePaths: true
26 | },
27 | reporters: ['progress', 'kjhtml'],
28 | port: 9876,
29 | colors: true,
30 | logLevel: config.LOG_INFO,
31 | autoWatch: true,
32 | browsers: ['Chrome'],
33 | singleRun: false,
34 | restartOnFileChange: true
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/projects/mobx-angular/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/mobx-angular",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/mobx-angular/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mobx-angular",
3 | "version": "4.8.0",
4 | "description": "Angular connector to MobX (2 and above)",
5 | "peerDependencies": {
6 | "@angular/common": ">=11.1.1",
7 | "@angular/core": ">=11.1.1",
8 | "mobx": ">=2",
9 | "tslib": "^2.0.0"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/mobxjs/mobx-angular.git"
14 | },
15 | "keywords": [
16 | "mobx",
17 | "angular2",
18 | "ng2",
19 | "angular",
20 | "ng2-mobx",
21 | "angular-mobx",
22 | "angular2-mobx",
23 | "mobx-angular",
24 | "mobx-angular2",
25 | "mobx-ng2",
26 | "state management",
27 | "mobxjs"
28 | ],
29 | "author": "Adam Klein ",
30 | "license": "MIT",
31 | "bugs": {
32 | "url": "https://github.com/mobxjs/mobx-angular/issues"
33 | },
34 | "homepage": "https://github.com/mobxjs/mobx-angular#readme"
35 | }
36 |
--------------------------------------------------------------------------------
/projects/mobx-angular/setup-tests.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
2 |
--------------------------------------------------------------------------------
/projects/mobx-angular/src/lib/mobx-angular.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { MobxAutorunDirective } from './mobx-autorun.directive';
3 | import { MobxReactionDirective } from './mobx-reaction.directive';
4 |
5 | const DIRECTIVES = [MobxAutorunDirective, MobxReactionDirective];
6 | @NgModule({
7 | declarations: DIRECTIVES,
8 | exports: DIRECTIVES
9 | })
10 | export class MobxAngularModule {}
11 |
--------------------------------------------------------------------------------
/projects/mobx-angular/src/lib/mobx-angular.spec.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import {
3 | ComponentFixture,
4 | fakeAsync,
5 | TestBed,
6 | tick
7 | } from '@angular/core/testing';
8 | import { makeAutoObservable, reaction } from 'mobx';
9 | import {
10 | MobxAutorunDirective,
11 | MobxReactionDirective,
12 | RouterStore
13 | } from '../public-api';
14 | import { Router } from '@angular/router';
15 | import { Location } from '@angular/common';
16 | import { RouterTestingModule } from '@angular/router/testing';
17 |
18 | let fullNameCalculations = 0;
19 | let firstCharCalculations = 0;
20 |
21 | class TestStore {
22 | firstName = 'James';
23 | lastName = 'Bond';
24 |
25 | constructor() {
26 | makeAutoObservable(this);
27 | }
28 |
29 | get fullName() {
30 | fullNameCalculations++;
31 | return `${this.firstName} ${this.lastName}`;
32 | }
33 |
34 | setNames(firstName, lastName) {
35 | Object.assign(this, { firstName, lastName });
36 | }
37 | }
38 |
39 | @Component({
40 | template: `
41 |
42 | {{ store.fullName }}
43 |
44 | Set Name
45 | `,
46 | changeDetection: ChangeDetectionStrategy.OnPush
47 | })
48 | class TestComponent {
49 | private store = new TestStore();
50 |
51 | constructor() {
52 | fullNameCalculations = 0;
53 | }
54 |
55 | setLastName() {
56 | this.store.lastName = 'Dean';
57 | }
58 | }
59 |
60 | @Component({
61 | template: `
62 |
63 | {{ char }}
64 |
65 | Set Name
66 | `,
67 | changeDetection: ChangeDetectionStrategy.OnPush
68 | })
69 | class TestReactionComponent {
70 | private store = new TestStore();
71 | char = 'J';
72 |
73 | constructor() {
74 | firstCharCalculations = 0;
75 | }
76 |
77 | getFirstLetter() {
78 | firstCharCalculations++;
79 | return (this.char = this.store.fullName[0]);
80 | }
81 |
82 | setLastName() {
83 | this.store.setNames('Michael', 'Jackson');
84 | }
85 | }
86 |
87 | @Component({
88 | template: ` `,
89 | changeDetection: ChangeDetectionStrategy.OnPush
90 | })
91 | class TestRouterRootComponent { }
92 |
93 | @Component({
94 | template: `
95 | home
96 | Go to Target
97 | `,
98 | changeDetection: ChangeDetectionStrategy.OnPush
99 | })
100 | class TestRouterHomeComponent {
101 | constructor(public routerStore: RouterStore, private router: Router) { }
102 |
103 | navigate() {
104 | this.router.navigateByUrl('/target');
105 | }
106 | }
107 |
108 | @Component({
109 | template: `
110 | target
111 | Back to Home
112 | `,
113 | changeDetection: ChangeDetectionStrategy.OnPush
114 | })
115 | class TestRouterTargetComponent {
116 | constructor(public routerStore: RouterStore, private router: Router) { }
117 |
118 | navigate() {
119 | this.router.navigateByUrl('/');
120 | }
121 | }
122 |
123 | describe('mobxAngular', () => {
124 | let component: TestComponent;
125 | let fixture: ComponentFixture;
126 | let fullname;
127 | let button;
128 | let firstchar;
129 |
130 | describe('mobxAutorun', () => {
131 | beforeEach(() => {
132 | TestBed.configureTestingModule({
133 | declarations: [MobxAutorunDirective, TestComponent]
134 | });
135 | fixture = TestBed.createComponent(TestComponent);
136 | fixture.detectChanges();
137 | component = fixture.componentInstance;
138 |
139 | fullname = fixture.nativeElement.querySelector('#fullname');
140 | button = fixture.nativeElement.querySelector('button');
141 | });
142 |
143 | it('should have correct content', () => {
144 | expect(fullname.textContent).toEqual('James Bond');
145 | expect(fullNameCalculations).toEqual(1);
146 | });
147 |
148 | it('should recompute value once', (done) => {
149 | button.click();
150 | setTimeout(() => {
151 | expect(fullname.textContent).toEqual('James Dean');
152 | expect(fullNameCalculations).toEqual(2);
153 | done();
154 | });
155 | });
156 | });
157 |
158 | describe('mobxReaction', () => {
159 | beforeEach(() => {
160 | TestBed.configureTestingModule({
161 | declarations: [MobxReactionDirective, TestReactionComponent]
162 | });
163 | fixture = TestBed.createComponent(TestReactionComponent);
164 | fixture.detectChanges();
165 | component = fixture.componentInstance;
166 |
167 | firstchar = fixture.nativeElement.querySelector('#firstchar');
168 | button = fixture.nativeElement.querySelector('button');
169 | });
170 |
171 | it('should call the reaction function once on init', () => {
172 | expect(firstchar.textContent).toEqual('J');
173 | expect(fixture.componentInstance.char).toEqual('J');
174 | expect(firstCharCalculations).toEqual(1);
175 | });
176 |
177 | it('should recompute value once', (done) => {
178 | button.click();
179 | setTimeout(() => {
180 | expect(firstchar.textContent).toEqual('M');
181 | expect(fixture.componentInstance.char).toEqual('M');
182 | expect(firstCharCalculations).toEqual(2);
183 |
184 | done();
185 | });
186 | });
187 |
188 | it('should not recompute every change detection', () => {
189 | fixture.detectChanges();
190 | fixture.detectChanges();
191 | fixture.detectChanges();
192 | expect(firstCharCalculations).toEqual(1);
193 | });
194 | });
195 |
196 | describe('routerStore', () => {
197 | let location: Location;
198 | let router: Router;
199 | let routerStore: RouterStore;
200 |
201 | beforeEach(() => {
202 | TestBed.configureTestingModule({
203 | imports: [
204 | RouterTestingModule.withRoutes([
205 | { path: '', component: TestRouterHomeComponent },
206 | { path: 'target', component: TestRouterTargetComponent }
207 | ])
208 | ],
209 | declarations: [
210 | TestRouterRootComponent,
211 | TestRouterHomeComponent,
212 | TestRouterTargetComponent
213 | ],
214 | providers: [RouterStore]
215 | }).compileComponents();
216 |
217 | fixture = TestBed.createComponent(TestRouterRootComponent);
218 | fixture.detectChanges();
219 |
220 | router = TestBed.inject(Router);
221 | location = TestBed.inject(Location);
222 | routerStore = TestBed.inject(RouterStore);
223 | });
224 |
225 | it('should update the observable url', fakeAsync(() => {
226 | router.navigate(['']);
227 | tick();
228 |
229 | button = fixture.nativeElement.querySelector('button');
230 | button.click();
231 | tick();
232 |
233 | expect(routerStore.url).toBe('/target');
234 |
235 | button = fixture.nativeElement.querySelector('button');
236 | button.click();
237 | tick();
238 |
239 | expect(routerStore.url).toBe('/');
240 | }));
241 |
242 | it('should react to changes in the observable url', fakeAsync(() => {
243 | router.navigate(['']);
244 | tick();
245 |
246 | let count = 0;
247 | reaction(
248 | () => routerStore.url,
249 | () => {
250 | count++;
251 | }
252 | );
253 |
254 | button = fixture.nativeElement.querySelector('button');
255 | button.click();
256 | tick();
257 |
258 | button = fixture.nativeElement.querySelector('button');
259 | button.click();
260 | tick();
261 |
262 | button = fixture.nativeElement.querySelector('button');
263 | button.click();
264 | tick();
265 |
266 | expect(count).toBe(3);
267 | }));
268 | });
269 | });
270 |
--------------------------------------------------------------------------------
/projects/mobx-angular/src/lib/mobx-autorun.directive.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | ViewContainerRef,
4 | TemplateRef,
5 | OnInit,
6 | OnDestroy,
7 | Input,
8 | EmbeddedViewRef
9 | } from '@angular/core';
10 | import { autorun, IAutorunOptions } from 'mobx';
11 | // import { mobxAngularDebug } from '../utils/mobx-angular-debug';
12 |
13 | @Directive({ selector: '[mobxAutorun]' })
14 | export class MobxAutorunDirective implements OnInit, OnDestroy {
15 | protected templateBindings = {};
16 | protected dispose: any;
17 | protected view: EmbeddedViewRef;
18 | private readonly allAutorunOptions: Array = [
19 | 'delay',
20 | 'scheduler',
21 | 'requiresObservable',
22 | 'name',
23 | 'onError'
24 | ];
25 | @Input() mobxAutorun;
26 |
27 | constructor(
28 | protected templateRef: TemplateRef,
29 | protected viewContainer: ViewContainerRef
30 | ) {}
31 |
32 | ngOnInit() {
33 | this.view = this.viewContainer.createEmbeddedView(this.templateRef);
34 |
35 | if (this.dispose) {
36 | this.dispose();
37 | }
38 |
39 | if (this.shouldDetach()) {
40 | this.view.detach();
41 | }
42 | this.autoDetect(this.view);
43 | // mobxAngularDebug(this.view, this.dispose);
44 | }
45 |
46 | shouldDetach() {
47 | return this.mobxAutorun && this.mobxAutorun.detach;
48 | }
49 |
50 | autoDetect(view: EmbeddedViewRef) {
51 | const opts: IAutorunOptions = this.getAutorunOptions();
52 |
53 | this.dispose = autorun(() => view.detectChanges(), opts);
54 | }
55 |
56 | getAutorunOptions(): IAutorunOptions {
57 | return Object.keys(this.mobxAutorun || {}).reduce((opts, current) => {
58 | if (this.allAutorunOptions.includes(current as keyof IAutorunOptions)) {
59 | opts[current] = this.mobxAutorun[current];
60 | }
61 | return opts;
62 | }, {});
63 | }
64 |
65 | ngOnDestroy() {
66 | if (this.dispose) {
67 | this.dispose();
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/projects/mobx-angular/src/lib/mobx-reaction.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, ViewContainerRef, TemplateRef, Input } from '@angular/core';
2 | import { IReactionOptions, reaction } from 'mobx';
3 | import { MobxAutorunDirective } from './mobx-autorun.directive';
4 |
5 | @Directive({ selector: '[mobxReaction]' })
6 | export class MobxReactionDirective extends MobxAutorunDirective {
7 | @Input() mobxReaction;
8 | @Input() mobxReactionOptions: IReactionOptions;
9 |
10 | constructor(
11 | protected templateRef: TemplateRef,
12 | protected viewContainer: ViewContainerRef
13 | ) {
14 | super(templateRef, viewContainer);
15 | }
16 |
17 | autoDetect(view) {
18 | const opts: IReactionOptions = Object.assign(
19 | { fireImmediately: true },
20 | this.mobxReactionOptions
21 | );
22 |
23 | this.dispose = reaction(
24 | this.mobxReaction,
25 | () => {
26 | view.detectChanges();
27 | },
28 | opts
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/projects/mobx-angular/src/lib/router-store.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { makeAutoObservable } from 'mobx';
3 | import {
4 | ActivatedRoute,
5 | ActivatedRouteSnapshot,
6 | NavigationEnd,
7 | Router
8 | } from '@angular/router';
9 | import { filter } from 'rxjs/operators';
10 |
11 | @Injectable({ providedIn: 'root' })
12 | export class RouterStore {
13 | url = '';
14 | routeSnapshot: ActivatedRouteSnapshot = null;
15 |
16 | constructor(private router: Router, private activatedRoute: ActivatedRoute) {
17 | makeAutoObservable(this);
18 |
19 | router.events
20 | .pipe(filter((event) => event instanceof NavigationEnd))
21 | .subscribe((e: NavigationEnd) => this.routeListener(e));
22 | }
23 |
24 | private routeListener(event: NavigationEnd) {
25 | this.routeSnapshot = this.activatedRoute.snapshot;
26 | this.url = event.urlAfterRedirects;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/projects/mobx-angular/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of mobx-angular
3 | */
4 |
5 | import { action as mobxAction } from 'mobx';
6 | import { computed as mobxComputed } from 'mobx';
7 | import { observable as mobxObservable } from 'mobx';
8 |
9 | export * from './lib/mobx-autorun.directive';
10 | export * from './lib/mobx-reaction.directive';
11 | export * from './lib/router-store.service';
12 | export * from './lib/mobx-angular.module';
13 |
14 | // Re-export mobx operators to be able to use inside components with AOT:
15 | export function actionInternal(...args) {
16 | return (mobxAction as any)(...args);
17 | }
18 | export const action: typeof mobxAction = Object.assign(
19 | actionInternal,
20 | mobxAction
21 | ) as any;
22 |
23 | function computedInternal(...args) {
24 | return (mobxComputed as any)(...args);
25 | }
26 | export const computed: typeof mobxComputed = Object.assign(
27 | computedInternal,
28 | mobxComputed
29 | ) as any;
30 |
31 | function observableInternal(...args) {
32 | return (mobxObservable as any)(...args);
33 | }
34 |
35 | export const observable: typeof mobxObservable = Object.assign(
36 | observableInternal,
37 | mobxObservable
38 | ) as any;
39 |
--------------------------------------------------------------------------------
/projects/mobx-angular/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js';
4 | import 'zone.js/testing';
5 | import { getTestBed } from '@angular/core/testing';
6 | import {
7 | BrowserDynamicTestingModule,
8 | platformBrowserDynamicTesting
9 | } from '@angular/platform-browser-dynamic/testing';
10 |
11 | // First, initialize the Angular testing environment.
12 | getTestBed().initTestEnvironment(
13 | BrowserDynamicTestingModule,
14 | platformBrowserDynamicTesting(),
15 | {
16 | teardown: { destroyAfterEach: false }
17 | }
18 | );
19 |
--------------------------------------------------------------------------------
/projects/mobx-angular/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/lib",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": ["dom", "es2018"]
10 | },
11 | "angularCompilerOptions": {
12 | "skipTemplateCodegen": true,
13 | "strictMetadataEmit": true,
14 | "enableResourceInlining": true,
15 | "enableIvy": false
16 | },
17 | "exclude": ["src/test.ts", "**/*.spec.ts"]
18 | }
19 |
--------------------------------------------------------------------------------
/projects/mobx-angular/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.lib.json",
3 | "angularCompilerOptions": {
4 | "compilationMode": "partial"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/projects/mobx-angular/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "emitDecoratorMetadata": true,
5 | "outDir": "../../out-tsc/spec",
6 | "types": ["jasmine", "node"]
7 | },
8 | "files": ["src/test.ts"],
9 | "include": ["**/*.spec.ts", "**/*.d.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/projects/mobx-angular/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "mobx", "camelCase"],
5 | "component-selector": [true, "element", "mobx", "kebab-case"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/tictactoe/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'.
--------------------------------------------------------------------------------
/projects/tictactoe/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter } = require('jasmine-spec-reporter');
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: ['./src/**/*.e2e-spec.ts'],
13 | capabilities: {
14 | browserName: 'chrome'
15 | },
16 | directConnect: true,
17 | baseUrl: 'http://localhost:4200/',
18 | framework: 'jasmine',
19 | jasmineNodeOpts: {
20 | showColors: true,
21 | defaultTimeoutInterval: 30000,
22 | print: function() {}
23 | },
24 | onPrepare() {
25 | require('ts-node').register({
26 | project: require('path').join(__dirname, './tsconfig.json')
27 | });
28 | jasmine
29 | .getEnv()
30 | .addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/projects/tictactoe/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 | import { browser, logging } from 'protractor';
3 |
4 | describe('workspace-project App', () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it('should display welcome message', () => {
12 | page.navigateTo();
13 | expect(page.getTitleText()).toEqual('tictactoe app is running!');
14 | });
15 |
16 | afterEach(async () => {
17 | // Assert that there are no errors emitted from the browser
18 | const logs = await browser
19 | .manage()
20 | .logs()
21 | .get(logging.Type.BROWSER);
22 | expect(logs).not.toContain(
23 | jasmine.objectContaining({
24 | level: logging.Level.SEVERE
25 | } as logging.Entry)
26 | );
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/projects/tictactoe/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo(): Promise {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | getTitleText(): Promise {
9 | return element(by.css('app-root .content span')).getText() as Promise<
10 | string
11 | >;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/projects/tictactoe/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": ["jasmine", "jasminewd2", "node"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/projects/tictactoe/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-spec-reporter'),
13 | require('karma-coverage'),
14 | require('@angular-devkit/build-angular/plugins/karma')
15 | ],
16 | client: {
17 | clearContext: false, // leave Jasmine Spec Runner output visible in browser
18 | jasmine: {
19 | random: false,
20 | }
21 | },
22 | coverageReporter: {
23 | reporters: [{ type: 'html' }, { type: 'text' }, { type: 'json-summary' }, { type: 'lcov' }],
24 | dir: require('path').join(__dirname, '../../coverage/tictactoe'),
25 | fixWebpackSourcePaths: true
26 | },
27 | reporters: ['progress', 'kjhtml'],
28 | port: 9876,
29 | colors: true,
30 | logLevel: config.LOG_INFO,
31 | autoWatch: true,
32 | browsers: ['Chrome'],
33 | singleRun: false,
34 | restartOnFileChange: true
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/app/app.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/tictactoe/src/app/app.component.scss
--------------------------------------------------------------------------------
/projects/tictactoe/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'ttt-root',
5 | template: `
6 |
7 | `,
8 | styles: []
9 | })
10 | export class AppComponent {}
11 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { HttpClientModule } from '@angular/common/http';
5 | import { MobxAngularModule } from 'mobx-angular';
6 |
7 | import { AppComponent } from './app.component';
8 | import { BoardComponent } from './components/board/board.component';
9 | import { CellComponent } from './components/cell/cell.component';
10 |
11 | import { ControlsComponent } from './components/controls/controls.component';
12 | import { GameComponent } from './components/game/game.component';
13 | import { ScoreComponent } from './components/score/score.component';
14 |
15 | @NgModule({
16 | declarations: [
17 | AppComponent,
18 | BoardComponent,
19 | CellComponent,
20 | ControlsComponent,
21 | GameComponent,
22 | ScoreComponent
23 | ],
24 | imports: [BrowserModule, FormsModule, HttpClientModule, MobxAngularModule],
25 | providers: [],
26 | bootstrap: [AppComponent]
27 | })
28 | export class AppModule {}
29 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/app/components/board/board.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { GameStore } from '../../services/game.store';
3 |
4 | @Component({
5 | selector: 'ttt-board',
6 | template: `
7 |
8 |
9 |
10 |
15 |
16 |
17 |
18 | `,
19 | styles: []
20 | })
21 | export class BoardComponent implements OnInit {
22 | constructor(public game: GameStore) {}
23 |
24 | ngOnInit() {}
25 | playCell(i, j) {
26 | if (this.game.board[i][j]) {
27 | alert('Please choose an empty cell!');
28 | } else {
29 | this.game.play(i, j);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/app/components/cell/cell.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'ttt-cell',
5 | template: `
6 |
7 | {{ value }}
8 | {{ nextValue }}
9 |
10 | `,
11 | styles: [
12 | `
13 | .next-value {
14 | opacity: 0;
15 | }
16 | `,
17 | `
18 | .cell-content:hover .next-value {
19 | opacity: 0.3;
20 | }
21 | `
22 | ]
23 | })
24 | export class CellComponent implements OnInit {
25 | @Input() value: string;
26 | @Input() nextValue: string;
27 | @Output() selectCell: EventEmitter = new EventEmitter();
28 |
29 | constructor() {}
30 |
31 | ngOnInit() {}
32 | }
33 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/app/components/controls/controls.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { GameStore } from '../../services/game.store';
3 |
4 | @Component({
5 | selector: 'ttt-controls',
6 | template: `
7 |
8 |
9 | 's turn
10 |
11 |
12 | has won the game
13 |
14 |
15 | and
16 | are at tie
17 |
18 | New Game
19 |
20 | `,
21 | styles: []
22 | })
23 | export class ControlsComponent implements OnInit {
24 | constructor(public game: GameStore) {}
25 |
26 | ngOnInit() {}
27 | }
28 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/app/components/game/game.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { GameStore } from '../../services/game.store';
3 |
4 | @Component({
5 | selector: 'ttt-game',
6 | template: `
7 |
8 |
9 |
10 | `,
11 | styles: [],
12 | providers: [GameStore]
13 | })
14 | export class GameComponent implements OnInit {
15 | constructor() {}
16 |
17 | ngOnInit() {}
18 | }
19 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/app/components/score/score.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { GameStore } from '../../services/game.store';
3 |
4 | @Component({
5 | selector: 'ttt-score',
6 | template: `
7 |
8 |
X: {{ game.score.X }}
9 |
O: {{ game.score.O }}
10 |
11 | `,
12 | styles: []
13 | })
14 | export class ScoreComponent implements OnInit {
15 | constructor(public game: GameStore) {}
16 |
17 | ngOnInit() {}
18 | }
19 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/app/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app.component';
2 | export * from './app.module';
3 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/app/services/game.store.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { observable, action, computed, reaction, makeObservable } from 'mobx';
3 |
4 | const OPONENT = {
5 | X: 'O',
6 | O: 'X'
7 | };
8 |
9 | @Injectable()
10 | export class GameStore {
11 | @observable board: string[][];
12 | @observable score = { X: 0, O: 0 };
13 | @observable firstPlayer = 'X';
14 |
15 | constructor() {
16 | makeObservable(this);
17 | this.resetGame();
18 | this._countScore();
19 | this._changeStartingPlayer();
20 | }
21 |
22 | @computed get currentPlayer(): string {
23 | return this.moves % 2 ? OPONENT[this.firstPlayer] : this.firstPlayer;
24 | }
25 | @computed get moves(): number {
26 | return (
27 | this.board[0].filter(cell => cell).length +
28 | this.board[1].filter(cell => cell).length +
29 | this.board[2].filter(cell => cell).length
30 | );
31 | }
32 | @computed get winner(): string {
33 | console.log('calc winner');
34 | // rows:
35 | if (this._rowHasWinner(0)) {
36 | return this.board[0][0];
37 | }
38 | if (this._rowHasWinner(1)) {
39 | return this.board[1][0];
40 | }
41 | if (this._rowHasWinner(2)) {
42 | return this.board[2][0];
43 | }
44 |
45 | // columns:
46 | if (this._columnHasWinner(0)) {
47 | return this.board[0][0];
48 | }
49 | if (this._columnHasWinner(1)) {
50 | return this.board[0][1];
51 | }
52 | if (this._columnHasWinner(2)) {
53 | return this.board[0][2];
54 | }
55 |
56 | // diagonals
57 | if (
58 | this.board[0][0] &&
59 | this.board[0][0] === this.board[1][1] &&
60 | this.board[1][1] === this.board[2][2]
61 | ) {
62 | return this.board[0][0];
63 | }
64 | if (
65 | this.board[0][2] &&
66 | this.board[0][2] === this.board[1][1] &&
67 | this.board[1][1] === this.board[2][0]
68 | ) {
69 | return this.board[0][2];
70 | }
71 |
72 | return null;
73 | }
74 | @computed get tie(): boolean {
75 | return this.moves === 9 && !this.winner;
76 | }
77 | @computed get ended(): boolean {
78 | return !!this.winner || this.tie;
79 | }
80 |
81 | @action play(i, j) {
82 | if (this.ended) {
83 | return;
84 | }
85 |
86 | this.board[i][j] = this.currentPlayer;
87 | }
88 |
89 | @action resetGame() {
90 | this.board = [
91 | observable(new Array(3)),
92 | observable(new Array(3)),
93 | observable(new Array(3))
94 | ];
95 | }
96 |
97 | private _rowHasWinner(index) {
98 | return (
99 | this.board[index][0] &&
100 | this.board[index][0] === this.board[index][1] &&
101 | this.board[index][1] === this.board[index][2]
102 | );
103 | }
104 |
105 | private _columnHasWinner(index) {
106 | return (
107 | this.board[0][index] &&
108 | this.board[0][index] === this.board[1][index] &&
109 | this.board[1][index] === this.board[2][index]
110 | );
111 | }
112 |
113 | private _countScore() {
114 | reaction(
115 | () => this.winner,
116 | winner => {
117 | if (winner) {
118 | this.score[winner]++;
119 | }
120 | }
121 | );
122 | }
123 |
124 | private _changeStartingPlayer() {
125 | reaction(
126 | () => this.ended,
127 | ended => {
128 | if (ended) {
129 | this.firstPlayer = OPONENT[this.firstPlayer];
130 | }
131 | }
132 | );
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/tictactoe/src/assets/.gitkeep
--------------------------------------------------------------------------------
/projects/tictactoe/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/projects/tictactoe/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/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/tictactoe/src/favicon.ico
--------------------------------------------------------------------------------
/projects/tictactoe/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Tictactoe
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/projects/tictactoe/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 |
--------------------------------------------------------------------------------
/projects/tictactoe/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 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 | /***************************************************************************************************
51 | * APPLICATION IMPORTS
52 | */
53 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 | // table, th, td {
3 | // border: 1px solid black;
4 | // border-collapse: collapse;
5 | // }
6 | // td {
7 | // width: 100px;
8 | // height: 100px;
9 | // }
10 |
11 | ttt-root {
12 | margin: 100px auto;
13 | display: block;
14 | width: 700px;
15 | padding: 20px 40px;
16 | background: #dff;
17 | box-shadow: 0 0 2px 2px rgba(0,0,0,0.3);
18 | }
19 | h1 {
20 | display: inline-block;
21 | }
22 | button {
23 | float: right;
24 | margin-top: 15px;
25 | font-size: 24px;
26 | border-radius: 2px;
27 | background: transparent;
28 | }
29 | table {
30 | margin: 0 auto;
31 | }
32 | .cell-content {
33 | cursor: pointer;
34 | font-size: 48px;
35 | background: white;
36 | width: 100px;
37 | height: 100px;
38 | display: inline-block;
39 | line-height: 100px;
40 | border-radius: 10px;
41 | text-align: center;
42 | }
43 |
--------------------------------------------------------------------------------
/projects/tictactoe/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | // First, initialize the Angular testing environment.
11 | getTestBed().initTestEnvironment(
12 | BrowserDynamicTestingModule,
13 | platformBrowserDynamicTesting(),
14 | {
15 | teardown: { destroyAfterEach: false }
16 | }
17 | );
18 |
--------------------------------------------------------------------------------
/projects/tictactoe/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/app",
5 | "types": []
6 | },
7 | "files": ["src/main.ts", "src/polyfills.ts"],
8 | "include": ["src/**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/projects/tictactoe/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.app.json"
3 | }
4 |
--------------------------------------------------------------------------------
/projects/tictactoe/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": ["jasmine", "node"]
6 | },
7 | "files": ["src/test.ts", "src/polyfills.ts"],
8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/projects/tictactoe/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "ttt", "camelCase"],
5 | "component-selector": [true, "element", "ttt", "kebab-case"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/todo-v6/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'.
--------------------------------------------------------------------------------
/projects/todo-v6/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter } = require('jasmine-spec-reporter');
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: [
13 | './src/**/*.e2e-spec.ts'
14 | ],
15 | capabilities: {
16 | browserName: 'chrome'
17 | },
18 | directConnect: true,
19 | baseUrl: 'http://localhost:4200/',
20 | framework: 'jasmine',
21 | jasmineNodeOpts: {
22 | showColors: true,
23 | defaultTimeoutInterval: 30000,
24 | print: function() {}
25 | },
26 | onPrepare() {
27 | require('ts-node').register({
28 | project: require('path').join(__dirname, './tsconfig.json')
29 | });
30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
31 | }
32 | };
--------------------------------------------------------------------------------
/projects/todo-v6/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 | import { browser, logging } from 'protractor';
3 |
4 | describe('workspace-project App', () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it('should display welcome message', () => {
12 | page.navigateTo();
13 | expect(page.getTitleText()).toEqual('todo-v6 app is running!');
14 | });
15 |
16 | afterEach(async () => {
17 | // Assert that there are no errors emitted from the browser
18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER);
19 | expect(logs).not.toContain(jasmine.objectContaining({
20 | level: logging.Level.SEVERE,
21 | } as logging.Entry));
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/projects/todo-v6/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo(): Promise {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | getTitleText(): Promise {
9 | return element(by.css('app-root .content span')).getText() as Promise;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/projects/todo-v6/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/projects/todo-v6/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-spec-reporter'),
13 | require('karma-coverage'),
14 | require('@angular-devkit/build-angular/plugins/karma')
15 | ],
16 | client: {
17 | clearContext: false, // leave Jasmine Spec Runner output visible in browser
18 | jasmine: {
19 | random: false,
20 | }
21 | },
22 | coverageReporter: {
23 | reporters: [{ type: 'html' }, { type: 'text' }, { type: 'json-summary' }, { type: 'lcov' }],
24 | dir: require('path').join(__dirname, '../../coverage/todo-v6'),
25 | fixWebpackSourcePaths: true
26 | },
27 | reporters: ['progress', 'kjhtml'],
28 | port: 9876,
29 | colors: true,
30 | logLevel: config.LOG_INFO,
31 | autoWatch: true,
32 | browsers: ['Chrome'],
33 | singleRun: false,
34 | restartOnFileChange: true
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/projects/todo-v6/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import { Todos } from './stores/todos.store';
3 | import { makeAutoObservable } from 'mobx';
4 |
5 | @Component({
6 | changeDetection: ChangeDetectionStrategy.OnPush,
7 | selector: 'app-root',
8 | template: `
9 |
25 | `
26 | })
27 | export class AppComponent {
28 | title = '';
29 | get titles() {
30 | return [this.title];
31 | }
32 |
33 | constructor(public todos: Todos) {
34 | makeAutoObservable(this);
35 | }
36 |
37 | addTodo() {
38 | this.todos.addTodo({ title: this.title });
39 | this.title = '';
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/projects/todo-v6/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import remotedev from 'mobx-remotedev';
5 |
6 | import { MobxAngularModule } from 'mobx-angular';
7 | import { Todos } from './stores/todos.store';
8 | import { AppComponent } from './app.component';
9 | import { SectionComponent } from './components/section/section.component';
10 | import { FooterComponent } from './components/footer/footer.component';
11 | import { CountComponent } from './components/count/count.component';
12 |
13 | @NgModule({
14 | declarations: [
15 | AppComponent,
16 | SectionComponent,
17 | FooterComponent,
18 | CountComponent
19 | ],
20 | imports: [BrowserModule, FormsModule, MobxAngularModule],
21 | providers: [
22 | { provide: Todos, useClass: remotedev(Todos, { global: true }), deps: [] }
23 | ],
24 | bootstrap: [AppComponent]
25 | })
26 | export class AppModule {}
27 |
--------------------------------------------------------------------------------
/projects/todo-v6/src/app/components/count/count.component.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import { Todos } from '../../stores/todos.store';
3 |
4 | @Component({
5 | changeDetection: ChangeDetectionStrategy.OnPush,
6 | selector: 'app-count',
7 | template: `
8 |
9 | {{ todos.uncompletedItems }} items left
10 |
11 | `
12 | })
13 | export class CountComponent {
14 | constructor(public todos: Todos) {}
15 | }
16 |
--------------------------------------------------------------------------------
/projects/todo-v6/src/app/components/footer/footer.component.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import { Todos } from '../../stores/todos.store';
3 |
4 | @Component({
5 | changeDetection: ChangeDetectionStrategy.OnPush,
6 | selector: 'app-footer',
7 | template: `
8 |
37 | `
38 | })
39 | export class FooterComponent {
40 | constructor(public todos: Todos) {}
41 | }
42 |
--------------------------------------------------------------------------------
/projects/todo-v6/src/app/components/section/section.component.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import { Todos } from '../../stores/todos.store';
3 |
4 | @Component({
5 | changeDetection: ChangeDetectionStrategy.OnPush,
6 | selector: 'app-section',
7 | template: `
8 |
34 | `
35 | })
36 | export class SectionComponent {
37 | constructor(public todos: Todos) {}
38 | }
39 |
--------------------------------------------------------------------------------
/projects/todo-v6/src/app/stores/todos.store.ts:
--------------------------------------------------------------------------------
1 | import {
2 | observable,
3 | computed,
4 | action,
5 | autorun,
6 | toJS,
7 | makeAutoObservable,
8 | makeObservable
9 | } from 'mobx';
10 | import { Injectable } from '@angular/core';
11 |
12 | export class Todo {
13 | completed = false;
14 | title: string;
15 |
16 | constructor({ title, completed }) {
17 | makeAutoObservable(this);
18 | this.completed = completed;
19 | this.title = title;
20 | }
21 |
22 | setCompleted(value) {
23 | this.completed = value;
24 | }
25 | }
26 |
27 | @Injectable()
28 | export class Todos {
29 | todos = [];
30 | filter = 'SHOW_ALL';
31 |
32 | constructor() {
33 | makeObservable(this, {
34 | todos: observable,
35 | filter: observable,
36 | addTodo: action,
37 | removeTodo: action,
38 | showAll: action,
39 | showCompleted: action,
40 | showActive: action,
41 | clearCompleted: action,
42 | setCompleteAll: action,
43 | filteredTodos: computed,
44 | uncompletedItems: computed
45 | });
46 | this.localStorageSync();
47 | }
48 |
49 | addTodo({ title, completed = false }) {
50 | this.todos.push(new Todo({ title, completed }));
51 | }
52 |
53 | removeTodo(todo) {
54 | const index = this.todos.indexOf(todo);
55 | this.todos.splice(index, 1);
56 | }
57 |
58 | showAll() {
59 | this.filter = 'SHOW_ALL';
60 | }
61 | showCompleted() {
62 | this.filter = 'COMPLETED';
63 | }
64 | showActive() {
65 | this.filter = 'ACTIVE';
66 | }
67 |
68 | clearCompleted() {
69 | this.todos = this._filter(this.todos, 'ACTIVE');
70 | }
71 |
72 | setCompleteAll(value) {
73 | this.todos.forEach(todo => todo.setCompleted(value));
74 | }
75 |
76 | get filteredTodos() {
77 | return this.filter !== 'SHOW_ALL'
78 | ? this._filter(this.todos, this.filter)
79 | : this.todos;
80 | }
81 |
82 | get uncompletedItems() {
83 | return this._filter(this.todos, false).length;
84 | }
85 |
86 | get allComplete() {
87 | return this.uncompletedItems === 0;
88 | }
89 |
90 | private _filter(todos, value) {
91 | return todos.filter(todo =>
92 | value === 'COMPLETED' ? todo.completed : !todo.completed
93 | );
94 | }
95 |
96 | private localStorageSync() {
97 | const initialTodos = JSON.parse(localStorage.todos || '[]');
98 | this.todos = initialTodos.map(todo => new Todo(todo));
99 | this.filter = JSON.parse(localStorage.filter || '"SHOW_ALL"');
100 |
101 | autorun(() => {
102 | localStorage.todos = JSON.stringify(toJS(this.todos));
103 | localStorage.filter = JSON.stringify(toJS(this.filter));
104 | });
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/projects/todo-v6/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/todo-v6/src/assets/.gitkeep
--------------------------------------------------------------------------------
/projects/todo-v6/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/projects/todo-v6/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/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/projects/todo-v6/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/todo-v6/src/favicon.ico
--------------------------------------------------------------------------------
/projects/todo-v6/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoV6
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/projects/todo-v6/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().bootstrapModule(AppModule)
12 | .catch(err => console.error(err));
13 |
--------------------------------------------------------------------------------
/projects/todo-v6/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 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 | /***************************************************************************************************
51 | * APPLICATION IMPORTS
52 | */
53 | (window as any).global = window;
54 |
--------------------------------------------------------------------------------
/projects/todo-v6/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 | @import "~todomvc-common/base.css";
3 | @import "~todomvc-app-css/index.css";
4 |
--------------------------------------------------------------------------------
/projects/todo-v6/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | // First, initialize the Angular testing environment.
11 | getTestBed().initTestEnvironment(
12 | BrowserDynamicTestingModule,
13 | platformBrowserDynamicTesting(),
14 | {
15 | teardown: { destroyAfterEach: false }
16 | }
17 | );
18 |
--------------------------------------------------------------------------------
/projects/todo-v6/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/app",
5 | "types": []
6 | },
7 | "files": [
8 | "src/main.ts",
9 | "src/polyfills.ts"
10 | ],
11 | "include": [
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/projects/todo-v6/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/projects/todo-v6/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/projects/todo/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'.
--------------------------------------------------------------------------------
/projects/todo/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter } = require('jasmine-spec-reporter');
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: ['./src/**/*.e2e-spec.ts'],
13 | capabilities: {
14 | browserName: 'chrome'
15 | },
16 | directConnect: true,
17 | baseUrl: 'http://localhost:4200/',
18 | framework: 'jasmine',
19 | jasmineNodeOpts: {
20 | showColors: true,
21 | defaultTimeoutInterval: 30000,
22 | print: function() {}
23 | },
24 | onPrepare() {
25 | require('ts-node').register({
26 | project: require('path').join(__dirname, './tsconfig.json')
27 | });
28 | jasmine
29 | .getEnv()
30 | .addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/projects/todo/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 | import { browser, logging } from 'protractor';
3 |
4 | describe('workspace-project App', () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it('should display welcome message', () => {
12 | page.navigateTo();
13 | expect(page.getTitleText()).toEqual('todo app is running!');
14 | });
15 |
16 | afterEach(async () => {
17 | // Assert that there are no errors emitted from the browser
18 | const logs = await browser
19 | .manage()
20 | .logs()
21 | .get(logging.Type.BROWSER);
22 | expect(logs).not.toContain(
23 | jasmine.objectContaining({
24 | level: logging.Level.SEVERE
25 | } as logging.Entry)
26 | );
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/projects/todo/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo(): Promise {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | getTitleText(): Promise {
9 | return element(by.css('app-root .content span')).getText() as Promise<
10 | string
11 | >;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/projects/todo/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": ["jasmine", "jasminewd2", "node"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/projects/todo/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-spec-reporter'),
13 | require('karma-coverage'),
14 | require('@angular-devkit/build-angular/plugins/karma')
15 | ],
16 | client: {
17 | clearContext: false, // leave Jasmine Spec Runner output visible in browser
18 | jasmine: {
19 | random: false,
20 | }
21 | },
22 | coverageReporter: {
23 | reporters: [{ type: 'html' }, { type: 'text' }, { type: 'json-summary' }, { type: 'lcov' }],
24 | dir: require('path').join(__dirname, './coverage/todo'),
25 | fixWebpackSourcePaths: true
26 | },
27 | reporters: ['progress', 'kjhtml'],
28 | port: 9876,
29 | colors: true,
30 | logLevel: config.LOG_INFO,
31 | autoWatch: true,
32 | browsers: ['Chrome'],
33 | singleRun: false,
34 | restartOnFileChange: true
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/projects/todo/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ChangeDetectionStrategy } from '@angular/core';
2 | import { observable, computed, action } from 'mobx-angular';
3 | import { Todos } from './stores/todos.store';
4 | import { makeObservable } from 'mobx';
5 |
6 | @Component({
7 | changeDetection: ChangeDetectionStrategy.OnPush,
8 | selector: 'app-root',
9 | template: `
10 |
26 | `
27 | })
28 | export class AppComponent {
29 | @observable.ref title = '';
30 | @computed.struct get titles() {
31 | return [this.title];
32 | }
33 |
34 | constructor(public todos: Todos) {
35 | makeObservable(this);
36 | }
37 |
38 | @action.bound addTodo() {
39 | this.todos.addTodo({ title: this.title });
40 | this.title = '';
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/projects/todo/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import remotedev from 'mobx-remotedev';
5 |
6 | import { MobxAngularModule } from 'mobx-angular';
7 | import { Todos } from './stores/todos.store';
8 | import { AppComponent } from './app.component';
9 | import { SectionComponent } from './components/section/section.component';
10 | import { FooterComponent } from './components/footer/footer.component';
11 | import { CountComponent } from './components/count/count.component';
12 |
13 | @NgModule({
14 | declarations: [
15 | AppComponent,
16 | SectionComponent,
17 | FooterComponent,
18 | CountComponent
19 | ],
20 | imports: [BrowserModule, FormsModule, MobxAngularModule],
21 | providers: [
22 | { provide: Todos, useClass: remotedev(Todos, { global: true }), deps: [] }
23 | ],
24 | bootstrap: [AppComponent]
25 | })
26 | export class AppModule {}
27 |
--------------------------------------------------------------------------------
/projects/todo/src/app/components/count/count.component.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import { Todos } from '../../stores/todos.store';
3 |
4 | @Component({
5 | changeDetection: ChangeDetectionStrategy.OnPush,
6 | selector: 'app-count',
7 | template: `
8 |
9 | {{ todos.uncompletedItems }} items left
10 |
11 | `
12 | })
13 | export class CountComponent {
14 | constructor(public todos: Todos) {}
15 | }
16 |
--------------------------------------------------------------------------------
/projects/todo/src/app/components/footer/footer.component.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import { Todos } from '../../stores/todos.store';
3 |
4 | @Component({
5 | changeDetection: ChangeDetectionStrategy.OnPush,
6 | selector: 'app-footer',
7 | template: `
8 |
37 | `
38 | })
39 | export class FooterComponent {
40 | constructor(public todos: Todos) {}
41 | }
42 |
--------------------------------------------------------------------------------
/projects/todo/src/app/components/section/section.component.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component } from '@angular/core';
2 | import { Todos } from '../../stores/todos.store';
3 |
4 | @Component({
5 | changeDetection: ChangeDetectionStrategy.OnPush,
6 | selector: 'app-section',
7 | template: `
8 |
34 | `
35 | })
36 | export class SectionComponent {
37 | constructor(public todos: Todos) {}
38 | }
39 |
--------------------------------------------------------------------------------
/projects/todo/src/app/stores/todos.store.ts:
--------------------------------------------------------------------------------
1 | import { observable, computed, action, autorun, toJS, makeObservable } from 'mobx';
2 | import { Injectable } from '@angular/core';
3 |
4 | export class Todo {
5 | @observable completed = false;
6 | @observable title: string;
7 |
8 | constructor({ title, completed }) {
9 | makeObservable(this);
10 | this.completed = completed;
11 | this.title = title;
12 | }
13 |
14 | @action setCompleted(value) {
15 | this.completed = value;
16 | }
17 | }
18 |
19 | @Injectable()
20 | export class Todos {
21 | @observable todos = [];
22 | @observable filter = 'SHOW_ALL';
23 |
24 | constructor() {
25 | makeObservable(this);
26 | this.localStorageSync();
27 | }
28 |
29 | @action addTodo({ title, completed = false }) {
30 | this.todos.push(new Todo({ title, completed }));
31 | }
32 |
33 | @action removeTodo(todo) {
34 | const index = this.todos.indexOf(todo);
35 | this.todos.splice(index, 1);
36 | }
37 |
38 | @action showAll() {
39 | this.filter = 'SHOW_ALL';
40 | }
41 | @action showCompleted() {
42 | this.filter = 'COMPLETED';
43 | }
44 | @action showActive() {
45 | this.filter = 'ACTIVE';
46 | }
47 |
48 | @action clearCompleted() {
49 | this.todos = this._filter(this.todos, 'ACTIVE');
50 | }
51 |
52 | @action setCompleteAll(value) {
53 | this.todos.forEach(todo => todo.setCompleted(value));
54 | }
55 |
56 | @computed get filteredTodos() {
57 | return this.filter !== 'SHOW_ALL'
58 | ? this._filter(this.todos, this.filter)
59 | : this.todos;
60 | }
61 |
62 | @computed get uncompletedItems() {
63 | return this._filter(this.todos, false).length;
64 | }
65 |
66 | @computed get allComplete() {
67 | return this.uncompletedItems === 0;
68 | }
69 |
70 | private _filter(todos, value) {
71 | return todos.filter(todo =>
72 | value === 'COMPLETED' ? todo.completed : !todo.completed
73 | );
74 | }
75 |
76 | private localStorageSync() {
77 | const initialTodos = JSON.parse(localStorage.todos || '[]');
78 | this.todos = initialTodos.map(todo => new Todo(todo));
79 | this.filter = JSON.parse(localStorage.filter || '"SHOW_ALL"');
80 |
81 | autorun(() => {
82 | localStorage.todos = JSON.stringify(toJS(this.todos));
83 | localStorage.filter = JSON.stringify(toJS(this.filter));
84 | });
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/projects/todo/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/todo/src/assets/.gitkeep
--------------------------------------------------------------------------------
/projects/todo/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/projects/todo/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/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/projects/todo/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobxjs/mobx-angular/30a4b44bd153486f1ac25e4fd022db21cc2cd96e/projects/todo/src/favicon.ico
--------------------------------------------------------------------------------
/projects/todo/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Todo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/projects/todo/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 |
--------------------------------------------------------------------------------
/projects/todo/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 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 | /***************************************************************************************************
51 | * APPLICATION IMPORTS
52 | */
53 |
54 | (window as any).global = window;
55 |
--------------------------------------------------------------------------------
/projects/todo/src/styles.scss:
--------------------------------------------------------------------------------
1 | @import "~todomvc-common/base.css";
2 | @import "~todomvc-app-css/index.css";
3 |
--------------------------------------------------------------------------------
/projects/todo/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | // First, initialize the Angular testing environment.
11 | getTestBed().initTestEnvironment(
12 | BrowserDynamicTestingModule,
13 | platformBrowserDynamicTesting(),
14 | {
15 | teardown: { destroyAfterEach: false }
16 | }
17 | );
18 |
--------------------------------------------------------------------------------
/projects/todo/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/app",
5 | "types": []
6 | },
7 | "files": ["src/main.ts", "src/polyfills.ts"],
8 | "include": ["src/**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/projects/todo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.app.json"
3 | }
4 |
--------------------------------------------------------------------------------
/projects/todo/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": ["jasmine", "node"]
6 | },
7 | "files": ["src/test.ts", "src/polyfills.ts"],
8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/projects/todo/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "app", "camelCase"],
5 | "component-selector": [true, "element", "app", "kebab-case"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "downlevelIteration": true,
9 | "experimentalDecorators": true,
10 | "useDefineForClassFields": false,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "importHelpers": true,
14 | "target": "ES2022",
15 | "typeRoots": ["node_modules/@types"],
16 | "lib": ["es2018", "dom"],
17 | "paths": {
18 | "mobx-angular": ["dist/mobx-angular/mobx-angular", "dist/mobx-angular"]
19 | }
20 | },
21 | "angularCompilerOptions": {
22 | "fullTemplateTypeCheck": true,
23 | "strictInjectionParameters": true
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tslint:recommended",
3 | "rulesDirectory": ["codelyzer"],
4 | "rules": {
5 | "array-type": false,
6 | "arrow-parens": false,
7 | "deprecation": {
8 | "severity": "warning"
9 | },
10 | "import-blacklist": [true, "rxjs/Rx"],
11 | "interface-name": false,
12 | "max-classes-per-file": false,
13 | "max-line-length": [true, 140],
14 | "member-access": false,
15 | "member-ordering": [
16 | true,
17 | {
18 | "order": [
19 | "static-field",
20 | "instance-field",
21 | "static-method",
22 | "instance-method"
23 | ]
24 | }
25 | ],
26 | "no-consecutive-blank-lines": false,
27 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"],
28 | "no-empty": false,
29 | "no-inferrable-types": [true, "ignore-params"],
30 | "no-non-null-assertion": true,
31 | "no-redundant-jsdoc": true,
32 | "no-switch-case-fall-through": true,
33 | "no-var-requires": false,
34 | "object-literal-key-quotes": [true, "as-needed"],
35 | "object-literal-sort-keys": false,
36 | "ordered-imports": false,
37 | "quotemark": [true, "single"],
38 | "trailing-comma": false,
39 | "component-class-suffix": true,
40 | "contextual-lifecycle": true,
41 | "directive-class-suffix": true,
42 | "no-conflicting-lifecycle": true,
43 | "no-host-metadata-property": true,
44 | "no-input-rename": true,
45 | "no-inputs-metadata-property": true,
46 | "no-output-native": true,
47 | "no-output-on-prefix": true,
48 | "no-output-rename": true,
49 | "no-outputs-metadata-property": true,
50 | "template-banana-in-box": true,
51 | "template-no-negated-async": true,
52 | "use-lifecycle-interface": true,
53 | "use-pipe-transform-interface": true
54 | }
55 | }
56 |
--------------------------------------------------------------------------------