-
-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
276 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
] |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ — or _configuring_ — 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 — 17. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters