docs: merge docs from next to master
This commit is contained in:
parent
2a61e17332
commit
33aa1fa735
19 changed files with 285 additions and 72 deletions
1
docs/CNAME
Normal file
1
docs/CNAME
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
mithril.js.org
|
||||||
|
|
@ -1,7 +1,38 @@
|
||||||
# Change log
|
# Change log
|
||||||
|
|
||||||
|
- [v1.0.2](#v101)
|
||||||
|
- [v1.0.1](#v101)
|
||||||
- [Migrating from v0.2.x](#migrating-from-v02x)
|
- [Migrating from v0.2.x](#migrating-from-v02x)
|
||||||
- [Older docs](http://mithril.js.org/archive/v0.2.5/change-log.html)
|
- [Older docs](http://mithril.js.org/archive/v0.2.5/index.html)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### v1.0.2
|
||||||
|
|
||||||
|
#### News
|
||||||
|
|
||||||
|
- support for ES6 class components
|
||||||
|
- updated typescript definitions
|
||||||
|
|
||||||
|
#### Bug fixes
|
||||||
|
|
||||||
|
- fix IE11 input[type] error - [#1610](https://github.com/lhorie/mithril.js/issues/1610)
|
||||||
|
- apply [#1609](https://github.com/lhorie/mithril.js/issues/1609) to unkeyed children case
|
||||||
|
- fix abort detection [#1612](https://github.com/lhorie/mithril.js/issues/1612)
|
||||||
|
- fix input value focus issue when value is loosely equal to old value [#1593](https://github.com/lhorie/mithril.js/issues/1593)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### v1.0.1
|
||||||
|
|
||||||
|
#### News
|
||||||
|
|
||||||
|
- performance improvements in IE [#1598](https://github.com/lhorie/mithril.js/pull/1598)
|
||||||
|
|
||||||
|
#### Bug fixes
|
||||||
|
|
||||||
|
- prevent infinite loop in non-existent default route - [#1579](https://github.com/lhorie/mithril.js/issues/1579)
|
||||||
|
- call correct lifecycle methods on children of recycled keyed vnodes - [#1609](https://github.com/lhorie/mithril.js/issues/1609)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -28,6 +59,7 @@ If you are migrating, consider using the [mithril-codemods](https://www.npmjs.co
|
||||||
- [`m.route` and anchor tags](#mroute-and-anchor-tags)
|
- [`m.route` and anchor tags](#mroute-and-anchor-tags)
|
||||||
- [Reading/writing the current route](#readingwriting-the-current-route)
|
- [Reading/writing the current route](#readingwriting-the-current-route)
|
||||||
- [Accessing route params](#accessing-route-params)
|
- [Accessing route params](#accessing-route-params)
|
||||||
|
- [Building/Parsing query strings](#buildingparsing-query-strings)
|
||||||
- [Preventing unmounting](#preventing-unmounting)
|
- [Preventing unmounting](#preventing-unmounting)
|
||||||
- [Run code on component removal](#run-code-on-component-removal)
|
- [Run code on component removal](#run-code-on-component-removal)
|
||||||
- [`m.request`](#mrequest)
|
- [`m.request`](#mrequest)
|
||||||
|
|
@ -459,6 +491,28 @@ m.route(document.body, "/booga", {
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Building/Parsing query strings
|
||||||
|
|
||||||
|
`v0.2.x` used methods hanging off of `m.route`, `m.route.buildQueryString()` and `m.route.parseQueryString()`. In `v1.x` these have been broken out and attached to the root `m`.
|
||||||
|
|
||||||
|
### `v0.2.x`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var qs = m.route.buildQueryString({ a : 1 });
|
||||||
|
|
||||||
|
var obj = m.route.parseQueryString("a=1");
|
||||||
|
```
|
||||||
|
|
||||||
|
### `v1.x`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var qs = m.buildQueryString({ a : 1 });
|
||||||
|
|
||||||
|
var obj = m.parseQueryString("a=1");
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Preventing unmounting
|
## Preventing unmounting
|
||||||
|
|
||||||
It is no longer possible to prevent unmounting via `onunload`'s `e.preventDefault()`. Instead you should explicitly call `m.route.set` when the expected conditions are met.
|
It is no longer possible to prevent unmounting via `onunload`'s `e.preventDefault()`. Instead you should explicitly call `m.route.set` when the expected conditions are met.
|
||||||
|
|
@ -588,11 +642,13 @@ greetAsync()
|
||||||
### `v1.x`
|
### `v1.x`
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var greetAsync = new Promise(function(resolve){
|
var greetAsync = function() {
|
||||||
setTimeout(function() {
|
return new Promise(function(resolve){
|
||||||
resolve("hello")
|
setTimeout(function() {
|
||||||
}, 1000)
|
resolve("hello")
|
||||||
})
|
}, 1000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
greetAsync()
|
greetAsync()
|
||||||
.then(function(value) {return value + " world"})
|
.then(function(value) {return value + " world"})
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
- [Structure](#structure)
|
- [Structure](#structure)
|
||||||
- [Lifecycle methods](#lifecycle-methods)
|
- [Lifecycle methods](#lifecycle-methods)
|
||||||
- [State](#state)
|
- [State](#state)
|
||||||
|
- [ES6 classes](#es6-classes)
|
||||||
- [Avoid-anti-patterns](#avoid-anti-patterns)
|
- [Avoid-anti-patterns](#avoid-anti-patterns)
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
@ -108,7 +109,7 @@ The state of a component can be accessed three ways: as a blueprint at initializ
|
||||||
|
|
||||||
#### At initialization
|
#### At initialization
|
||||||
|
|
||||||
Any property attached to the component object is copied for every instance of the component. This allows simple state initialization.
|
The component object is the prototype of each component instance, so any property defined on the component object will be accessible as a property of `vnode.state`. This allows simple state initialization.
|
||||||
|
|
||||||
In the example below, `data` is a property of the `ComponentWithInitialState` component's state object.
|
In the example below, `data` is a property of the `ComponentWithInitialState` component's state object.
|
||||||
|
|
||||||
|
|
@ -170,6 +171,44 @@ Be aware that when using ES5 functions, the value of `this` in nested anonymous
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### ES6 classes
|
||||||
|
|
||||||
|
Components can also be written using ES6 class syntax:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
class ES6ClassComponent {
|
||||||
|
view() {
|
||||||
|
return m("div", "Hello from an ES6 class")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
They can be consumed in the same way regular components can.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// EXAMPLE: via m.render
|
||||||
|
m.render(document.body, m(ES6ClassComponent))
|
||||||
|
|
||||||
|
// EXAMPLE: via m.mount
|
||||||
|
m.mount(document.body, ES6ClassComponent)
|
||||||
|
|
||||||
|
// EXAMPLE: via m.route
|
||||||
|
m.route(document.body, "/", {
|
||||||
|
"/": ES6ClassComponent
|
||||||
|
})
|
||||||
|
|
||||||
|
// EXAMPLE: component composition
|
||||||
|
class AnotherES6ClassComponent {
|
||||||
|
view() {
|
||||||
|
return m("main", [
|
||||||
|
m(ES6ClassComponent)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Avoid anti-patterns
|
### Avoid anti-patterns
|
||||||
|
|
||||||
Although Mithril is flexible, some code patterns are discouraged:
|
Although Mithril is flexible, some code patterns are discouraged:
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@ Special thanks to:
|
||||||
- Leon Sorokin, for writing a DOM instrumentation tool that helped improve performance in Mithril 1.0
|
- Leon Sorokin, for writing a DOM instrumentation tool that helped improve performance in Mithril 1.0
|
||||||
- Jordan Walke, whose work on React was prior art to the implementation of keys in Mithril
|
- Jordan Walke, whose work on React was prior art to the implementation of keys in Mithril
|
||||||
- Pierre-Yves Gérardy, who consistently makes high quality contributions
|
- Pierre-Yves Gérardy, who consistently makes high quality contributions
|
||||||
|
- Gyandeep Singh, who contributed significant IE performance improvements
|
||||||
|
|
||||||
Other people who also deserve recognition:
|
Other people who also deserve recognition:
|
||||||
|
|
||||||
- Arthur Clemens - creator of [Polythene](https://github.com/ArthurClemens/Polythene) and the [HTML-to-Mithril converter](http://arthurclemens.github.io/mithril-template-converter/index.html)
|
- Arthur Clemens - creator of [Polythene](https://github.com/ArthurClemens/Polythene) and the [HTML-to-Mithril converter](http://arthurclemens.github.io/mithril-template-converter/index.html)
|
||||||
- Stephan Hoyer - creator of [mithril-node-render](https://github.com/StephanHoyer/mithril-node-render), [mithril-query](https://github.com/StephanHoyer/mithril-query) and [mithril-source-hint](https://github.com/StephanHoyer/mithril-source-hint)
|
- Stephan Hoyer - creator of [mithril-node-render](https://github.com/StephanHoyer/mithril-node-render), [mithril-query](https://github.com/StephanHoyer/mithril-query) and [mithril-source-hint](https://github.com/StephanHoyer/mithril-source-hint)
|
||||||
- the countless people who have reported and fixed bugs, participated in discussions, and helped promote Mithril
|
- the countless people who have reported and fixed bugs, participated in discussions, and helped promote Mithril
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,11 @@ Generates a fragment [vnode](vnodes.md)
|
||||||
|
|
||||||
`vnode = m.fragment(attrs, children)`
|
`vnode = m.fragment(attrs, children)`
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
Argument | Type | Required | Description
|
||||||
----------- | ------------------------------------ | -------- | ---
|
----------- | --------------------------------------------------- | -------- | ---
|
||||||
`attrs` | `Object` | Yes | A map of attributes
|
`attrs` | `Object` | Yes | A map of attributes
|
||||||
`children` | `Array<Vnode>|String|Number|Boolean` | Yes | A list of vnodes
|
`children` | `Array<Vnode|String|Number|Boolean|null|undefined>` | Yes | A list of vnodes
|
||||||
**returns** | `Vnode` | | A fragment [vnode](vnodes.md)
|
**returns** | `Vnode` | | A fragment [vnode](vnodes.md)
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
[How to read signatures](signatures.md)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ However, if you're starting something new, do consider giving Mithril a try, if
|
||||||
|
|
||||||
## Why use Mithril?
|
## Why use Mithril?
|
||||||
|
|
||||||
In one sentence: because **Mithril is pragmatic**. The 10 minutes [guide](index.md) is a good example: that's how long it takes to learn components, XHR and routing - and that's just about the right amount of knowledge needed to build useful applications.
|
In one sentence: because **Mithril is pragmatic**. This [10 minute guide](index.md) is a good example: that's how long it takes to learn components, XHR and routing - and that's just about the right amount of knowledge needed to build useful applications.
|
||||||
|
|
||||||
Mithril is all about getting meaningful work done efficiently. Doing file uploads? [The docs show you how](request.md#file-uploads). Authentication? [Documented too](route.md#authentication). Exit animations? [You got it](animation.md). No extra libraries, no magic.
|
Mithril is all about getting meaningful work done efficiently. Doing file uploads? [The docs show you how](request.md#file-uploads). Authentication? [Documented too](route.md#authentication). Exit animations? [You got it](animation.md). No extra libraries, no magic.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
|
"use strict"
|
||||||
|
|
||||||
var fs = require("fs")
|
var fs = require("fs")
|
||||||
var path = require("path")
|
var path = require("path")
|
||||||
var marked = require("marked")
|
var marked = require("marked")
|
||||||
var layout = fs.readFileSync("./docs/layout.html", "utf-8")
|
var layout = fs.readFileSync("./docs/layout.html", "utf-8")
|
||||||
var version = JSON.parse(fs.readFileSync("./package.json", "utf-8")).version
|
var version = JSON.parse(fs.readFileSync("./package.json", "utf-8")).version
|
||||||
try {fs.mkdirSync("../mithril")} catch (e) {}
|
try {fs.mkdirSync("./dist")} catch (e) {/* ignore */}
|
||||||
try {fs.mkdirSync("../mithril/archive")} catch (e) {}
|
try {fs.mkdirSync("./dist/archive")} catch (e) {/* ignore */}
|
||||||
try {fs.mkdirSync("../mithril/archive/v" + version)} catch (e) {}
|
try {fs.mkdirSync("./dist/archive/v" + version)} catch (e) {/* ignore */}
|
||||||
try {fs.mkdirSync("../mithril/archive/v" + version + "/lib")} catch (e) {}
|
try {fs.mkdirSync("./dist/archive/v" + version + "/lib")} catch (e) {/* ignore */}
|
||||||
try {fs.mkdirSync("../mithril/archive/v" + version + "/lib/prism")} catch (e) {}
|
try {fs.mkdirSync("./dist/archive/v" + version + "/lib/prism")} catch (e) {/* ignore */}
|
||||||
try {fs.mkdirSync("../mithril/lib")} catch (e) {}
|
try {fs.mkdirSync("./dist/lib")} catch (e) {/* ignore */}
|
||||||
try {fs.mkdirSync("../mithril/lib/prism")} catch (e) {}
|
try {fs.mkdirSync("./dist/lib/prism")} catch (e) {/* ignore */}
|
||||||
|
|
||||||
var guides = fs.readFileSync("docs/guides.md", "utf-8")
|
var guides = fs.readFileSync("docs/guides.md", "utf-8")
|
||||||
var methods = fs.readFileSync("docs/methods.md", "utf-8")
|
var methods = fs.readFileSync("docs/methods.md", "utf-8")
|
||||||
|
|
@ -25,7 +27,7 @@ function generate(pathname) {
|
||||||
generate(pathname + "/" + filename)
|
generate(pathname + "/" + filename)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else if (!pathname.match(/tutorials|archive/)) {
|
else if (!pathname.match(/tutorials|archive|guides|methods/)) {
|
||||||
if (pathname.match(/\.md$/)) {
|
if (pathname.match(/\.md$/)) {
|
||||||
var outputFilename = pathname.replace(/\.md$/, ".html")
|
var outputFilename = pathname.replace(/\.md$/, ".html")
|
||||||
var markdown = fs.readFileSync(pathname, "utf-8")
|
var markdown = fs.readFileSync(pathname, "utf-8")
|
||||||
|
|
@ -33,7 +35,7 @@ function generate(pathname) {
|
||||||
.replace(/`((?:\S| -> |, )+)(\|)(\S+)`/gim, function(match, a, b, c) { // fix pipes in code tags
|
.replace(/`((?:\S| -> |, )+)(\|)(\S+)`/gim, function(match, a, b, c) { // fix pipes in code tags
|
||||||
return "<code>" + (a + b + c).replace(/\|/g, "|") + "</code>"
|
return "<code>" + (a + b + c).replace(/\|/g, "|") + "</code>"
|
||||||
})
|
})
|
||||||
.replace(/(^# .+?(?:\r?\n){2,}?)(?:(-(?:.|\r|\n)+?)((?:\r?\n){2,})|)/m, function(match, title, nav, space) { // inject menu
|
.replace(/(^# .+?(?:\r?\n){2,}?)(?:(-(?:.|\r|\n)+?)((?:\r?\n){2,})|)/m, function(match, title, nav) { // inject menu
|
||||||
var file = path.basename(pathname)
|
var file = path.basename(pathname)
|
||||||
var link = new RegExp("([ \t]*)(- )(\\[.+?\\]\\(" + file + "\\))")
|
var link = new RegExp("([ \t]*)(- )(\\[.+?\\]\\(" + file + "\\))")
|
||||||
var replace = function(match, space, li, link) {
|
var replace = function(match, space, li, link) {
|
||||||
|
|
@ -53,14 +55,14 @@ function generate(pathname) {
|
||||||
.replace(/\[version\]/, version) // update version
|
.replace(/\[version\]/, version) // update version
|
||||||
.replace(/\[body\]/, markedHtml)
|
.replace(/\[body\]/, markedHtml)
|
||||||
.replace(/<h(.) id="([^"]+?)">(.+?)<\/h.>/gim, function(match, n, id, text) { // fix anchors
|
.replace(/<h(.) id="([^"]+?)">(.+?)<\/h.>/gim, function(match, n, id, text) { // fix anchors
|
||||||
return "<h" + n + " id=\"" + text.toLowerCase().replace(/<(\/?)code>/g, "").replace(/<a.*?>.+?<\/a>/g, "").replace(/\.|\[|\]|"|\/|\(|\)/g, "").replace(/\s/g, "-") + "\">" + text + "</h" + n + ">"
|
return "<h" + n + ' id="' + text.toLowerCase().replace(/<(\/?)code>/g, "").replace(/<a.*?>.+?<\/a>/g, "").replace(/\.|\[|\]|"|\/|\(|\)/g, "").replace(/\s/g, "-") + '">' + text + "</h" + n + ">"
|
||||||
})
|
})
|
||||||
fs.writeFileSync("../mithril/archive/v" + version + "/" + outputFilename.replace(/^docs\//, ""), html, "utf-8")
|
fs.writeFileSync("./dist/archive/v" + version + "/" + outputFilename.replace(/^docs\//, ""), html, "utf-8")
|
||||||
fs.writeFileSync("../mithril/" + outputFilename.replace(/^docs\//, ""), html, "utf-8")
|
fs.writeFileSync("./dist/" + outputFilename.replace(/^docs\//, ""), html, "utf-8")
|
||||||
}
|
}
|
||||||
else if (!pathname.match(/lint|generate/)) {
|
else if (!pathname.match(/lint|generate/)) {
|
||||||
fs.writeFileSync("../mithril/archive/v" + version + "/" + pathname.replace(/^docs\//, ""), fs.readFileSync(pathname, "utf-8"), "utf-8")
|
fs.writeFileSync("./dist/archive/v" + version + "/" + pathname.replace(/^docs\//, ""), fs.readFileSync(pathname, "utf-8"), "utf-8")
|
||||||
fs.writeFileSync("../mithril/" + pathname.replace(/^docs\//, ""), fs.readFileSync(pathname, "utf-8"), "utf-8")
|
fs.writeFileSync("./dist/" + pathname.replace(/^docs\//, ""), fs.readFileSync(pathname, "utf-8"), "utf-8")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ Mithril is used by companies like Vimeo and Nike, and open source platforms like
|
||||||
|
|
||||||
If you are an experienced developer and want to know how Mithril compares to other frameworks, see the [framework comparison](framework-comparison.md) page.
|
If you are an experienced developer and want to know how Mithril compares to other frameworks, see the [framework comparison](framework-comparison.md) page.
|
||||||
|
|
||||||
|
Mithril supports browsers all the way back to IE9, no polyfills required.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Getting started
|
### Getting started
|
||||||
|
|
@ -54,7 +56,7 @@ Let's create an HTML file to follow along:
|
||||||
|
|
||||||
```markup
|
```markup
|
||||||
<body>
|
<body>
|
||||||
<script src="http://unpkg.com/mithril/mithril.js"></script>
|
<script src="//unpkg.com/mithril/mithril.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var root = document.body
|
var root = document.body
|
||||||
|
|
||||||
|
|
@ -93,7 +95,7 @@ Let's wrap our text in an `<h1>` tag.
|
||||||
m.render(root, m("h1", "My first app"))
|
m.render(root, m("h1", "My first app"))
|
||||||
```
|
```
|
||||||
|
|
||||||
The `m()` function can be used to describe any HTML structure you want. So if you to add a class to the `<h1>`:
|
The `m()` function can be used to describe any HTML structure you want. So if you need to add a class to the `<h1>`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
m("h1", {class: "title"}, "My first app")
|
m("h1", {class: "title"}, "My first app")
|
||||||
|
|
@ -231,7 +233,7 @@ var count = 0
|
||||||
var increment = function() {
|
var increment = function() {
|
||||||
m.request({
|
m.request({
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
url: "http://rem-rest-api.herokuapp.com/api/tutorial/1",
|
url: "//rem-rest-api.herokuapp.com/api/tutorial/1",
|
||||||
data: {count: count + 1},
|
data: {count: count + 1},
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ Argument | Type | Required | Descript
|
||||||
`options.type` | `any = Function(any)` | No | A constructor to be applied to each object in the response. Defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function).
|
`options.type` | `any = Function(any)` | No | A constructor to be applied to each object in the response. Defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function).
|
||||||
`options.callbackName` | `String` | No | The name of the function that will be called as the callback. Defaults to a randomized string (e.g. `_mithril_6888197422121285_0({a: 1})`
|
`options.callbackName` | `String` | No | The name of the function that will be called as the callback. Defaults to a randomized string (e.g. `_mithril_6888197422121285_0({a: 1})`
|
||||||
`options.callbackKey` | `String` | No | The name of the querystring parameter name that specifies the callback name. Defaults to `callback` (e.g. `/someapi?callback=_mithril_6888197422121285_0`)
|
`options.callbackKey` | `String` | No | The name of the querystring parameter name that specifies the callback name. Defaults to `callback` (e.g. `/someapi?callback=_mithril_6888197422121285_0`)
|
||||||
|
`options.background` | `Boolean` | No | If `false`, redraws mounted components upon completion of the request. If `true`, it does not. Defaults to `false`.
|
||||||
**returns** | `Promise` | | A promise that resolves to the response data, after it has been piped through `type` method
|
**returns** | `Promise` | | A promise that resolves to the response data, after it has been piped through `type` method
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
[How to read signatures](signatures.md)
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ var link = <a href={url}>{greeting + "!"}</a>
|
||||||
Components can be used by using a convention of uppercasing the first letter of the component name:
|
Components can be used by using a convention of uppercasing the first letter of the component name:
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
m.mount(document.body, <MyComponent />)
|
m.render(document.body, <MyComponent />)
|
||||||
// equivalent to m.mount(document.body, m(MyComponent))
|
// equivalent to m.render(document.body, m(MyComponent))
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
54
docs/keys.md
54
docs/keys.md
|
|
@ -3,7 +3,6 @@
|
||||||
- [What are keys](#what-are-keys)
|
- [What are keys](#what-are-keys)
|
||||||
- [How to use](#how-to-use)
|
- [How to use](#how-to-use)
|
||||||
- [Debugging key related issues](#debugging-key-related-issues)
|
- [Debugging key related issues](#debugging-key-related-issues)
|
||||||
- [Avoid anti-patterns](#avoid-anti-patterns)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -152,3 +151,56 @@ var things = [
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Avoid mixing keyed and non-keyed vnodes in the same array
|
||||||
|
|
||||||
|
An array of vnodes must have only keyed vnodes or non-keyed vnodes, but not both. If you need to mix them, create a nested array.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// AVOID
|
||||||
|
m("div", [
|
||||||
|
m("div", "a"),
|
||||||
|
m("div", {key: 1}, "b"),
|
||||||
|
])
|
||||||
|
|
||||||
|
// PREFER
|
||||||
|
m("div", [
|
||||||
|
m("div", {key: 0}, "a"),
|
||||||
|
m("div", {key: 1}, "b"),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
// PREFER
|
||||||
|
m("div", [
|
||||||
|
m("div", "a"),
|
||||||
|
[
|
||||||
|
m("div", {key: 1}, "b"),
|
||||||
|
]
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Avoid passing model data directly to components if the model uses `key` as a data property
|
||||||
|
|
||||||
|
The `key` property may appear in your data model in a way that conflicts with Mithril's key logic. For example, a component may represent an entity whose `key` property is expected to change over time. This can lead to components receiving the wrong data, re-initialise, or change positions unexpectedly. If your data model uses the `key` property, make sure to wrap the data such that Mithril doesn't misinterpret it as a rendering instruction:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Data model
|
||||||
|
var users = [
|
||||||
|
{id: 1, name: "John", key: 'a'},
|
||||||
|
{id: 2, name: "Mary", key: 'b'},
|
||||||
|
]
|
||||||
|
|
||||||
|
// Later on...
|
||||||
|
users[0].key = 'c'
|
||||||
|
|
||||||
|
// AVOID
|
||||||
|
users.map(function(user){
|
||||||
|
// The component for John will be destroyed and recreated
|
||||||
|
return m(UserComponent, user)
|
||||||
|
})
|
||||||
|
|
||||||
|
// PREFER
|
||||||
|
users.map(function(user){
|
||||||
|
// Key is specifically extracted: data model is given its own property
|
||||||
|
return m(UserComponent, {key: user.id, model: user})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
|
||||||
11
docs/lint.js
11
docs/lint.js
|
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
"use strict"
|
||||||
|
|
||||||
var fs = require("fs")
|
var fs = require("fs")
|
||||||
var path = require("path")
|
var path = require("path")
|
||||||
|
|
@ -100,11 +101,11 @@ function ensureLinkIsValid(file, data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function initMocks() {
|
function initMocks() {
|
||||||
global.window = require("../test-utils/browserMock")()
|
global.window = require("../test-utils/browserMock")() // eslint-disable-line global-require
|
||||||
global.document = window.document
|
global.document = window.document
|
||||||
global.m = require("../index")
|
global.m = require("../index") // eslint-disable-line global-require
|
||||||
global.o = require("../ospec/ospec")
|
global.o = require("../ospec/ospec") // eslint-disable-line global-require
|
||||||
global.stream = require("../stream")
|
global.stream = require("../stream") // eslint-disable-line global-require
|
||||||
global.alert = function() {}
|
global.alert = function() {}
|
||||||
|
|
||||||
//routes consumed by request.md
|
//routes consumed by request.md
|
||||||
|
|
@ -121,7 +122,7 @@ function initMocks() {
|
||||||
"GET /api/v1/todos": function() {
|
"GET /api/v1/todos": function() {
|
||||||
return {status: 200, responseText: JSON.stringify([])}
|
return {status: 200, responseText: JSON.stringify([])}
|
||||||
},
|
},
|
||||||
"PUT /api/v1/users/1": function() {
|
"PUT /api/v1/users/1": function(request) {
|
||||||
return {status: 200, responseText: request.query.callback ? request.query.callback + "([])" : "[]"}
|
return {status: 200, responseText: request.query.callback ? request.query.callback + "([])" : "[]"}
|
||||||
},
|
},
|
||||||
"POST /api/v1/upload": function() {
|
"POST /api/v1/upload": function() {
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ In contrast, traversing a javascript data structure has a much more predictable
|
||||||
|
|
||||||
### Differences from m.render
|
### Differences from m.render
|
||||||
|
|
||||||
A component rendered via `m.mount` automatically auto-redraws in response to view events, `m.redraw()` calls or `m.request()` calls. Vnodes rendered via `m.render()` do not. Note that calls to `m.prop()` do not trigger auto-redraws.
|
A component rendered via `m.mount` automatically auto-redraws in response to view events, `m.redraw()` calls or `m.request()` calls. Vnodes rendered via `m.render()` do not.
|
||||||
|
|
||||||
`m.mount()` is suitable for application developers integrating Mithril widgets into existing codebases where routing is handled by another library or framework, while still enjoying Mithril's auto-redrawing facilities.
|
`m.mount()` is suitable for application developers integrating Mithril widgets into existing codebases where routing is handled by another library or framework, while still enjoying Mithril's auto-redrawing facilities.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,8 @@ Argument | Type | Required | Descr
|
||||||
`options.headers` | `Object` | No | Headers to append to the request before sending it (applied right before `options.config`).
|
`options.headers` | `Object` | No | Headers to append to the request before sending it (applied right before `options.config`).
|
||||||
`options.type` | `any = Function(any)` | No | A constructor to be applied to each object in the response. Defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function).
|
`options.type` | `any = Function(any)` | No | A constructor to be applied to each object in the response. Defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function).
|
||||||
`options.serialize` | `string = Function(any)` | No | A serialization method to be applied to `data`. Defaults to `JSON.stringify`, or if `options.data` is an instance of [`FormData`](https://developer.mozilla.org/en/docs/Web/API/FormData), defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function) (i.e. `function(value) {return value}`).
|
`options.serialize` | `string = Function(any)` | No | A serialization method to be applied to `data`. Defaults to `JSON.stringify`, or if `options.data` is an instance of [`FormData`](https://developer.mozilla.org/en/docs/Web/API/FormData), defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function) (i.e. `function(value) {return value}`).
|
||||||
`options.deserialize` | `any = Function(string)` | No | A deserialization method to be applied to the response. Defaults to a small wrapper around `JSON.parse` that returns `null` for empty responses.
|
`options.deserialize` | `any = Function(string)` | No | A deserialization method to be applied to the `xhr.responseText`. Defaults to a small wrapper around `JSON.parse` that returns `null` for empty responses. If `extract` is defined, `deserialize` will be skipped.
|
||||||
`options.extract` | `string = Function(xhr, options)` | No | A hook to specify how the XMLHttpRequest response should be read. Useful for reading response headers and cookies. Defaults to a function that returns `xhr.responseText`. If defined, the `xhr` parameter is the XMLHttpRequest instance used for the request, and `options` is the object that was passed to the `m.request` call. If a custom `extract` callback is set, `options.deserialize` is ignored and the string returned from the extract callback will not automatically be parsed as JSON.
|
`options.extract` | `any = Function(xhr, options)` | No | A hook to specify how the XMLHttpRequest response should be read. Useful for processing response data, reading headers and cookies. By default this is a function that returns `xhr.responseText`, which is in turn passed to `deserialize`. If a custom `extract` callback is provided, the `xhr` parameter is the XMLHttpRequest instance used for the request, and `options` is the object that was passed to the `m.request` call. Additionally, `deserialize` will be skipped and the value returned from the extract callback will not automatically be parsed as JSON.
|
||||||
`options.useBody` | `Boolean` | No | Force the use of the HTTP body section for `data` in `GET` requests when set to `true`, or the use of querystring for other HTTP methods when set to `false`. Defaults to `false` for `GET` requests and `true` for other methods.
|
`options.useBody` | `Boolean` | No | Force the use of the HTTP body section for `data` in `GET` requests when set to `true`, or the use of querystring for other HTTP methods when set to `false`. Defaults to `false` for `GET` requests and `true` for other methods.
|
||||||
`options.background` | `Boolean` | No | If `false`, redraws mounted components upon completion of the request. If `true`, it does not. Defaults to `false`.
|
`options.background` | `Boolean` | No | If `false`, redraws mounted components upon completion of the request. If `true`, it does not. Defaults to `false`.
|
||||||
**returns** | `Promise` | | A promise that resolves to the response data, after it has been piped through the `extract`, `deserialize` and `type` methods
|
**returns** | `Promise` | | A promise that resolves to the response data, after it has been piped through the `extract`, `deserialize` and `type` methods
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
- [m.route.get](#mrouteget)
|
- [m.route.get](#mrouteget)
|
||||||
- [m.route.prefix](#mrouteprefix)
|
- [m.route.prefix](#mrouteprefix)
|
||||||
- [m.route.link](#mroutelink)
|
- [m.route.link](#mroutelink)
|
||||||
|
- [m.route.param](#mrouteparam)
|
||||||
- [RouteResolver](#routeresolver)
|
- [RouteResolver](#routeresolver)
|
||||||
- [routeResolver.onmatch](#routeresolveronmatch)
|
- [routeResolver.onmatch](#routeresolveronmatch)
|
||||||
- [routeResolver.render](#routeresolverrender)
|
- [routeResolver.render](#routeresolverrender)
|
||||||
|
|
@ -100,12 +101,41 @@ Argument | Type | Required | Description
|
||||||
|
|
||||||
##### m.route.link
|
##### m.route.link
|
||||||
|
|
||||||
`eventHandler = m.route.link(vnode)`
|
This function can be used as the `oncreate` (and `onupdate`) hook in a `m("a")` vnode:
|
||||||
|
|
||||||
|
```JS
|
||||||
|
m("a[href=/]", {oncreate: m.route.link})`.
|
||||||
|
```
|
||||||
|
|
||||||
|
Using `m.route.link` as a `oncreate` hook causes the link to behave as a router link (i.e. it navigates to the route specified in `href`, instead of nagivating away from the current page to the URL specified in `href`.
|
||||||
|
|
||||||
|
If the `href` attribute is not static, the `onupdate` hook must also be set:
|
||||||
|
|
||||||
|
```JS
|
||||||
|
m("a", {href: someVariable, oncreate: m.route.link, onupdate: m.route.link})`
|
||||||
|
```
|
||||||
|
|
||||||
|
`m.route.link(vnode)`
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
Argument | Type | Required | Description
|
||||||
----------------- | ----------- | -------- | ---
|
----------------- | ----------- | -------- | ---
|
||||||
`vnode` | `Vnode` | Yes | This method is meant to be used in conjunction with an `<a>` [vnode](vnodes.md)'s [`oncreate` hook](lifecycle-methods.md)
|
`vnode` | `Vnode` | Yes | This method is meant to be used as or in conjunction with an `<a>` [vnode](vnodes.md)'s [`oncreate` and `onupdate` hooks](lifecycle-methods.md)
|
||||||
**returns** | Function(e) | | Returns an event handler that calls `m.route.set` with the link's `href` as the `path`
|
**returns** | | | Returns `undefined`
|
||||||
|
|
||||||
|
##### m.route.param
|
||||||
|
|
||||||
|
Retrieves a route parameter. A route parameter is a key-value pair. Route parameters may come from a few different places:
|
||||||
|
|
||||||
|
- route interpolations (e.g. if a route is `/users/:id`, and it resolves to `/users/1`, the route parameter has a key `id` and value `"1"`)
|
||||||
|
- router querystrings (e.g. if the path is `/users?page=1`, the route parameter has a key `page` and value `"1"`)
|
||||||
|
- `history.state` (e.g. if history.state is `{foo: "bar"}`, the route parameter has key `foo` and value `"bar"`)
|
||||||
|
|
||||||
|
`value = m.route.param(key)`
|
||||||
|
|
||||||
|
Argument | Type | Required | Description
|
||||||
|
----------------- | --------------- | -------- | ---
|
||||||
|
`key` | `String` | No | A route parameter name (e.g. `id` in route `/users/:id`, or `page` in path `/users/1?page=3`, or a key in `history.state`)
|
||||||
|
**returns** | `String|Object` | | Returns a value for the specified key. If a key is not specified, it returns an object that contains all the interpolation keys
|
||||||
|
|
||||||
#### RouteResolver
|
#### RouteResolver
|
||||||
|
|
||||||
|
|
@ -123,11 +153,11 @@ For more information on `onmatch`, see the [advanced component resolution](#adva
|
||||||
|
|
||||||
`routeResolver.onmatch(args, requestedPath)`
|
`routeResolver.onmatch(args, requestedPath)`
|
||||||
|
|
||||||
Argument | Type | Description
|
Argument | Type | Description
|
||||||
--------------- | ------------------------------ | ---
|
--------------- | ---------------------------------------- | ---
|
||||||
`args` | `Object` | The [routing parameters](#routing-parameters)
|
`args` | `Object` | The [routing parameters](#routing-parameters)
|
||||||
`requestedPath` | `String` | The router path requested by the last routing action, including interpolated routing parameter values, but without the prefix. When `onmatch` is called, the resolution for this path is not complete and `m.route.get()` still returns the previous path.
|
`requestedPath` | `String` | The router path requested by the last routing action, including interpolated routing parameter values, but without the prefix. When `onmatch` is called, the resolution for this path is not complete and `m.route.get()` still returns the previous path.
|
||||||
**returns** | `Component|Promise<Component>` | Returns a component or a promise that resolves to a component
|
**returns** | `Component|Promise<Component>|undefined` | Returns a component or a promise that resolves to a component
|
||||||
|
|
||||||
If `onmatch` returns a component or a promise that resolves to a component, this component is used as the `vnode.tag` for the first argument in the RouteResolver's `render` method. Otherwise, `vnode.tag` is set to `"div"`. Similarly, if the `onmatch` method is omitted, `vnode.tag` is also `"div"`.
|
If `onmatch` returns a component or a promise that resolves to a component, this component is used as the `vnode.tag` for the first argument in the RouteResolver's `render` method. Otherwise, `vnode.tag` is set to `"div"`. Similarly, if the `onmatch` method is omitted, `vnode.tag` is also `"div"`.
|
||||||
|
|
||||||
|
|
@ -139,11 +169,11 @@ The `render` method is called on every redraw for a matching route. It is simila
|
||||||
|
|
||||||
`vnode = routeResolve.render(vnode)`
|
`vnode = routeResolve.render(vnode)`
|
||||||
|
|
||||||
Argument | Type | Description
|
Argument | Type | Description
|
||||||
------------------- | --------------- | -----------
|
------------------- | -------------------- | -----------
|
||||||
`vnode` | `Object` | A [vnode](vnodes.md) whose attributes object contains routing parameters. If onmatch does not return a component or a promise that resolves to a component, the vnode's `tag` field defaults to `"div"`
|
`vnode` | `Object` | A [vnode](vnodes.md) whose attributes object contains routing parameters. If onmatch does not return a component or a promise that resolves to a component, the vnode's `tag` field defaults to `"div"`
|
||||||
`vnode.attrs` | `Object` | A map of URL parameter values
|
`vnode.attrs` | `Object` | A map of URL parameter values
|
||||||
**returns** | `Vnode` | Returns a vnode
|
**returns** | `Array<Vnode>|Vnode` | The [vnodes](vnodes.md) to be rendered
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -257,8 +287,6 @@ In the example above, we defined a route `/edit/:id`. This creates a dynamic rou
|
||||||
|
|
||||||
It's possible to have multiple arguments in a route, for example `/edit/:projectID/:userID` would yield the properties `projectID` and `userID` on the component's vnode attributes object.
|
It's possible to have multiple arguments in a route, for example `/edit/:projectID/:userID` would yield the properties `projectID` and `userID` on the component's vnode attributes object.
|
||||||
|
|
||||||
In addition to routing parameters, the `attrs` object also includes a `path` property that contains the current route path, and a `route` property that contains the matched routed.
|
|
||||||
|
|
||||||
#### Key parameter
|
#### Key parameter
|
||||||
|
|
||||||
When a user navigates from a parameterized route to the same route with a different parameter (e.g. going from `/page/1` to `/page/2` given a route `/page/:id`, the component would not be recreated from scratch since both routes resolve to the same component, and thus result in a virtual dom in-place diff. This has the side-effect of triggering the `onupdate` hook, rather than `oninit`/`oncreate`. However, it's relatively common for a developer to want to synchronize the recreation of the component to the route change event.
|
When a user navigates from a parameterized route to the same route with a different parameter (e.g. going from `/page/1` to `/page/2` given a route `/page/:id`, the component would not be recreated from scratch since both routes resolve to the same component, and thus result in a virtual dom in-place diff. This has the side-effect of triggering the `onupdate` hook, rather than `oninit`/`oncreate`. However, it's relatively common for a developer to want to synchronize the recreation of the component to the route change event.
|
||||||
|
|
@ -385,7 +413,7 @@ var Layout = {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In the example above, the layout merely consists of a `<div class="layout">` that contains the children passed to the component, but in a real life scenario it could be as complex as neeeded.
|
In the example above, the layout merely consists of a `<div class="layout">` that contains the children passed to the component, but in a real life scenario it could be as complex as needed.
|
||||||
|
|
||||||
One way to wrap the layout is to define an anonymous component in the routes map:
|
One way to wrap the layout is to define an anonymous component in the routes map:
|
||||||
|
|
||||||
|
|
@ -501,7 +529,7 @@ For the sake of simplicity, in the example above, the user's logged in status is
|
||||||
var Auth = {
|
var Auth = {
|
||||||
username: "",
|
username: "",
|
||||||
password: "",
|
password: "",
|
||||||
|
|
||||||
setUsername: function(value) {
|
setUsername: function(value) {
|
||||||
Auth.username = value
|
Auth.username = value
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ var User = {
|
||||||
module.exports = User
|
module.exports = User
|
||||||
```
|
```
|
||||||
|
|
||||||
Then we can add an `m.request` call to make an XHR request. For this tutorial, we'll make XHR calls to the [REM](http://rem-rest-api.herokuapp.com/) API, a mock REST API designed for rapid prototyping. This API returns a list of users from the `GET http://rem-rest-api.herokuapp.com/api/users` endpoint. Let's use `m.request` to make an XHR request and populate our data with the response of that endpoint.
|
Then we can add an `m.request` call to make an XHR request. For this tutorial, we'll make XHR calls to the [REM](http://rem-rest-api.herokuapp.com/) API, a mock REST API designed for rapid prototyping. This API returns a list of users from the `GET https://rem-rest-api.herokuapp.com/api/users` endpoint. Let's use `m.request` to make an XHR request and populate our data with the response of that endpoint.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// src/models/User.js
|
// src/models/User.js
|
||||||
|
|
@ -85,7 +85,7 @@ var User = {
|
||||||
loadList: function() {
|
loadList: function() {
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "http://rem-rest-api.herokuapp.com/api/users",
|
url: "https://rem-rest-api.herokuapp.com/api/users",
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
|
|
@ -324,7 +324,7 @@ module.exports = {
|
||||||
m("input.input[type=text][placeholder=First name]"),
|
m("input.input[type=text][placeholder=First name]"),
|
||||||
m("label.label", "Last name"),
|
m("label.label", "Last name"),
|
||||||
m("input.input[placeholder=Last name]"),
|
m("input.input[placeholder=Last name]"),
|
||||||
m("button.button[type=submit]", "Save"),
|
m("button.button[type=button]", "Save"),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -357,7 +357,7 @@ var User = {
|
||||||
loadList: function() {
|
loadList: function() {
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "http://rem-rest-api.herokuapp.com/api/users",
|
url: "https://rem-rest-api.herokuapp.com/api/users",
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
|
|
@ -380,7 +380,7 @@ var User = {
|
||||||
loadList: function() {
|
loadList: function() {
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "http://rem-rest-api.herokuapp.com/api/users",
|
url: "https://rem-rest-api.herokuapp.com/api/users",
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
|
|
@ -392,7 +392,7 @@ var User = {
|
||||||
load: function(id) {
|
load: function(id) {
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "http://rem-rest-api.herokuapp.com/api/users/:id",
|
url: "https://rem-rest-api.herokuapp.com/api/users/:id",
|
||||||
data: {id: id},
|
data: {id: id},
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
|
|
@ -420,7 +420,7 @@ module.exports = {
|
||||||
m("input.input[type=text][placeholder=First name]", {value: User.current.firstName}),
|
m("input.input[type=text][placeholder=First name]", {value: User.current.firstName}),
|
||||||
m("label.label", "Last name"),
|
m("label.label", "Last name"),
|
||||||
m("input.input[placeholder=Last name]", {value: User.current.lastName}),
|
m("input.input[placeholder=Last name]", {value: User.current.lastName}),
|
||||||
m("button.button[type=submit]", "Save"),
|
m("button.button[type=button]", "Save"),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -461,7 +461,12 @@ var User = require("../models/User")
|
||||||
module.exports = {
|
module.exports = {
|
||||||
oninit: function(vnode) {User.load(vnode.attrs.id)},
|
oninit: function(vnode) {User.load(vnode.attrs.id)},
|
||||||
view: function() {
|
view: function() {
|
||||||
return m("form", [
|
return m("form", {
|
||||||
|
onsubmit: function(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
User.save()
|
||||||
|
}
|
||||||
|
}, [
|
||||||
m("label.label", "First name"),
|
m("label.label", "First name"),
|
||||||
m("input.input[type=text][placeholder=First name]", {
|
m("input.input[type=text][placeholder=First name]", {
|
||||||
oninput: m.withAttr("value", function(value) {User.current.firstName = value}),
|
oninput: m.withAttr("value", function(value) {User.current.firstName = value}),
|
||||||
|
|
@ -472,7 +477,7 @@ module.exports = {
|
||||||
oninput: m.withAttr("value", function(value) {User.current.lastName = value}),
|
oninput: m.withAttr("value", function(value) {User.current.lastName = value}),
|
||||||
value: User.current.lastName
|
value: User.current.lastName
|
||||||
}),
|
}),
|
||||||
m("button.button[type=submit]", {onclick: User.save}, "Save"),
|
m("button.button[type=button]", "Save"),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -491,7 +496,7 @@ var User = {
|
||||||
loadList: function() {
|
loadList: function() {
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "http://rem-rest-api.herokuapp.com/api/users",
|
url: "https://rem-rest-api.herokuapp.com/api/users",
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
|
|
@ -503,7 +508,7 @@ var User = {
|
||||||
load: function(id) {
|
load: function(id) {
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "http://rem-rest-api.herokuapp.com/api/users/:id",
|
url: "https://rem-rest-api.herokuapp.com/api/users/:id",
|
||||||
data: {id: id},
|
data: {id: id},
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
|
|
@ -515,7 +520,7 @@ var User = {
|
||||||
save: function() {
|
save: function() {
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
url: "http://rem-rest-api.herokuapp.com/api/users/:id",
|
url: "https://rem-rest-api.herokuapp.com/api/users/:id",
|
||||||
data: User.current,
|
data: User.current,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,14 @@ Streams are NOT bundled with Mithril's core distribution. To include the Streams
|
||||||
var Stream = require("mithril/stream")
|
var Stream = require("mithril/stream")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also download the module directly if your environment does not support a bundling toolchain:
|
||||||
|
|
||||||
|
```markup
|
||||||
|
<script src="https://unpkg.com/mithril-stream"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
When loaded directly with a `<script>` tag (rather than required), the stream library will be exposed as `window.m.stream`. If `window.m` is already defined (e.g. because you also use the main Mithril script), it will attach itself to the existing object. Otherwise it creates a new `window.m`. If you want to use streams in conjunction with Mithril as raw script tags, you should include Mithril in your page before `mithril-stream`, because `mithril` will otherwise overwrite the `window.m` object defined by `mithril-stream`. This is not a concern when the libraries are consumed as CommonJS modules (using `require(...)`).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Signature
|
### Signature
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,22 @@ npm test
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Running mithril in a non-browser environment
|
||||||
|
|
||||||
|
Mithril has a few dependencies on globals that exist in all its supported browser environments but are missing in all non-browser environments. To work around this you can use the browser mocks that ship with the mithril npm package.
|
||||||
|
|
||||||
|
The simplest way to do this is ensure the following snippet of code runs **before** you include mithril itself in your project.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Polyfill DOM env for mithril
|
||||||
|
global.window = require("mithril/test-utils/browserMock.js")();
|
||||||
|
global.document = window.document;
|
||||||
|
```
|
||||||
|
|
||||||
|
Once that snippet has been run you can `require("mithril")` and it should be quite happy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Good testing practices
|
### Good testing practices
|
||||||
|
|
||||||
Generally speaking, there are two ways to write tests: upfront and after the fact.
|
Generally speaking, there are two ways to write tests: upfront and after the fact.
|
||||||
|
|
|
||||||
|
|
@ -73,9 +73,11 @@ Property | Type | Description
|
||||||
`text` | `(String|Number|Boolean)?` | This is used instead of `children` if a vnode contains a text node as its only child. This is done for performance reasons. Component vnodes never use the `text` property even if they have a text node as their only child.
|
`text` | `(String|Number|Boolean)?` | This is used instead of `children` if a vnode contains a text node as its only child. This is done for performance reasons. Component vnodes never use the `text` property even if they have a text node as their only child.
|
||||||
`dom` | `Element?` | Points to the element that corresponds to the vnode. This property is `undefined` in the `oninit` lifecycle method. In fragments and trusted HTML vnodes, `dom` points to the first element in the range.
|
`dom` | `Element?` | Points to the element that corresponds to the vnode. This property is `undefined` in the `oninit` lifecycle method. In fragments and trusted HTML vnodes, `dom` points to the first element in the range.
|
||||||
`domSize` | `Number?` | This is only set in fragment and trusted HTML vnodes, and it's `undefined` in all other vnode types. It defines the number of DOM elements that the vnode represents (starting from the element referenced by the `dom` property).
|
`domSize` | `Number?` | This is only set in fragment and trusted HTML vnodes, and it's `undefined` in all other vnode types. It defines the number of DOM elements that the vnode represents (starting from the element referenced by the `dom` property).
|
||||||
`state` | `Object` | An object that is persisted between redraws. In component vnodes, `state` is a shallow clone of the component object.
|
`state` | `Object`? | An object that is persisted between redraws. It is provided by the core engine when needed. In component vnodes, the `state` inherits prototypically from the component object/class.
|
||||||
`events` | `Object?` | An object that is persisted between redraws and that stores event handlers so that they can be removed using the DOM API. The `events` property is `undefined` if there are no event handlers defined. This property is only used internally by Mithril, do not use it.
|
`events` | `Object?` | An object that is persisted between redraws and that stores event handlers so that they can be removed using the DOM API. The `events` property is `undefined` if there are no event handlers defined. This property is only used internally by Mithril, do not use it.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Vnode types
|
### Vnode types
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue