neo.mjs 6.13.0 → 6.14.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.
- package/apps/covid/neo-config.json +1 -1
- package/apps/portal/view/ViewportController.mjs +5 -4
- package/apps/portal/view/home/ContentBox.mjs +80 -0
- package/apps/portal/view/home/MainContainer.mjs +51 -15
- package/apps/portal/view/learn/ContentTreeList.mjs +10 -3
- package/apps/portal/view/learn/MainContainerController.mjs +37 -5
- package/apps/portal/view/learn/MainContainerModel.mjs +51 -7
- package/apps/portal/view/learn/PageContainer.mjs +21 -9
- package/apps/sharedcovid/neo-config.json +1 -1
- package/examples/form/field/select/MainContainer.mjs +1 -1
- package/package.json +3 -3
- package/resources/data/deck/learnneo/pages/2023-10-14T19-25-08-153Z.md +16 -1
- package/resources/data/deck/learnneo/pages/ComponentsAndContainers.md +180 -0
- package/resources/data/deck/learnneo/pages/Config.md +11 -4
- package/resources/data/deck/learnneo/pages/DescribingTheUI.md +6 -0
- package/resources/data/deck/learnneo/pages/Earthquakes.md +36 -9
- package/resources/data/deck/learnneo/pages/Events.md +55 -43
- package/resources/data/deck/learnneo/pages/GuideEvents.md +9 -8
- package/resources/data/deck/learnneo/pages/References.md +14 -7
- package/resources/data/deck/learnneo/pages/TodoList.md +241 -0
- package/resources/data/deck/learnneo/pages/WhyNeo-Quick.md +6 -11
- package/resources/data/deck/learnneo/tree.json +2 -0
- package/resources/scss/src/apps/portal/home/ContentBox.scss +26 -0
- package/resources/scss/src/apps/portal/home/MainContainer.scss +4 -12
- package/resources/scss/src/apps/portal/learn/MainContainer.scss +0 -7
- package/resources/scss/src/apps/portal/learn/PageContainer.scss +35 -0
- package/resources/scss/src/apps/portal/learn/PageSectionsPanel.scss +8 -0
- package/src/component/Base.mjs +16 -1
- package/src/controller/Application.mjs +12 -1
- package/src/controller/Base.mjs +15 -4
- package/src/controller/Component.mjs +5 -1
- package/src/core/Observable.mjs +62 -22
- package/src/form/field/Base.mjs +21 -9
- package/src/form/field/Select.mjs +166 -117
- package/src/form/field/Text.mjs +7 -10
- package/src/main/DomEvents.mjs +2 -1
- package/src/main/addon/MonacoEditor.mjs +2 -2
- package/src/main/addon/Navigator.mjs +27 -24
- package/src/selection/ListModel.mjs +13 -14
- package/src/selection/Model.mjs +10 -10
- package/src/util/HashHistory.mjs +1 -0
- package/src/worker/App.mjs +5 -1
- package/src/worker/Manager.mjs +14 -7
- package/test/components/files/form/field/Select.mjs +6 -4
@@ -0,0 +1,180 @@
|
|
1
|
+
## Introduction
|
2
|
+
|
3
|
+
Neo.mjs views are made up of components and containers.
|
4
|
+
|
5
|
+
A component is a visual widget, such as a button, label, or text field. A container is a visual
|
6
|
+
collection of components.
|
7
|
+
|
8
|
+
`Neo.component.Base` is the base class for all components. It introduces some common features, such as
|
9
|
+
event handling, binding, and some life-cycle methods.
|
10
|
+
|
11
|
+
`Neo.container.Base` is the base class for all containers. Containers are also components.
|
12
|
+
|
13
|
+
## Neo.container.Base
|
14
|
+
|
15
|
+
Containers are commonly used, although there are many specialized sub-classes, such as panels and toolbars.
|
16
|
+
|
17
|
+
|
18
|
+
Containers have two key properties:
|
19
|
+
|
20
|
+
- `items`, which are the components within the container, and
|
21
|
+
- `layout`, which describes how the items are arranged
|
22
|
+
|
23
|
+
## Neo.component.Base
|
24
|
+
|
25
|
+
The component base class introduces common component features, but is rarely used itself because it's so
|
26
|
+
primitive. Components introduce various properties, such as `width`, `height`, `cls` (to specify CSS classes for the component).
|
27
|
+
|
28
|
+
Here's a container, with one child item.
|
29
|
+
|
30
|
+
<pre data-neo>
|
31
|
+
import Container from '../../../../src/container/Base.mjs';
|
32
|
+
|
33
|
+
class MainView extends Container {
|
34
|
+
static config = {
|
35
|
+
className: 'Example.view.MainView',
|
36
|
+
layout : {ntype:'vbox', align:'start'},
|
37
|
+
items : [{
|
38
|
+
ntype : 'component', // Or module:Component
|
39
|
+
style : {border: 'thin solid red;'}, // Styling is usually done via "cls"
|
40
|
+
height: 100,
|
41
|
+
width : 200
|
42
|
+
}]
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
Neo.setupClass(MainView);
|
47
|
+
</pre>
|
48
|
+
|
49
|
+
Components also have an `html`. The `html` property is rarely used, and goes against the abstract philosophy of Neo.mjs, but
|
50
|
+
sometimes it's handy as a placeholder as you stub out views.
|
51
|
+
|
52
|
+
<pre data-neo>
|
53
|
+
import Container from '../../../../src/container/Base.mjs';
|
54
|
+
|
55
|
+
class MainView extends Container {
|
56
|
+
static config = {
|
57
|
+
className: 'Example.view.MainView',
|
58
|
+
layout : {ntype:'vbox', align:'start'},
|
59
|
+
items : [{
|
60
|
+
ntype : 'component', // Or module:Component
|
61
|
+
style : {border: 'thin solid red;'}, // Styling is usually done via "cls"
|
62
|
+
html : 'This is a placeholder for a more sophisticated component we\'ll add later.',
|
63
|
+
height: 100,
|
64
|
+
width : 200
|
65
|
+
}]
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
Neo.setupClass(MainView);
|
70
|
+
</pre>
|
71
|
+
|
72
|
+
|
73
|
+
## Layout
|
74
|
+
|
75
|
+
The `layout` config specifies how components are arranged within a container. Here are examples of
|
76
|
+
some commonly-used layouts.
|
77
|
+
|
78
|
+
### Fit layout
|
79
|
+
|
80
|
+
Fix is used when there's a single child. The component is sized to fit the container.
|
81
|
+
|
82
|
+
<pre data-neo>
|
83
|
+
import Container from '../../../../src/container/Base.mjs';
|
84
|
+
|
85
|
+
class MainView extends Container {
|
86
|
+
static config = {
|
87
|
+
className: 'Example.view.MainView',
|
88
|
+
layout : 'fit', // If no configs are needed, simply use the ntype of the layout
|
89
|
+
items : [{
|
90
|
+
ntype: 'component',
|
91
|
+
style: {backgroundColor: 'lightgreen'}, // The camel-cased property converts to the hyphenated css style
|
92
|
+
}]
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
Neo.setupClass(MainView);
|
97
|
+
</pre>
|
98
|
+
|
99
|
+
### Vbox and hbox
|
100
|
+
|
101
|
+
Items are arranged vertically or horizontally. On-axis and off-axis alignment can be specified.
|
102
|
+
|
103
|
+
<pre data-neo>
|
104
|
+
import Button from '../../../../src/button/Base.mjs';
|
105
|
+
import Container from '../../../../src/container/Base.mjs';
|
106
|
+
|
107
|
+
class MainView extends Container {
|
108
|
+
static config = {
|
109
|
+
className: 'Example.view.MainView',
|
110
|
+
layout : {ntype:'vbox', align:'start'}, // Change the ntype to 'hbox'
|
111
|
+
items : [{
|
112
|
+
module : Button,
|
113
|
+
iconCls: 'fa fa-home',
|
114
|
+
text : 'Home'
|
115
|
+
}, {
|
116
|
+
module : Button,
|
117
|
+
iconCls: 'fa fa-star',
|
118
|
+
text : 'Star'
|
119
|
+
}]
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
Neo.setupClass(MainView);
|
124
|
+
</pre>
|
125
|
+
|
126
|
+
### Card
|
127
|
+
|
128
|
+
Having multiple child items, one of which is visible.
|
129
|
+
|
130
|
+
<pre data-neo>
|
131
|
+
import Button from '../../../../src/button/Base.mjs';
|
132
|
+
import Base from '../../../../src/container/Base.mjs';
|
133
|
+
|
134
|
+
class MainView extends Base {
|
135
|
+
static config = {
|
136
|
+
className: 'Example.view.MainView',
|
137
|
+
layout : 'vbox',
|
138
|
+
items : [{
|
139
|
+
ntype: 'toolbar',
|
140
|
+
dock : 'top',
|
141
|
+
items: [{
|
142
|
+
ntype: 'button',
|
143
|
+
text: 'Click me to cycle through the cards',
|
144
|
+
ui: 'ghost',
|
145
|
+
iconCls: 'fa fa-chevron-right',
|
146
|
+
iconPosition: 'right',
|
147
|
+
handler: data => {
|
148
|
+
const container = data.component.up('container').getReference('cardContainer');
|
149
|
+
container.layout.activeIndex = (container.layout.activeIndex +1) % container.items.length;
|
150
|
+
}
|
151
|
+
}]
|
152
|
+
}, {
|
153
|
+
ntype: 'container',
|
154
|
+
reference: 'cardContainer',
|
155
|
+
layout: 'card',
|
156
|
+
flex: 1,
|
157
|
+
items: [{
|
158
|
+
ntype : 'component',
|
159
|
+
style: {backgroundColor: 'lightsalmon'}, // The camel-cased property converts to the hyphated css style
|
160
|
+
}, {
|
161
|
+
ntype : 'component',
|
162
|
+
style: {backgroundColor: 'darkseagreen'} // https://drafts.csswg.org/css-color/#named-colors
|
163
|
+
}, {
|
164
|
+
ntype : 'component',
|
165
|
+
style: {backgroundColor: 'cornflowerblue'} // Who came up with these names?
|
166
|
+
}]
|
167
|
+
}]
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
Neo.setupClass(MainView);
|
172
|
+
</pre>
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
|
177
|
+
## Reusing containers
|
178
|
+
|
179
|
+
|
180
|
+
## Lifecycle methods
|
@@ -1,9 +1,13 @@
|
|
1
|
+
## Introduction
|
2
|
+
|
1
3
|
As you've probably noticed, Neo.mjs classes have a `static config` property.
|
2
4
|
|
3
5
|
The config describes properties you can specify as you create an instance of the class.
|
4
6
|
Any config in the class, or its ancestors, can be specified.
|
5
7
|
|
6
|
-
|
8
|
+
## Simple properties and lifecycle properties
|
9
|
+
|
10
|
+
In addition, Neo.mjs uses that information to set up property lifecycle
|
7
11
|
methods.
|
8
12
|
|
9
13
|
Here's an example of a new component class `Simple` with three config properties:
|
@@ -12,6 +16,9 @@ Here's an example of a new component class `Simple` with three config properties
|
|
12
16
|
2. `foo` — an instance property
|
13
17
|
2. `bar_` — another instance property
|
14
18
|
|
19
|
+
The `Simple` class introduces syntax. It doesn't have any content, so if you run the code you won't
|
20
|
+
see anything. We'll change that in the next example.
|
21
|
+
|
15
22
|
<pre data-neo>
|
16
23
|
import Component from '../../../../src/component/Base.mjs';
|
17
24
|
import Container from '../../../../src/container/Base.mjs';
|
@@ -46,8 +53,7 @@ class MainView extends Container {
|
|
46
53
|
Neo.setupClass(MainView);
|
47
54
|
</pre>
|
48
55
|
|
49
|
-
|
50
|
-
change that in the next example.
|
56
|
+
## Detecting when a value changes
|
51
57
|
|
52
58
|
Note that the `bar` property is defined with an underscore at the end. That tags the property as
|
53
59
|
a _lifecyle property_. A lifecycle property provides methods that are run as the property is
|
@@ -75,7 +81,6 @@ class Simple extends Component {
|
|
75
81
|
}
|
76
82
|
afterSetBar(value, oldValue){
|
77
83
|
this.html = value;
|
78
|
-
this.fire('barChange', {component: this, value, oldValue});
|
79
84
|
}
|
80
85
|
|
81
86
|
}
|
@@ -103,6 +108,8 @@ This time if you run the code you'll see "hi there" in the view. That's because
|
|
103
108
|
configured with `bar: 'hi there'`, and since that's a lifecycle property the `afterSetBar()` method
|
104
109
|
is run. That method updates the view with the passed value.
|
105
110
|
|
111
|
+
## Firing an event when a value changes
|
112
|
+
|
106
113
|
Typically, the _afterSet_ method is used to update a view or to fire an event.
|
107
114
|
|
108
115
|
Look at this code: `afterSetBar()` fires an event, and the config in the `items[]` is listening to it.
|
@@ -1,3 +1,5 @@
|
|
1
|
+
## Introduction
|
2
|
+
|
1
3
|
A Neo.mjs view is comprised of components and containers. A component is a visual widget, like a button,
|
2
4
|
and a container is a visual collection of components.
|
3
5
|
|
@@ -8,6 +10,8 @@ have a few key configs, including `text` and `iconCls`. The configs are properti
|
|
8
10
|
use to describe the component you're creating> You can also access or set the properties dynamically.
|
9
11
|
|
10
12
|
|
13
|
+
## A view with one component
|
14
|
+
|
11
15
|
<pre data-neo>
|
12
16
|
import Button from '../../../../src/button/Base.mjs';
|
13
17
|
import Container from '../../../../src/container/Base.mjs';
|
@@ -42,6 +46,8 @@ Containers also have a `layout` property, which describes how the items are arra
|
|
42
46
|
|
43
47
|
Let's put a second button in the container.
|
44
48
|
|
49
|
+
## A view with two components
|
50
|
+
|
45
51
|
<pre data-neo>
|
46
52
|
import Button from '../../../../src/button/Base.mjs';
|
47
53
|
import Container from '../../../../src/container/Base.mjs';
|
@@ -120,7 +120,7 @@ As you can see, `MicroLoader.mjs` runs `Main.mjs`, which in turn spawns the thre
|
|
120
120
|
- `neomjs-app-worker` is where app logic is run
|
121
121
|
|
122
122
|
Neo.mjs apps run in multiple webworkers, and each webworker is run in a separate parallel thread.
|
123
|
-
Parallel processing — along wih the efficient way the
|
123
|
+
Parallel processing — along wih the efficient way the vdom worker applies delta updates — is why Neo.mjs applications run so fast.
|
124
124
|
|
125
125
|
##Commonly-used Scripts
|
126
126
|
|
@@ -294,7 +294,7 @@ layout: {
|
|
294
294
|
|
295
295
|
## Debugging
|
296
296
|
|
297
|
-
At startup a Neo.mjs application launches three Web Workers:
|
297
|
+
At startup, a Neo.mjs application launches three Web Workers:
|
298
298
|
|
299
299
|
- neomjs-app-worker
|
300
300
|
- neomjs-data-worker
|
@@ -1153,7 +1153,7 @@ and each class is simpler than using complex source files that try to configure
|
|
1153
1153
|
|
1154
1154
|
<!-- /lab -->
|
1155
1155
|
|
1156
|
-
|
1156
|
+
## Google Maps Add-on
|
1157
1157
|
|
1158
1158
|
Neo.mjs has a Google Map component. This component is a little different than a button or table,
|
1159
1159
|
becauase it's implemented as a _main thread add-on_.
|
@@ -1169,14 +1169,10 @@ to also provide a wrapper class so it can be used like any other component withi
|
|
1169
1169
|
Maps is implemented: there's a main-thread add-on and a corresponding Neo.mjs component. The add-on is
|
1170
1170
|
specified in _neo-config.json_, and the component is imported and used like any other component.
|
1171
1171
|
|
1172
|
-
Ultimately, normal components are responsible for specifying how
|
1173
|
-
they're rendered (which is usually handled by Neo.mjs).
|
1174
|
-
|
1175
1172
|
How do you specify which main-thread add-ons you want? If you recall the script you used to create the starter
|
1176
1173
|
app, it has a step that asks what add-ons you want. That results in populating the `mainThreadAddons` property
|
1177
|
-
in `neo-config.json`. We didn't choose Google Maps when we ran the script, but we need it
|
1178
|
-
need to edit `neo-config.json
|
1179
|
-
`neo-config.json`.
|
1174
|
+
in `neo-config.json`. We didn't choose Google Maps when we ran the script, but we need it, which means we'll
|
1175
|
+
need to edit `neo-config.json`. Google Maps also requires an API key, which is also configured there.
|
1180
1176
|
|
1181
1177
|
The Google Maps component has a few key configs:
|
1182
1178
|
|
@@ -1193,6 +1189,20 @@ Marker store records are required to have these properties:
|
|
1193
1189
|
|
1194
1190
|
<!-- lab -->
|
1195
1191
|
|
1192
|
+
<details>
|
1193
|
+
<summary>Get the code for the custom add-on</summary>
|
1194
|
+
At the time this tutorial was written, the Neo.mjs Google Maps addon was about to be updated to
|
1195
|
+
accomodate Google's "AdvancedMarker" class. Until that's ready, we're going to use a modified version of the add-on.
|
1196
|
+
|
1197
|
+
Download and unzip this file, and copy the two source files to the corresponding subdirectories in
|
1198
|
+
your workspace's `src` directory. Note that `src` already contains some files, so don't replace the whole
|
1199
|
+
directory, but instead, move the files to their individual locations.
|
1200
|
+
|
1201
|
+
<a href="https://s3.amazonaws.com/mjs.neo.learning.images/zip/src.zip">src.zip</a>
|
1202
|
+
|
1203
|
+
<img src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/CopyGoogleMapsFiles.png" width="30%%"></img>
|
1204
|
+
</details>
|
1205
|
+
|
1196
1206
|
<details>
|
1197
1207
|
<summary>Specify the main-thread add-on</summary>
|
1198
1208
|
|
@@ -1215,6 +1225,10 @@ Edit `apps/earthquakes/neo-config.json` and add entries for the Google Maps add-
|
|
1215
1225
|
}
|
1216
1226
|
</pre>
|
1217
1227
|
|
1228
|
+
It's unusual to need to edit `neo-config.json`. The app theme is specified there, and so are main thread add-ons.
|
1229
|
+
In our case, we're adding `WS/GoogleMaps` which in turn requires that we specify the map key. The `WS/`
|
1230
|
+
prefix tells Neo.mjs that the add-on is in our workspace, rather than an add-on provided by Neo.mjs.
|
1231
|
+
|
1218
1232
|
Save and refresh, and you'll see a console log emanating from the plugin.
|
1219
1233
|
|
1220
1234
|
<img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/earthquakes/GoogleMapsLoaded.png"></img>
|
@@ -1406,3 +1420,16 @@ Save, refresh, and confirm that you see the value logged when you click on a map
|
|
1406
1420
|
|
1407
1421
|
|
1408
1422
|
<!-- /lab -->
|
1423
|
+
|
1424
|
+
## Summary
|
1425
|
+
|
1426
|
+
Congratulations on completing the tutorial!
|
1427
|
+
|
1428
|
+
The goals was to give you hands-on coding a simple app to let you get a feel for syntax
|
1429
|
+
and to introduce some basic Neo.mjs concepts
|
1430
|
+
|
1431
|
+
- Declarative, abstract code
|
1432
|
+
- Class-based coding, and the ability to extend any class, such as `view/earthquakes/Table.mjs`
|
1433
|
+
- View models, to share properties and objects, such as the store shared by the table and map
|
1434
|
+
- Events, specified via `listeners:{}`
|
1435
|
+
- Controllers, to hold event listeners and other procedural logic
|
@@ -1,3 +1,5 @@
|
|
1
|
+
## Introduction
|
2
|
+
|
1
3
|
All components fire events. For example, form fields fire a `change` event, various
|
2
4
|
focus events, and others. Some other types fire events too, such as `Neo.data.Store`,
|
3
5
|
which fires a `load` event after the store is loaded with data.
|
@@ -5,34 +7,35 @@ which fires a `load` event after the store is loaded with data.
|
|
5
7
|
Some terminology related to events is that events are _fired_, and as a result, some
|
6
8
|
event _handler_ — or _listener_ — is run.
|
7
9
|
|
10
|
+
## Listeners
|
11
|
+
|
8
12
|
To specify an event handler, use `listeners: {}`, specifying in as many event/handler
|
9
13
|
pairs as you need.
|
10
14
|
|
11
15
|
The code below shows two text fields, with `listeners` for `change` and `focusEnter`.
|
12
|
-
(The events for any component are
|
16
|
+
(The events for any component are documented in the API docs.)
|
13
17
|
|
14
18
|
<pre data-neo>
|
15
|
-
import
|
19
|
+
import Container from '../../../../src/container/Base.mjs';
|
16
20
|
import TextField from '../../../../src/form/field/Text.mjs';
|
17
|
-
|
21
|
+
|
22
|
+
class MainView extends Container {
|
18
23
|
static config = {
|
19
|
-
className
|
20
|
-
layout: {ntype:'vbox', align:'start'},
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
+
className: 'Example.view.MainView',
|
25
|
+
layout : {ntype:'vbox', align:'start'},
|
26
|
+
|
27
|
+
itemDefaults: {
|
28
|
+
module : TextField,
|
24
29
|
listeners: {
|
25
|
-
change: data => Neo.Main.log({value:data.value}),
|
30
|
+
change : data => Neo.Main.log({value:data.value}),
|
26
31
|
focusEnter: data => Neo.Main.log({value: `Entering ${data.component.labelText}`})
|
27
32
|
}
|
28
33
|
},
|
29
|
-
|
30
|
-
|
31
|
-
labelText
|
32
|
-
|
33
|
-
|
34
|
-
focusEnter: data => Neo.Main.log({value: `Entering ${data.component.labelText}`})
|
35
|
-
}
|
34
|
+
|
35
|
+
items: [{
|
36
|
+
labelText: 'First name'
|
37
|
+
}, {
|
38
|
+
labelText: 'Last name'
|
36
39
|
}]
|
37
40
|
}
|
38
41
|
}
|
@@ -42,37 +45,39 @@ Neo.setupClass(MainView);
|
|
42
45
|
If you run the example, and open the browser's debugger, you'll see the console being logged as you type or give
|
43
46
|
focus to either field.
|
44
47
|
|
48
|
+
## In-line or separated into a controller
|
49
|
+
|
45
50
|
Note that the handlers specify an in-line function. For trivial cases, that might be ok. But normally
|
46
51
|
you'd want better separation of concerns by placing those event handlers in a separate class. Neo.mjs provides
|
47
52
|
that with a _component controller_.
|
48
53
|
|
49
54
|
A `Neo.controller.Component` is a simple class associated with a component class. As a view is created, an
|
50
|
-
instance of its associated
|
55
|
+
instance of its associated controller is automatically created.
|
51
56
|
|
52
57
|
<pre data-neo>
|
53
|
-
import
|
54
|
-
import Controller
|
55
|
-
import TextField from
|
58
|
+
import Container from '../../../../src/container/Base.mjs';
|
59
|
+
import Controller from '../../../../src/controller/Component.mjs';
|
60
|
+
import TextField from '../../../../src/form/field/Text.mjs';
|
56
61
|
|
57
62
|
class MainViewController extends Controller {
|
58
63
|
static config = {
|
59
64
|
className: 'Example.view.MainViewController'
|
60
65
|
}
|
61
|
-
onChange(data){
|
62
|
-
|
66
|
+
onChange(data) {
|
67
|
+
Neo.Main.log({value: data.value})
|
63
68
|
}
|
64
69
|
}
|
65
70
|
Neo.setupClass(MainViewController);
|
66
71
|
|
67
72
|
|
68
|
-
class MainView extends
|
73
|
+
class MainView extends Container {
|
69
74
|
static config = {
|
70
75
|
className : 'Example.view.MainView',
|
71
76
|
controller: MainViewController,
|
72
|
-
layout: {ntype:'vbox', align:'start'},
|
73
|
-
items
|
74
|
-
module: TextField,
|
75
|
-
labelText
|
77
|
+
layout : {ntype:'vbox', align:'start'},
|
78
|
+
items : [{
|
79
|
+
module : TextField,
|
80
|
+
labelText: 'Name',
|
76
81
|
listeners: {
|
77
82
|
change: 'onChange'
|
78
83
|
}
|
@@ -88,12 +93,16 @@ to make it easier to see the source code for every class being used. But in an
|
|
88
93
|
actual applications the controller class would be coded in its own source file — named something
|
89
94
|
like `MainViewController.mjs` — and that would be imported into the view.)
|
90
95
|
|
96
|
+
## Neo.core.Observable
|
97
|
+
|
91
98
|
The ability to fire events and add listeners is provided by `Neo.core.Observable`, which is mixed into
|
92
99
|
classes that need that ability. All components are observable, `Neo.data.Store` is observable, and some
|
93
100
|
others. `Neo.core.Observable` introduces a few methods and properties, such as `listeners`, which
|
94
101
|
is used in the examples above, `on()` for procedurally adding an event listener, and `fire()`, which is
|
95
102
|
how you fire events in the custom classes you create.
|
96
103
|
|
104
|
+
## Firing an event when a value changes
|
105
|
+
|
97
106
|
Here's example illustrating how `fire()` is used. The code defines a `ToggleButton`
|
98
107
|
class, which is just a button with a `checked` property: the button shows a checked or unchecked
|
99
108
|
checkbox depending on the value of `checked`.
|
@@ -103,37 +112,40 @@ We'll discuss that at length later, but in a nutshell, config properties ending
|
|
103
112
|
automatically get lifecycle methods run before the value is assigned, after the value is assigned, and
|
104
113
|
before the value is accessed. We're using the _after_ method to fire a `change` event.
|
105
114
|
|
115
|
+
|
106
116
|
<pre data-neo>
|
107
|
-
import
|
108
|
-
import
|
109
|
-
import TextField from '../../../../src/form/field/Text.mjs';
|
117
|
+
import Button from '../../../../src/button/Base.mjs';
|
118
|
+
import Container from '../../../../src/container/Base.mjs';
|
110
119
|
|
111
120
|
class ToggleButton extends Button {
|
112
121
|
static config = {
|
113
122
|
className: 'Example.view.ToggleButton',
|
114
|
-
checked_: false
|
123
|
+
checked_ : false
|
115
124
|
}
|
116
|
-
afterSetChecked(checked){
|
117
|
-
this.iconCls = checked?'fa fa-square-check':'fa fa-square';
|
118
|
-
|
125
|
+
afterSetChecked(checked) {
|
126
|
+
this.iconCls = checked ? 'fa fa-square-check' : 'fa fa-square';
|
127
|
+
|
128
|
+
// This is where our custom event is being fired
|
129
|
+
this.fire('change', {component: this, checked})
|
119
130
|
}
|
120
|
-
onClick(data){
|
131
|
+
onClick(data) {
|
121
132
|
super.onClick(data);
|
122
|
-
this.checked = !this.checked
|
133
|
+
this.checked = !this.checked
|
123
134
|
}
|
124
135
|
}
|
125
136
|
Neo.setupClass(ToggleButton);
|
126
137
|
|
127
138
|
|
128
|
-
class MainView extends
|
139
|
+
class MainView extends Container {
|
129
140
|
static config = {
|
130
|
-
className
|
131
|
-
layout: {ntype:'vbox', align:'start'},
|
132
|
-
items
|
133
|
-
module: ToggleButton,
|
134
|
-
text: 'Toggle',
|
141
|
+
className: 'Example.view.MainView',
|
142
|
+
layout : {ntype:'vbox', align:'start'},
|
143
|
+
items : [{
|
144
|
+
module : ToggleButton,
|
145
|
+
text : 'Toggle',
|
135
146
|
listeners: {
|
136
|
-
|
147
|
+
// Here, we're listening to the custom event
|
148
|
+
change: data => Neo.Main.log({value: data.checked})
|
137
149
|
}
|
138
150
|
}]
|
139
151
|
}
|
@@ -1,12 +1,13 @@
|
|
1
1
|
[Add content on listeners options here]
|
2
2
|
|
3
|
-
How are events set up?
|
4
|
-
that can be mixed into any class. It maintains
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
How are events set up? The details don't really matter, but in case you're curious:
|
4
|
+
Neo.mjs has a `Neo.core.Observable` class that can be mixed into any class. It maintains
|
5
|
+
a `listeners` object map that's a key-value pair, where the key is the event name, and
|
6
|
+
the value is an array of function references. The first time a listener is added, an
|
7
|
+
entry is added to the map using the event name as the key, and the event handler added
|
8
|
+
as the first item in the associated array. If another listener is added for the same
|
9
|
+
event, a second item is added to the array. If a new event is added, a new entry is
|
10
|
+
added. Etc. When the event is fired, Neo.mjs looks up the map entry for the event name,
|
11
|
+
then runs each function in the array, passing whatever data is specified in the call to `fire()`.
|
11
12
|
|
12
13
|
<img style="width:80%" src="https://s3.amazonaws.com/mjs.neo.learning.images/gettingStarted/events/ObservableInMemory.png"></img>
|
@@ -1,3 +1,5 @@
|
|
1
|
+
## Introduction
|
2
|
+
|
1
3
|
Controllers often need to get references to components in order to update
|
2
4
|
the UI or access component properties.
|
3
5
|
There are two common ways of doing that:
|
@@ -5,11 +7,13 @@ There are two common ways of doing that:
|
|
5
7
|
- Using the component references passed to the event handler
|
6
8
|
- Tagging a component with a `reference` and using `this.getReference()` in the controller
|
7
9
|
|
10
|
+
## References are usually passed to event handlers
|
11
|
+
|
8
12
|
Here's an example with one button. Clicking on the button will disable it.
|
9
13
|
As you can see, the handler uses the component reference pass in via `data.component`.
|
10
14
|
|
11
15
|
<pre data-neo>
|
12
|
-
import Button from '../../../../src/
|
16
|
+
import Button from '../../../../src/button/Base.mjs';
|
13
17
|
import Container from '../../../../src/container/Base.mjs';
|
14
18
|
import Controller from '../../../../src/controller/Component.mjs';
|
15
19
|
|
@@ -39,12 +43,14 @@ class MainView extends Container {
|
|
39
43
|
Neo.setupClass(MainView);
|
40
44
|
</pre>
|
41
45
|
|
46
|
+
## Using getReference()
|
47
|
+
|
42
48
|
But what if we need to get a reference to another component in the view? In that case
|
43
49
|
you tag the component you need with a `reference` config, then use `getReference()` in
|
44
50
|
the controller.
|
45
51
|
|
46
52
|
<pre data-neo>
|
47
|
-
import Button from '../../../../src/
|
53
|
+
import Button from '../../../../src/button/Base.mjs';
|
48
54
|
import Container from '../../../../src/container/Base.mjs';
|
49
55
|
import Controller from '../../../../src/controller/Component.mjs';
|
50
56
|
|
@@ -53,10 +59,10 @@ class MainViewController extends Controller {
|
|
53
59
|
className: 'Example.view.MainViewController'
|
54
60
|
}
|
55
61
|
onDisableButtonClick(data){
|
56
|
-
data.component.disabled = true
|
62
|
+
data.component.disabled = true
|
57
63
|
}
|
58
64
|
onEnableButtonClick(data){
|
59
|
-
this.getReference('myButton').disabled = false
|
65
|
+
this.getReference('myButton').disabled = false
|
60
66
|
}
|
61
67
|
}
|
62
68
|
Neo.setupClass(MainViewController);
|
@@ -82,6 +88,7 @@ class MainView extends Container {
|
|
82
88
|
Neo.setupClass(MainView);
|
83
89
|
</pre>
|
84
90
|
|
91
|
+
## Getting a reference when debugging
|
85
92
|
|
86
93
|
There are other ways of getting references, but these are either non-standard or for debugging.
|
87
94
|
For example, components have an `up()` method, and containers have a `down()` method. These look
|
@@ -98,7 +105,7 @@ The following example gets a reference to the _Learn_ button at the top of this
|
|
98
105
|
Again — that use of `Neo.findFirst()` might be handy when debugging, but it should never be used in app logic.
|
99
106
|
|
100
107
|
<pre data-neo>
|
101
|
-
import Button from '../../../../src/
|
108
|
+
import Button from '../../../../src/button/Base.mjs';
|
102
109
|
import Container from '../../../../src/container/Base.mjs';
|
103
110
|
|
104
111
|
class MainView extends Container {
|
@@ -110,14 +117,14 @@ class MainView extends Container {
|
|
110
117
|
text : 'Change Learn caption',
|
111
118
|
handler: data=>{
|
112
119
|
const component = Neo.findFirst({text:'Learn'});
|
113
|
-
component?.set({text: 'Yikes!', ui: 'primary'})
|
120
|
+
component?.set({text: 'Yikes!', ui: 'primary'})
|
114
121
|
}
|
115
122
|
}, {
|
116
123
|
module : Button,
|
117
124
|
text : 'Restore Learn caption',
|
118
125
|
handler: data=>{
|
119
126
|
const component = Neo.findFirst({text:'Yikes!'});
|
120
|
-
component?.set({text: 'Learn', ui: 'ghost'})
|
127
|
+
component?.set({text: 'Learn', ui: 'ghost'})
|
121
128
|
}
|
122
129
|
}]
|
123
130
|
}
|