@expressive/mvc 1.0.0-beta.2 → 1.0.0-beta.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -78
- package/lib/index.js +980 -829
- package/package.json +14 -9
- package/types/context.d.ts +57 -0
- package/types/index.d.ts +3 -20
- package/types/instructions.d.ts +227 -100
- package/types/model.d.ts +193 -34
- package/types/selector.ts +18 -0
- package/types/types.d.ts +7 -1
- package/types/controller.d.ts +0 -56
- package/types/react.d.ts +0 -46
package/README.md
CHANGED
|
@@ -14,22 +14,28 @@
|
|
|
14
14
|
<p align="center">
|
|
15
15
|
<a href="https://www.npmjs.com/package/@expressive/mvc"><img alt="NPM" src="https://badge.fury.io/js/%40expressive%2Fmvc.svg"></a>
|
|
16
16
|
<a href=""><img alt="Build" src="https://shields-staging.herokuapp.com/npm/types/@expressive/mvc.svg"></a>
|
|
17
|
+
<img src="https://img.shields.io/badge/Coverage-100%25-brightgreen.svg">
|
|
17
18
|
</p>
|
|
18
19
|
|
|
19
20
|
<p align="center">
|
|
20
|
-
Define classes to power UI
|
|
21
|
+
Define classes to power UI by extending <code>Model</code>.<br/>
|
|
21
22
|
Builtin hooks will manage renders, as needed, for any data.<br/>
|
|
22
23
|
When properties change, your components will too.<br/>
|
|
23
24
|
</p>
|
|
24
25
|
|
|
25
26
|
<br/>
|
|
26
|
-
<
|
|
27
|
+
<p align="center">
|
|
28
|
+
<b>Have suggestions, need help or just want to discuss?</b><br/><br/>
|
|
29
|
+
<a href="https://join.slack.com/t/expressivejs/shared_invite/zt-s2j5cdhz-gffKn3bTATMbXf~iq4pvHg" alt="Join Slack">
|
|
30
|
+
<img src="https://img.shields.io/badge/Slack-Come%20say%20hi!-blueviolet" />
|
|
31
|
+
</a>
|
|
32
|
+
</p>
|
|
27
33
|
|
|
28
|
-
|
|
34
|
+
<br/>
|
|
35
|
+
<h1 id="overview-section">Overview</h1>
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
Classes which extend `Model` can manage behavior for components. Custom models are easy to define, use and even provide. While hooks are great, state and logic are not a part of well-formed JSX. Models help wrap that stuff in robust, portable controllers instead.
|
|
31
38
|
|
|
32
|
-
This behavior combines with actions, computed properties, events, and components themselves to facilitate a **M**odel-**V**iew-**C**ontroller pattern in React.
|
|
33
39
|
|
|
34
40
|
<br/>
|
|
35
41
|
|
|
@@ -50,7 +56,7 @@ class Counter extends Model {
|
|
|
50
56
|
|
|
51
57
|
### Step 2
|
|
52
58
|
|
|
53
|
-
Pick a built-in hook, such as `use()`, to make
|
|
59
|
+
Pick a built-in hook, such as `use()`, to make a component stateful.
|
|
54
60
|
|
|
55
61
|
```jsx
|
|
56
62
|
const KitchenCounter = () => {
|
|
@@ -70,10 +76,10 @@ const KitchenCounter = () => {
|
|
|
70
76
|
[See it in action](https://codesandbox.io/s/example-counter-th8xl) 🚀 You've already got something usable!
|
|
71
77
|
|
|
72
78
|
<br/>
|
|
73
|
-
<
|
|
79
|
+
<h2 id="good-start">Things you can do</h2>
|
|
74
80
|
|
|
75
|
-
###
|
|
76
|
-
|
|
81
|
+
### Track any number of values
|
|
82
|
+
Updating any of them will trigger a render.
|
|
77
83
|
|
|
78
84
|
```jsx
|
|
79
85
|
class State extends Model {
|
|
@@ -88,9 +94,10 @@ const MyComponent = () => {
|
|
|
88
94
|
return <div>...</div>
|
|
89
95
|
}
|
|
90
96
|
```
|
|
97
|
+
<br/>
|
|
91
98
|
|
|
92
99
|
### Refresh using simple assignment
|
|
93
|
-
Reserved `set` loops back to instance, to update from inside a component.
|
|
100
|
+
Reserved `set` loops back to the instance, to update from inside a component.
|
|
94
101
|
|
|
95
102
|
```jsx
|
|
96
103
|
class Hello extends Model {
|
|
@@ -107,36 +114,36 @@ const MyComponent = () => {
|
|
|
107
114
|
)
|
|
108
115
|
}
|
|
109
116
|
```
|
|
117
|
+
<br/>
|
|
110
118
|
|
|
111
119
|
### Share state via context
|
|
112
|
-
|
|
120
|
+
Provide and consume with simple methods. Classes are their own key.
|
|
113
121
|
|
|
114
122
|
```jsx
|
|
115
123
|
import Model, { Provider } from "@expressive/mvc";
|
|
116
124
|
|
|
117
125
|
class Shared extends Model {
|
|
118
|
-
|
|
119
|
-
bar = 2;
|
|
126
|
+
value = 1;
|
|
120
127
|
}
|
|
121
128
|
|
|
122
129
|
const Parent = () => (
|
|
123
130
|
<Provider of={Shared}>
|
|
124
|
-
<
|
|
125
|
-
<Bar />
|
|
131
|
+
<Child />
|
|
126
132
|
</Provider>
|
|
127
133
|
)
|
|
128
134
|
|
|
129
|
-
const
|
|
130
|
-
const {
|
|
135
|
+
const Child = () => {
|
|
136
|
+
const { value } = Shared.get();
|
|
131
137
|
|
|
132
138
|
return (
|
|
133
|
-
<p>
|
|
139
|
+
<p>Shared value is: {value}!</p>
|
|
134
140
|
)
|
|
135
141
|
}
|
|
136
142
|
```
|
|
143
|
+
<br/>
|
|
137
144
|
|
|
138
|
-
### Respond to
|
|
139
|
-
Hooks
|
|
145
|
+
### Respond to lifecycle
|
|
146
|
+
Hooks aware of lifecycle will call method-handlers you define.
|
|
140
147
|
|
|
141
148
|
```jsx
|
|
142
149
|
class Timer extends Model {
|
|
@@ -145,7 +152,6 @@ class Timer extends Model {
|
|
|
145
152
|
|
|
146
153
|
componentDidMount(){
|
|
147
154
|
const inc = () => this.elapsed++;
|
|
148
|
-
|
|
149
155
|
this.interval = setInterval(inc, 1000);
|
|
150
156
|
}
|
|
151
157
|
|
|
@@ -157,20 +163,21 @@ class Timer extends Model {
|
|
|
157
163
|
const MyTimer = () => {
|
|
158
164
|
const { elapsed } = Timer.use();
|
|
159
165
|
|
|
160
|
-
return <
|
|
166
|
+
return <p>I've existed for { elapsed } seconds!</p>;
|
|
161
167
|
}
|
|
162
168
|
```
|
|
169
|
+
<br/>
|
|
163
170
|
|
|
164
171
|
### Control components with `async`
|
|
165
172
|
|
|
166
173
|
```jsx
|
|
167
174
|
class Greetings extends Model {
|
|
168
175
|
response = undefined;
|
|
169
|
-
|
|
176
|
+
waiting = false;
|
|
170
177
|
error = false;
|
|
171
178
|
|
|
172
179
|
sayHello = async () => {
|
|
173
|
-
this.
|
|
180
|
+
this.waiting = true;
|
|
174
181
|
|
|
175
182
|
try {
|
|
176
183
|
const res = await fetch("https://my.api/hello");
|
|
@@ -183,15 +190,15 @@ class Greetings extends Model {
|
|
|
183
190
|
}
|
|
184
191
|
|
|
185
192
|
const MyComponent = () => {
|
|
186
|
-
const { error, response,
|
|
193
|
+
const { error, response, waiting, sayHello } = Greetings.use();
|
|
187
194
|
|
|
188
195
|
if(response)
|
|
189
196
|
return <p>Server said: {response}</p>
|
|
190
197
|
|
|
191
198
|
if(error)
|
|
192
|
-
return <p>There was an issue saying hello
|
|
199
|
+
return <p>There was an issue saying hello</p>
|
|
193
200
|
|
|
194
|
-
if(
|
|
201
|
+
if(waiting)
|
|
195
202
|
return <p>Sent! Waiting on response...</p>
|
|
196
203
|
|
|
197
204
|
return (
|
|
@@ -199,14 +206,17 @@ const MyComponent = () => {
|
|
|
199
206
|
)
|
|
200
207
|
}
|
|
201
208
|
```
|
|
209
|
+
<br/>
|
|
202
210
|
|
|
203
|
-
### Extend
|
|
204
|
-
|
|
211
|
+
### Extend to configure
|
|
212
|
+
Use class extension to set needed properties.
|
|
213
|
+
Reusable logic is easy to implement, document and share.
|
|
205
214
|
|
|
206
215
|
```ts
|
|
207
216
|
abstract class About extends Model {
|
|
208
217
|
abstract name: string;
|
|
209
218
|
abstract birthday: Date;
|
|
219
|
+
abstract wants: any;
|
|
210
220
|
|
|
211
221
|
happyBirthday(){
|
|
212
222
|
// ...
|
|
@@ -216,11 +226,13 @@ abstract class About extends Model {
|
|
|
216
226
|
class AboutMe extends About {
|
|
217
227
|
name = "John Doe";
|
|
218
228
|
birthday = new Date("January 1");
|
|
219
|
-
wants = "PS5";
|
|
229
|
+
wants = "a PS5";
|
|
220
230
|
}
|
|
221
231
|
```
|
|
222
232
|
|
|
223
|
-
|
|
233
|
+
</br>
|
|
234
|
+
|
|
235
|
+
#### And you can do more, all with type safety and code-assist, out of the box.
|
|
224
236
|
|
|
225
237
|
</br>
|
|
226
238
|
|
|
@@ -243,9 +255,9 @@ class AboutMe extends About {
|
|
|
243
255
|
  • [Lifecycle](#concept-lifecycle) <br/>
|
|
244
256
|
  • [Events](#concept-events) <br/>
|
|
245
257
|
    ◦ [Listening](#concept-listen-event) <br/>
|
|
246
|
-
    ◦ [
|
|
258
|
+
    ◦ [Dispatch](#concept-push-event) <br/>
|
|
247
259
|
    ◦ [Built-in](#concept-builtin-events) <br/>
|
|
248
|
-
  • [Monitored
|
|
260
|
+
  • [Monitored Props](#concept-external) <br/>
|
|
249
261
|
  • [Async and callbacks](#concept-async) <br/>
|
|
250
262
|
|
|
251
263
|
**Shared State** <br/>
|
|
@@ -262,10 +274,10 @@ class AboutMe extends About {
|
|
|
262
274
|
    ◦ [`get`](#method-get) <br/>
|
|
263
275
|
    ◦ [`tap`](#method-tap) <br/>
|
|
264
276
|
  • [Consumer](#concept-consumer) <br/>
|
|
265
|
-
    ◦ [
|
|
266
|
-
    ◦ [`get`
|
|
267
|
-
    ◦ [`tap`
|
|
268
|
-
    ◦ [`has`
|
|
277
|
+
    ◦ [children](#consumer-render) <br/>
|
|
278
|
+
    ◦ [`get`](#consumer-get) <br/>
|
|
279
|
+
    ◦ [`tap`](#consumer-tap) <br/>
|
|
280
|
+
    ◦ [`has`](#consumer-has) <br/>
|
|
269
281
|
|
|
270
282
|
**Composition** <br/>
|
|
271
283
|
  • [Simple Composition](#concept-compose) <br/>
|
|
@@ -288,11 +300,11 @@ class AboutMe extends About {
|
|
|
288
300
|
  • [Auto Debounce](#concept-debounce) <br/>
|
|
289
301
|
<!--   • [Selectors](#concept-selectors) <br/> -->
|
|
290
302
|
|
|
291
|
-
### API
|
|
303
|
+
<!-- ### API
|
|
292
304
|
  • [Model](#api-controller) <br/>
|
|
293
305
|
  • [Singleton](#api-singleton) <br/>
|
|
294
306
|
  • [Reserved](#api-reserved) <br/>
|
|
295
|
-
  • [Lifecycle](#api-lifecycle) <br/>
|
|
307
|
+
  • [Lifecycle](#api-lifecycle) <br/> -->
|
|
296
308
|
|
|
297
309
|
<br/>
|
|
298
310
|
|
|
@@ -315,23 +327,22 @@ import Model from "@expressive/mvc";
|
|
|
315
327
|
Ultimately, the workflow is simple.
|
|
316
328
|
|
|
317
329
|
1. Create a class. Fill it with the values, getters, and methods you'll need.
|
|
318
|
-
2. Extend `Model` (or any derivative, for that matter)
|
|
319
|
-
3. Within a component, use built-in methods as you would
|
|
320
|
-
4. Destructure the values
|
|
330
|
+
2. Extend `Model` (or any derivative, for that matter), making it reactive.
|
|
331
|
+
3. Within a component, use built-in methods as you would normal hooks.
|
|
332
|
+
4. Destructure out the values used by a component, to then subscribe.
|
|
321
333
|
5. Update those values on demand. Component will sync automagically. ✨
|
|
322
334
|
<br/>
|
|
323
335
|
|
|
324
|
-
|
|
336
|
+
> You may recognize this as similar to approach pioneered by [MobX](https://mobx.js.org/README.html). Here though, we avoid using decorators and other boilerplate.
|
|
325
337
|
|
|
326
|
-
|
|
327
|
-
Here's some library jargon which will be good to know.
|
|
338
|
+
### Glossary
|
|
328
339
|
|
|
329
|
-
- **Model**: Any class you'll write extending `Model`.
|
|
330
|
-
- **Controller**: An instance of
|
|
331
|
-
- **State**: The values
|
|
332
|
-
- **Subscriber**: A hook, or otherwise
|
|
333
|
-
- **View**: A function-component which
|
|
334
|
-
- **Element**: Instance of a component
|
|
340
|
+
- **Model**: Any class you'll write extending `Model`. Defines a type of state.
|
|
341
|
+
- **Controller**: An instance of Model, containing its state and behavior.
|
|
342
|
+
- **State**: The values managed by a controller, as defined by the model.
|
|
343
|
+
- **Subscriber**: A hook, or otherwise a callback, responding to updates.
|
|
344
|
+
- **View**: A function-component which accepts hooks, to contain a controller.
|
|
345
|
+
- **Element**: Instance of a component mounted, having state and lifecycle.
|
|
335
346
|
|
|
336
347
|
<br/>
|
|
337
348
|
<h2 id="concept-simple">First Steps</h2>
|
|
@@ -495,10 +506,10 @@ class Timer extends Model {
|
|
|
495
506
|
|
|
496
507
|
A getter's value is cached, facing the user. It will only run when a dependency changes, and **not upon access** as you might expect. However, getters are also lazy, so first access is one exception.
|
|
497
508
|
|
|
498
|
-
|
|
509
|
+
Getters run whenever a controller thinks they *could* change. Make sure to design them along these lines:
|
|
499
510
|
- Getters should be deterministic. Only expect a change where inputs have changed.
|
|
500
511
|
- Avoid computing from values which change a lot, but don't change output as often.
|
|
501
|
-
- [Goes without saying](https://www.youtube.com/watch?v=0i0IlSKn0sE "Goes Without Saying") but, **side-effects are an anti-pattern** and
|
|
512
|
+
- [Goes without saying](https://www.youtube.com/watch?v=0i0IlSKn0sE "Goes Without Saying") but, **side-effects are an anti-pattern** and may cause infinite loops.
|
|
502
513
|
|
|
503
514
|
<br/>
|
|
504
515
|
|
|
@@ -534,7 +545,7 @@ const SayHello = ({ fullName }) => {
|
|
|
534
545
|
|
|
535
546
|
<h2 id="concept-passing-props">Assigning props to state</h2>
|
|
536
547
|
|
|
537
|
-
Besides `use`, similar methods will assign values after a controller is created.
|
|
548
|
+
Besides `use`, similar methods will assign values after a controller is created. Good alternative to a manual setup.
|
|
538
549
|
|
|
539
550
|
<h4 id="method-uses"><code>Model.uses(source, keys?)</code></h4>
|
|
540
551
|
|
|
@@ -622,7 +633,6 @@ class TimerControl extends Model {
|
|
|
622
633
|
|
|
623
634
|
componentDidMount(){
|
|
624
635
|
const inc = () => this.elapsed++;
|
|
625
|
-
|
|
626
636
|
this.interval = setInterval(inc, 1000);
|
|
627
637
|
}
|
|
628
638
|
|
|
@@ -655,7 +665,7 @@ Besides state change, what a subscriber really cares about is events. *Updates a
|
|
|
655
665
|
|
|
656
666
|
<br />
|
|
657
667
|
|
|
658
|
-
<
|
|
668
|
+
<h2 id="concept-listen-event">Listening for events manually</h2>
|
|
659
669
|
|
|
660
670
|
```js
|
|
661
671
|
const normalCallback = (value, key) => {
|
|
@@ -684,7 +694,7 @@ Will also expire when triggered. May be cancelled with the returned callback.
|
|
|
684
694
|
If `callback` is not provided, `once` will return a Promise of keys watched/updated.
|
|
685
695
|
|
|
686
696
|
<br />
|
|
687
|
-
<
|
|
697
|
+
<h2 id="concept-listen-effect">Defining an effect</h2>
|
|
688
698
|
|
|
689
699
|
```js
|
|
690
700
|
function effectCallback(state){
|
|
@@ -715,30 +725,31 @@ function subscribedCallback(state){
|
|
|
715
725
|
```
|
|
716
726
|
#### `state.effect(subscribedCallback) => remove`
|
|
717
727
|
|
|
718
|
-
If no explicit `keys` are given, effect callback will self-subscribe. Just like a hook, it
|
|
728
|
+
If no explicit `keys` are given, the effect callback will self-subscribe. Just like a hook, it detects values used and automatically will update for new ones.
|
|
719
729
|
|
|
720
|
-
> Note: In order to scan for property access,
|
|
730
|
+
> Note: In order to scan for property access, this effect will run at-least once, immediately.
|
|
721
731
|
|
|
722
732
|
<br />
|
|
723
|
-
<
|
|
733
|
+
<h2 id="concept-push-event">Pushing your own events</h2>
|
|
724
734
|
|
|
725
735
|
#### `state.update(key)`
|
|
726
736
|
|
|
727
737
|
Fires a synthetic event, sent to all listeners for key(s) selected, be them components or callback-listeners above. Any `string` or `symbol` may be a key. Naturally, a key which is defined on state already, will be treated as an update.
|
|
728
738
|
|
|
729
739
|
> Events make it easier to design around closures and callbacks, keeping as few things on your model as possible. Event methods may also be used externally, for other code to interact with.
|
|
730
|
-
<br /><br />
|
|
731
740
|
|
|
732
|
-
<
|
|
741
|
+
<br />
|
|
742
|
+
<h2 id="concept-builtin-event">Listening for built-in events</h2>
|
|
733
743
|
|
|
734
744
|
Controllers will emit lifecycle events for a bound component (depending on the hook).
|
|
735
745
|
|
|
736
746
|
> Often lifecycle is critical to a controller's correct behavior. While we do have lifecycle-methods, it's recommended to use events where able. This way, if your class is extended and redefines a handler, yours is not at the mercy of a `super[event]()` call.
|
|
737
747
|
|
|
738
748
|
Lifecycle events share names with their respective methods, [listed here](#lifecycle-api).
|
|
739
|
-
<br /><br />
|
|
740
749
|
|
|
741
|
-
|
|
750
|
+
<br />
|
|
751
|
+
|
|
752
|
+
## Event handling in-practice:
|
|
742
753
|
|
|
743
754
|
```js
|
|
744
755
|
class TickTockClock extends Model {
|
|
@@ -796,8 +807,6 @@ Sometimes, we want to react to changes coming from outside, usually via props. O
|
|
|
796
807
|
|
|
797
808
|
This method helps watch an object by running [`assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) on it *every render*. Because controllers only react to new values, this makes for a simple way to watch externals. Combine this with getters and event-listeners, to do things when inputs change.
|
|
798
809
|
|
|
799
|
-
<br />
|
|
800
|
-
|
|
801
810
|
```ts
|
|
802
811
|
class ActivityTracker extends Model {
|
|
803
812
|
active = undefined;
|
|
@@ -842,8 +851,6 @@ const Activate = () => {
|
|
|
842
851
|
|
|
843
852
|
<sup><a href="https://codesandbox.io/s/example-using-method-wvrtu">View in CodeSandbox</a></sup>
|
|
844
853
|
|
|
845
|
-
With this, we can interact with all different sorts of state!
|
|
846
|
-
|
|
847
854
|
> **Note:** Method is also picky (ala `uses`), and will ignore values which don't already exist in state.
|
|
848
855
|
|
|
849
856
|
<br />
|
|
@@ -1012,7 +1019,6 @@ export class FooBar extends Model {
|
|
|
1012
1019
|
> Models and their consumers do not need to live in same file, let alone module. Want to interact with another library's state? This can make for a pro-consumer solution. ☝️😑
|
|
1013
1020
|
|
|
1014
1021
|
<br/>
|
|
1015
|
-
|
|
1016
1022
|
<h2 id="concept-provider">Providing an instance</h2>
|
|
1017
1023
|
|
|
1018
1024
|
Here, pass a controller to the `of` prop. For any children, it will be made accessible via its class.
|
|
@@ -1035,6 +1041,7 @@ export const App = () => {
|
|
|
1035
1041
|
}
|
|
1036
1042
|
```
|
|
1037
1043
|
|
|
1044
|
+
<br/>
|
|
1038
1045
|
<h2 id="concept-provider-spawning">Spawning an instance</h2>
|
|
1039
1046
|
|
|
1040
1047
|
Without constructor-arguments, creating the instance separately can be an unnecessary step. Pass a Model itself, to both create and provide a state in one sitting.
|
|
@@ -1049,6 +1056,7 @@ export const App = () => {
|
|
|
1049
1056
|
}
|
|
1050
1057
|
```
|
|
1051
1058
|
|
|
1059
|
+
<br/>
|
|
1052
1060
|
<h2 id="concept-provider-props">Spawning with props</h2>
|
|
1053
1061
|
|
|
1054
1062
|
When a Model is passed to `of` directly, the Provider will behave similarly to [`Model.using()`](#concept-external). All other props are forwarded to state and likewise observed.
|
|
@@ -1065,6 +1073,7 @@ const FancyFooBar = (props) => {
|
|
|
1065
1073
|
|
|
1066
1074
|
> Now, consumers can see incoming-data, which only the parent has access to!
|
|
1067
1075
|
|
|
1076
|
+
<br/>
|
|
1068
1077
|
<h2 id="concept-provider-multi">Providing Multiple</h2>
|
|
1069
1078
|
|
|
1070
1079
|
Finally, `of` can accept a collection of models and/or state objects. Mix and match as needed to ensure a DRY, readible root.
|
|
@@ -1191,7 +1200,7 @@ export class FooBar extends Model {
|
|
|
1191
1200
|
```
|
|
1192
1201
|
|
|
1193
1202
|
<br/>
|
|
1194
|
-
<h2 id="concept-consumer-hooks">Hooks
|
|
1203
|
+
<h2 id="concept-consumer-hooks">Method Hooks</h2>
|
|
1195
1204
|
|
|
1196
1205
|
Models make fetching a state super easy. On every model, there are defined three `useContext` like methods. They find, return and maintain the nearest instance of class they belong to.
|
|
1197
1206
|
|
|
@@ -1296,7 +1305,7 @@ const LazyConsumer = () => {
|
|
|
1296
1305
|
<br />
|
|
1297
1306
|
<h2 id="concept-ambient">Peer Consumers</h2>
|
|
1298
1307
|
|
|
1299
|
-
While context is great for components, controllers can use it too, that of the component where used. This is helped by importing a
|
|
1308
|
+
While context is great for components, controllers can use it too, that of the component where used. This is helped by importing a special function, likewise called `tap`.
|
|
1300
1309
|
> [This is an instruction](#concept-instruction), in-short a factory function. It tells the controller while initializing: the defined property is special, run some logic to set that up.
|
|
1301
1310
|
|
|
1302
1311
|
On your Model, assign a property with the value of `tap(Model)`, passing in the type of state you're interested in.
|
|
@@ -1320,7 +1329,7 @@ class Greet extends Model {
|
|
|
1320
1329
|
}
|
|
1321
1330
|
}
|
|
1322
1331
|
```
|
|
1323
|
-
Create a controller which taps another one. Make sure it's wrapped by proper `Provider`, and controller will fetch that instance from context.
|
|
1332
|
+
Create a controller which taps another one. Make sure it's wrapped by proper `Provider`, and the controller will fetch that instance from context.
|
|
1324
1333
|
```js
|
|
1325
1334
|
import { Provider } from "@expressive/mvc";
|
|
1326
1335
|
|
|
@@ -1409,8 +1418,8 @@ class FooBar extends Model {
|
|
|
1409
1418
|
return this.foo.value + this.bar.value;
|
|
1410
1419
|
}
|
|
1411
1420
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1421
|
+
addFoo = () => this.bar.value = this.value;
|
|
1422
|
+
addBar = () => this.foo.value = this.value;
|
|
1414
1423
|
}
|
|
1415
1424
|
```
|
|
1416
1425
|
|
|
@@ -1418,17 +1427,17 @@ class FooBar extends Model {
|
|
|
1418
1427
|
|
|
1419
1428
|
```js
|
|
1420
1429
|
const FibonacciApp = () => {
|
|
1421
|
-
const { foo, bar, value,
|
|
1430
|
+
const { foo, bar, value, addFoo, addBar } = FooBar.use();
|
|
1422
1431
|
|
|
1423
1432
|
return (
|
|
1424
1433
|
<div>
|
|
1425
1434
|
<div>
|
|
1426
1435
|
Foo + Bar = {value}
|
|
1427
1436
|
</div>
|
|
1428
|
-
<div onClick={
|
|
1437
|
+
<div onClick={addBar}>
|
|
1429
1438
|
Foo's value is ${foo.value}, click to add in bar!
|
|
1430
1439
|
</div>
|
|
1431
|
-
<div onClick={
|
|
1440
|
+
<div onClick={addFoo}>
|
|
1432
1441
|
Bar's value is ${bar.value}, click to add in foo!
|
|
1433
1442
|
</div>
|
|
1434
1443
|
</div>
|
|
@@ -1466,7 +1475,6 @@ class Dependant extends Model {
|
|
|
1466
1475
|
|
|
1467
1476
|
> Here we've learned how Models allow controllers to cooperate and play off of eachother. They allow us to break-up behavior into smaller pieces, building-blocks if you will. With that, we have an easy way to separate concerns, freeing up focus for more added nuance and features.
|
|
1468
1477
|
|
|
1469
|
-
<br/>
|
|
1470
1478
|
<br/>
|
|
1471
1479
|
|
|
1472
1480
|
### :construction: More Docs are on the way! 🏗
|