Skip to content

Commit

Permalink
Learning section
Browse files Browse the repository at this point in the history
  • Loading branch information
maxrahder committed Dec 28, 2023
1 parent 00a67f1 commit f22f0a0
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 9 deletions.
22 changes: 22 additions & 0 deletions resources/data/deck/learnneo/data/theBeatles.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"first": "John",
"last": "Lennon",
"dob": "1940/10/09"
},
{
"first": "Paul",
"last": "McCartney",
"dob": "1942/06/18"
},
{
"first": "George",
"last": "Harrison",
"dob": "1943/02/25"
},
{
"first": "Ringo",
"last": "Starr",
"dob": "1940/07/07"
}
]
1 change: 0 additions & 1 deletion resources/data/deck/learnneo/p/AddingProperties.md

This file was deleted.

1 change: 0 additions & 1 deletion resources/data/deck/learnneo/p/ComponentState.md

This file was deleted.

132 changes: 132 additions & 0 deletions resources/data/deck/learnneo/p/Config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
As you've probably noticed, Neo.mjs classes have a `static config` property.

The `config` specifies properties that are applied to instances as they are
created. In addition, Neo.mjs uses that information to set up propoerty lifecycle
methods.

Here's an example of a new component class `Simple` with three config properties:

1. `className` — used by Neo.mjs to keep track of every class
2. `foo` — an instance property
2. `bar_` — another instance property

<pre data-neo>
import Component from '../../../../src/component/Base.mjs';
import Container from '../../../../src/container/Base.mjs';

class Simple extends Component {
static config = {
className: 'Example.view.Simple',

foo: 1, // An instance field and its initial (default) value
bar_: '' // Another instance field -- note the underscore at the end

}

}
Neo.applyClassConfig(Simple);


class MainView extends Container {
static config = {
className: 'Example.view.MainView',

items: [{
module: Simple,

foo: 17 // This is applied to the instance
bar: 'hi there' // This is applied to the instance

}]

}
}
Neo.applyClassConfig(MainView);
</pre>

The `Simple` class doesn't have any content, so if you run the code you won't see anything.
But if you were to open the debugger in the `neomjs-app-worker` context you could see the values
via `Neo.findFirst('[foo]').foo`. or `Neo.findFirst('[foo]').bar` We'll talk more about `findFirst()`
and the console below.

Note that the `bar` property was defined with an underscore at the end. That tags the property as
a _lifecyle property_. A lifecycle property provides methods that are run as the property is
updated or accessed. You're free to implment the methods to implement business rules, normalize
values, or have side-effects, such as updating a view or firing an event.

<pre data-neo>
import Component from '../../../../src/component/Base.mjs';
import Container from '../../../../src/container/Base.mjs';

class Simple extends Component {
static config = {
className: 'Example.view.Simple',

foo: 1, // An instance field and its initial (default) value
bar_: null // Another instance field -- note the underscore at the end

}

beforeGetBar(){

}
beforeSetBar(value, oldValue){
// Use value if it's not empty
if (value) return value;
}
afterSetBar(value, oldValue){
this.html = value;
this.fire('barChange', {component: this, value, oldValue});
}

}
Neo.applyClassConfig(Simple);


class MainView extends Container {
static config = {
className: 'Example.view.MainView',

items: [{
module: Simple,

foo: 17 , // This is applied to the instance
bar: 'hi there' // This is applied to the instance

}]

}
}
Neo.applyClassConfig(MainView);
</pre>


<pre>
items: [{
module: Simple,
html: 'simple', // This property is introduced in Neo.component.Base

foo: 17 // This is applied to the instance

}]
</pre>

The config `{ module: Simple, html: 'Simple', foo: 17 }` is an object literal
_describing_ &mdash; or _configuring_ &mdash; the item in our main view. It's saying we want an instance
of `Simple`, with two properties applied to the new instance:

- `html`, this isn't used very often, but it's handy for stubbing out views
- `foo`, which is the property introduced in `Examples.view.Simple`


Before moving on, let's break down that console statement.

First, Neo.mjs is multi-threaded. Your app logic runs in the `neomjs-app-worker` context.
In Chrome devtools you need to switch to
that context via the dropdown at the top of of console panel. (We'll talk more about debugging
and the console in a later topic.)

The statement `Neo.findFirst('[foo]')` finds the first component with a `foo` property. The `Neo.findFirst()`
method is handy for getting component references when debugging.

When we add `.foo` to that, we're simply inspecting the value of that property at runtime &mdash; 17.
2 changes: 1 addition & 1 deletion resources/data/deck/learnneo/p/Events.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ To specify an event handler, use `listeners: {}`, specifying in as many event/ha
pairs as you need.

The code below shows two text fields, with `listeners` for `change` and `focusEnter`.
(The events for any component is documened in the API docs.)
(The events for any component are documened in the API docs.)

<pre data-neo>
import Base from '../../../../src/container/Base.mjs';
Expand Down
118 changes: 117 additions & 1 deletion resources/data/deck/learnneo/p/Extending.md
Original file line number Diff line number Diff line change
@@ -1 +1,117 @@
### todo: Extending Components

In theory, a Neo.mjs app could be defined in a single `.mjs` source file. But that would be very hard to
maintain, and any reusable configs would have to be duplicated. Instead, each of your views and reusable
widgets will be defined as its own class. The result is simpler views which are inherently reusable and easier
to test.

Consider this code. It's a panel with a header and a table. The table has a store.

<pre data-neo>
import Base from '../../../../src/container/Panel.mjs';
import Button from '../../../../src/button/Base.mjs';
import Table from '../../../../src/table/Container.mjs';

class MainView extends Base {
static config = {
className : 'Example.view.MainView',
headers: [{
dock: 'top',
items: [{
module: Button,
text: 'She loves me...',
handler: () => Neo.Main.alert({message: 'Yeah, yeah yeah!'})
}]
}],
items : [{
module: Table,
store: {
autoLoad: true,
url: '../../resources/data/deck/learnneo/data/theBeatles.json',
model: {
fields: [{name: 'first'}, {name: 'last'}, {name: 'dob', type: 'date'}]
}
},
columns: [{
text: 'First',
dataField: 'first',
}, {
text: 'Last',
dataField: 'last',
}]
}]
}
}
Neo.applyClassConfig(MainView);
</pre>

If you wanted, any of the configs can be refactored into their own class. Here, the button, store, and table
have been refactored into their own classes, and the main view is using them. The main view is simpler and
more abstract, and each class can be reused, tested, and maintained independently.

<pre data-neo>
import Base from '../../../../src/container/Panel.mjs';
import Button from '../../../../src/button/Base.mjs';
import Table from '../../../../src/table/Container.mjs';
import Store from '../../../../src/data/Store.mjs';

class BeatlesButton extends Button {
static config = {
className: 'Example.view.BeatlesButton',
text: 'She loves me...',
handler: () => Neo.Main.alert({message: 'Yeah, yeah yeah!'})
}
}
Neo.applyClassConfig(BeatlesButton);

class BeatlesStore extends Store {
static config = {
className: 'Example.view.BeatlesStore',
autoLoad: true,
url: '../../resources/data/deck/learnneo/data/theBeatles.json',
model: {
fields: [{name: 'first'}, {name: 'last'}, {name: 'dob', type: 'date'}]
}
}
}
Neo.applyClassConfig(BeatlesStore);

class BeatlesTable extends Table {
static config = {
className: 'Example.view.BeatlesTable',
columns: [{
text: 'First',
dataField: 'first',
}, {
text: 'Last',
dataField: 'last',
}]
}
}
Neo.applyClassConfig(BeatlesTable);

class MainView extends Base {
static config = {
className : 'Example.view.MainView',
headers: [{
dock: 'top',
items: [{
module: BeatlesButton,
}]
}],
items : [{
module: BeatlesTable,
store: {
module: BeatlesStore
},
}]
}
}
Neo.applyClassConfig(MainView);
</pre>

There are several use-cases for creating your own classes:

- For reuse
- To isolate complexity
- To add events or methods to the new class
- To test the component independently
9 changes: 4 additions & 5 deletions resources/data/deck/learnneo/t.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
{"name": "Workspaces and Applications", "parentId": "GettingStarted", "isLeaf": true, "id": "2023-10-14T19-25-08-153Z"},
{"name": "Describing a View", "parentId": "GettingStarted", "isLeaf": true, "id": "DescribingTheUI"},
{"name": "Events", "parentId": "GettingStarted", "isLeaf": true, "id": "Events"},
{"name": "Component References", "parentId": "GettingStarted", "isLeaf": true, "id": "References"},
{"name": "Updating View State", "parentId": "GettingStarted", "isLeaf": true, "id": "ComponentState"},
{"name": "Extending Components", "parentId": "GettingStarted", "isLeaf": true, "id": "Extending"},
{"name": "Adding Properties", "parentId": "GettingStarted", "isLeaf": true, "id": "AddingProperties"},
{"name": "Component Models and Binding", "parentId": "GettingStarted", "isLeaf": true, "id": "ComponentModels"},
{"name": "Component References", "parentId": "GettingStarted", "isLeaf": true, "id": "References"},
{"name": "Extending Classes", "parentId": "GettingStarted", "isLeaf": true, "id": "Extending"},
{"name": "Config", "parentId": "GettingStarted", "isLeaf": true, "id": "Config"},
{"name": "Shared Binable Data", "parentId": "GettingStarted", "isLeaf": true, "id": "ComponentModels"},
{"name": "What about HTML tags?", "parentId": "GettingStarted", "isLeaf": true, "id": "WhatAboutHTML"},
{"name": "Tutorials", "parentId": null, "isLeaf": false, "expanded": false, "id": "Tutorials"},
{"name": "Rock Scissors Paper", "parentId": "Tutorials", "isLeaf": true, "expanded": false, "id": "RSP"},
Expand Down

0 comments on commit f22f0a0

Please sign in to comment.