Update migration, fix various minor issues

- Lot of people couldn't migrate to v1 and plan to reevaluate when v2 is
  released.
- It's "npm" not "NPM". It doesn't stand for anything, and it never
  has - it was initially chosen simply because it was easy to type.
  It has a lot of unofficial backronyms with "Node Package Manager"
  being one of the most common ones, but it's never officially stood
  for anything as an acronym *or* initialism.
- Fixed a few errors in the change log, like non-breaking changes being
  included in the "Breaking Changes" section and an inaccuracy in the
  summary of a particular change.
- Fixed RawGit URLs to point to GitHack, which is a lighter proxy that
  offloads caching to Cloudflare instead of also implementing it itself.
  (It also just uses nginx for all the important server logic, so it
  scales better.)
- Add a few more v0.2 references as appropriate
This commit is contained in:
Isiah Meadows 2019-07-24 05:01:20 -04:00
parent 8186818e10
commit 234b1c9302
26 changed files with 1389 additions and 935 deletions

View file

@ -1,4 +1,4 @@
mithril.js [![NPM Version](https://img.shields.io/npm/v/mithril.svg)](https://www.npmjs.com/package/mithril) [![NPM License](https://img.shields.io/npm/l/mithril.svg)](https://www.npmjs.com/package/mithril) [![NPM Downloads](https://img.shields.io/npm/dm/mithril.svg)](https://www.npmjs.com/package/mithril) [![Donate at OpenCollective](https://img.shields.io/opencollective/all/mithriljs.svg?colorB=brightgreen)](https://opencollective.com/mithriljs)
mithril.js [![npm Version](https://img.shields.io/npm/v/mithril.svg)](https://www.npmjs.com/package/mithril) [![npm License](https://img.shields.io/npm/l/mithril.svg)](https://www.npmjs.com/package/mithril) [![npm Downloads](https://img.shields.io/npm/dm/mithril.svg)](https://www.npmjs.com/package/mithril) [![Donate at OpenCollective](https://img.shields.io/opencollective/all/mithriljs.svg?colorB=brightgreen)](https://opencollective.com/mithriljs)
==========
<p align="center">
@ -29,17 +29,19 @@ Mithril supports IE11, Firefox ESR, and the last two versions of Firefox, Edge,
### CDN
```html
<script src="https://unpkg.com/mithril@next/mithril.js"></script>
<!-- or -->
<script src="https://cdn.jsdelivr.net/npm/mithril@next/mithril.js"></script>
<!-- Development: whichever you prefer -->
<script src="https://unpkg.com/mithril/mithril.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mithril/mithril.js"></script>
<!-- Production: whichever you prefer -->
<script src="https://unpkg.com/mithril/mithril.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mithril/mithril.min.js"></script>
```
### npm
```bash
# For the most recent stable version
$ npm install mithril --save
# For the most recent unstable version
$ npm install mithril@next --save
npm install mithril --save
```
The ["Getting started" guide](https://mithril.js.org/#getting-started) is a good place to start learning how to use mithril.

View file

@ -9,7 +9,7 @@
### Technology choices
Animations are often used to make applications come alive. Nowadays, browsers have good support for CSS animations, and there are [various](https://greensock.com/gsap) [libraries](http://velocityjs.org/) that provide fast JavaScript-based animations. There's also an upcoming [Web API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Using_the_Web_Animations_API) and a [polyfill](https://github.com/web-animations/web-animations-js) if you like living on the bleeding edge.
Animations are often used to make applications come alive. Nowadays, browsers have good support for CSS animations, and there are [various](https://greensock.com/gsap) [libraries](https://velocityjs.org/) that provide fast JavaScript-based animations. There's also an upcoming [Web API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Using_the_Web_Animations_API) and a [polyfill](https://github.com/web-animations/web-animations-js) if you like living on the bleeding edge.
Mithril does not provide any animation APIs per se, since these other options are more than sufficient to achieve rich, complex animations. Mithril does, however, offer hooks to make life easier in some specific cases where it's traditionally difficult to make animations work.
@ -102,4 +102,4 @@ Note that the `onbeforeremove` hook only fires on the element that loses its `pa
When creating animations, it's recommended that you only use the `opacity` and `transform` CSS rules, since these can be hardware-accelerated by modern browsers and yield better performance than animating `top`, `left`, `width`, and `height`.
It's also recommended that you avoid the `box-shadow` rule and selectors like `:nth-child`, since these are also resource intensive options. If you want to animate a `box-shadow`, consider [putting the `box-shadow` rule on a pseudo element, and animate that element's opacity instead](http://tobiasahlin.com/blog/how-to-animate-box-shadow/). Other things that can be expensive include large or dynamically scaled images and overlapping elements with different `position` values (e.g. an absolute positioned element over a fixed element).
It's also recommended that you avoid the `box-shadow` rule and selectors like `:nth-child`, since these are also resource intensive options. If you want to animate a `box-shadow`, consider [putting the `box-shadow` rule on a pseudo element, and animate that element's opacity instead](https://tobiasahlin.com/blog/how-to-animate-box-shadow/). Other things that can be expensive include large or dynamically scaled images and overlapping elements with different `position` values (e.g. an absolute positioned element over a fixed element).

View file

@ -41,7 +41,7 @@ var Home = {
}
m.route(document.body, "/home", {
"/home": Home, // defines `http://localhost/#!/home`
"/home": Home, // defines `https://example.com/#!/home`
})
```

View file

@ -1,17 +1,11 @@
# Change log
- [v2.0.0-rc](#v200rc)
- [v1.1.6](#v116)
- [v1.1.5](#v115)
- [v1.1.4](#v114)
- [v1.1.3](#v113)
- [v1.1.2](#v112)
- [v1.1.1](#v111)
- [v1.1.0](#v110)
- [v1.0.1](#v101)
- [Migrating from v0.2.x](#migrating-from-v02x)
- [v1.x docs](http://mithril.js.org/archive/v1.1.6/index.html)
- [v0.2 docs](http://mithril.js.org/archive/v0.2.5/index.html)
- [v2.0.0](#v200)
- [Migrating from v1.x](migration-v1x.md)
- [Migrating from v0.2.x](migration-v02x.md)
- [v1.x changelog](https://mithril.js.org/archive/v1.1.6/change-log.html)
- [v1.x docs](https://mithril.js.org/archive/v1.1.6/index.html)
- [v0.2 docs](https://mithril.js.org/archive/v0.2.5/index.html)
- [`ospec` change log](https://github.com/MithrilJS/mithril.js/blob/master/ospec/change-log.md)
- [`mithril/stream` change log](https://github.com/MithrilJS/mithril.js/blob/master/stream/change-log.md)
@ -19,7 +13,7 @@
### Upcoming...
### v2.0.0-rc
### v2.0.0
#### Breaking changes
@ -31,9 +25,8 @@
- hyperscript: when attributes have a `null` or `undefined` value, they are treated as if they were absent. [#1773](https://github.com/MithrilJS/mithril.js/issues/1773) ([#2174](https://github.com/MithrilJS/mithril.js/pull/2174))
- API: `m.request` errors no longer copy response fields to the error, but instead assign the parsed JSON response to `error.response` and the HTTP status code `error.code`.
- hyperscript: when an attribute is defined on both the first and second argument (as a CSS selector and an `attrs` field, respectively), the latter takes precedence, except for `class` attributes that are still added together. [#2172](https://github.com/MithrilJS/mithril.js/issues/2172) ([#2174](https://github.com/MithrilJS/mithril.js/pull/2174))
- render: Align custom elements to work like normal elements, minus all the HTML-specific magic. ([#2221](https://github.com/MithrilJS/mithril.js/pull/2221))
- cast className using toString ([#2309](https://github.com/MithrilJS/mithril.js/pull/2309))
- render: call attrs' hooks first, with express exception of `onbeforeupdate` to allow attrs to block components from even diffing ([#2297](https://github.com/MithrilJS/mithril.js/pull/2297))
- render: call attrs' hooks last, with express exception of `onbeforeupdate` to allow attrs to block components from even diffing ([#2297](https://github.com/MithrilJS/mithril.js/pull/2297))
- API: `m.withAttr` removed. ([#2317](https://github.com/MithrilJS/mithril.js/pull/2317))
- request: `data` has now been split to `params` and `body` and `useBody` has been removed in favor of just using `body`. ([#2361](https://github.com/MithrilJS/mithril.js/pull/2361))
- route, request: Interpolated arguments are URL-escaped (and for declared routes, URL-unescaped) automatically. If you want to use a raw route parameter, use a variadic parameter like in `/asset/:path.../view`. This was previously only available in `m.route` route definitions, but it's now usable in both that and where paths are accepted. ([#2361](https://github.com/MithrilJS/mithril.js/pull/2361))
@ -69,11 +62,6 @@
- The new component handles many more edge cases around user interaction, including accessibility.
- Link navigation can be disabled and cancelled.
- Link targets can be trivially changed.
- API: Full DOM no longer required to execute `require("mithril")`. You just need to set the necessary globals to *something*, even if `null` or `undefined`, so they can be properly used. ([#2469](https://github.com/MithrilJS/mithril.js/pull/2469) [@isiahmeadows](https://github.com/isiahmeadows))
- This enables isomorphic use of `m.route.Link` and `m.route.prefix`.
- This enables isomorphic use of `m.request`, provided the `background: true` option is set and that an `XMLHttpRequest` polyfill is included as necessary.
- Note that methods requiring DOM operations will still throw errors, such as `m.render(...)`, `m.redraw()`, and `m.route(...)`.
#### News
@ -100,6 +88,11 @@
- route: Use `m.mount(root, null)` to unsubscribe and clean up after a `m.route(root, ...)` call. ([#2453](https://github.com/MithrilJS/mithril.js/pull/2453))
- render: new `redraw` parameter exposed any time a child event handler is used ([#2458](https://github.com/MithrilJS/mithril.js/pull/2458) [@isiahmeadows](https://github.com/isiahmeadows))
- route: `m.route.SKIP` can be returned from route resolvers to skip to the next route ([#2469](https://github.com/MithrilJS/mithril.js/pull/2469) [@isiahmeadows](https://github.com/isiahmeadows))
- API: Full DOM no longer required to execute `require("mithril")`. You just need to set the necessary globals to *something*, even if `null` or `undefined`, so they can be properly used. ([#2469](https://github.com/MithrilJS/mithril.js/pull/2469) [@isiahmeadows](https://github.com/isiahmeadows))
- This enables isomorphic use of `m.route.Link` and `m.route.prefix`.
- This enables isomorphic use of `m.request`, provided the `background: true` option is set and that an `XMLHttpRequest` polyfill is included as necessary.
- Note that methods requiring DOM operations will still throw errors, such as `m.render(...)`, `m.redraw()`, and `m.route(...)`.
- render: Align custom elements to work like normal elements, minus all the HTML-specific magic. ([#2221](https://github.com/MithrilJS/mithril.js/pull/2221))
#### Bug fixes
@ -135,826 +128,3 @@
- docs: clarify valid key usage ([#2452](https://github.com/MithrilJS/mithril.js/pull/2452) [@isiahmeadows](https://github.com/isiahmeadows))
- route: don't pollute globals ([#2453](https://github.com/MithrilJS/mithril.js/pull/2453) [@isiahmeadows](https://github.com/isiahmeadows))
- request: track xhr replacements correctly ([#2455](https://github.com/MithrilJS/mithril.js/pull/2455) [@isiahmeadows](https://github.com/isiahmeadows))
---
### v1.2.0
#### News
- Promise polyfill implementation separated from polyfilling logic.
- `PromisePolyfill` is now available on the exported/global `m`.
#### Bug fixes
- core: Workaround for [Internet Explorer bug](https://www.tjvantoll.com/2013/08/30/bugs-with-document-activeelement-in-internet-explorer/) when running in an iframe
#### Note
- Stream references no longer magically coerce to their underlying values ([#2150](https://github.com/MithrilJS/mithril.js/pull/2150), stream breaking change: `mithril-stream@2.0.0`)
---
### v1.1.6
#### Bug fixes
- core: render() function can no longer prevent from changing `document.activeElement` in lifecycle hooks ([#1988](https://github.com/MithrilJS/mithril.js/pull/1988), [@purplecode](https://github.com/purplecode))
- core: don't call `onremove` on the children of components that return null from the view [#1921](https://github.com/MithrilJS/mithril.js/issues/1921) [@octavore](https://github.com/octavore) ([#1922](https://github.com/MithrilJS/mithril.js/pull/1922))
- hypertext: correct handling of shared attributes object passed to `m()`. Will copy attributes when it's necessary [#1941](https://github.com/MithrilJS/mithril.js/issues/1941) [@s-ilya](https://github.com/s-ilya) ([#1942](https://github.com/MithrilJS/mithril.js/pull/1942))
#### Ospec improvements
- ospec v1.4.0
- Added support for async functions and promises in tests ([#1928](https://github.com/MithrilJS/mithril.js/pull/1928), [@StephanHoyer](https://github.com/StephanHoyer))
- Error handling for async tests with `done` callbacks supports error as first argument ([#1928](https://github.com/MithrilJS/mithril.js/pull/1928))
- Error messages which include newline characters do not swallow the stack trace [#1495](https://github.com/MithrilJS/mithril.js/issues/1495) ([#1984](https://github.com/MithrilJS/mithril.js/pull/1984), [@RodericDay](https://github.com/RodericDay))
- ospec v2.0.0 (to be released)
- Added support for custom reporters ([#2009](https://github.com/MithrilJS/mithril.js/pull/2020))
- Make Ospec more [Flems](https://flems.io)-friendly ([#2034](https://github.com/MithrilJS/mithril.js/pull/2034))
- Works either as a global or in CommonJS environments
- the o.run() report is always printed asynchronously (it could be synchronous before if none of the tests were async).
- Properly point to the assertion location of async errors [#2036](https://github.com/MithrilJS/mithril.js/issues/2036)
- expose the default reporter as `o.report(results)`
- Don't try to access the stack traces in IE9
---
### v1.1.5
#### Bug fixes
- API: If a user sets the Content-Type header within a request's options, that value will be the entire header value rather than being appended to the default value [#1919](https://github.com/MithrilJS/mithril.js/issues/1919) ([#1924](https://github.com/MithrilJS/mithril.js/pull/1924), [@tskillian](https://github.com/tskillian))
---
### v1.1.4
#### Bug fixes
- Fix IE bug where active element is null causing render function to throw error ([#1943](https://github.com/MithrilJS/mithril.js/pull/1943), [@JacksonJN](https://github.com/JacksonJN))
#### Ospec improvements:
- Log using util.inspect to show object content instead of "[object Object]" ([#1661](https://github.com/MithrilJS/mithril.js/issues/1661), [@porsager](https://github.com/porsager))
---
### v1.1.3
#### Bug fixes
- move out npm dependencies added by mistake
---
### v1.1.2
#### Bug fixes
- core: Namespace fixes [#1819](https://github.com/MithrilJS/mithril.js/issues/1819), ([#1825](https://github.com/MithrilJS/mithril.js/pull/1825) [@SamuelTilly](https://github.com/SamuelTilly)), [#1820](https://github.com/MithrilJS/mithril.js/issues/1820) ([#1864](https://github.com/MithrilJS/mithril.js/pull/1864)), [#1872](https://github.com/MithrilJS/mithril.js/issues/1872) ([#1873](https://github.com/MithrilJS/mithril.js/pull/1873))
- core: Fix select option to allow empty string value [#1814](https://github.com/MithrilJS/mithril.js/issues/1814) ([#1828](https://github.com/MithrilJS/mithril.js/pull/1828) [@spacejack](https://github.com/spacejack))
- core: Reset e.redraw when it was set to `false` [#1850](https://github.com/MithrilJS/mithril.js/issues/1850) ([#1890](https://github.com/MithrilJS/mithril.js/pull/1890))
- core: differentiate between `{ value: "" }` and `{ value: 0 }` for form elements [#1595 comment](https://github.com/MithrilJS/mithril.js/pull/1595#issuecomment-304071453) ([#1862](https://github.com/MithrilJS/mithril.js/pull/1862))
- core: Don't reset the cursor of textareas in IE10 when setting an identical `value` [#1870](https://github.com/MithrilJS/mithril.js/issues/1870) ([#1871](https://github.com/MithrilJS/mithril.js/pull/1871))
- hypertext: Correct handling of `[value=""]` ([#1843](https://github.com/MithrilJS/mithril.js/issues/1843), [@CreaturesInUnitards](https://github.com/CreaturesInUnitards))
- router: Don't overwrite the options object when redirecting from `onmatch with m.route.set()` [#1857](https://github.com/MithrilJS/mithril.js/issues/1857) ([#1889](https://github.com/MithrilJS/mithril.js/pull/1889))
- stream: Move the "use strict" directive inside the IIFE [#1831](https://github.com/MithrilJS/mithril.js/issues/1831) ([#1893](https://github.com/MithrilJS/mithril.js/pull/1893))
---
#### Docs / Repo maintenance
Our thanks to [@0joshuaolson1](https://github.com/0joshuaolson1), [@ACXgit](https://github.com/ACXgit), [@cavemansspa](https://github.com/cavemansspa), [@CreaturesInUnitards](https://github.com/CreaturesInUnitards), [@dlepaux](https://github.com/dlepaux), [@isaaclyman](https://github.com/isaaclyman), [@kevinkace](https://github.com/kevinkace), [@micellius](https://github.com/micellius), [@spacejack](https://github.com/spacejack) and [@yurivish](https://github.com/yurivish)
#### Other
- Addition of a performance regression test suite ([#1789](https://github.com/MithrilJS/mithril.js/issues/1789))
---
### v1.1.1
#### Bug fixes
- hyperscript: Allow `0` as the second argument to `m()` - [#1752](https://github.com/MithrilJS/mithril.js/issues/1752) / [#1753](https://github.com/MithrilJS/mithril.js/pull/1753) ([@StephanHoyer](https://github.com/StephanHoyer))
- hyperscript: restore `attrs.class` handling to what it was in v1.0.1 - [#1764](https://github.com/MithrilJS/mithril.js/issues/1764) / [#1769](https://github.com/MithrilJS/mithril.js/pull/1769)
- documentation improvements ([@JAForbes](https://github.com/JAForbes), [@smuemd](https://github.com/smuemd), [@hankeypancake](https://github.com/hankeypancake))
---
### v1.1.0
#### News
- support for ES6 class components
- support for closure components
- improvements in build and release automation
#### Bug fixes
- fix IE11 input[type] error - [#1610](https://github.com/MithrilJS/mithril.js/issues/1610)
- apply [#1609](https://github.com/MithrilJS/mithril.js/issues/1609) to unkeyed children case
- fix abort detection [#1612](https://github.com/MithrilJS/mithril.js/issues/1612)
- fix input value focus issue when value is loosely equal to old value [#1593](https://github.com/MithrilJS/mithril.js/issues/1593)
---
### v1.0.1
#### News
- performance improvements in IE [#1598](https://github.com/MithrilJS/mithril.js/pull/1598)
#### Bug fixes
- prevent infinite loop in non-existent default route - [#1579](https://github.com/MithrilJS/mithril.js/issues/1579)
- call correct lifecycle methods on children of recycled keyed vnodes - [#1609](https://github.com/MithrilJS/mithril.js/issues/1609)
---
### Migrating from `v0.2.x`
`v1.x` is largely API-compatible with `v0.2.x`, but there are some breaking changes.
If you are migrating, consider using the [mithril-codemods](https://www.npmjs.com/package/mithril-codemods) tool to help automate the most straightforward migrations.
- [`m.prop` removed](#mprop-removed)
- [`m.component` removed](#mcomponent-removed)
- [`config` function](#config-function)
- [Changes in redraw behaviour](#changes-in-redraw-behaviour)
- [No more redraw locks](#no-more-redraw-locks)
- [Cancelling redraw from event handlers](#cancelling-redraw-from-event-handlers)
- [Synchronous redraw removed](#synchronous-redraw-removed)
- [`m.startComputation`/`m.endComputation` removed](#mstartcomputationmendcomputation-removed)
- [Component `controller` function](#component-controller-function)
- [Component arguments](#component-arguments)
- [`view()` parameters](#view-parameters)
- [Passing components to `m()`](#passing-components-to-m)
- [Passing vnodes to `m.mount()` and `m.route()`](#passing-vnodes-to-mmount-and-mroute)
- [`m.route.mode`](#mroutemode)
- [`m.route` and anchor tags](#mroute-and-anchor-tags)
- [Reading/writing the current route](#readingwriting-the-current-route)
- [Accessing route params](#accessing-route-params)
- [Building/Parsing query strings](#buildingparsing-query-strings)
- [Preventing unmounting](#preventing-unmounting)
- [Run code on component removal](#run-code-on-component-removal)
- [`m.request`](#mrequest)
- [`m.deferred` removed](#mdeferred-removed)
- [`m.sync` removed](#msync-removed)
- [`xlink` namespace required](#xlink-namespace-required)
- [Nested arrays in views](#nested-arrays-in-views)
- [`vnode` equality checks](#vnode-equality-checks)
---
## `m.prop` removed
In `v1.x`, `m.prop()` is now a more powerful stream micro-library, but it's no longer part of core. You can read about how to use the optional Streams module in [the documentation](stream.md).
### `v0.2.x`
```javascript
var m = require("mithril")
var num = m.prop(1)
```
### `v1.x`
```javascript
var m = require("mithril")
var prop = require("mithril/stream")
var num = prop(1)
var doubled = num.map(function(n) {return n * 2})
```
---
## `m.component` removed
In `v0.2.x` components could be created using either `m(component)` or `m.component(component)`. `v1.x` only supports `m(component)`.
### `v0.2.x`
```javascript
// These are equivalent
m.component(component)
m(component)
```
### `v1.x`
```javascript
m(component)
```
---
## `config` function
In `v0.2.x` mithril provided a single lifecycle method, `config`. `v1.x` provides much more fine-grained control over the lifecycle of a vnode.
### `v0.2.x`
```javascript
m("div", {
config : function(element, isInitialized) {
// runs on each redraw
// isInitialized is a boolean representing if the node has been added to the DOM
}
})
```
### `v1.x`
More documentation on these new methods is available in [lifecycle-methods.md](lifecycle-methods.md).
```javascript
m("div", {
// Called before the DOM node is created
oninit : function(vnode) { /*...*/ },
// Called after the DOM node is created
oncreate : function(vnode) { /*...*/ },
// Called before the node is updated, return false to cancel
onbeforeupdate : function(vnode, old) { /*...*/ },
// Called after the node is updated
onupdate : function(vnode) { /*...*/ },
// Called before the node is removed, return a Promise that resolves when
// ready for the node to be removed from the DOM
onbeforeremove : function(vnode) { /*...*/ },
// Called before the node is removed, but after onbeforeremove calls done()
onremove : function(vnode) { /*...*/ }
})
```
If available the DOM-Element of the vnode can be accessed at `vnode.dom`.
---
## Changes in redraw behaviour
Mithril's rendering engine still operates on the basis of semi-automated global redraws, but some APIs and behaviours differ:
### No more redraw locks
In v0.2.x, Mithril allowed 'redraw locks' which temporarily prevented blocked draw logic: by default, `m.request` would lock the draw loop on execution and unlock when all pending requests had resolved - the same behaviour could be invoked manually using `m.startComputation()` and `m.endComputation()`. The latter APIs and the associated behaviour has been removed in v1.x. Redraw locking can lead to buggy UIs: the concerns of one part of the application should not be allowed to prevent other parts of the view from updating to reflect change.
### Cancelling redraw from event handlers
`m.mount()` and `m.route()` still automatically redraw after a DOM event handler runs. Cancelling these redraws from within your event handlers is now done by setting the `redraw` property on the passed-in event object to `false`.
#### `v0.2.x`
```javascript
m("div", {
onclick : function(e) {
m.redraw.strategy("none")
}
})
```
#### `v1.x`
```javascript
m("div", {
onclick : function(e) {
e.redraw = false
}
})
```
### Synchronous redraw removed
In v0.2.x it was possible to force mithril to redraw immediately by passing a truthy value to `m.redraw()`. This behavior complicated usage of `m.redraw()` and caused some hard-to-reason about issues and has been removed.
#### `v0.2.x`
```javascript
m.redraw(true) // redraws immediately & synchronously
```
#### `v1.x`
```javascript
m.redraw() // schedules a redraw on the next requestAnimationFrame tick
```
### `m.startComputation`/`m.endComputation` removed
They are considered anti-patterns and have a number of problematic edge cases, so they no longer exist in v1.x.
---
## Component `controller` function
In `v1.x` there is no more `controller` property in components, use `oninit` instead.
### `v0.2.x`
```javascript
m.mount(document.body, {
controller : function() {
var ctrl = this
ctrl.fooga = 1
},
view : function(ctrl) {
return m("p", ctrl.fooga)
}
})
```
### `v1.x`
```javascript
m.mount(document.body, {
oninit : function(vnode) {
vnode.state.fooga = 1
},
view : function(vnode) {
return m("p", vnode.state.fooga)
}
})
// OR
m.mount(document.body, {
oninit : function(vnode) {
var state = this // this is bound to vnode.state by default
state.fooga = 1
},
view : function(vnode) {
var state = this // this is bound to vnode.state by default
return m("p", state.fooga)
}
})
```
---
## Component arguments
Arguments to a component in `v1.x` must be an object, simple values like `String`/`Number`/`Boolean` will be treated as text children. Arguments are accessed within the component by reading them from the `vnode.attrs` object.
### `v0.2.x`
```javascript
var component = {
controller : function(options) {
// options.fooga === 1
},
view : function(ctrl, options) {
// options.fooga == 1
}
}
m("div", m.component(component, { fooga : 1 }))
```
### `v1.x`
```javascript
var component = {
oninit : function(vnode) {
// vnode.attrs.fooga === 1
},
view : function(vnode) {
// vnode.attrs.fooga == 1
}
}
m("div", m(component, { fooga : 1 }))
```
---
## `view()` parameters
In `v0.2.x` view functions are passed a reference to the `controller` instance and (optionally) any options passed to the component. In `v1.x` they are passed **only** the `vnode`, exactly like the `controller` function.
### `v0.2.x`
```javascript
m.mount(document.body, {
controller : function() {},
view : function(ctrl, options) {
// ...
}
})
```
### `v1.x`
```javascript
m.mount(document.body, {
oninit : function(vnode) {
// ...
},
view : function(vnode) {
// Use vnode.state instead of ctrl
// Use vnode.attrs instead of options
}
})
```
---
## Passing components to `m()`
In `v0.2.x` you could pass components as the second argument of `m()` w/o any wrapping required. To help with consistency in `v1.x` they must always be wrapped with a `m()` invocation.
### `v0.2.x`
```javascript
m("div", component)
```
### `v1.x`
```javascript
m("div", m(component))
```
---
## Passing vnodes to `m.mount()` and `m.route()`
In `v0.2.x`, `m.mount(element, component)` tolerated [vnodes](vnodes.md) as second arguments instead of [components](components.md) (even though it wasn't documented). Likewise, `m.route(element, defaultRoute, routes)` accepted vnodes as values in the `routes` object.
In `v1.x`, components are required instead in both cases.
### `v0.2.x`
```javascript
m.mount(element, m('i', 'hello'))
m.mount(element, m(Component, attrs))
m.route(element, '/', {
'/': m('b', 'bye')
})
```
### `v1.x`
```javascript
m.mount(element, {view: function () {return m('i', 'hello')}})
m.mount(element, {view: function () {return m(Component, attrs)}})
m.route(element, '/', {
'/': {view: function () {return m('b', 'bye')}}
})
```
---
## `m.route.mode`
In `v0.2.x` the routing mode could be set by assigning a string of `"pathname"`, `"hash"`, or `"search"` to `m.route.mode`. In `v.1.x` it is replaced by `m.route.prefix(prefix)` where `prefix` can be `#`, `?`, or an empty string (for "pathname" mode). The new API also supports hashbang (`#!`), which is the default, and it supports non-root pathnames and arbitrary mode variations such as querybang (`?!`)
### `v0.2.x`
```javascript
m.route.mode = "pathname"
m.route.mode = "search"
```
### `v1.x`
```javascript
m.route.prefix("")
m.route.prefix("?")
```
---
## `m.route()` and anchor tags
Handling clicks on anchor tags via the mithril router is similar to `v0.2.x` but uses a new lifecycle method and API.
### `v0.2.x`
```javascript
// When clicked this link will load the "/path" route instead of navigating
m("a", {
href : "/path",
config : m.route
})
```
### `v1.x`
```javascript
// When clicked this link will load the "/path" route instead of navigating
m("a", {
href : "/path",
oncreate : m.route.link
})
```
---
## Reading/writing the current route
In `v0.2.x` all interaction w/ the current route happened via `m.route()`. In `v1.x` this has been broken out into two functions.
### `v0.2.x`
```javascript
// Getting the current route
m.route()
// Setting a new route
m.route("/other/route")
```
### `v1.x`
```javascript
// Getting the current route
m.route.get()
// Setting a new route
m.route.set("/other/route")
```
---
## Accessing route params
In `v0.2.x` reading route params was entirely handled through `m.route.param()`. This API is still available in `v1.x`, and additionally any route params are passed as properties in the `attrs` object on the vnode.
### `v0.2.x`
```javascript
m.route(document.body, "/booga", {
"/:attr" : {
controller : function() {
m.route.param("attr") // "booga"
},
view : function() {
m.route.param("attr") // "booga"
}
}
})
```
### `v1.x`
```javascript
m.route(document.body, "/booga", {
"/:attr" : {
oninit : function(vnode) {
vnode.attrs.attr // "booga"
m.route.param("attr") // "booga"
},
view : function(vnode) {
vnode.attrs.attr // "booga"
m.route.param("attr") // "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
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.
### `v0.2.x`
```javascript
var Component = {
controller: function() {
this.onunload = function(e) {
if (condition) e.preventDefault()
}
},
view: function() {
return m("a[href=/]", {config: m.route})
}
}
```
### `v1.x`
```javascript
var Component = {
view: function() {
return m("a", {onclick: function() {if (!condition) m.route.set("/")}})
}
}
```
---
## Run code on component removal
Components no longer call `this.onunload` when they are being removed. They now use the standardized lifecycle hook `onremove`.
### `v0.2.x`
```javascript
var Component = {
controller: function() {
this.onunload = function(e) {
// ...
}
},
view: function() {
// ...
}
}
```
### `v1.x`
```javascript
var Component = {
onremove : function() {
// ...
}
view: function() {
// ...
}
}
```
---
## m.request
Promises returned by [m.request](request.md) are no longer `m.prop` getter-setters. In addition, `initialValue`, `unwrapSuccess` and `unwrapError` are no longer supported options.
In addition, requests no longer have `m.startComputation`/`m.endComputation` semantics. Instead, redraws are always triggered when a request promise chain completes (unless `background:true` is set).
### `v0.2.x`
```javascript
var data = m.request({
method: "GET",
url: "https://api.github.com/",
initialValue: [],
})
setTimeout(function() {
console.log(data())
}, 1000)
```
### `v1.x`
```javascript
var data = []
m.request({
method: "GET",
url: "https://api.github.com/",
})
.then(function (responseBody) {
data = responseBody
})
setTimeout(function() {
console.log(data) // note: not a getter-setter
}, 1000)
```
Additionally, if the `extract` option is passed to `m.request` the return value of the provided function will be used directly to resolve the request promise, and the `deserialize` callback is ignored.
---
## `m.deferred` removed
`v0.2.x` used its own custom asynchronous contract object, exposed as `m.deferred`, which was used as the basis for `m.request`. `v1.x` uses Promises instead, and implements a [polyfill](promises.md) in non-supporting environments. In situations where you would have used `m.deferred`, you should use Promises instead.
### `v0.2.x`
```javascript
var greetAsync = function() {
var deferred = m.deferred()
setTimeout(function() {
deferred.resolve("hello")
}, 1000)
return deferred.promise
}
greetAsync()
.then(function(value) {return value + " world"})
.then(function(value) {console.log(value)}) //logs "hello world" after 1 second
```
### `v1.x`
```javascript
var greetAsync = function() {
return new Promise(function(resolve){
setTimeout(function() {
resolve("hello")
}, 1000)
})
}
greetAsync()
.then(function(value) {return value + " world"})
.then(function(value) {console.log(value)}) //logs "hello world" after 1 second
```
---
## `m.sync` removed
Since `v1.x` uses standards-compliant Promises, `m.sync` is redundant. Use `Promise.all` instead.
### `v0.2.x`
```javascript
m.sync([
m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }),
])
.then(function (users) {
console.log("Contributors:", users[0].name, "and", users[1].name)
})
```
### `v1.x`
```javascript
Promise.all([
m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }),
])
.then(function (users) {
console.log("Contributors:", users[0].name, "and", users[1].name)
})
```
---
## `xlink` namespace required
In `v0.2.x`, the `xlink` namespace was the only supported attribute namespace, and it was supported via special casing behavior. Now namespace parsing is fully supported, and namespaced attributes should explicitly declare their namespace.
### `v0.2.x`
```javascript
m("svg",
// the `href` attribute is namespaced automatically
m("image[href='image.gif']")
)
```
### `v1.x`
```javascript
m("svg",
// User-specified namespace on the `href` attribute
m("image[xlink:href='image.gif']")
)
```
---
## Nested arrays in views
Arrays now represent [fragments](fragment.md), which are structurally significant in v1.x virtual DOM. Whereas nested arrays in v0.2.x would be flattened into one continuous list of virtual nodes for the purposes of diffing, v1.x preserves the array structure - the children of any given array are not considered siblings of those of adjacent arrays.
---
## `vnode` equality checks
If a vnode is strictly equal to the vnode occupying its place in the last draw, v1.x will skip that part of the tree without checking for mutations or triggering any lifecycle methods in the subtree. The component documentation contains [more detail on this issue](components.md#avoid-creating-component-instances-outside-views).

View file

@ -68,7 +68,7 @@ members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
available at [https://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
[homepage]: https://contributor-covenant.org
[version]: https://contributor-covenant.org/version/1/4/

View file

@ -21,6 +21,6 @@ Special thanks to:
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](https://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)
- the countless people who have reported and fixed bugs, participated in discussions, and helped promote Mithril

View file

@ -2,11 +2,10 @@
Here are some examples of Mithril in action
- [Animation](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/animation/mosaic.html)
- [Animation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/animation/mosaic.html)
- [Community Added Examples](https://how-to-mithril.js.org)
- [DBMonster](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html)
- [Markdown Editor](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/editor/index.html)
- SVG: [Clock](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/svg/clock.html), [Ring](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/svg/ring.html), [Tiger](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/svg/tiger.html)
- [ThreadItJS](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/threaditjs/index.html)
- [TodoMVC](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/todomvc/index.html)
- [DBMonster](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html)
- [Markdown Editor](https://raw.githack.com/MithrilJS/mithril.js/master/examples/editor/index.html)
- SVG: [Clock](https://raw.githack.com/MithrilJS/mithril.js/master/examples/svg/clock.html), [Ring](https://raw.githack.com/MithrilJS/mithril.js/master/examples/svg/ring.html), [Tiger](https://raw.githack.com/MithrilJS/mithril.js/master/examples/svg/tiger.html)
- [ThreadItJS](https://raw.githack.com/MithrilJS/mithril.js/master/examples/threaditjs/index.html)
- [TodoMVC](https://raw.githack.com/MithrilJS/mithril.js/master/examples/todomvc/index.html)

View file

@ -74,7 +74,7 @@ What these numbers show is that not only does Mithril initializes significantly
Update performance can be even more important than first-render performance, since updates can happen many times while a Single Page Application is running.
A useful tool to benchmark update performance is a tool developed by the Ember team called DbMonster. It updates a table as fast as it can and measures frames per second (FPS) and JavaScript times (min, max and mean). The FPS count can be difficult to evaluate since it also includes browser repaint times and `setTimeout` clamping delay, so the most meaningful number to look at is the mean render time. You can compare a [React implementation](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/dbmonster/react/index.html) and a [Mithril implementation](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Sample results are shown below:
A useful tool to benchmark update performance is a tool developed by the Ember team called DbMonster. It updates a table as fast as it can and measures frames per second (FPS) and JavaScript times (min, max and mean). The FPS count can be difficult to evaluate since it also includes browser repaint times and `setTimeout` clamping delay, so the most meaningful number to look at is the mean render time. You can compare a [React implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/react/index.html) and a [Mithril implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Sample results are shown below:
React | Mithril
------- | -------
@ -139,7 +139,7 @@ Also, remember that frameworks like Angular and Mithril are designed for non-tri
##### Update performance
A useful tool to benchmark update performance is a tool developed by the Ember team called DbMonster. It updates a table as fast as it can and measures frames per second (FPS) and JavaScript times (min, max and mean). The FPS count can be difficult to evaluate since it also includes browser repaint times and `setTimeout` clamping delay, so the most meaningful number to look at is the mean render time. You can compare an [Angular implementation](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/dbmonster/angular/index.html) and a [Mithril implementation](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Both implementations are naive (i.e. no optimizations). Sample results are shown below:
A useful tool to benchmark update performance is a tool developed by the Ember team called DbMonster. It updates a table as fast as it can and measures frames per second (FPS) and JavaScript times (min, max and mean). The FPS count can be difficult to evaluate since it also includes browser repaint times and `setTimeout` clamping delay, so the most meaningful number to look at is the mean render time. You can compare an [Angular implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/angular/index.html) and a [Mithril implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Both implementations are naive (i.e. no optimizations). Sample results are shown below:
Angular | Mithril
------- | -------
@ -193,7 +193,7 @@ Library load times matter in applications that don't stay open for long periods
##### Update performance
A useful tool to benchmark update performance is a tool developed by the Ember team called DbMonster. It updates a table as fast as it can and measures frames per second (FPS) and JavaScript times (min, max and mean). The FPS count can be difficult to evaluate since it also includes browser repaint times and `setTimeout` clamping delay, so the most meaningful number to look at is the mean render time. You can compare a [Vue implementation](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/dbmonster/vue/index.html) and a [Mithril implementation](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Both implementations are naive (i.e. no optimizations). Sample results are shown below:
A useful tool to benchmark update performance is a tool developed by the Ember team called DbMonster. It updates a table as fast as it can and measures frames per second (FPS) and JavaScript times (min, max and mean). The FPS count can be difficult to evaluate since it also includes browser repaint times and `setTimeout` clamping delay, so the most meaningful number to look at is the mean render time. You can compare a [Vue implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/vue/index.html) and a [Mithril implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Both implementations are naive (i.e. no optimizations). Sample results are shown below:
Vue | Mithril
------ | -------

View file

@ -122,8 +122,8 @@ m("section.container")
m("input[type=text][placeholder=Name]")
// <input type="text" placeholder="Name" />
m("a#exit.external[href='http://example.com']", "Leave")
// <a id="exit" class="external" href="http://example.com">Leave</a>
m("a#exit.external[href='https://example.com']", "Leave")
// <a id="exit" class="external" href="https://example.com">Leave</a>
```
If you omit the tag name, Mithril assumes a `div` tag.
@ -215,7 +215,7 @@ m("a-scene", [
])
```
And yes, this translates to both attributes and properties, and it works just like they would in the DOM. Using [Brick's `brick-deck`](http://brick.mozilla.io/docs/brick-deck) as an example, they have a `selected-index` attribute with a corresponding `selectedIndex` getter/setter property.
And yes, this translates to both attributes and properties, and it works just like they would in the DOM. Using [Brick's `brick-deck`](https://brick.mozilla.io/docs/brick-deck) as an example, they have a `selected-index` attribute with a corresponding `selectedIndex` getter/setter property.
```javascript
m("brick-deck[selected-index=0]", [/* ... */]) // lowercase
@ -232,7 +232,7 @@ For custom elements, it doesn't auto-stringify properties, in case they are obje
m("my-special-element", {
whitelist: [
"https://example.com",
"http://neverssl.com",
"https://neverssl.com",
"https://google.com",
],
})
@ -462,7 +462,7 @@ You cannot use JavaScript statements such as `if` or `for` within JavaScript exp
In Mithril, well-formed HTML is valid JSX. Little effort other than copy-pasting is required to integrate an independently produced HTML file into a project using JSX.
When using hyperscript, it's necessary to convert HTML to hyperscript syntax before the code can be run. To facilitate this, you can [use the HTML-to-Mithril-template converter](http://arthurclemens.github.io/mithril-template-converter/index.html).
When using hyperscript, it's necessary to convert HTML to hyperscript syntax before the code can be run. To facilitate this, you can [use the HTML-to-Mithril-template converter](https://arthurclemens.github.io/mithril-template-converter/index.html).
---

View file

@ -69,7 +69,7 @@ Let's create an HTML file to follow along:
To make things simpler you can fork this pen which already has the latest version of mithril loaded.
<p data-height="265" data-theme-id="light" data-slug-hash="XRrXVR" data-default-tab="js,result" data-user="tivac" data-embed-version="2" data-pen-title="Mithril Scaffold" data-preview="true" class="codepen">See the Pen <a href="https://codepen.io/tivac/pen/XRrXVR/">Mithril Scaffold</a> by Pat Cavit (<a href="http://codepen.io/tivac">@tivac</a>) on <a href="http://codepen.io">CodePen</a>.</p>
<p data-height="265" data-theme-id="light" data-slug-hash="XRrXVR" data-default-tab="js,result" data-user="tivac" data-embed-version="2" data-pen-title="Mithril Scaffold" data-preview="true" class="codepen">See the Pen <a href="https://codepen.io/tivac/pen/XRrXVR/">Mithril Scaffold</a> by Pat Cavit (<a href="https://codepen.io/tivac">@tivac</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
Mithril is also loaded onto this page already, so you can start poking at the `m` object in the developer console right away if you'd like!
@ -96,7 +96,7 @@ As you can see, you use the same code to both create and update HTML. Mithril au
#### Live Example
<p data-height="265" data-theme-id="light" data-slug-hash="KmPdOO" data-default-tab="js,result" data-user="tivac" data-embed-version="2" data-pen-title="Mithril Hello World" data-preview="true" class="codepen">See the Pen <a href="https://codepen.io/tivac/pen/KmPdOO/">Mithril Hello World</a> by Pat Cavit (<a href="http://codepen.io/tivac">@tivac</a>) on <a href="http://codepen.io">CodePen</a>.</p>
<p data-height="265" data-theme-id="light" data-slug-hash="KmPdOO" data-default-tab="js,result" data-user="tivac" data-embed-version="2" data-pen-title="Mithril Hello World" data-preview="true" class="codepen">See the Pen <a href="https://codepen.io/tivac/pen/KmPdOO/">Mithril Hello World</a> by Pat Cavit (<a href="https://codepen.io/tivac">@tivac</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
---
@ -135,7 +135,7 @@ m("main", [
#### Live Example
<p data-height="275" data-theme-id="light" data-slug-hash="gWYade" data-default-tab="js,result" data-user="tivac" data-embed-version="2" data-pen-title="Simple Mithril Example" data-preview="true" class="codepen">See the Pen <a href="https://codepen.io/tivac/pen/gWYade/">Simple Mithril Example</a> by Pat Cavit (<a href="http://codepen.io/tivac">@tivac</a>) on <a href="http://codepen.io">CodePen</a>.</p>
<p data-height="275" data-theme-id="light" data-slug-hash="gWYade" data-default-tab="js,result" data-user="tivac" data-embed-version="2" data-pen-title="Simple Mithril Example" data-preview="true" class="codepen">See the Pen <a href="https://codepen.io/tivac/pen/gWYade/">Simple Mithril Example</a> by Pat Cavit (<a href="https://codepen.io/tivac">@tivac</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
Note: If you prefer `<html>` syntax, [it's possible to use it via a Babel plugin](jsx.md).
@ -206,7 +206,7 @@ If you're wondering about performance, it turns out Mithril is very fast at rend
#### Live Example
<p data-height="300" data-theme-id="light" data-slug-hash="rmBOQV" data-default-tab="js,result" data-user="tivac" data-embed-version="2" data-pen-title="Mithril Component Example" data-preview="true" class="codepen">See the Pen <a href="https://codepen.io/tivac/pen/rmBOQV/">Mithril Component Example</a> by Pat Cavit (<a href="http://codepen.io/tivac">@tivac</a>) on <a href="http://codepen.io">CodePen</a>.</p>
<p data-height="300" data-theme-id="light" data-slug-hash="rmBOQV" data-default-tab="js,result" data-user="tivac" data-embed-version="2" data-pen-title="Mithril Component Example" data-preview="true" class="codepen">See the Pen <a href="https://codepen.io/tivac/pen/rmBOQV/">Mithril Component Example</a> by Pat Cavit (<a href="https://codepen.io/tivac">@tivac</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
---
@ -238,13 +238,13 @@ m.route(root, "/splash", {
The `m.route` function still has the same auto-redrawing functionality that `m.mount` does, and it also enables URL awareness; in other words, it lets Mithril know what to do when it sees a `#!` in the URL.
The `"/splash"` right after `root` means that's the default route, i.e. if the hashbang in the URL doesn't point to one of the defined routes (`/splash` and `/hello`, in our case), then Mithril redirects to the default route. So if you open the page in a browser and your URL is `http://localhost`, then you get redirected to `http://localhost/#!/splash`.
The `"/splash"` right after `root` means that's the default route, i.e. if the hashbang in the URL doesn't point to one of the defined routes (`/splash` and `/hello`, in our case), then Mithril redirects to the default route. So if you open the page in a browser and your URL is `https://localhost`, then you get redirected to `https://localhost/#!/splash`.
Also, as you would expect, clicking on the link on the splash page takes you to the click counter screen we created earlier. Notice that now your URL will point to `http://localhost/#!/hello`. You can navigate back and forth to the splash page using the browser's back and next button.
Also, as you would expect, clicking on the link on the splash page takes you to the click counter screen we created earlier. Notice that now your URL will point to `https://localhost/#!/hello`. You can navigate back and forth to the splash page using the browser's back and next button.
#### Live Example
<p data-height="300" data-theme-id="light" data-slug-hash="qmWOvr" data-default-tab="js,result" data-user="tivac" data-embed-version="2" data-pen-title="Mithril Routing Example" data-preview="true" class="codepen">See the Pen <a href="https://codepen.io/tivac/pen/qmWOvr/">Mithril Routing Example</a> by Pat Cavit (<a href="http://codepen.io/tivac">@tivac</a>) on <a href="http://codepen.io">CodePen</a>.</p>
<p data-height="300" data-theme-id="light" data-slug-hash="qmWOvr" data-default-tab="js,result" data-user="tivac" data-embed-version="2" data-pen-title="Mithril Routing Example" data-preview="true" class="codepen">See the Pen <a href="https://codepen.io/tivac/pen/qmWOvr/">Mithril Routing Example</a> by Pat Cavit (<a href="https://codepen.io/tivac">@tivac</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
---
@ -253,7 +253,7 @@ Also, as you would expect, clicking on the link on the splash page takes you to
Basically, XHR is just a way to talk to a server.
Let's change our click counter to make it save data on a server. For the server, we'll use [REM](http://rem-rest-api.herokuapp.com), a mock REST API designed for toy apps like this tutorial.
Let's change our click counter to make it save data on a server. For the server, we'll use [REM](https://rem-rest-api.herokuapp.com), a mock REST API designed for toy apps like this tutorial.
First we create a function that calls `m.request`. The `url` specifies an endpoint that represents a resource, the `method` specifies the type of action we're taking (typically the `PUT` method [upserts](https://en.wiktionary.org/wiki/upsert)), `body` is the payload that we're sending to the endpoint and `withCredentials` means to enable cookies (a requirement for the REM API to work)
@ -291,7 +291,7 @@ Clicking the button should now update the count.
#### Live Example
<p data-height="265" data-theme-id="light" data-slug-hash="WjeQBW" data-default-tab="js,result" data-user="tivac" data-embed-version="2" data-pen-title="Mithril XHR Example" data-preview="true" class="codepen">See the Pen <a href="https://codepen.io/tivac/pen/WjeQBW/">Mithril XHR Example</a> by Pat Cavit (<a href="http://codepen.io/tivac">@tivac</a>) on <a href="http://codepen.io">CodePen</a>.</p>
<p data-height="265" data-theme-id="light" data-slug-hash="WjeQBW" data-default-tab="js,result" data-user="tivac" data-embed-version="2" data-pen-title="Mithril XHR Example" data-preview="true" class="codepen">See the Pen <a href="https://codepen.io/tivac/pen/WjeQBW/">Mithril XHR Example</a> by Pat Cavit (<a href="https://codepen.io/tivac">@tivac</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
---

View file

@ -1,7 +1,7 @@
# Installation
- [CDN](#cdn)
- [NPM](#npm)
- [npm](#npm)
- [Quick start with Webpack](#quick-start-with-webpack)
- [TypeScript](#typescript)
@ -15,7 +15,7 @@ If you're new to JavaScript or just want a very simple setup to get your feet we
---
### NPM
### npm
```bash
$ npm install mithril --save
@ -83,11 +83,11 @@ $ npm start
#### Step by step
For production-level projects, the recommended way of installing Mithril is to use NPM.
For production-level projects, the recommended way of installing Mithril is to use npm.
NPM (Node package manager) is the default package manager that is bundled w/ Node.js. It is widely used as the package manager for both client-side and server-side libraries in the JavaScript ecosystem. Download and install [Node.js](https://nodejs.org); NPM will be automatically installed as well.
npm is the default package manager that is bundled w/ Node.js. It is widely used as the package manager for both client-side and server-side libraries in the JavaScript ecosystem. Download and install [Node](https://nodejs.org); npm is bundled with that and installed alongside it.
To use Mithril via NPM, go to your project folder, and run `npm init --yes` from the command line. This will create a file called `package.json`.
To use Mithril via npm, go to your project folder, and run `npm init --yes` from the command line. This will create a file called `package.json`.
```bash
npm init --yes
@ -113,11 +113,11 @@ m.render(document.body, "hello world")
Modularization is the practice of separating the code into files. Doing so makes it easier to find code, understand what code relies on what code, and test.
CommonJS is a de-facto standard for modularizing JavaScript code, and it's used by Node.js, as well as tools like [Browserify](http://browserify.org/) and [Webpack](https://webpack.js.org/). It's a robust, battle-tested precursor to ES6 modules. Although the syntax for ES6 modules is specified in Ecmascript 6, the actual module loading mechanism is not. If you wish to use ES6 modules despite the non-standardized status of module loading, you can use tools like [Rollup](http://rollupjs.org/) or [Babel](https://babeljs.io/).
CommonJS is a de-facto standard for modularizing JavaScript code, and it's used by Node.js, as well as tools like [Browserify](https://browserify.org/) and [Webpack](https://webpack.js.org/). It's a robust, battle-tested precursor to ES6 modules. Although the syntax for ES6 modules is specified in Ecmascript 6, the actual module loading mechanism is not. If you wish to use ES6 modules despite the non-standardized status of module loading, you can use tools like [Rollup](https://rollupjs.org/) or [Babel](https://babeljs.io/).
Most browser today do not natively support modularization systems (CommonJS or ES6), so modularized code must be bundled into a single JavaScript file before running in a client-side application.
A popular way for creating a bundle is to setup an NPM script for [Webpack](https://webpack.js.org/). To install Webpack, run this from the command line:
A popular way for creating a bundle is to setup an npm script for [Webpack](https://webpack.js.org/). To install Webpack, run this from the command line:
```bash
npm install webpack webpack-cli --save-dev
@ -140,7 +140,7 @@ The `-d` flag tells webpack to use development mode, which produces source maps
The `--watch` flag tells webpack to watch the file system and automatically recreate `app.js` if file changes are detected.
Now you can run the script via `npm start` in your command line window. This looks up the `webpack` command in the NPM path, reads `index.js` and creates a file called `app.js` which includes both Mithril and the `hello world` code above. If you want to run the `webpack` command directly from the command line, you need to either add `node_modules/.bin` to your PATH, or install webpack globally via `npm install webpack -g`. It's, however, recommended that you always install webpack locally and use npm scripts, to ensure builds are reproducible in different computers.
Now you can run the script via `npm start` in your command line window. This looks up the `webpack` command in the npm path, reads `index.js` and creates a file called `app.js` which includes both Mithril and the `hello world` code above. If you want to run the `webpack` command directly from the command line, you need to either add `node_modules/.bin` to your PATH, or install webpack globally via `npm install webpack -g`. It's, however, recommended that you always install webpack locally and use npm scripts, to ensure builds are reproducible in different computers.
```
npm start
@ -159,7 +159,7 @@ Now that you have created a bundle, you can then reference the `bin/app.js` file
</html>
```
As you've seen above, importing a module in CommonJS is done via the `require` function. You can reference NPM modules by their library names (e.g. `require("mithril")` or `require("jquery")`), and you can reference your own modules via relative paths minus the file extension (e.g. if you have a file called `mycomponent.js` in the same folder as the file you're importing to, you can import it by calling `require("./mycomponent")`).
As you've seen above, importing a module in CommonJS is done via the `require` function. You can reference npm modules by their library names (e.g. `require("mithril")` or `require("jquery")`), and you can reference your own modules via relative paths minus the file extension (e.g. if you have a file called `mycomponent.js` in the same folder as the file you're importing to, you can import it by calling `require("./mycomponent")`).
To export a module, assign what you want to export to the special `module.exports` object:
@ -187,7 +187,7 @@ Note that in this example, we're using `m.mount`, which wires up the component t
If you open bin/app.js, you'll notice that the Webpack bundle is not minified, so this file is not ideal for a live application. To generate a minified file, open `package.json` and add a new npm script:
```
```json
{
"name": "my-project",
"scripts": {
@ -236,21 +236,6 @@ npm start
The source file `index.js` will be compiled (bundled) and a browser window opens showing the result. Any changes in the source files will instantly get recompiled and the browser will refresh reflecting the changes.
#### Mithril bundler
Mithril comes with a bundler tool of its own. It is sufficient for ES5-based projects that have no other dependencies other than Mithril, but it's currently considered experimental for projects that require other NPM dependencies. It produces smaller bundles than webpack, but you should not use it in production yet.
If you want to try it and give feedback, you can open `package.json` and change the npm script for webpack to this:
```
{
"name": "my-project",
"scripts": {
"build": "bundle src/index.js --output bin/app.js --watch"
}
}
```
#### Vanilla
If you don't have the ability to run a bundler script due to company security policies, there's an options to not use a module system at all:

View file

@ -38,9 +38,9 @@ When using JSX, it's possible to interpolate JavaScript expressions within JSX t
```jsx
var greeting = "Hello"
var url = "http://google.com"
var url = "https://google.com"
var link = <a href={url}>{greeting}!</a>
// yields <a href="http://google.com">Hello!</a>
// yields <a href="https://google.com">Hello!</a>
```
Components can be used by using a convention of uppercasing the first letter of the component name:
@ -56,7 +56,7 @@ m.render(document.body, <MyComponent />)
The simplest way to use JSX is via a [Babel](https://babeljs.io/) plugin.
Babel requires NPM, which is automatically installed when you install [Node.js](https://nodejs.org/en/). Once NPM is installed, create a project folder and run this command:
Babel requires npm, which is automatically installed when you install [Node.js](https://nodejs.org/en/). Once npm is installed, create a project folder and run this command:
```bash
npm init -y
@ -340,4 +340,4 @@ function SummaryView() {
In Mithril, well-formed HTML is generally valid JSX. Little more than just pasting raw HTML is required for things to just work. About the only things you'd normally have to do are change unquoted property values like `attr=value` to `attr="value"` and change void elements like `<input>` to `<input />`, this being due to JSX being based on XML and not HTML.
When using hyperscript, you often need to translate HTML to hyperscript syntax to use it. To help speed up this process along, you can use a [community-created HTML-to-Mithril-template converter](http://arthurclemens.github.io/mithril-template-converter/index.html) to do much of it for you.
When using hyperscript, you often need to translate HTML to hyperscript syntax to use it. To help speed up this process along, you can use a [community-created HTML-to-Mithril-template converter](https://arthurclemens.github.io/mithril-template-converter/index.html) to do much of it for you.

View file

@ -176,7 +176,7 @@ function traverseDirectory(pathname, callback) {
//run
traverseDirectory("./docs", function(pathname) {
if (pathname.indexOf(".md") > -1 && !pathname.match(/change-log|node_modules/)) {
if (pathname.indexOf(".md") > -1 && !pathname.match(/change-log|migration-|node_modules/)) {
fs.readFile(pathname, "utf8", function(err, data) {
if (err) console.log(err)
else lint(pathname, data)

935
docs/migration-v02x.md Normal file
View file

@ -0,0 +1,935 @@
# Migrating from v0.2.x
v1.x and v2.x are largely API-compatible with v0.2.x, but there are some breaking changes. Migrating to v2.x is nearly identical, so the notes below apply mostly to both.
If you are migrating, consider using the [mithril-codemods](https://www.npmjs.com/package/mithril-codemods) tool to help automate the most straightforward migrations.
- [`m.prop` removed](#mprop-removed)
- [`m.component` removed](#mcomponent-removed)
- [`m.withAttr` removed](#mwithattr-removed)
- [`m.version` removed](#mversion-removed)
- [`config` function](#config-function)
- [Changes in redraw behaviour](#changes-in-redraw-behaviour)
- [No more redraw locks](#no-more-redraw-locks)
- [Cancelling redraw from event handlers](#cancelling-redraw-from-event-handlers)
- [Synchronous redraw changed](#synchronous-redraw)
- [`m.startComputation`/`m.endComputation` removed](#mstartcomputationmendcomputation-removed)
- [Component `controller` function](#component-controller-function)
- [Component arguments](#component-arguments)
- [Component vnode children](#component-children)
- [DOM vnode children](#dom-vnode-children)
- [Keys](#keys)
- [`view()` parameters](#view-parameters)
- [Passing components to `m()`](#passing-components-to-m)
- [Passing vnodes to `m.mount()` and `m.route()`](#passing-vnodes-to-mmount-and-mroute)
- [`m.route.mode`](#mroutemode)
- [`m.route()` and anchor tags](#mroute-and-anchor-tags)
- [Path templates](#path-templates)
- [Reading/writing the current route](#readingwriting-the-current-route)
- [Accessing route params](#accessing-route-params)
- [Building/Parsing query strings](#buildingparsing-query-strings)
- [Preventing unmounting](#preventing-unmounting)
- [Run code on component removal](#run-code-on-component-removal)
- [`m.request`](#mrequest)
- [Default `responseType` for `m.request`](#default-responsetype-for-mrequest)
- [`m.deferred` removed](#mdeferred-removed)
- [`m.sync` removed](#msync-removed)
- [`xlink` namespace required](#xlink-namespace-required)
- [Nested arrays in views](#nested-arrays-in-views)
- [`vnode` equality checks](#vnode-equality-checks)
---
## `m.prop` removed
In v2.x, `m.prop()` was converted into now a more powerful stream micro-library, but it's no longer part of core. You can read about how to use the optional Streams module in [the documentation](stream.md).
### v0.2.x
```javascript
var m = require("mithril")
var num = m.prop(1)
```
### v2.x
```javascript
var m = require("mithril")
var prop = require("mithril/stream")
var num = prop(1)
var doubled = num.map(function(n) { return n * 2 })
```
---
## `m.component` removed
In v0.2.x components could be created using either `m(Component)` or `m.component(Component)`. v2.x only support `m(Component)`.
### v0.2.x
```javascript
// These are equivalent
m.component(Component)
m(Component)
```
### v2.x
```javascript
m(Component)
```
---
## `m.withAttr` removed
In v0.2.x event listeners could use `oninput: m.withAttr("value", func)` and similar. In v2.x, just read them directly from the event's target. It synergized well with `m.prop`, but since that was removed in favor of an out of core solution and v1.x didn't see similar broad, idiomatic usage of streams, `m.withAttr` lost most of its usefulness.
### v0.2.x
```javascript
var value = m.prop("")
// In your view
m("input[type=text]", {
value: value(),
oninput: m.withAttr("value", value),
})
```
### v2.x
```javascript
var value = ""
// In your view
m("input[type=text]", {
value: value,
oninput: function (ev) { value = ev.target.value },
})
```
---
## `m.version` removed
It served little use in general, and you can always add it back yourself. You should prefer feature detection for knowing what features are available, and the v2.x API is designed to better enable this.
---
## `config` function
In v0.2.x mithril provided a single lifecycle method, `config`. v2.x provide much more fine-grained control over the lifecycle of a vnode.
### v0.2.x
```javascript
m("div", {
config: function(element, isInitialized) {
// runs on each redraw
// isInitialized is a boolean representing if the node has been added to the DOM
}
})
```
### v2.x
More documentation on these new methods is available in [lifecycle-methods.md](lifecycle-methods.md).
```javascript
m("div", {
// Called before the DOM node is created
oninit: function(vnode) { /*...*/ },
// Called after the DOM node is created
oncreate: function(vnode) { /*...*/ },
// Called before the node is updated, return false to cancel
onbeforeupdate: function(vnode, old) { /*...*/ },
// Called after the node is updated
onupdate: function(vnode) { /*...*/ },
// Called before the node is removed, return a Promise that resolves when
// ready for the node to be removed from the DOM
onbeforeremove: function(vnode) { /*...*/ },
// Called before the node is removed, but after onbeforeremove calls done()
onremove: function(vnode) { /*...*/ }
})
```
If available the DOM-Element of the vnode can be accessed at `vnode.dom`.
---
## Changes in redraw behaviour
Mithril's rendering engine still operates on the basis of semi-automated global redraws, but some APIs and behaviours differ:
### No more redraw locks
In v0.2.x, Mithril allowed 'redraw locks' which temporarily prevented blocked draw logic: by default, `m.request` would lock the draw loop on execution and unlock when all pending requests had resolved - the same behaviour could be invoked manually using `m.startComputation()` and `m.endComputation()`. The latter APIs and the associated behaviour has been removed in v2.x without replacement. Redraw locking can lead to buggy UIs: the concerns of one part of the application should not be allowed to prevent other parts of the view from updating to reflect change.
### Cancelling redraw from event handlers
`m.mount()` and `m.route()` still automatically redraw after a DOM event handler runs. Cancelling these redraws from within your event handlers is now done by setting the `redraw` property on the passed-in event object to `false`.
#### v0.2.x
```javascript
m("div", {
onclick: function(e) {
m.redraw.strategy("none")
}
})
```
#### v2.x
```javascript
m("div", {
onclick: function(e) {
e.redraw = false
}
})
```
### Synchronous redraw changed
In v0.2.x it was possible to force mithril to redraw immediately by passing a truthy value to `m.redraw()`. In v2.x, this functionality was split into two different methods for clarity.
#### v0.2.x
```javascript
m.redraw(true) // redraws immediately & synchronously
```
#### v2.x
```javascript
m.redraw() // schedules a redraw on the next requestAnimationFrame tick
m.redraw.sync() // invokes a redraw immediately and waits for it to complete
```
### `m.startComputation`/`m.endComputation` removed
They are considered anti-patterns and have a number of problematic edge cases, so they were removed without replacement in v2.x.
---
## Component `controller` function
In v2.x, there is no more `controller` property in components - use `oninit` instead.
### v0.2.x
```javascript
m.mount(document.body, {
controller: function() {
var ctrl = this
ctrl.fooga = 1
},
view: function(ctrl) {
return m("p", ctrl.fooga)
}
})
```
### v2.x
```javascript
m.mount(document.body, {
oninit: function(vnode) {
vnode.state.fooga = 1
},
view: function(vnode) {
return m("p", vnode.state.fooga)
}
})
// OR
m.mount(document.body, {
// this is bound to vnode.state by default
oninit: function(vnode) {
this.fooga = 1
},
view: function(vnode) {
return m("p", this.fooga)
}
})
```
---
## Component arguments
Arguments to a component in v2.x must be an object, simple values like `String`/`Number`/`Boolean` will be treated as text children. Arguments are accessed within the component by reading them from the `vnode.attrs` object.
### v0.2.x
```javascript
var Component = {
controller: function(options) {
// options.fooga === 1
},
view: function(ctrl, options) {
// options.fooga === 1
}
}
m("div", m.component(Component, { fooga: 1 }))
```
### v2.x
```javascript
var Component = {
oninit: function(vnode) {
// vnode.attrs.fooga === 1
},
view: function(vnode) {
// vnode.attrs.fooga === 1
}
}
m("div", m(Component, { fooga: 1 }))
```
---
## Component vnode children
In v0.2.x, component vnode children were not normalized, just passed as extra arguments, and they were not flattened, either. (Internally, it was just returning a partially applied component that was diffed based on the component being partially applied.) In v2.x, component vnode children are passed via `vnode.children` as a resolved array of children, but like v0.2.x, the individual children themselves are not normalized, nor is the children array flattened.
### v0.2.x
```javascript
var Component = {
controller: function(value, renderProp) {
// value === "value"
// typeof renderProp === "function"
},
view: function(ctrl, value, renderProp) {
// value === "value"
// typeof renderProp === "function"
}
}
m("div", m.component(Component, "value", function(key) { return "child" }))
```
### v2.x
```javascript
var Component = {
oninit: function(vnode) {
// vnode.children[0] === "value"
// typeof vnode.children[1] === "function"
},
view: function(vnode) {
// vnode.children[0] === "value"
// typeof vnode.children[1] === "function"
},
}
m("div", m(Component, "value", function(key) { return "child" }))
```
---
## DOM vnode children
In v0.2.x, the children of DOM nodes were represented literally with no normalization aside from using the children directly if only a single array child is present. It returned a structure more like this, with the strings represented literally.
```js
m("div", "value", ["nested"])
// Becomes:
{
tag: "div",
attrs: {},
children: [
"value",
["nested"],
]
}
```
In v2.x, children of DOM vnodes are normalized to objects of a single consistent structure.
```js
m("div", "value", ["nested"])
// Becomes roughly:
{
tag: "div",
attrs: null,
children: [
{tag: "#", children: "value"},
{tag: "[", children: [
{tag: "#", children: "nested"},
]},
]
}
```
If only a single text child is present on a DOM vnode, it instead sets `text` to that value.
```js
m("div", "value")
// Becomes roughly:
{
tag: "div",
attrs: null,
text: "",
children: undefined,
}
```
See [the vnode docs](vnodes.md) for more details on the v2.x vnode structure and how things are normalized.
*Most of the v2.x vnode properties here are omitted for brevity.*
---
## Keys
In v0.2.x, you could mix keyed and unkeyed vnodes freely.
In v2.x, children lists of both fragments and elements must be either all keyed or all unkeyed. Holes are considered unkeyed for the purposes of this check, too - it no longer ignores them.
If you need to work around it, use the idiom of a fragment containing a single vnode, like `[m("div", {key: whatever})]`.
---
## `view()` parameters
In v0.2.x view functions are passed a reference to the `controller` instance and (optionally) any options passed to the component. In v2.x they are passed **only** the `vnode`, exactly like the `controller` function.
### v0.2.x
```javascript
m.mount(document.body, {
controller: function() {},
view: function(ctrl, options) {
// ...
}
})
```
### v2.x
```javascript
m.mount(document.body, {
oninit: function(vnode) {
// ...
},
view: function(vnode) {
// Use vnode.state instead of ctrl
// Use vnode.attrs instead of options
}
})
```
---
## Passing components to `m()`
In v0.2.x you could pass components as the second argument of `m()` w/o any wrapping required. To help with consistency in v2.x they must always be wrapped with a `m()` invocation.
### v0.2.x
```javascript
m("div", Component)
```
### v2.x
```javascript
m("div", m(Component))
```
---
## Passing vnodes to `m.mount()` and `m.route()`
In v0.2.x, `m.mount(element, component)` tolerated [vnodes](vnodes.md) as second arguments instead of [components](components.md) (even though it wasn't documented). Likewise, `m.route(element, defaultRoute, routes)` accepted vnodes as values in the `routes` object.
In v2.x, components are required instead in both cases.
### v0.2.x
```javascript
m.mount(element, m('i', 'hello'))
m.mount(element, m(Component, attrs))
m.route(element, '/', {
'/': m('b', 'bye')
})
```
### v2.x
```javascript
m.mount(element, {view: function () {return m('i', 'hello')}})
m.mount(element, {view: function () {return m(Component, attrs)}})
m.route(element, '/', {
'/': {view: function () {return m('b', 'bye')}}
})
```
---
## `m.route.mode`
In v0.2.x the routing mode could be set by assigning a string of `"pathname"`, `"hash"`, or `"search"` to `m.route.mode`. In `v.1.x` it is replaced by `m.route.prefix = prefix` where `prefix` can any prefix. If it starts with `#`, it works in "hash" mode, `?` for "search" mode, and any other character (or the empty string) for "pathname" mode. It also supports combinations of the above like `m.route.prefix = "/path/#!"` or `?#`.
The default was changed to also use a `#!` (hashbang) prefix instead of just `#`. So if you were using the default behavior and want to retain your existing URLs, specify `m.route.prefix = "#"` before initializing the routes.
### v0.2.x
```javascript
m.route.mode = "hash"
m.route.mode = "pathname"
m.route.mode = "search"
```
### v2.x
```javascript
// Direct equivalents
m.route.prefix = "#"
m.route.prefix = ""
m.route.prefix = "?"
```
---
## `m.route()` and anchor tags
Handling routable links now uses a special built-in component instead of an attribute. If you were using this on `<button>`s and the like, you can specify that tag name using a `selector: "button"` attribute.
### v0.2.x
```javascript
// When clicked this link will load the "/path" route instead of navigating
m("a", {
href: "/path",
config: m.route
})
```
### v2.x
```javascript
// When clicked this link will load the "/path" route instead of navigating
m(m.route.Link, {
href: "/path",
})
```
---
## Path templates
In v1.x, there were three separate path template syntaxes that, although they were similar, had 2 separately designed syntaxes and 3 different implementations. It was defined in a fairly ad-hoc way, and parameters weren't generally escaped. Now, everything is either encoded if it's `:key`, raw if it's `:key...`. If things are unexpectedly encoded, use `:path...`. It's that simple.
Concretely, here's how it affects each method:
### `m.request` URLs
Path components in v2.x are escaped automatically when interpolated and they read their values from `params`. In v0.2.x, `m.request({url: "/user/:name/photos/:id", data: {name: "a/b", id: "c/d"}})` would send its request with the URL set to `/user/a%2Fb/photos/c/d`. In v2.x, the corresponding `m.request({url: "/user/:name/photos/:id", params: {name: "a/b", id: "c/d"}})` would send its request to `/user/a%2Fb/photos/c%2Fd`. If you deliberately *want* to interpolate a key unescaped, use `:key...` instead.
Interpolations in inline query strings, like in `/api/search?q=:query`, are not performed in v2.x. Pass those via `params` with appropriate key names instead, without specifying it in the query string.
Do note that this applies to `m.jsonp` as well. When migrating from `m.request` + `dataType: "jsonp"` to `m.jsonp`, you also need to be aware of this.
### `m.route(route, params, shouldReplaceHistoryEntry)` paths
These permit interpolations now, and they work identically to that of `m.request`.
### `m.route` route patterns
Path keys of the form `:key...` return their URL decoded in v1.x, but return the raw URL in v2.x.
Previously, stuff like `:key.md` were erroneously accepted, with the resulting parameter's value set to `keymd: "..."`. This is no longer the case - the `.md` is part of the pattern now, not the name.
---
## Reading/writing the current route
In v0.2.x all interaction w/ the current route happened via `m.route()`. In v2.x this has been broken out into two functions.
### v0.2.x
```javascript
// Getting the current route
m.route()
// Setting a new route
m.route("/other/route")
```
### v2.x
```javascript
// Getting the current route
m.route.get()
// Setting a new route
m.route.set("/other/route")
```
---
## Accessing route params
In v0.2.x reading route params was entirely handled through `m.route.param()`. This API is still available in v2.x, and additionally any route params are passed as properties in the `attrs` object on the vnode.
### v0.2.x
```javascript
m.route(document.body, "/booga", {
"/:attr": {
controller: function() {
m.route.param("attr") // "booga"
},
view: function() {
m.route.param("attr") // "booga"
}
}
})
```
### v2.x
```javascript
m.route(document.body, "/booga", {
"/:attr": {
oninit: function(vnode) {
vnode.attrs.attr // "booga"
m.route.param("attr") // "booga"
},
view: function(vnode) {
vnode.attrs.attr // "booga"
m.route.param("attr") // "booga"
}
}
})
```
---
## Building/Parsing query strings
v0.2.x used methods hanging off of `m.route`, `m.route.buildQueryString()` and `m.route.parseQueryString()`. In v2.x these have been broken out and moved to the root `m`.
### v0.2.x
```javascript
var qs = m.route.buildQueryString({ a: 1 });
var obj = m.route.parseQueryString("a=1");
```
### v2.x
```javascript
var qs = m.buildQueryString({ a: 1 });
var obj = m.parseQueryString("a=1");
```
Also, in v2.x, `{key: undefined}` is serialized as `key=undefined` by `m.buildQueryString` and methods that use it like `m.request`. In v0.2.x, the key was omitted and this carried over to `m.request`. If you were previously relying on this, change your code to omit the keys from the object entirely. It may be worth using a simple utility to strip all keys from an object whose values are `undefined` if you can't easily do that and you need to retain v0.2.x behavior.
```javascript
// Call whenever you need to omit `undefined` parameters from an object.
function omitUndefineds(object) {
var result = {}
for (var key in object) {
if ({}.hasOwnProperty.call(object, key)) {
var value = object[key]
if (Array.isArray(value)) {
result[key] = value.map(omitUndefineds)
} else if (value != null && typeof value === "object") {
result[key] = omitUndefineds(value)
} else if (value !== undefined) {
result[key] = value
}
}
}
return result
}
```
---
## 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.
### v0.2.x
```javascript
var Component = {
controller: function() {
this.onunload = function(e) {
if (condition) e.preventDefault()
}
},
view: function() {
return m("a[href=/]", {config: m.route})
}
}
```
### v2.x
```javascript
var Component = {
view: function() {
return m("a", {
onclick: function() { if (!condition) m.route.set("/") },
})
}
}
```
---
## Run code on component removal
Components no longer call `this.onunload` when they are being removed. They now use the standardized lifecycle hook `onremove`.
### v0.2.x
```javascript
var Component = {
controller: function() {
this.onunload = function(e) {
// ...
}
},
view: function() {
// ...
}
}
```
### v2.x
```javascript
var Component = {
onremove: function() {
// ...
}
view: function() {
// ...
}
}
```
---
## `m.request`
Promises returned by [m.request](request.md) are no longer `m.prop` getter-setters. In addition, `initialValue`, `unwrapSuccess` and `unwrapError` are no longer supported options.
In addition, requests no longer have `m.startComputation`/`m.endComputation` semantics. Instead, redraws are always triggered when a request promise chain completes (unless `background: true` is set).
The `data` parameter has now been split into `params`, query parameters interpolated into the URL and appended to the request, and `body`, the body to send in the underlying XHR.
In v0.2.x, you would use a `dataType: "jsonp"` to initiate a JSONP request. In v2.x, you now use [`m.jsonp`](jsonp.md), which carries mostly the same API as `m.request` without the XHR-related parts.
### v0.2.x
```javascript
var data = m.request({
method: "GET",
url: "https://api.github.com/",
initialValue: [],
})
setTimeout(function() {
console.log(data())
}, 1000)
m.request({
method: "POST",
url: "https://api.github.com/",
data: someJson,
})
```
### v2.x
```javascript
var data = []
m.request({
method: "GET",
url: "https://api.github.com/",
})
.then(function (responseBody) {
data = responseBody
})
setTimeout(function() {
console.log(data) // note: not a getter-setter
}, 1000)
m.request({
method: "POST",
url: "https://api.github.com/",
body: someJson,
})
// OR
var data = []
m.request("https://api.github.com/")
.then(function (responseBody) {
data = responseBody
})
setTimeout(function() {
console.log(data) // note: not a getter-setter
}, 1000)
m.request("https://api.github.com/", {
method: "POST",
body: someJson,
})
```
Additionally, if the `extract` option is passed to `m.request` the return value of the provided function will be used directly to resolve the request promise, and the `deserialize` callback is ignored.
---
## `m.request` headers
In v0.2.x, Mithril didn't set any headers on requests by default. Now, it sets up to 2 headers:
- `Content-Type: application/json; charset=utf-8` for requests with JSON bodies that are `!= null`
- `Accept: application/json, text/*` for requests expecting JSON responses
The first of the two headers, `Content-Type`, will trigger a CORS prefetch as it [is not a CORS-safelisted request header](https://fetch.spec.whatwg.org/#cors-safelisted-request-header) due to the specified content type, and that could introduce new errors depending on how CORS is configured on your server. If you run into issues with this, you may need to override that header in question by passing `headers: {"Content-Type": "text/plain"}`. (The `Accept` header doesn't trigger anything, so you don't need to override that.)
*The only content types that the Fetch spec lets avoid CORS prefetch checks are `application/x-www-form-urlencoded`, `multipart/form-data`, and `text/plain`. It doesn't allow anything else, and it intentionally disallows JSON.*
---
## `m.deferred` removed
v0.2.x used its own custom asynchronous contract object, exposed as `m.deferred`, which was used as the basis for `m.request`. v2.x uses Promises instead, and implements a [polyfill](promises.md) in non-supporting environments. In situations where you would have used `m.deferred`, you should use Promises instead.
### v0.2.x
```javascript
var greetAsync = function() {
var deferred = m.deferred()
setTimeout(function() {
deferred.resolve("hello")
}, 1000)
return deferred.promise
}
greetAsync()
.then(function(value) {return value + " world"})
.then(function(value) {console.log(value)}) //logs "hello world" after 1 second
```
### v2.x
```javascript
var greetAsync = function() {
return new Promise(function(resolve){
setTimeout(function() {
resolve("hello")
}, 1000)
})
}
greetAsync()
.then(function(value) {return value + " world"})
.then(function(value) {console.log(value)}) //logs "hello world" after 1 second
```
---
## `m.sync` removed
Since v2.x uses standards-compliant Promises, `m.sync` is redundant. Use `Promise.all` instead.
### v0.2.x
```javascript
m.sync([
m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }),
])
.then(function (users) {
console.log("Contributors:", users[0].name, "and", users[1].name)
})
```
### v2.x
```javascript
Promise.all([
m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }),
])
.then(function (users) {
console.log("Contributors:", users[0].name, "and", users[1].name)
})
```
---
## `xlink` namespace required
In v0.2.x, the `xlink` namespace was the only supported attribute namespace, and it was supported via special casing behavior. Now namespace parsing is fully supported, and namespaced attributes should explicitly declare their namespace.
### v0.2.x
```javascript
m("svg",
// the `href` attribute is namespaced automatically
m("image[href='image.gif']")
)
```
### v2.x
```javascript
m("svg",
// User-specified namespace on the `href` attribute
m("image[xlink:href='image.gif']")
)
```
---
## Nested arrays in views
Arrays now represent [fragments](fragment.md), which are structurally significant in v2.x virtual DOM. Whereas nested arrays in v0.2.x would be flattened into one continuous list of virtual nodes for the purposes of diffing, v2.x preserves the array structure - the children of any given array are not considered siblings of those of adjacent arrays.
---
## `vnode` equality checks
If a vnode is strictly equal to the vnode occupying its place in the last draw, v2.x will skip that part of the tree without checking for mutations or triggering any lifecycle methods in the subtree. The component documentation contains [more detail on this issue](components.md#avoid-creating-component-instances-outside-views).

362
docs/migration-v1x.md Normal file
View file

@ -0,0 +1,362 @@
# Migrating from v1.x
v2.x is almost entirely API-compatible with v1.x, but there are some breaking changes.
- [Assigning to `vnode.state`](#assigning-to-vnodestate)
- [Changes to route anchors](#changes-to-route-anchors)
- [Changes to `m.request` errors](#changes-to-mrequest-errors)
- [`m.withAttr` removed](#mwithattr-removed)
- [`m.route.prefix`](#mrouteprefix)
- [`m.request`/`m.jsonp` params and body](#mrequest-params-and-body)
- [Path templates](#path-templates)
- [Lifecycle call order](#lifecycle-call-order)
- [`m.redraw` synchronicity](#mredraw)
- [Selector attribute precedence](#selector-attribute-precedence)
- [Children normalization](#children-normalization)
- [Default `responseType` for `m.request`](#default-responsetype-for-mrequest)
- [`m.request` headers](#mrequest-headers)
- [Query parameters in hash strings in routes](#query-parameters-in-hash-strings-in-routes)
- [Keys](#keys)
- [`m.version` remvoed](#mversion-removed)
---
## Assigning to `vnode.state`
In v1.x, you could manipulate `vnode.state` and assign anything you wanted. In v2.x, an error will be thrown if it changes. Migration may vary, but most cases, it's as simple as changing references `vnode.state` to `vnode.state.foo`, picking an appropriate name for `foo` (like maybe `count` if it's a counter's current value).
### v1.x
```javascript
var Counter = {
oninit: function(vnode) { vnode.state = 0 },
view: function(vnode) {
return m(".counter", [
m("button", {onclick: function() { vnode.state-- }}, "-")
vnode.state,
m("button", {onclick: function() { vnode.state++ }}, "+")
])
}
}
```
### v2.x
```javascript
var Counter = {
oninit: function(vnode) { vnode.state.count = 0 },
view: function(vnode) {
return m(".counter", [
m("button", {onclick: function() { vnode.state.count-- }}, "-")
vnode.state.count,
m("button", {onclick: function() { vnode.state.count++ }}, "+")
])
}
}
```
*When v1.0 first released, class and closure components didn't exist, so it just pulled what it needed from `vnode.tag`. This implementation detail is what allowed you to do it, and some began to rely on it. It was also implied as possible in some places within the docs. Now, things are different, and this makes it a little easier to manage from an implementation standpoint as there's only one reference to state, not two.*
---
## Changes to route anchors
In v1.x, you previously used `oncreate: m.route.link` and, if the link could change, `onupdate: m.route.link` as well, each as lifecycle hooks on the vnode that could be routed with. In v2.x, you now use an [`m.route.Link` component](route.md#mroutelink). The selector can be specified via a `selector:` attribute in case you were using anything other than `m("a", ...)`, options can be specified via `options:`, you can disable it via `disabled:`, and other attributes can be specified inline including `href:` (required). The `selector:` itself can contain be any selector valid as the first argument for `m`, and the attributes `[href=...]` and `[disabled]` can be specified in the selector as well as the normal options.
### v1.x
```javascript
m("a", {
href: "/path",
oncreate: m.route.link,
})
m("button", {
href: "/path",
oncreate: m.route.link,
})
m("button.btn[href=/path]", {
oncreate: m.route.link,
})
```
### v2.x
```javascript
m(m.route.Link, {
href: "/path",
})
m(m.route.Link, {
selector: "button",
href: "/path",
})
m(m.route.Link, {
selector: "button.btn[href=/path]",
})
```
---
## Changes to `m.request` errors
In v1.x, `m.request` parsed errors from JSON calls and assigned the resulting parsed object's properties to the response. So, if you received a response with status 403 and a body of `{"code": "backoff", "timeout": 1000}`, the error would have two extra properties: `err.code = "backoff"` and `err.timeout = 1000`.
In v2.x, the response is assigned to a `response` property on the result instead, and a `code` property contains the resulting status code. So if you received a response with status 403 and a body of `{"code": "backoff", "timeout": 1000}`, the error would have assigned to it two properties: `err.response = {code: "backoff", timeout: 1000}` and `err.code = 403`.
---
## `m.withAttr` removed
In v1.x, event listeners could use `oninput: m.withAttr("value", func)` and similar. In v2.x, just read them directly from the event's target. It synergized well with streams, but since the idiom of `m.withAttr("value", stream)` was not *nearly* as common as `m.withAttr("value", prop)`, `m.withAttr` lost most of its usefulness and so it was removed.
### v1.x
```javascript
var value = ""
// In your view
m("input[type=text]", {
value: value(),
oninput: m.withAttr("value", function(v) { value = v }),
})
// OR
var value = m.stream("")
// In your view
m("input[type=text]", {
value: value(),
oninput: m.withAttr("value", value),
})
```
### v2.x
```javascript
var value = ""
// In your view
m("input[type=text]", {
value: value,
oninput: function (ev) { value = ev.target.value },
})
// OR
var value = m.stream("")
// In your view
m("input[type=text]", {
value: value(),
oninput: function (ev) { value(ev.target.value) },
})
```
---
## `m.route.prefix`
In v1.x, `m.route.prefix` was a function called via `m.route.prefix(prefix)`. It's now a property you set to via `m.route.prefix = prefix`
### v1.x
```javascript
m.route.prefix("/root")
```
### v2.x
```javascript
m.route.prefix = "/root"
```
---
## `m.request`/`m.jsonp` params and body
The `data` and `useBody` were refactored into `params`, query parameters interpolated into the URL and appended to the request, and `body`, the body to send in the underlying XHR. This gives you much better control over the actual request sent and allows you to both interpolate into query parameters with `POST` requests and create `GET` requests with bodies.
`m.jsonp`, having no meaningful "body", just uses `params`, so renaming `data` to `params` is sufficient for that method.
### v1.x
```javascript
m.request("https://example.com/api/user/:id", {
method: "GET",
data: {id: user.id}
})
m.request("https://example.com/api/user/create", {
method: "POST",
data: userData
})
```
### v2.x
```javascript
m.request("https://example.com/api/user/:id", {
method: "GET",
params: {id: user.id}
})
m.request("https://example.com/api/user/create", {
method: "POST",
body: userData
})
```
---
## Path templates
In v1.x, there were three separate path template syntaxes that, although they were similar, had 2 separately designed syntaxes and 3 different implementations. It was defined in a fairly ad-hoc way, and parameters weren't generally escaped. Now, everything is either encoded if it's `:key`, raw if it's `:key...`. If things are unexpectedly encoded, use `:path...`. It's that simple.
Concretely, here's how it affects each method:
### `m.request` and `m.jsonp` URLs, `m.route.set` paths
Path components in v2.x are escaped automatically when interpolated. Suppose you invoke `m.route.set("/user/:name/photos/:id", {name: user.name, id: user.id})`. Previously, if `user` was `{name: "a/b", id: "c/d"}`, this would set the route to `/user/a%2Fb/photos/c/d`, but it will now set it to `/user/a%2Fb/photos/c%2Fd`. If you deliberately *want* to interpolate a key unescaped, use `:key...` instead.
Keys in v2.x cannot contain any instances of `.` or `-`. In v1.x, they could contain anything other than `/`.
Interpolations in inline query strings, like in `/api/search?q=:query`, are not performed in v2.x. Pass those via `params` with appropriate key names instead, without specifying it in the query string.
### `m.route` route patterns
Path keys of the form `:key...` return their URL decoded in v1.x, but return the raw URL in v2.x.
Previously, stuff like `:key.md` were erroneously accepted, with the resulting parameter's value set to `keymd: "..."`. This is no longer the case - the `.md` is part of the pattern now, not the name.
---
## Lifecycle call order
In v1.x, attribute lifecycle hooks on component vnodes were called *before* the component's own lifecycle hooks in all cases. In v2.x, this is the case only for `onbeforeupdate`. So you may need to adjust your code accordingly.
### v1.x
```javascript
var Comp = {
oncreate: function() {
console.log("Component oncreate")
},
view: function() {
return m("div")
},
}
m.mount(document.body, {
view: function() {
return m(Comp, {
oncreate: function() {
console.log("Attrs oncreate")
},
})
}
})
// Logs:
// Attrs oncreate
// Component oncreate
```
### v2.x
```javascript
var Comp = {
oncreate: function() {
console.log("Component oncreate")
},
view: function() {
return m("div")
},
}
m.mount(document.body, {
view: function() {
return m(Comp, {
oncreate: function() {
console.log("Attrs oncreate")
},
})
}
})
// Logs:
// Component oncreate
// Attrs oncreate
```
---
## `m.redraw` synchronicity
`m.redraw()` in v2.x is always async. You can specifically request a synchronous redraw via `m.redraw.sync()` provided no redraw is currently occurring.
---
## Selector attribute precedence
In v1.x, selector attributes took precedence over attributes specified in the attributes object. For instance, `m("[a=b]", {a: "c"}).attrs` returned `{a: "b"}`.
In v2.x, attributes specified in the attributes object take precedence over selector attributes. For instance, `m("[a=b]", {a: "c"}).attrs` returns `{a: "c"}`.
Note that this is technically reverting to v0.2.x behavior.
---
## Children normalization
In v1.x, component vnode children were normalized like other vnodes. In v2.x, this is no longer the case and you will need to plan accordingly. This does not affect the normalization done on render.
---
## `m.request` headers
In v1.x, Mithril set these two headers on all non-`GET` requests, but only when `useBody` was set to `true` (the default) and the other conditions listed hold:
- `Content-Type: application/json; charset=utf-8` for requests with JSON bodies
- `Accept: application/json, text/*` for requests expecting JSON responses
In v2.x, Mithril sets the first for all requests with JSON bodies that are `!= null` and omits it by default otherwise, and this is done independent of which method is chosen, including on `GET` requests.
The first of the two headers, `Content-Type`, will trigger a CORS prefetch as it [is not a CORS-safelisted request header](https://fetch.spec.whatwg.org/#cors-safelisted-request-header) due to the specified content type, and that could introduce new errors depending on how CORS is configured on your server. If you run into issues with this, you may need to override that header in question by passing `headers: {"Content-Type": "text/plain"}`. (The `Accept` header doesn't trigger anything, so you don't need to override that.)
*The only content types that the Fetch spec lets avoid CORS prefetch checks are `application/x-www-form-urlencoded`, `multipart/form-data`, and `text/plain`. It doesn't allow anything else, and it intentionally disallows JSON.*
---
## Query parameters in hash strings in routes
In v1.x, you could specify query parameters for routes in both the query string and hash string, so `m.route.set("/route?foo=1&bar=2")`, `m.route.set("/route?foo=1#bar=2")`, and `m.route.set("/route#foo=1&bar=2")` were all equivalent and the attributes extracted from them would have been `{foo: "1", bar: "2"}`.
In v2.x, the contents of hash strings are ignored but preserved. So the attributes extracted from each would be this:
- `m.route.set("/route?foo=1&bar=2")` &rarr; `{foo: "1", bar: "2"}`
- `m.route.set("/route?foo=1#bar=2")` &rarr; `{foo: "1"}`
- `m.route.set("/route#foo=1&bar=2")` &rarr; `{}`
The reason for doing this is because URLs like `https://example.com/#!/route#key` are technically invalid per the [URL spec](https://url.spec.whatwg.org/#url-code-points) and were even invalid per the [RFC that preceded it](https://tools.ietf.org/html/rfc3986#appendix-A), and it's only a quirk of the HTML spec that they're allowed. (The HTML spec should've required IDs and location fragments to be valid URL fragments from the start instead if it wanted to follow spec.)
Or in short, stop using invalid URLs!
---
## Keys
In v1.x, you could mix keyed and unkeyed vnodes freely. If the first node is keyed, a keyed diff is performed, assuming every element has a key and just ignoring holes as it goes. Otherwise, an iterative diff is performed, and if a node has a key, it would be checked that it didn't change at the same time tags and similar are checked.
In v2.x, children lists of both fragments and elements must be either all keyed or all unkeyed. Holes are considered unkeyed for the purposes of this check, too - it no longer ignores them.
If you need to work around it, use the idiom of a fragment containing a single vnode, like `[m("div", {key: whatever})]`.
---
## `m.version` removed
It served little use in general, and you can always add it back yourself. You should prefer feature detection for knowing what features are available, and the v2.x API is designed to better enable this.

View file

@ -26,4 +26,5 @@
- Misc
- [Framework comparison](framework-comparison.md)
- [Change log/Migration](change-log.md)
- [v1 Documentation](archive/v1.1.6)
- [v1 Documentation](https://mithril.js.org/archive/v1.1.6/)
- [v0.2 Documentation](https://mithril.js.org/archive/v0.2.5/)

View file

@ -43,7 +43,7 @@ var Home = {
}
m.route(document.body, "/home", {
"/home": Home, // defines `http://localhost/#!/home`
"/home": Home, // defines `https://localhost/#!/home`
})
```
@ -297,9 +297,9 @@ Routing without page refreshes is made partially possible by the [`history.pushS
The routing strategy dictates how a library might actually implement routing. There are three general strategies that can be used to implement a SPA routing system, and each has different caveats:
- `m.route.prefix = '#!'` (default) Using the [fragment identifier](https://en.wikipedia.org/wiki/Fragment_identifier) (aka the hash) portion of the URL. A URL using this strategy typically looks like `http://localhost/#!/page1`
- `m.route.prefix = '?'` Using the querystring. A URL using this strategy typically looks like `http://localhost/?/page1`
- `m.route.prefix = ''` Using the pathname. A URL using this strategy typically looks like `http://localhost/page1`
- `m.route.prefix = '#!'` (default) Using the [fragment identifier](https://en.wikipedia.org/wiki/Fragment_identifier) (aka the hash) portion of the URL. A URL using this strategy typically looks like `https://localhost/#!/page1`
- `m.route.prefix = '?'` Using the querystring. A URL using this strategy typically looks like `https://localhost/?/page1`
- `m.route.prefix = ''` Using the pathname. A URL using this strategy typically looks like `https://localhost/page1`
Using the hash strategy is guaranteed to work in browsers that don't support `history.pushState`, because it can fall back to using `onhashchange`. Use this strategy if you want to keep the hashes purely local.
@ -503,8 +503,8 @@ m.route.prefix = "?"
m.route.prefix = "#"
// set to pathname strategy on a non-root URL
// e.g. if the app lives under `http://localhost/my-app` and something else
// lives under `http://localhost`
// e.g. if the app lives under `https://localhost/my-app` and something else
// lives under `https://localhost`
m.route.prefix = "/my-app"
```

View file

@ -24,13 +24,13 @@ The `<!doctype html>` line indicates this is an HTML 5 document. The first `char
We could create the entire application in a single JavaScript file, but doing so would make it difficult to navigate the codebase later on. Instead, let's split the code into *modules*, and assemble these modules into a *bundle* `bin/app.js`.
There are many ways to setup a bundler tool, but most are distributed via NPM. In fact, most modern JavaScript libraries and tools are distributed that way, including Mithril. NPM stands for Node.js Package Manager. To download NPM, [install Node.js](https://nodejs.org/en/); NPM is installed automatically with it. Once you have Node.js and NPM installed, open the command line and run this command:
There are many ways to setup a bundler tool, but most are distributed via npm. In fact, most modern JavaScript libraries and tools are distributed that way, including Mithril. To download npm, [install Node.js](https://nodejs.org/en/); npm is installed automatically with it. Once you have Node.js and npm installed, open the command line and run this command:
```bash
npm init -y
```
If NPM is installed correctly, a file `package.json` will be created. This file will contain a skeleton project meta-description file. Feel free to edit the project and author information in this file.
If npm is installed correctly, a file `package.json` will be created. This file will contain a skeleton project meta-description file. Feel free to edit the project and author information in this file.
---
@ -76,7 +76,7 @@ var 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 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.
Then we can add an `m.request` call to make an XHR request. For this tutorial, we'll make XHR calls to the [REM](https://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.
*Note: third-party cookies may have to be enabled for the REM endpoint to work.*
@ -138,7 +138,7 @@ module.exports = {
By default, Mithril views are described using [hyperscript](hyperscript.md). Hyperscript offers a terse syntax that can be indented more naturally than HTML for complex tags, and since its syntax is just JavaScript, it's possible to leverage a lot of JavaScript tooling ecosystem. For example:
- You can use [Babel](es6.md) to transpile ES6+ to ES5 for IE and to transpile [JSX](jsx.md) (an inline HTML-like syntax extension) to appropriate hyperscript calls.
- You can use [ESLint](http://eslint.org/) for easy linting with no special plugins.
- You can use [ESLint](https://eslint.org/) for easy linting with no special plugins.
- You can use [Terser](https://github.com/terser-js/terser) or [UglifyJS](https://github.com/mishoo/UglifyJS2) (ES5 only) to minify your code easily.
- You can use [Istanbul](https://github.com/istanbuljs/nyc) for code coverage.
- You can use [TypeScript](https://www.typescriptlang.org/) for easy code analysis. (There are [community-supported type definitions available](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/mithril), so you don't need to roll your own.)

View file

@ -86,7 +86,7 @@ data.title = "' onerror='alert(1)"
data.title = "' onmouseover='alert(1)"
// An attack that does not use JavaScript
data.description = "<a href='http://evil.com/login-page-that-steals-passwords.html'>Click here to read more</a>"
data.description = "<a href='https://evil.com/login-page-that-steals-passwords.html'>Click here to read more</a>"
```
There are countless non-obvious ways of creating malicious code, so it is highly recommended that you use a [whitelist](https://en.wikipedia.org/wiki/Whitelist) of permitted HTML tags, attributes and attribute values, as opposed to a [blacklist](https://en.wikipedia.org/wiki/Blacklisting) to sanitize the user input. It's also highly recommended that you use a proper HTML parser, instead of regular expressions for sanitization, because regular expressions are extremely difficult to test for all edge cases.
@ -134,7 +134,7 @@ Here's the example snippet for the [Facebook Like button](https://developers.fac
<!-- Your like button code -->
<div class="fb-like"
data-href="http://www.your-domain.com/your-page.html"
data-href="https://www.your-domain.com/your-page.html"
data-layout="standard"
data-action="like"
data-show-faces="true">
@ -157,7 +157,7 @@ var FacebookLikeButton = {
view: function() {
return [
m("#fb-root"),
m("#fb-like[data-href=http://www.your-domain.com/your-page.html][data-layout=standard][data-action=like][data-show-faces=true]")
m("#fb-like[data-href=https://www.your-domain.com/your-page.html][data-layout=standard][data-action=like][data-show-faces=true]")
]
}
}

View file

@ -9,7 +9,7 @@
<div id="root"></div>
<script src="../../mithril.js"></script>
<script>
//http://codepen.io/RobinVr/pen/kEoqc
//https://codepen.io/RobinVr/pen/kEoqc
var ring = m(".top",
m(".middle",
m("svg[preserveAspectRatio='xMidYMid meet'][version='1.1'][viewBox='0 0 584 586'][xmlns='http://www.w3.org/2000/svg'][xmlns:sketch='http://www.bohemiancoding.com/sketch/ns'][xmlns:xlink='http://www.w3.org/1999/xlink']", [

View file

@ -80,7 +80,7 @@ var Header = {
m("p.head_links", [
m("a[href='https://github.com/koglerjs/threaditjs/tree/master/examples/mithril']", "Source"),
" | ",
m("a[href='http://threaditjs.com']", "ThreaditJS Home"),
m("a[href='https://threaditjs.com']", "ThreaditJS Home"),
]),
m("h2", [
m(m.route.Link, {href: "/"}, "ThreaditJS: Mithril"),

View file

@ -3,8 +3,8 @@
<head>
<meta charset="utf-8">
<title>Mithril • ThreadIt.js</title>
<link rel="stylesheet" href="http://threaditjs.com/reset.css"/>
<link rel="stylesheet" href="http://threaditjs.com/shared.css"/>
<link rel="stylesheet" href="https://threaditjs.com/reset.css"/>
<link rel="stylesheet" href="https://threaditjs.com/shared.css"/>
<link rel="stylesheet" href="colors.css"/>
</head>
<body>

View file

@ -10,7 +10,7 @@
<section id="todoapp"></section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
<p>Part of <a href="https://todomvc.com">TodoMVC</a></p>
</footer>
<script src="../../mithril.js"></script>
<script src="todomvc.js"></script>

View file

@ -1,4 +1,4 @@
ospec [![NPM Version](https://img.shields.io/npm/v/ospec.svg)](https://www.npmjs.com/package/ospec) [![NPM License](https://img.shields.io/npm/l/ospec.svg)](https://www.npmjs.com/package/ospec)
ospec [![npm Version](https://img.shields.io/npm/v/ospec.svg)](https://www.npmjs.com/package/ospec) [![npm License](https://img.shields.io/npm/l/ospec.svg)](https://www.npmjs.com/package/ospec)
=====
[About](#about) | [Usage](#usage) | [CLI](#command-line-interface) | [API](#api) | [Goals](#goals)
@ -371,7 +371,7 @@ ospec '**/*.test.js' --ignore 'folder1/**' --require esm ./my-file.js
### Run ospec directly from the command line:
ospec comes with an executable named `ospec`. NPM auto-installs local binaries to `./node_modules/.bin/`. You can run ospec by running `./node_modules/.bin/ospec` from your project root, but there are more convenient methods to do so that we will soon describe.
ospec comes with an executable named `ospec`. npm auto-installs local binaries to `./node_modules/.bin/`. You can run ospec by running `./node_modules/.bin/ospec` from your project root, but there are more convenient methods to do so that we will soon describe.
ospec doesn't work when installed globally (`npm install -g`). Using global scripts is generally a bad idea since you can end up with different, incompatible versions of the same package installed locally and globally.

View file

@ -1,4 +1,4 @@
mithril-stream [![NPM Version](https://img.shields.io/npm/v/mithril-stream.svg)](https://www.npmjs.com/package/mithril-stream) [![NPM License](https://img.shields.io/npm/l/mithril-stream.svg)](https://www.npmjs.com/package/mithril-stream)
mithril-stream [![npm Version](https://img.shields.io/npm/v/mithril-stream.svg)](https://www.npmjs.com/package/mithril-stream) [![npm License](https://img.shields.io/npm/l/mithril-stream.svg)](https://www.npmjs.com/package/mithril-stream)
==============
Mithril's `m.stream` as a standalone module.

View file

@ -20,7 +20,7 @@ module.exports = function(options) {
var spymap = []
// This way I'm not also implementing a partial `URL` polyfill. Based on the
// regexp at http://urlregex.com/, but adapted to allow relative URLs and
// regexp at https://urlregex.com/, but adapted to allow relative URLs and
// care only about HTTP(S) URLs.
var urlHash = "#[?!/+=&;%@.\\w_-]*"
var urlQuery = "\\?[!/+=&;%@.\\w_-]*"