neo.mjs 6.18.3 → 6.19.0

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.
Files changed (81) hide show
  1. package/README.md +28 -214
  2. package/apps/ServiceWorker.mjs +2 -2
  3. package/apps/colors/view/ViewportController.mjs +7 -3
  4. package/apps/portal/data/blog.json +13 -0
  5. package/apps/portal/view/HeaderToolbar.mjs +2 -2
  6. package/apps/portal/view/Viewport.mjs +4 -2
  7. package/apps/portal/view/ViewportController.mjs +89 -8
  8. package/apps/portal/view/blog/Container.mjs +8 -8
  9. package/apps/portal/view/blog/List.mjs +6 -6
  10. package/apps/portal/view/home/MainContainer.mjs +3 -2
  11. package/apps/portal/view/home/parts/Colors.mjs +2 -2
  12. package/apps/portal/view/home/parts/How.mjs +3 -3
  13. package/apps/portal/view/home/parts/MainNeo.mjs +6 -7
  14. package/apps/portal/view/home/parts/References.mjs +88 -0
  15. package/apps/portal/view/learn/MainContainer.mjs +3 -2
  16. package/apps/portal/view/learn/MainContainerController.mjs +11 -0
  17. package/apps/portal/view/learn/PageContainer.mjs +5 -3
  18. package/apps/portal/view/services/Component.mjs +73 -0
  19. package/apps/website/data/blog.json +13 -0
  20. package/examples/ServiceWorker.mjs +2 -2
  21. package/examples/component/carousel/MainContainer.mjs +42 -33
  22. package/examples/layout/cube/MainContainer.mjs +217 -0
  23. package/examples/layout/cube/app.mjs +6 -0
  24. package/examples/layout/cube/index.html +11 -0
  25. package/examples/layout/cube/neo-config.json +6 -0
  26. package/package.json +6 -6
  27. package/resources/data/deck/learnneo/pages/Earthquakes-01-goals.md +32 -0
  28. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-01-generate-a-workspace.md +47 -0
  29. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-02-generate-the-starter-app.md +150 -0
  30. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-03-debugging.md +136 -0
  31. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-04-fetch-data.md +146 -0
  32. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-05-refactor-the-table.md +146 -0
  33. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-06-use-a-view-model.md +301 -0
  34. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-07-use-the-google-maps-addon.md +175 -0
  35. package/resources/data/deck/learnneo/pages/Earthquakes-Lab-08-events.md +38 -0
  36. package/resources/data/deck/learnneo/pages/Earthquakes.md +8 -8
  37. package/resources/data/deck/learnneo/pages/Glossary.md +0 -0
  38. package/resources/data/deck/learnneo/pages/GuideEvents.md +80 -1
  39. package/resources/data/deck/learnneo/tree.json +2 -1
  40. package/resources/images/apps/portal/neo-references.png +0 -0
  41. package/resources/scss/src/apps/portal/Viewport.scss +18 -0
  42. package/resources/scss/src/apps/portal/blog/Container.scss +7 -7
  43. package/resources/scss/src/apps/portal/blog/List.scss +20 -16
  44. package/resources/scss/src/apps/portal/home/parts/MainNeo.scss +4 -5
  45. package/resources/scss/src/apps/portal/home/parts/References.scss +46 -0
  46. package/resources/scss/src/apps/portal/learn/ContentTreeList.scss +20 -0
  47. package/resources/scss/src/apps/portal/learn/ContentView.scss +4 -0
  48. package/resources/scss/src/apps/portal/learn/MainContainer.scss +1 -1
  49. package/resources/scss/src/apps/portal/learn/PageContainer.scss +22 -16
  50. package/resources/scss/src/apps/portal/services/Component.scss +20 -0
  51. package/resources/scss/src/component/Carousel.scss +21 -0
  52. package/resources/scss/src/examples/layout/cube/MainContainer.scss +7 -0
  53. package/resources/scss/src/layout/Cube.scss +80 -0
  54. package/resources/scss/src/tab/Container.scss +10 -10
  55. package/resources/scss/theme-neo-light/apps/portal/blog/Container.scss +3 -0
  56. package/resources/scss/theme-neo-light/form/field/Search.scss +1 -1
  57. package/resources/scss/theme-neo-light/tooltip/Base.scss +1 -1
  58. package/src/DefaultConfig.mjs +2 -2
  59. package/src/Main.mjs +15 -1
  60. package/src/Neo.mjs +14 -3
  61. package/src/component/Base.mjs +18 -1
  62. package/src/container/Base.mjs +3 -1
  63. package/src/dialog/Base.mjs +1 -2
  64. package/src/layout/Base.mjs +43 -6
  65. package/src/layout/Card.mjs +21 -59
  66. package/src/layout/Cube.mjs +428 -0
  67. package/src/layout/Fit.mjs +9 -38
  68. package/src/layout/Flexbox.mjs +16 -17
  69. package/src/layout/Form.mjs +13 -70
  70. package/src/layout/Grid.mjs +6 -18
  71. package/src/main/mixin/DeltaUpdates.mjs +16 -3
  72. package/src/util/Array.mjs +36 -0
  73. package/src/vdom/Helper.mjs +328 -445
  74. package/src/vdom/VNode.mjs +12 -1
  75. package/test/siesta/siesta.js +16 -1
  76. package/test/siesta/tests/VdomCalendar.mjs +2111 -37
  77. package/test/siesta/tests/VdomHelper.mjs +283 -47
  78. package/test/siesta/tests/vdom/Advanced.mjs +367 -0
  79. package/test/siesta/tests/vdom/layout/Cube.mjs +189 -0
  80. package/test/siesta/tests/vdom/table/Container.mjs +133 -0
  81. package/resources/scss/theme-neo-light/apps/portal/learn/ContentTreeList.scss +0 -23
@@ -0,0 +1,301 @@
1
+ <details>
2
+ <summary>Look at network traffic</summary>
3
+
4
+ Before making any changes, open devtools in the Network tab and refresh _earthquakes_. You'll see two
5
+ calls to the web service.
6
+
7
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/EarthquakesTwoTablesTwoCalls.png"></img>
8
+
9
+ </details>
10
+
11
+ <details>
12
+ <summary>Copy the store config to the view model</summary>
13
+
14
+ View models have two key configs: `data` and `stores`.
15
+
16
+ - `data` holds name/value pairs where the value can be a simple value, or object references
17
+ - `stores` holds configs of stores
18
+
19
+ Add a `stores` property to the view model config that holds a copy of the store.
20
+
21
+ <pre data-javascript>
22
+ import Base from '../../../node_modules/neo.mjs/src/container/Base.mjs';
23
+ import Controller from './MainViewController.mjs';
24
+ import EarthquakesTable from './earthquakes/Table.mjs';
25
+ import Store from '../../../node_modules/neo.mjs/src/data/Store.mjs';
26
+ import ViewModel from './MainViewModel.mjs';
27
+
28
+ class MainView extends Base {
29
+ static config = {
30
+ className: 'Earthquakes.view.MainView',
31
+ ntype: 'earthquakes-main',
32
+
33
+ controller: {module: Controller},
34
+ model: {
35
+ module: ViewModel,
36
+ stores: {
37
+ earthquakes: {
38
+ module: Store,
39
+ model: {
40
+ fields: [{
41
+ name: "humanReadableLocation"
42
+ }, {
43
+ name: "size"
44
+ }, {
45
+ name: "timestamp",
46
+ type: "Date"
47
+ }]
48
+ },
49
+ url: "https://apis.is/earthquake/is",
50
+ responseRoot: "results",
51
+ autoLoad: true
52
+ },
53
+ }
54
+ },
55
+
56
+ layout: {
57
+ ntype: 'vbox', align: 'stretch'
58
+ },
59
+ items: [{
60
+ module: EarthquakesTable,
61
+ store: {
62
+ module: Store,
63
+ model: {
64
+ fields: [{
65
+ name: "humanReadableLocation"
66
+ }, {
67
+ name: "size"
68
+ }, {
69
+ name: "timestamp",
70
+ type: "Date"
71
+ }]
72
+ },
73
+ url: "https://apis.is/earthquake/is",
74
+ responseRoot: "results",
75
+ autoLoad: true
76
+ },
77
+ style: {width: '100%'}
78
+ },{
79
+ module: EarthquakesTable,
80
+ store: {
81
+ module: Store,
82
+ model: {
83
+ fields: [{
84
+ name: "humanReadableLocation"
85
+ }, {
86
+ name: "size"
87
+ }, {
88
+ name: "timestamp",
89
+ type: "Date"
90
+ }]
91
+ },
92
+ url: "https://apis.is/earthquake/is",
93
+ responseRoot: "results",
94
+ autoLoad: true
95
+ },
96
+ style: {width: '100%'}
97
+ }],
98
+ }
99
+ }
100
+
101
+ Neo.setupClass(MainView);
102
+
103
+ export default MainView;
104
+
105
+ </pre>
106
+
107
+ In the `stores` config we named the store _earthquakes_. We could have named it anything, like _foo_
108
+ or _myStore_. We're calling it _earthquakes_ simply because that seems like a good descriptive name
109
+ of the data the store holds.
110
+
111
+ At this point we have _three_ identical store configs! Save and refresh, and look at network traffic &mdash; you
112
+ should see three calls.
113
+
114
+ Having an instance in the view model means we can share it. It can be shared anywhere in the containment
115
+ hierarchy. The app doesn't have much of a hierarchy: it's just the main view and two child components (the two
116
+ tables). But now that the store is in the parent's view model we can share it.
117
+
118
+ </details>
119
+
120
+ <details>
121
+ <summary>Use the shared store</summary>
122
+
123
+ The way to bind an instance to a view model property is with the `bind` config. For example
124
+
125
+ bind: {
126
+ store: 'stores.earthquakes'
127
+ }
128
+
129
+ binds a `store` property to a store called `foo`. The code is saying _in the future, when the value
130
+ of "stores.earthquakes" changes, assign it to this object's "store" property_. In this case, `stores.earthquakes`
131
+ starts out undefined, then at runtime within a few milliseconds as the view model is processed, the configured
132
+ store is created and a reference is assigned to `stores.earthquakes`. That wakes the binding up, and the
133
+ value is assigned to the table's `store` property.
134
+
135
+ Replace each table's `store` config with the binding.
136
+
137
+ <pre data-javascript>
138
+
139
+ import Base from '../../../node_modules/neo.mjs/src/container/Base.mjs';
140
+ import Controller from './MainViewController.mjs';
141
+ import EarthquakesTable from './earthquakes/Table.mjs';
142
+ import Store from '../../../node_modules/neo.mjs/src/data/Store.mjs';
143
+ import ViewModel from './MainViewModel.mjs';
144
+
145
+ class MainView extends Base {
146
+ static config = {
147
+ className: 'Earthquakes.view.MainView',
148
+ ntype: 'earthquakes-main',
149
+ controller: {module: Controller},
150
+ model: {
151
+ module: ViewModel,
152
+ stores: {
153
+ earthquakes: {
154
+ module: Store,
155
+ model: {
156
+ fields: [{
157
+ name: "humanReadableLocation"
158
+ }, {
159
+ name: "size"
160
+ }, {
161
+ name: "timestamp",
162
+ type: "Date"
163
+ }]
164
+ },
165
+ url: "https://apis.is/earthquake/is",
166
+ responseRoot: "results",
167
+ autoLoad: true
168
+ },
169
+ }
170
+ },
171
+
172
+ layout: { ntype: 'vbox', align: 'stretch' },
173
+ items: [{
174
+ module: EarthquakesTable,
175
+ bind: {
176
+ store: 'stores.earthquakes'
177
+ },
178
+ style: {width: '100%'}
179
+ },{
180
+ module: EarthquakesTable,
181
+ bind: {
182
+ store: 'stores.earthquakes'
183
+ },
184
+ style: {width: '100%'}
185
+ }],
186
+ }
187
+ }
188
+
189
+ Neo.setupClass(MainView);
190
+
191
+ export default MainView;
192
+ </pre>
193
+
194
+ Save, refresh, and look at network traffic: you'll see a _single_ call to the web service.
195
+
196
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/EarthquakesTwoTablesOneCall.png"></img>
197
+
198
+ You can further prove we're using a shared instance by running these statements in the console.
199
+
200
+ <pre data-javascript>
201
+ a = Neo.findFirst({ntype:'earthquakes-main'}).model.stores.earthquakes;
202
+ b = Neo.find({ntype:'earthquakes-table'})[0].store;
203
+ c = Neo.find({ntype:'earthquakes-table'})[1].store;
204
+
205
+ (a === b) && (a === c) && (b === c) // true
206
+ </pre>
207
+
208
+ </details>
209
+
210
+ <details>
211
+ <summary>Use the view model class</summary>
212
+
213
+ We configured the view model in-line, in the `model` config at the top of `MainView`. But the starter app
214
+ has a `MainViewModel` class. In theory, if you have a trivial view model you could configure it in-line. But
215
+ in general you want to keep that code separate by coding it in a separate class. This is what we did for the
216
+ table config &mdash; we started by coding it in-line in the main view, then we refactored it into its own
217
+ class. The result was a simpler and more abstract main view. We want to do the same for the view model.
218
+
219
+ Since the starter app already provides `MainViewModel`, all we need to do is copy the `stores` property.
220
+
221
+ Here's the resulting code you should place into `MainViewModel.mjs`.
222
+
223
+ <pre data-javascript>
224
+ import Model from '../../../node_modules/neo.mjs/src/model/Component.mjs';
225
+ import Store from '../../../node_modules/neo.mjs/src/data/Store.mjs';
226
+
227
+ class MainViewModel extends Model {
228
+ static config = {
229
+ className: 'Earthquakes.view.MainViewModel',
230
+
231
+ data: {},
232
+ stores: {
233
+ earthquakes: {
234
+ module: Store,
235
+ model: {
236
+ fields: [{
237
+ name: "humanReadableLocation"
238
+ }, {
239
+ name: "size"
240
+ }, {
241
+ name: "timestamp",
242
+ type: "Date"
243
+ }]
244
+ },
245
+ url: "https://apis.is/earthquake/is",
246
+ responseRoot: "results",
247
+ autoLoad: true
248
+ },
249
+ }
250
+ }
251
+ }
252
+
253
+ Neo.setupClass(MainViewModel);
254
+
255
+ export default MainViewModel;
256
+ </pre>
257
+
258
+ And you need to remove the `stores` config from the main view as follows.
259
+
260
+ <pre data-javascript>
261
+ import Container from '../../../node_modules/neo.mjs/src/container/Base.mjs';
262
+ import Controller from './MainViewController.mjs';
263
+ import EarthquakesTable from './earthquakes/Table.mjs';
264
+ import ViewModel from './MainViewModel.mjs';
265
+
266
+ class MainView extends Container {
267
+ static config = {
268
+ className: 'Earthquakes.view.MainView',
269
+ ntype: 'earthquakes-main',
270
+ controller: {module: Controller},
271
+ model: {
272
+ module: ViewModel
273
+ },
274
+
275
+ layout: { ntype: 'vbox', align: 'stretch' },
276
+ items: [{
277
+ module: EarthquakesTable,
278
+ bind: {
279
+ store: 'stores.earthquakes'
280
+ },
281
+ style: {width: '100%'}
282
+ },{
283
+ module: EarthquakesTable,
284
+ bind: {
285
+ store: 'stores.earthquakes'
286
+ },
287
+ style: {width: '100%'}
288
+ }]
289
+ }
290
+ }
291
+
292
+ Neo.setupClass(MainView);
293
+
294
+ export default MainView;
295
+ </pre>
296
+
297
+ The refactorings to have separate table and view model classes means the code is more modular, more reusable,
298
+ and each class is simpler than using complex source files that try to configure every detail.
299
+
300
+ </details>
301
+
@@ -0,0 +1,175 @@
1
+ <details>
2
+ <summary>Get the code for the custom add-on</summary>
3
+ At the time this tutorial was written, the Neo.mjs Google Maps addon was about to be updated to
4
+ accomodate Google's "AdvancedMarker" class. Until that's ready, we're going to use a modified version of the add-on.
5
+
6
+ Download and unzip this file, and copy the two source files to the corresponding subdirectories in
7
+ your workspace's `src` directory. Note that `src` already contains some files, so don't replace the whole
8
+ directory, but instead, move the files to their individual locations.
9
+
10
+ <a href="https://s3.amazonaws.com/mjs.neo.learning.images/zip/src.zip">src.zip</a>
11
+
12
+ <img src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/CopyGoogleMapsFiles.png" width="30%%"></img>
13
+ </details>
14
+
15
+ <details>
16
+ <summary>Specify the main-thread add-on</summary>
17
+
18
+ Edit `apps/earthquakes/neo-config.json` and add entries for the Google Maps add-on and the map key.
19
+
20
+ <pre data-javascript>
21
+ {
22
+ "appPath": "../../apps/earthquakes/app.mjs",
23
+ "basePath": "../../",
24
+ "environment": "development",
25
+ "mainPath": "../node_modules/neo.mjs/src/Main.mjs",
26
+ "mainThreadAddons": [
27
+ "DragDrop",
28
+ "WS/GoogleMaps",
29
+ "Stylesheet"
30
+ ],
31
+ "googleMapsApiKey": "AIzaSyD4Y2xvl9mGT8HiVvQiZluT5gah3OIveCE",
32
+ "themes" : ["neo-theme-neo-light"],
33
+ "workerBasePath": "../../node_modules/neo.mjs/src/worker/"
34
+ }
35
+ </pre>
36
+
37
+ It's unusual to need to edit `neo-config.json`. The app theme is specified there, and so are main thread add-ons.
38
+ In our case, we're adding `WS/GoogleMaps` which in turn requires that we specify the map key. The `WS/`
39
+ prefix tells Neo.mjs that the add-on is in our workspace, rather than an add-on provided by Neo.mjs.
40
+
41
+ Save and refresh, and you'll see a console log emanating from the plugin.
42
+
43
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/GoogleMapsLoaded.png"></img>
44
+
45
+ </details>
46
+
47
+ <details>
48
+ <summary>Add the required fields to the records</summary>
49
+
50
+ The Google Maps component has a `markerStore` property, which is a reference to a store whose records have
51
+ the properties `title` and `location`, where `location` is of the form `{lat: 0, lng: 0}`. The `fields:[]`
52
+ lets us implement those via two properties:
53
+
54
+ - `mapping` &mdash; the path to a feed property holding the value
55
+ - `calculate` &mdash; a function that returns a value
56
+
57
+ Edit `apps/earthquakes/view/MainViewModel.mjs` and modify `fields` as follows.
58
+
59
+ <pre data-javascript>
60
+ fields: [{
61
+ name: "humanReadableLocation",
62
+ }, {
63
+ name: "size",
64
+ }, {
65
+ name: "timestamp",
66
+ type: "Date",
67
+ }, {
68
+ name: 'title',
69
+ mapping: "humanReadableLocation"
70
+ }, {
71
+ name: "position",
72
+ calculate: (data, field, item)=>({lat: item.latitude, lng: item.longitude})
73
+ }],
74
+ </pre>
75
+
76
+ As you can see, _title_ is mapped to the existing feed value _humanReadableLocation_, and _position_ is
77
+ calculated by returning an object with _lat_ and _lng_ set to the corresponding values from the feed.
78
+
79
+ Save and refresh _earthquakes_. You can use the debugger to inspect the store via _Shift-Ctrl-right-click_ and
80
+ putting the main view into a global variable. Then run
81
+
82
+ temp1.getModel().stores.earthquakes.items
83
+
84
+ Look at one of the items and you should see that _title_ and _location_ are in each record.
85
+
86
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/StoreHasTitleAndLocation.png"></img>
87
+
88
+ </details>
89
+
90
+ <details>
91
+ <summary>Use the Google Map Component</summary>
92
+
93
+ We're going to replace the top table with a Google Map. To do that we need to import the Google Maps component
94
+ and show it implace of the top table. The map should be centered on Iceland. To wit
95
+
96
+ <pre>
97
+ {
98
+ module: GoogleMapsComponent,
99
+ flex: 1,
100
+ center: {
101
+ lat: 64.8014187,
102
+ lng: -18.3096357
103
+ },
104
+ zoom: 6
105
+ }
106
+ </pre>
107
+
108
+ If we replace the top table with the map, `view/MainView.mjs` ends up with this content.
109
+
110
+ <pre data-javascript>
111
+
112
+ import Container from '../container/Base.mjs';
113
+ import Controller from './MainViewController.mjs';
114
+ import EarthquakesTable from './earthquakes/Table.mjs';
115
+ import GoogleMapsComponent from '../component/wrapper/GoogleMaps.mjs';
116
+ import ViewModel from './MainViewModel.mjs';
117
+
118
+ class MainView extends Container {
119
+ static config = {
120
+ className: 'Earthquakes.view.MainView',
121
+ ntype: 'earthquakes-main',
122
+ controller: {module: Controller},
123
+ model: {
124
+ module: ViewModel
125
+ },
126
+
127
+ layout: { ntype: 'vbox', align: 'stretch' },
128
+ items: [{
129
+ module: GoogleMapsComponent,
130
+ flex: 1,
131
+ center: {
132
+ lat: 64.8014187,
133
+ lng: -18.3096357
134
+ },
135
+ zoom: 6
136
+ },{
137
+ module: EarthquakesTable,
138
+ bind: {
139
+ store: 'stores.earthquakes'
140
+ },
141
+ style: {width: '100%'},
142
+ wrapperStyle: {
143
+ height: 'auto' // Because neo-table-wrapper sets height:'100%', which it probably shouldn't
144
+ }
145
+ }],
146
+ }
147
+ }
148
+
149
+ Neo.setupClass(MainView);
150
+
151
+ export default MainView;
152
+ </pre>
153
+
154
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/CenteredMap.png"></img>
155
+
156
+
157
+ </details>
158
+
159
+ <details>
160
+ <summary>Show the markers</summary>
161
+
162
+ The markers are shown by setting up the marker store, which is a regular store whose records must contain
163
+ _location_ and _title_. We assign the store using a `bind`, just like we did with the tables.
164
+
165
+ Add this config to the map.
166
+
167
+ <pre data-javascript>
168
+ bind: {
169
+ markerStore: 'stores.earthquakes'
170
+ },
171
+ </pre>
172
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/InitialMapWithMarkers.png"></img>
173
+
174
+ </details>
175
+
@@ -0,0 +1,38 @@
1
+ In this lab you'll set up an event handler for the table and map.
2
+
3
+ <details>
4
+ <summary>Add a listener to the table</summary>
5
+
6
+ Tables fire a select event, passing an object that contains a reference to the corresponding row.
7
+
8
+ Add this table config:
9
+
10
+ listeners: {
11
+ select: (data) => console.log(data.record)
12
+ }
13
+
14
+ Save and refresh, then click on a table row. If you look at the debugger console you'll see the record being logged.
15
+
16
+ Just for fun, expand the logged value and look for the size property. If you recall, that's a value from the feed, and one of the things we configured in the store's fields:[].
17
+
18
+ In the console, click on the ellipses by size and enter a new value, like 2.5. (Don't enter a larger value, or you may destroy that part of Iceland.)
19
+
20
+ <img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/LogTableClick.png"></img>
21
+
22
+ After changing the value you should immediately see it reflected in the table row.
23
+
24
+ </details>
25
+
26
+ <details>
27
+ <summary>Add a listener to a map event
28
+ </summary>
29
+
30
+ Now add a `markerClick` listener to the Google Map.
31
+
32
+ listeners: {
33
+ markerClick: data => console.log(data.data.record)
34
+ },
35
+
36
+ Save, refresh, and confirm that you see the value logged when you click on a map marker.
37
+
38
+ </details>
@@ -532,7 +532,7 @@ class MainView extends Base {
532
532
  type: "Date",
533
533
  }],
534
534
  },
535
- url: "https://apis.is/earthquake/is",
535
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
536
536
  responseRoot: "results",
537
537
  autoLoad: true,
538
538
  },
@@ -604,7 +604,7 @@ Here's the config for the store.
604
604
  type: "Date",
605
605
  }],
606
606
  },
607
- url: "https://apis.is/earthquake/is",
607
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
608
608
  responseRoot: "results",
609
609
  autoLoad: true,
610
610
  }
@@ -773,7 +773,7 @@ static config = {
773
773
  type: "Date",
774
774
  }],
775
775
  },
776
- url: "https://apis.is/earthquake/is",
776
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
777
777
  responseRoot: "results",
778
778
  autoLoad: true,
779
779
  },
@@ -896,7 +896,7 @@ class MainView extends Base {
896
896
  type: "Date"
897
897
  }]
898
898
  },
899
- url: "https://apis.is/earthquake/is",
899
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
900
900
  responseRoot: "results",
901
901
  autoLoad: true
902
902
  },
@@ -920,7 +920,7 @@ class MainView extends Base {
920
920
  type: "Date"
921
921
  }]
922
922
  },
923
- url: "https://apis.is/earthquake/is",
923
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
924
924
  responseRoot: "results",
925
925
  autoLoad: true
926
926
  },
@@ -939,7 +939,7 @@ class MainView extends Base {
939
939
  type: "Date"
940
940
  }]
941
941
  },
942
- url: "https://apis.is/earthquake/is",
942
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
943
943
  responseRoot: "results",
944
944
  autoLoad: true
945
945
  },
@@ -1012,7 +1012,7 @@ class MainView extends Base {
1012
1012
  type: "Date"
1013
1013
  }]
1014
1014
  },
1015
- url: "https://apis.is/earthquake/is",
1015
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
1016
1016
  responseRoot: "results",
1017
1017
  autoLoad: true
1018
1018
  },
@@ -1092,7 +1092,7 @@ class MainViewModel extends Model {
1092
1092
  type: "Date"
1093
1093
  }]
1094
1094
  },
1095
- url: "https://apis.is/earthquake/is",
1095
+ url: "https://nameless-tundra-27404.herokuapp.com/go/?fn=earthquakes",
1096
1096
  responseRoot: "results",
1097
1097
  autoLoad: true
1098
1098
  },
File without changes
@@ -231,6 +231,7 @@ relating to changes to the store. The event handler if a function run when the e
231
231
  A binding detects a changed view model value, and assigns it to a property.
232
232
 
233
233
 
234
+
234
235
  ### When to use an event
235
236
 
236
237
  Events and event handlers are used when you need to run non-trivial logic in response to the event. For example, you
@@ -245,9 +246,87 @@ like a button click or component focus.
245
246
  A binding is a way to keep properties in sync with values in the view model hierarchy. For example, button text,
246
247
  field values, or store properties, can simultaneously reflect the same view model property. That's pretty handy,
247
248
  but keep in mind that a class can define a property as a _lifecycle property_. That means that updating a property
248
- can may result in complex logic being triggered. Furthermore, a _two way binding_ meansx
249
+ can may result in complex logic being triggered. Furthermore, a _two way binding_ means a change to a property
250
+ will automatically be reflected in the view model.
251
+
252
+ ### A simple comparison
253
+
254
+ To contrast syntax, and to illustrate the simplicity of a binding, let's look at two exmaples of updating a component
255
+ to reflect the value of a text field. THe first example uses events; the second uses bindings.
256
+
257
+ <pre data-neo>
258
+ import Container from '../container/Base.mjs';
259
+ import TextField from '../form/field/Text.mjs';
260
+ import Component from '../component/Base.mjs';
249
261
 
262
+ class MainView extends Container {
263
+ static config = {
264
+ className: 'Example.view.MainView',
265
+ layout : {ntype:'vbox', align:'start'},
250
266
 
267
+ items: [{
268
+ module : TextField,
269
+ labelText: 'Text',
270
+ reference: 'textFieldOne',
271
+ value: 'Hello',
272
+ listeners: {
273
+ change : 'up.onTextChange'
274
+ }
275
+ }, {
276
+ module : TextField,
277
+ labelText: 'Text',
278
+ reference: 'textFieldTwo',
279
+ value: 'world!',
280
+ listeners: {
281
+ change : 'up.onTextChange'
282
+ }
283
+ }, {
284
+ module: Component,
285
+ reference: 'foo',
286
+ }]
287
+ }
288
+ onTextChange(data){
289
+ this.getReference('foo').html = `${this.getReference('textFieldOne').value} ${this.getReference('textFieldTwo').value}`
290
+ }
291
+ }
292
+ Neo.setupClass(MainView);
293
+ </pre>
294
+
295
+ <pre data-neo>
296
+ import Container from '../container/Base.mjs';
297
+ import TextField from '../form/field/Text.mjs';
298
+ import Component from '../component/Base.mjs';
299
+
300
+ class MainView extends Container {
301
+ static config = {
302
+ className: 'Example.view.MainView',
303
+ model: {
304
+ data: {
305
+ foo: 'Hello',
306
+ bar: 'world!'
307
+ }
308
+ },
309
+ layout : {ntype:'vbox', align:'start'},
310
+
311
+ items: [{
312
+ module : TextField,
313
+ labelText: 'Text',
314
+ bind: {value: {twoWay: true, value: data => data.foo}}
315
+ }, {
316
+ module : TextField,
317
+ labelText: 'Text',
318
+ bind: {value: {twoWay: true, value: data => data.bar}}
319
+ }, {
320
+ module: Component,
321
+ bind: {html: data => `${data.foo} ${data.bar}`}
322
+ }]
323
+ }
324
+ onTextChange(data){
325
+ this.getReference('foo').html = data.value;
326
+ }
327
+ }
328
+ Neo.setupClass(MainView);
329
+ </pre>
251
330
 
252
331
  ##
253
332