improve docs

This commit is contained in:
Leo Horie 2015-03-13 22:52:34 -04:00
parent 52c804b97f
commit 0f45d22448
3 changed files with 112 additions and 19 deletions

View file

@ -91,6 +91,96 @@ Note that adding a `key` property in the list of attributes (`{name: "users"}` a
---
### Dealing with state
#### Stateless components
Controllers receive arguments passed to the `m.module` call, but this does not mean controllers are a necessary middle man in a component.
Instead of copying arguments to the controller object, and then passing the controller object to the view, it is often desirable that views always update based on the most current list of arguments being passed to a component.
The following example illustrates this pattern:
```javascript
var MyApp = {
controller: function() {
this.temp = m.prop(10) //kelvin
},
view: function(ctrl) {
return m("div", [
m("input", {oninput: m.withAttr("value", ctrl.temp), value: ctrl.temp()}), "K",
m("br"),
m.module(TemperatureConverter, {value: ctrl.temp()})
]);
}
};
var TemperatureConverter = {
controller: function() {
//note how the controller does not handle the input arguments
//define some helper functions to be called from the view
this.kelvinToCelsius = function(value) {
return value - 273.15
}
this.kelvinToFahrenheit = function() {
return (value 9 / 5 * (v - 273.15)) + 32
}
},
view: function(ctrl, options) {
return m('div', [
"celsius:", ctrl.kelvinToCelsius(options.value),
m("br"),
"fahrenheit:", ctrl.kelvinToFahrenheit(options.value),
]);
}
};
m.module(document.body, MyApp);
```
Here, the temperature value from the input is passed to the TemperatureConverter view directly, and transformation functions are called from there. This should be the preferred pattern for components that display data that is always derived from the most current input.
#### Parameterized initial state
The ability to handle arguments in the controller is useful for setting up the initial state for a component whose state depends on input data:
```javascript
var MyComponent = {
controller: function(args) {
//we only want to make this call once
this.things = m.request({method: "GET", url: "/api/things/", {data: args}}) //slice the data in some way
},
view: function(ctrl) {
return m("ul", [
ctrl.things().map(function(name) {
return m("li", thing.name)
})
]);
}
};
```
#### Data-driven component identity
A component can be re-initialized from scratch by changing the `key` associated with it. This is useful for re-running ajax calls for different model entities.
```javascript
var people = [
{id: 1, name: "John"},
{id: 2, name: "Mary"}
]
//ajax and display a list of projects for John
m.render(document.body, m.module(ProjectList, {key: people[0].id, value: people[0]})
//ajax and display a list of projects for Mary
//here, since the key is different, the ProjectList component is recreated from scratch, which runs the controller, re-generates the DOM, and re-initializes any applicable 3rd party plugins in configs
m.render(document.body, m.module(ProjectList, {key: people[1].id, value: people[1]})
```
Note that the rules for keys apply for components the same way they do for regular elements: it is not allowed to have duplicate keys as children of the same parent, and they must be either strings or numbers (or something with a `.toString()` implementation that makes the entity locally uniquely identifiable when serialized).
---
### Unloading components
Modules declared in templates can also call `onunload` and its `e.preventDefault()` like regular modules. The `onunload` event is called if an instantiated module is removed from a virtual element tree via a redraw.

View file

@ -47,7 +47,7 @@ var myModule = {
}
```
In addition to holding a controller and a view, a module is typically also used to store data that pertains to it.
In addition to holding a controller and a view, a module can also be used to store data that pertains to it.
Let's create a module.

View file

@ -76,17 +76,19 @@
<div class="col(8,8,12)">
<h2>Sample code</h2>
<pre><code class="language-javascript">//namespace
var app = {};
<pre><code class="language-javascript">
//model
app.PageList = function() {
return m.request({method: "GET", url: "pages.json"});
var Page = {
list: function() {
return m.request({method: "GET", url: "pages.json"});
}
};
//controller
app.controller = function() {
var pages = app.PageList();
var Demo = {};
Demo.controller = function() {
var pages = Page.list();
return {
pages: pages,
rotate: function() {
@ -96,7 +98,7 @@ app.controller = function() {
};
//view
app.view = function(ctrl) {
Demo.view = function(ctrl) {
return [
ctrl.pages().map(function(page) {
return m("a", {href: page.url}, page.title);
@ -106,7 +108,7 @@ app.view = function(ctrl) {
};
//initialize
m.module(document.getElementById("example"), app);</code></pre>
m.module(document.getElementById("example"), Demo);</code></pre>
</div>
<div class="col(4,4,12)">
@ -114,17 +116,18 @@ m.module(document.getElementById("example"), app);</code></pre>
<div id="example" class="example output">
<script src="mithril.min.js"></script>
<script>
//namespace
var app = {};
//model
app.PageList = function() {
return m.request({method: "GET", url: "pages.json"});
var Page = {
list: function() {
return m.request({method: "GET", url: "pages.json"});
}
};
//controller
app.controller = function() {
var pages = app.PageList();
var Demo = {};
Demo.controller = function() {
var pages = Page.list();
return {
pages: pages,
rotate: function() {
@ -134,7 +137,7 @@ app.controller = function() {
};
//view
app.view = function(ctrl) {
Demo.view = function(ctrl) {
return [
ctrl.pages().map(function(page) {
return m("a", {href: page.url}, page.title);
@ -144,7 +147,7 @@ app.view = function(ctrl) {
};
//initialize
m.module(document.getElementById("example"), app);
m.module(document.getElementById("example"), Demo);
</script>
</div>
</div>