neo.mjs 6.37.0 → 6.39.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/ServiceWorker.mjs +2 -2
- package/examples/ServiceWorker.mjs +2 -2
- package/package.json +1 -1
- package/resources/data/deck/learnneo/pages/benefits/FormsEngine.md +265 -0
- package/resources/data/deck/learnneo/pages/benefits/{Multi-Threading.md → MultiThreading.md} +6 -2
- package/resources/data/deck/learnneo/tree.json +2 -1
- package/resources/scss/src/apps/portal/learn/ContentView.scss +2 -1
- package/src/DefaultConfig.mjs +2 -2
package/apps/ServiceWorker.mjs
CHANGED
package/package.json
CHANGED
@@ -0,0 +1,265 @@
|
|
1
|
+
## Forms include a State-Provider
|
2
|
+
|
3
|
+
You do not need to define a state tree on your own.
|
4
|
+
It if sufficient to just use namespaces inside the `name` attribute of each field.
|
5
|
+
|
6
|
+
<pre data-neo>
|
7
|
+
import Button from '../button/Base.mjs';
|
8
|
+
import FormContainer from '../form/Container.mjs';
|
9
|
+
import TextField from '../form/field/Text.mjs';
|
10
|
+
|
11
|
+
class MainView extends FormContainer {
|
12
|
+
static config = {
|
13
|
+
className: 'Example.view.MainView',
|
14
|
+
layout : {ntype:'vbox', align:'start'},
|
15
|
+
|
16
|
+
items: [{
|
17
|
+
module : TextField,
|
18
|
+
labelText: 'Firstname',
|
19
|
+
name : 'user.firstname',
|
20
|
+
value : 'John'
|
21
|
+
}, {
|
22
|
+
module : TextField,
|
23
|
+
labelText: 'Lastname',
|
24
|
+
name : 'user.lastname',
|
25
|
+
value : 'Doe'
|
26
|
+
}, {
|
27
|
+
module : Button,
|
28
|
+
handler: 'up.getFormValues',
|
29
|
+
style : {marginTop: '1em'},
|
30
|
+
text : 'Get Form Values'
|
31
|
+
}]
|
32
|
+
}
|
33
|
+
|
34
|
+
async getFormValues(data) {
|
35
|
+
const formValues = await this.getValues();
|
36
|
+
// Logs {user: {firstname: 'John', lastname: 'Doe'}}
|
37
|
+
Neo.Main.log({value: formValues})
|
38
|
+
}
|
39
|
+
}
|
40
|
+
Neo.setupClass(MainView);
|
41
|
+
</pre>
|
42
|
+
|
43
|
+
## Forms can get validated without being mounted
|
44
|
+
|
45
|
+
Neo.mjs Forms live as a pure abstraction layer inside JavaScript.
|
46
|
+
The following example is similar to the first one, but this time the form instance will not get mounted.
|
47
|
+
Getting the field values still works like before.
|
48
|
+
|
49
|
+
Use case: In case you have a form split into multiple pages and only one of them is mounted to keep
|
50
|
+
the DOM minimal, you can still get all field values.
|
51
|
+
|
52
|
+
<pre data-neo>
|
53
|
+
import Button from '../button/Base.mjs';
|
54
|
+
import Container from '../container/Base.mjs';
|
55
|
+
import FormContainer from '../form/Container.mjs';
|
56
|
+
import TextField from '../form/field/Text.mjs';
|
57
|
+
|
58
|
+
const myForm = Neo.create({
|
59
|
+
module: FormContainer,
|
60
|
+
items : [{
|
61
|
+
module : TextField,
|
62
|
+
labelText: 'Firstname',
|
63
|
+
name : 'user.firstname',
|
64
|
+
value : 'John'
|
65
|
+
}, {
|
66
|
+
module : TextField,
|
67
|
+
labelText: 'Lastname',
|
68
|
+
name : 'user.lastname',
|
69
|
+
value : 'Doe'
|
70
|
+
}]
|
71
|
+
});
|
72
|
+
|
73
|
+
class MainView extends Container {
|
74
|
+
static config = {
|
75
|
+
className: 'Example.view.MainView',
|
76
|
+
layout : {ntype:'vbox', align:'start'},
|
77
|
+
|
78
|
+
items: [{
|
79
|
+
module : Button,
|
80
|
+
handler: 'up.getFormValues',
|
81
|
+
text : 'Get Form Values'
|
82
|
+
}]
|
83
|
+
}
|
84
|
+
|
85
|
+
async getFormValues(data) {
|
86
|
+
const formValues = await myForm.getValues();
|
87
|
+
// Logs {user: {firstname: 'John', lastname: 'Doe'}}
|
88
|
+
Neo.Main.log({value: formValues})
|
89
|
+
}
|
90
|
+
}
|
91
|
+
Neo.setupClass(MainView);
|
92
|
+
</pre>
|
93
|
+
|
94
|
+
## Nested Forms
|
95
|
+
|
96
|
+
Inside the DOM, it is impossible to nest form tags.
|
97
|
+
Neo forms can get easily mapped to other nodes:</br>
|
98
|
+
`{module: FormContainer, tag: 'div'}`
|
99
|
+
|
100
|
+
This allows us to nest forms and validate or retrieve form values of each nested form or
|
101
|
+
for the top-level form (including all nested items) as we see fit.
|
102
|
+
|
103
|
+
Inside the example preview, clear the user lastname via hitting the x-button.
|
104
|
+
Afterwards, click on the 3 buttons at the bottom and inspect the output inside the main window console carefully.
|
105
|
+
|
106
|
+
The main form will log:
|
107
|
+
<pre data-javascript>
|
108
|
+
{
|
109
|
+
account: 'My Account',
|
110
|
+
product: {brand: 'Tesla', name: 'Car'},
|
111
|
+
user : {firstname: 'John', lastname: null}
|
112
|
+
}
|
113
|
+
'isValid: false'
|
114
|
+
</pre>
|
115
|
+
|
116
|
+
The user form will log:
|
117
|
+
<pre data-javascript>
|
118
|
+
{user: {firstname: 'John', lastname: null}}
|
119
|
+
'isValid: false'
|
120
|
+
</pre>
|
121
|
+
|
122
|
+
The product form will log:
|
123
|
+
<pre data-javascript>
|
124
|
+
{product: {brand: 'Tesla', name: 'Car'}}
|
125
|
+
'isValid: true'
|
126
|
+
</pre>
|
127
|
+
|
128
|
+
<pre data-neo>
|
129
|
+
import Button from '../button/Base.mjs';
|
130
|
+
import Container from '../container/Base.mjs';
|
131
|
+
import FormContainer from '../form/Container.mjs';
|
132
|
+
import TabContainer from '../tab/Container.mjs';
|
133
|
+
import TextField from '../form/field/Text.mjs';
|
134
|
+
|
135
|
+
class MainView extends FormContainer {
|
136
|
+
static config = {
|
137
|
+
className: 'Example.view.MainView',
|
138
|
+
layout : {ntype:'vbox', align:'stretch'},
|
139
|
+
|
140
|
+
items: [{
|
141
|
+
module : TextField,
|
142
|
+
flex : 'none',
|
143
|
+
labelPosition: 'inline',
|
144
|
+
labelText : 'Account',
|
145
|
+
name : 'account',
|
146
|
+
value : 'My Account'
|
147
|
+
}, {
|
148
|
+
module: TabContainer,
|
149
|
+
items : [{
|
150
|
+
module : FormContainer,
|
151
|
+
itemDefaults : {module: TextField, labelPosition: 'inline'},
|
152
|
+
layout : {ntype:'vbox', align:'start'},
|
153
|
+
reference : 'user-form',
|
154
|
+
tabButtonConfig: {text: 'User'},
|
155
|
+
tag : 'div',
|
156
|
+
|
157
|
+
items: [{
|
158
|
+
labelText: 'Firstname',
|
159
|
+
name : 'user.firstname',
|
160
|
+
value : 'John'
|
161
|
+
}, {
|
162
|
+
labelText: 'Lastname',
|
163
|
+
name : 'user.lastname',
|
164
|
+
required : true,
|
165
|
+
value : 'Doe'
|
166
|
+
}]
|
167
|
+
}, {
|
168
|
+
module : FormContainer,
|
169
|
+
itemDefaults : {module: TextField, labelPosition: 'inline'},
|
170
|
+
layout : {ntype:'vbox', align:'start'},
|
171
|
+
reference : 'product-form',
|
172
|
+
tabButtonConfig: {text: 'Product'},
|
173
|
+
tag : 'div',
|
174
|
+
|
175
|
+
items: [{
|
176
|
+
labelText: 'Name',
|
177
|
+
name : 'product.name',
|
178
|
+
value : 'Car'
|
179
|
+
}, {
|
180
|
+
labelText: 'Brand',
|
181
|
+
name : 'product.brand',
|
182
|
+
required : true,
|
183
|
+
value : 'Tesla'
|
184
|
+
}]
|
185
|
+
}]
|
186
|
+
}, {
|
187
|
+
module : Container,
|
188
|
+
flex : 'none',
|
189
|
+
itemDefaults: {module: Button},
|
190
|
+
layout : {ntype: 'hbox'},
|
191
|
+
|
192
|
+
items : [{
|
193
|
+
handler: 'up.getMainFormValues',
|
194
|
+
text : 'Get Main Values'
|
195
|
+
}, {
|
196
|
+
handler: 'up.getUserFormValues',
|
197
|
+
text : 'Get User Values'
|
198
|
+
}, {
|
199
|
+
handler: 'up.getProductFormValues',
|
200
|
+
text : 'Get Product Values'
|
201
|
+
}]
|
202
|
+
}]
|
203
|
+
}
|
204
|
+
|
205
|
+
async getMainFormValues(data) {
|
206
|
+
const formValues = await this.getValues();
|
207
|
+
Neo.Main.log({value: formValues});
|
208
|
+
|
209
|
+
const isValid = await this.validate();
|
210
|
+
Neo.Main.log({value: `isValid: ${isValid}`});
|
211
|
+
}
|
212
|
+
|
213
|
+
async getProductFormValues(data) {
|
214
|
+
const
|
215
|
+
form = this.getReference('product-form'),
|
216
|
+
formValues = await form.getValues();
|
217
|
+
|
218
|
+
Neo.Main.log({value: formValues});
|
219
|
+
|
220
|
+
const isValid = await form.validate();
|
221
|
+
Neo.Main.log({value: `isValid: ${isValid}`});
|
222
|
+
}
|
223
|
+
|
224
|
+
async getUserFormValues(data) {
|
225
|
+
const
|
226
|
+
form = this.getReference('user-form'),
|
227
|
+
formValues = await form.getValues();
|
228
|
+
|
229
|
+
Neo.Main.log({value: formValues});
|
230
|
+
|
231
|
+
const isValid = await form.validate();
|
232
|
+
Neo.Main.log({value: `isValid: ${isValid}`});
|
233
|
+
}
|
234
|
+
}
|
235
|
+
Neo.setupClass(MainView);
|
236
|
+
</pre>
|
237
|
+
|
238
|
+
Bonus: Inspect the DOM Inside the `TabContainer`.
|
239
|
+
You will notice that only the active Tab is mounted inside the DOM.
|
240
|
+
|
241
|
+
1. We can still get field values of unmounted forms
|
242
|
+
2. We can still validate unmounted forms
|
243
|
+
|
244
|
+
## Nested lazy-loaded Forms
|
245
|
+
|
246
|
+
If you look close into the `Button` handlers of the last example:
|
247
|
+
`getValues()` and `validate()` are both async.
|
248
|
+
|
249
|
+
The reason for this is that `form.getFields()` itself is async as well:
|
250
|
+
It will lazy-load (but not necessarily mount) missing fields when needed.
|
251
|
+
|
252
|
+
The lazy-loading use case is not easy to display inside the `LivePreview`,
|
253
|
+
since it does rely on defining child modules inside their own class files
|
254
|
+
and dynamically importing them.
|
255
|
+
|
256
|
+
In a nutshell:
|
257
|
+
<pre data-javascript>
|
258
|
+
{
|
259
|
+
module: TabContainer,
|
260
|
+
items : [
|
261
|
+
{module: () => import('./MyChildForm1')},
|
262
|
+
{module: () => import('./MyChildForm2')}
|
263
|
+
]
|
264
|
+
}
|
265
|
+
</pre>
|
package/resources/data/deck/learnneo/pages/benefits/{Multi-Threading.md → MultiThreading.md}
RENAMED
@@ -23,7 +23,9 @@ On its own, a Browser will just use ***one*** core per tab / window.
|
|
23
23
|
|
24
24
|
Meaning: your Angular or React apps look like this:
|
25
25
|
|
26
|
-
|
26
|
+
<p style="overflow-x: auto;">
|
27
|
+
<img alt="Current State of Apps" src="https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/images/apps-today.png">
|
28
|
+
</p>
|
27
29
|
|
28
30
|
The more JavaScript tasks are running inside your app, the slower it will get.
|
29
31
|
The worst scenario is a complete UI freeze where your one core is at 100%
|
@@ -79,7 +81,9 @@ In case you are not familiar with what an "actor" means, definitely read it firs
|
|
79
81
|
To resolve this performance bottleneck, we want to get main threads as idle as possible, so that they can fully focus on
|
80
82
|
rendering / dynamically manipulating the DOM:
|
81
83
|
|
82
|
-
|
84
|
+
<p style="overflow-x: auto;">
|
85
|
+
<img alt="App Worker Concept" src="https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/images/app-worker.png">
|
86
|
+
</p>
|
83
87
|
|
84
88
|
The worst case that could happen now is that your app worker will slow down and this core runs at 100%. However,
|
85
89
|
this will not affect your UI (rendering thread → main).
|
@@ -2,11 +2,12 @@
|
|
2
2
|
{"name": "Welcome!", "parentId": null, "isLeaf": true, "id": "Welcome" },
|
3
3
|
{"name": "Benefits", "parentId": null, "isLeaf": false, "id": "WhyNeo"},
|
4
4
|
{"name": "Introduction ", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Intro"},
|
5
|
-
{"name": "Multi-Threading", "parentId": "WhyNeo", "isLeaf": true, "id": "benefits.
|
5
|
+
{"name": "Multi-Threading", "parentId": "WhyNeo", "isLeaf": true, "id": "benefits.MultiThreading"},
|
6
6
|
{"name": "Extreme Speed", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Speed"},
|
7
7
|
{"name": "Multi-Window Applications", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Multi-Window"},
|
8
8
|
{"name": "Quick Application Development", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Quick"},
|
9
9
|
{"name": "Complexity and Effort", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Effort"},
|
10
|
+
{"name": "Forms Engine", "parentId": "WhyNeo", "isLeaf": true, "id": "benefits.FormsEngine"},
|
10
11
|
{"name": "Features and Benefits Summary", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Features"},
|
11
12
|
{"name": "Getting Started", "parentId": null, "isLeaf": false, "id": "GettingStarted", "collapsed": true},
|
12
13
|
{"name": "Setup", "parentId": "GettingStarted", "isLeaf": true, "id": "Setup"},
|
package/src/DefaultConfig.mjs
CHANGED
@@ -260,12 +260,12 @@ const DefaultConfig = {
|
|
260
260
|
useVdomWorker: true,
|
261
261
|
/**
|
262
262
|
* buildScripts/injectPackageVersion.mjs will update this value
|
263
|
-
* @default '6.
|
263
|
+
* @default '6.39.0'
|
264
264
|
* @memberOf! module:Neo
|
265
265
|
* @name config.version
|
266
266
|
* @type String
|
267
267
|
*/
|
268
|
-
version: '6.
|
268
|
+
version: '6.39.0'
|
269
269
|
};
|
270
270
|
|
271
271
|
Object.assign(DefaultConfig, {
|