v0.2.4
This commit is contained in:
parent
f226d56481
commit
5d2b6ea2b7
68 changed files with 11860 additions and 4 deletions
80
archive/v0.2.4/README.md
Normal file
80
archive/v0.2.4/README.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
[](http://js.org)
|
||||
[](https://gitter.im/lhorie/mithril.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://travis-ci.org/lhorie/mithril.js)
|
||||
|
||||
# Mithril
|
||||
|
||||
A Javascript Framework for Building Brilliant Applications
|
||||
|
||||
See the [website](http://mithril.js.org) for documentation
|
||||
|
||||
There's also a [blog](http://lhorie.github.io/mithril-blog) and a [mailing list](https://groups.google.com/forum/#!forum/mithriljs)
|
||||
|
||||
---
|
||||
|
||||
## What is Mithril?
|
||||
|
||||
Mithril is a client-side MVC framework - a tool to organize code in a way that is easy to think about and to maintain.
|
||||
|
||||
### Light-weight
|
||||
|
||||
- Only 7.8 kB gzipped, no dependencies
|
||||
- Small API, small learning curve
|
||||
|
||||
### Robust
|
||||
|
||||
- Safe-by-default templates
|
||||
- Hierarchical MVC via components
|
||||
|
||||
### Fast
|
||||
|
||||
- Virtual DOM diffing and compilable templates
|
||||
- Intelligent auto-redrawing system
|
||||
|
||||
---
|
||||
|
||||
## Sample code
|
||||
|
||||
```javascript
|
||||
//namespace
|
||||
var app = {};
|
||||
|
||||
//model
|
||||
app.PageList = function() {
|
||||
return m.request({method: "GET", url: "pages.json"});
|
||||
};
|
||||
|
||||
//controller
|
||||
app.controller = function() {
|
||||
var pages = app.PageList();
|
||||
return {
|
||||
pages: pages,
|
||||
rotate: function() {
|
||||
pages().push(pages().shift());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//view
|
||||
app.view = function(ctrl) {
|
||||
return [
|
||||
ctrl.pages().map(function(page) {
|
||||
return m("a", {href: page.url}, page.title);
|
||||
}),
|
||||
m("button", {onclick: ctrl.rotate}, "Rotate links")
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
//initialize
|
||||
m.mount(document.getElementById("example"), app);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Learn more
|
||||
|
||||
- [Installation](http://mithril.js.org/installation.html)
|
||||
- [Tutorial](http://mithril.js.org/getting-started.html)
|
||||
- [Differences from Other Frameworks](http://mithril.js.org/comparison.html)
|
||||
- [Benchmarks](http://mithril.js.org/benchmarks.html)
|
||||
160
archive/v0.2.4/auto-redrawing.html
Normal file
160
archive/v0.2.4/auto-redrawing.html
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>The Auto-Redrawing System
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="the-auto-redrawing-system">The Auto-Redrawing System</h2>
|
||||
<p>Mithril is designed around the principle that data always flows from the model to the view. This makes it easy to reason about the state of the UI and to test it. In order to implement this principle, the rendering engine must run a redraw algorithm globally to ensure no parts of the UI are out of sync with the data. While at first glance, it may seem expensive to run a global redraw every time data changes, Mithril makes it possible to do this efficiently thanks to its fast diffing algorithm, which only updates the DOM where it needs to be updated. Because the DOM is by far the largest bottleneck in rendering engines, Mithril's approach of running a diff against a virtual representation of the DOM and only batching changes to the real DOM as needed is surprisingly performant.</p>
|
||||
<p>In addition, Mithril attempts to intelligently redraw only when it is appropriate in an application lifecycle. Most frameworks redraw aggressively and err on the side of redrawing too many times because, as it turns out, determining the best time to do a redraw is quite complicated if we want to be as efficient as possible.</p>
|
||||
<p>Mithril employs a variety of mechanisms to decide the best time and the best strategy to redraw. By default, Mithril is configured to auto-redraw from scratch after component controllers are initialized, and it is configured to diff after event handlers are triggered. In addition, it's possible for non-Mithril asynchronous callbacks to trigger auto-redrawing by calling <code>m.startComputation</code> and <code>m.endComputation</code> in appropriate places (see below). Any code that is between a <code>m.startComputation</code> and its respective <code>m.endComputation</code> call is said to live in the <em>context</em> of its respective pair of function calls.</p>
|
||||
<p>It's possible to defer a redraw by calling <code>m.request</code> or by manually nesting <a href="mithril.computation.html"><code>m.startComputation</code> and <code>m.endComputation</code></a> contexts. The way the redrawing engine defers redrawing is by keeping an internal counter that is incremented by <code>m.startComputation</code> and decremented by <code>m.endComputation</code>. Once that counter reaches zero, Mithril redraws. By strategically placing calls to this pair of functions, it is possible to stack asynchronous data services in any number of ways within a context without the need to pass state variables around the entire application. The end result is that you can call <code>m.request</code> and other integrated data services seamlessly, and Mithril will wait for all of the asynchronous operations to complete before attempting to redraw.</p>
|
||||
<p>In addition to being aware of data availability when deciding to redraw, Mithril is also aware of browser availability: if several redraws are triggered in a short amount of time, Mithril batches them so that at most only one redraw happens within a single animation frame (around 16ms). Since computer screens are not able to display changes faster than a frame, this optimization saves CPU cycles and helps UIs stay responsive even in the face of spammy data changes.</p>
|
||||
<p>Mithril also provides several hooks to control its redrawing behavior with a deep level of granularity: <a href="mithril.computation.html"><code>m.startComputation</code> and <code>m.endComputation</code></a> create redrawable contexts. <a href="mithril.redraw.html"><code>m.redraw</code></a> forces a redraw to happen in the next available frame (or optionally, it can redraw immediately for synchronous processing). The <a href="mithril.html#persisting-dom-elements-across-route-changes">config's retain flag</a> can be used to change how specific elements are redrawn when routes change. <a href="mithril.redraw.html#strategy"><code>m.redraw.strategy</code></a> can change the way Mithril runs the next scheduled redraw. Finally, the low-level <a href="mithril.render.html"><code>m.render</code></a> can also be used if a developer chooses to opt out of rest of the framework altogether.</p>
|
||||
<hr>
|
||||
<h3 id="integrating-with-the-auto-redrawing-system">Integrating with The Auto-Redrawing System</h3>
|
||||
<p>If you need to do custom asynchronous calls without using Mithril's API, and find that your views are not redrawing automatically, you should consider using <code>m.startComputation</code> / <code>m.endComputation</code> so that Mithril can intelligently auto-redraw once your custom code finishes running.</p>
|
||||
<p>In order to integrate asynchronous code to Mithril's autoredrawing system, you should call <code>m.startComputation</code> BEFORE making an asynchronous call, and <code>m.endComputation</code> at the end of the asynchronous callback.</p>
|
||||
<pre><code class="lang-javascript">//this service waits 1 second, logs "hello" and then notifies the view that
|
||||
//it may start redrawing (if no other asynchronous operations are pending)
|
||||
var doStuff = function() {
|
||||
m.startComputation(); //call `startComputation` before the asynchronous `setTimeout`
|
||||
|
||||
setTimeout(function() {
|
||||
console.log("hello");
|
||||
|
||||
m.endComputation(); //call `endComputation` at the end of the callback
|
||||
}, 1000);
|
||||
};
|
||||
</code></pre>
|
||||
<p>To integrate synchronous code, call <code>m.startComputation</code> at the beginning of the method, and <code>m.endComputation</code> at the end.</p>
|
||||
<pre><code class="lang-javascript">window.onfocus = function() {
|
||||
m.startComputation(); //call before everything else in the event handler
|
||||
|
||||
doStuff();
|
||||
|
||||
m.endComputation(); //call after everything else in the event handler
|
||||
}
|
||||
</code></pre>
|
||||
<p>For each <code>m.startComputation</code> call a library makes, it MUST also make one and ONLY one corresponding <code>m.endComputation</code> call.</p>
|
||||
<p>If you want to a recurring callback (such as <code>setInterval</code> or a web socket event handler) to trigger redraws, you should call <code>m.startComputation</code> at the beginning of the function, not outside of it.</p>
|
||||
<pre><code>setInterval(function() {
|
||||
m.startComputation(); //call before everything else in the event handler
|
||||
|
||||
doStuff();
|
||||
|
||||
m.endComputation(); //call after everything else in the event handler
|
||||
})
|
||||
</code></pre><hr>
|
||||
<h3 id="integrating-multiple-execution-threads">Integrating multiple execution threads</h3>
|
||||
<p>When <a href="integration.html">integrating with third party libraries</a>, you might find that you need to call asynchronous methods from outside of Mithril's API.</p>
|
||||
<p>In order to integrate non-trivial asynchronous code with Mithril's auto-redrawing system, you need to ensure all execution threads call <code>m.startComputation</code> / <code>m.endComputation</code>.</p>
|
||||
<p>An execution thread is basically any amount of code that runs before other asynchronous threads start to run.</p>
|
||||
<p>Integrating multiple execution threads can be done in two different ways: in a layered fashion or in comprehensive fashion.</p>
|
||||
<h4 id="layered-integration">Layered integration</h4>
|
||||
<p>Layered integration is recommended for modular code where many different APIs may be put together at the application level.</p>
|
||||
<p>Below is an example where various methods implemented with a third party library can be integrated in layered fashion: any of the methods can be used in isolation or in combination.</p>
|
||||
<p>Notice how <code>doBoth</code> repeatedly calls <code>m.startComputation</code> since that method calls both <code>doSomething</code> and <code>doAnother</code>. This is perfectly valid: there are three asynchronous computations pending after the <code>jQuery.when</code> method is called, and therefore, three pairs of <code>m.startComputation</code> / <code>m.endComputation</code> in play.</p>
|
||||
<pre><code class="lang-javascript">var doSomething = function(callback) {
|
||||
m.startComputation(); //call `startComputation` before the asynchronous AJAX request
|
||||
|
||||
return jQuery.ajax("/something").done(function() {
|
||||
if (callback) callback();
|
||||
|
||||
m.endComputation(); //call `endComputation` at the end of the callback
|
||||
});
|
||||
};
|
||||
var doAnother = function(callback) {
|
||||
m.startComputation(); //call `startComputation` before the asynchronous AJAX request
|
||||
|
||||
return jQuery.ajax("/another").done(function() {
|
||||
if (callback) callback();
|
||||
m.endComputation(); //call `endComputation` at the end of the callback
|
||||
});
|
||||
};
|
||||
var doBoth = function(callback) {
|
||||
m.startComputation(); //call `startComputation` before the asynchronous synchronization method
|
||||
|
||||
jQuery.when(doSomething(), doAnother()).then(function() {
|
||||
if (callback) callback();
|
||||
|
||||
m.endComputation(); //call `endComputation` at the end of the callback
|
||||
})
|
||||
};
|
||||
</code></pre>
|
||||
<h4 id="comprehensive-integration">Comprehensive integration</h4>
|
||||
<p>Comprehensive integration is recommended if integrating a monolithic series of asynchronous operations. In contrast to layered integration, it minimizes the number of <code>m.startComputation</code> / <code>m.endComputation</code> calls to avoid clutter.</p>
|
||||
<p>The example below shows a convoluted series of AJAX requests implemented with a third party library.</p>
|
||||
<pre><code class="lang-javascript">var doSomething = function(callback) {
|
||||
m.startComputation(); //call `startComputation` before everything else
|
||||
|
||||
jQuery.ajax("/something").done(function() {
|
||||
doStuff();
|
||||
jQuery.ajax("/another").done(function() {
|
||||
doMoreStuff();
|
||||
jQuery.ajax("/more").done(function() {
|
||||
if (callback) callback();
|
||||
|
||||
m.endComputation(); //call `endComputation` at the end of everything
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
99
archive/v0.2.4/benchmarks.html
Normal file
99
archive/v0.2.4/benchmarks.html
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Benchmarks
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="benchmarks">Benchmarks</h2>
|
||||
<p>These benchmarks were designed to measure Javascript running time for Mithril in comparison with other popular Javascript MVC frameworks. Javascript running time is significant because the gzipped size of a framework can be misleading in terms of how much code actually runs on page loads. In my experience, page loads happen far more commonly than one would expect in single page applications: power users open multiple tabs, and mobile users open and close the browser very frequently. And as far as templating engines go, the initial page load represents the worst case for the rendering algorithm since there is very little room for performance optimization tricks. It's arguably also <a href="http://blog.kissmetrics.com/loading-time/">one of the most important metrics when it comes to performance</a>.</p>
|
||||
<p>The numbers shown here are best-run results for all frameworks, except for Mithril's case, for which I'm taking the worst-run result. The numbers aren't statistically rigorous (e.g. I didn't bother to calculate standard deviation), but they should be enough to give a rough idea of what is faster than what.</p>
|
||||
<p>Generally speaking, these tests are making a deliberate effort to be <strong>biased in favor of other frameworks:</strong> for example, I don't load "optional-but-usually-used-in-real-life" things like the router module for Angular, or Marionette in Backbone's case, and I load the entirety of Mithril. In addition, this test deliberately avoids triggering <code>requestAnimationFrame</code>-based performance optimizations for Mithril, since this optimization does not exist in many frameworks and <a href="http://jsperf.com/angular-vs-knockout-vs-ember/308"><em>severely</em> skews numbers in Mithril's favor</a> in CPU-intensive situations like parallax sites. I'm also NOT using the <a href="optimizing-performance.html#compiling-templates">Mithril template compiler</a>, which would also skew the benchmark in Mithril's favor.</p>
|
||||
<p>To run the execution time tests below, click on their respective links, run the profiler from your desired browser's developer tools and measure the running time of a page refresh (lower is better).</p>
|
||||
<div class="performance" style="padding:0 0 0 10px;">
|
||||
<div class="row">
|
||||
<div class="col(4,4,6)">
|
||||
<h3>Loading</h3>
|
||||
<table>
|
||||
<tr><td><a href="comparisons/mithril.parsing.html">Mithril</a></td><td><span class="bar" style="background:#161;width:1%;"></span> 0.28ms</td></tr>
|
||||
<tr><td><a href="comparisons/jquery.parsing.html">jQuery</a></td><td><span class="bar" style="background:#66c;width:26%;"></span> 13.11ms</td></tr>
|
||||
<tr><td><a href="comparisons/backbone.parsing.html">Backbone</a></td><td><span class="bar" style="background:#33c;width:37%;"></span> 18.54ms</td></tr>
|
||||
<tr><td><a href="comparisons/angular.parsing.html">Angular</a></td><td><span class="bar" style="background:#c33;width:14%;"></span> 7.49ms</td></tr>
|
||||
<tr><td><a href="comparisons/react.parsing.html">React</a></td><td><span class="bar" style="background:#6af;width:50%;"></span> 24.99ms</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col(8,8,12)">
|
||||
<h3>Rendering</h3>
|
||||
<table>
|
||||
<tr><td><a href="comparisons/mithril.rendering.html">Mithril</a></td><td><span class="bar" style="background:#161;width:4%;"></span> 9.44ms (uncompiled)</td></tr>
|
||||
<tr><td><a href="comparisons/jquery.rendering.html">jQuery</a></td><td><span class="bar" style="background:#66c;width:17%;"></span> 40.27ms</td></tr>
|
||||
<tr><td><a href="comparisons/backbone.rendering.html">Backbone</a></td><td><span class="bar" style="background:#33c;width:10%;"></span> 23.05ms</td></tr>
|
||||
<tr><td><a href="comparisons/angular.rendering.html">Angular</a></td><td><span class="bar" style="background:#c33;width:50%;"></span> 118.63ms</td></tr>
|
||||
<tr><td><a href="comparisons/react.rendering.html">React</a></td><td><span class="bar" style="background:#6af;width:33%;"></span> 79.65ms</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>Feel free to implement versions of the tests above in other frameworks, if you wish. The code is very simple.</p>
|
||||
<hr>
|
||||
<h3 id="todomvc-benchmark">TodoMVC Benchmark</h3>
|
||||
<p>There's a TodoMVC benchmark with a variety of popular and obscure frameworks here:</p>
|
||||
<p><a href="http://matt-esch.github.io/mercury-perf/">http://matt-esch.github.io/mercury-perf/</a></p>
|
||||
<p>The benchmark consists of creating 100 todos, marking them as completed, and then deleting them. It aims to give an idea of how frameworks perform under real-world-ish conditions running idiomatic code (as opposed to micro-benchmarks, which tend to take advantage of obscure tricks and aggressive optimizations that sacrifice maintainability for extra speed).</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
20
archive/v0.2.4/bower.json
Normal file
20
archive/v0.2.4/bower.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "mithril",
|
||||
"version": "0.2.4",
|
||||
"main": "mithril.min.js",
|
||||
"description": "A Javascript Framework for building brilliant applications",
|
||||
"authors": ["Leo Horie <lhorie@hotmail.com>"],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"*.html",
|
||||
"*.css",
|
||||
"*.zip",
|
||||
"*.json",
|
||||
"mithril-tests.js",
|
||||
"archive",
|
||||
"comparisons",
|
||||
"lib",
|
||||
"tools"
|
||||
],
|
||||
"keywords": ["mithril", "mvc", "framework"]
|
||||
}
|
||||
571
archive/v0.2.4/change-log.html
Normal file
571
archive/v0.2.4/change-log.html
Normal file
|
|
@ -0,0 +1,571 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Change Log
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="change-log">Change Log</h2>
|
||||
<p><a href="http://mithril.js.org/archive/v0.2.4">v0.2.4</a></p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fix regression that caused errors to be swallowed in promises returned by m.request <a href="https://github.com/lhorie/mithril.js/issues/968">#968</a></li>
|
||||
<li>fix ReferenceError when calling an event handler via mithril-query without an event argument</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.2.3">v0.2.3</a></p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fix regression that prevented string keys</li>
|
||||
<li>fix handling of read-only attributes <a href="https://github.com/lhorie/mithril.js/issues/925">#925</a></li>
|
||||
<li>fix double unloading issue <a href="https://github.com/lhorie/mithril.js/issues/931">#931</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.2.2-rc.1">v0.2.2-rc.1</a></p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>revert regressions from 0.2.1 refactor</li>
|
||||
<li>revert <code>finally</code> because it's not in the ES6 promise spec</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.2.1">v0.2.1</a></p>
|
||||
<p><strong>IMPORTANT NOTE: Due to some unfortunate factors, 0.2.1 is not a stable release. Please use either 0.2.0 or latest instead</strong></p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>large refactor to take better advantage of Chrome js optimizations and improve source code readability (thanks to @isiahmeadows)</li>
|
||||
<li>added <code>catch</code> to promises</li>
|
||||
<li>improvements and fixes in the documentation and wiki</li>
|
||||
<li><code>m(component, ...args)</code> can now be used as a shorthand for <code>m.component(component, ...args)</code></li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>errors thrown from the exception monitor no longer freeze redrawing</li>
|
||||
<li>fix edge case with falsy keys</li>
|
||||
<li>fix controller prototype inheritance in component controllers</li>
|
||||
<li>fix return value of <code>parseQueryString</code> if input is empty string</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.2.0">v0.2.0</a> - improved components</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>Mithril modules will be referred to as <em>components</em> from now on.</li>
|
||||
<li>Virtual DOM tree can now contain <a href="mithril.component.html">components</a></li>
|
||||
<li>Components can now be parameterized via <code>m.component</code></li>
|
||||
</ul>
|
||||
<h3 id="deprecations-">Deprecations:</h3>
|
||||
<ul>
|
||||
<li><p><code>m.module</code> has been renamed <code>m.mount</code>. Calling <code>m.module</code> will still work, but should be considered deprecated. Rationale: Mithril modules and components are the same thing, therefore from now on, they will be referred to as components, since that name is more descriptive of their purpose, and causes less confusion in the face of ES6 modules.</p>
|
||||
<p>In order to migrate, search for <code>m.module</code> calls and replace them with <code>m.mount</code>. The method signature is the same.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fix diff edge case in <code><select></code> <a href="https://github.com/lhorie/mithril.js/issues/569">#569</a></li>
|
||||
<li>fix support for arrays in template compiler</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.34">v0.1.34</a> - maintenance</p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fix identity bug when mixing unkeyable elements in a tree <a href="https://github.com/lhorie/mithril.js/issues/524">#524</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.33">v0.1.33</a> - maintenance</p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fix diff bug when mixing <code>undefined</code> in a tree <a href="https://github.com/lhorie/mithril.js/issues/524">#524</a></li>
|
||||
<li>fix reference to map file in package.json for cdnjs</li>
|
||||
<li>fix links in documentation</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.32">v0.1.32</a> - maintenance</p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fix regression caused by <a href="https://github.com/lhorie/mithril.js/issues/454">#454</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.31">v0.1.31</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>Typescript definitions are more strongly typed</li>
|
||||
<li>m.request's <code>unwrapSuccess</code> and <code>unwrapError</code> callbacks now receive the XMLHttpRequest instance as a second parameter</li>
|
||||
<li>3rd parameter for <code>m.route(route, params, shouldReplaceHistory)</code> is now public</li>
|
||||
<li>exact routes now have higher precedence than routes w/ variables <a href="https://github.com/lhorie/mithril.js/issues/452">#452</a></li>
|
||||
<li>there's now a <code>retain</code> flag to control on-route-change diff strategy on a per-element basis</li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fix routing bug in IE9 <a href="https://github.com/lhorie/mithril.js/issues/320">#320</a></li>
|
||||
<li>fix ordering bug in m.trust when using HTML entities <a href="https://github.com/lhorie/mithril.js/issues/453">#453</a></li>
|
||||
<li>set promise's default value to initialValue if coming from m.request <a href="https://github.com/lhorie/mithril.js/issues/454">#454</a></li>
|
||||
<li>fix dom element ownership bug when mixing keyed elements and third party plugin elements <a href="https://github.com/lhorie/mithril.js/issues/463">#463</a></li>
|
||||
<li>fix edge case in flatten algorithm <a href="https://github.com/lhorie/mithril.js/issues/448">#448</a></li>
|
||||
<li>prevent unnecessary DOM move operation when mixing keyed and unkeyed elements <a href="https://github.com/lhorie/mithril.js/issues/398">#398</a></li>
|
||||
<li>revert <a href="https://github.com/lhorie/mithril.js/issues/382">#382</a> due to diff regression <a href="https://github.com/lhorie/mithril.js/issues/512">#512</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.30">v0.1.30</a> - maintenance</p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fix history.back() regression <a href="https://github.com/lhorie/mithril.js/issues/435">#435</a></li>
|
||||
<li>fix module.view's <code>this</code> association regression in Haxe environment <a href="https://github.com/lhorie/mithril.js/issues/434">#434</a></li>
|
||||
<li>fix array serialization syntax in querystrings <a href="https://github.com/lhorie/mithril.js/issues/440">#440</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.29">v0.1.29</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>Calling m.module without a module now unloads the current one <a href="https://github.com/lhorie/mithril.js/issues/420">#420</a></li>
|
||||
<li>Both <code>controller</code> and <code>view</code> properties in modules are now optional</li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>prevent empty class attributes <a href="https://github.com/lhorie/mithril.js/issues/382">#382</a></li>
|
||||
<li>array-to-querystring serialization in <code>m.request</code> now behaves like jQuery <a href="https://github.com/lhorie/mithril.js/issues/426">#426</a></li>
|
||||
<li>fix querystring detection bug in pathname mode <a href="https://github.com/lhorie/mithril.js/issues/425">#425</a></li>
|
||||
<li>don't add history entry if reloading from a link <a href="https://github.com/lhorie/mithril.js/issues/428">#428</a></li>
|
||||
<li>fix key association when DOM order is modified by external code <a href="https://github.com/lhorie/mithril.js/issues/424">#424</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.28">v0.1.28</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>Landed some performance improvements</li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>throw error if root element is null in m.module/m.route <a href="https://github.com/lhorie/mithril.js/issues/388">#388</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.27">v0.1.27</a> - maintenance</p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>prevent strategy("none") event contamination <a href="https://github.com/lhorie/mithril.js/issues/378">#378</a></li>
|
||||
<li>fix equality strictness <a href="https://github.com/lhorie/mithril.js/issues/379">#379</a></li>
|
||||
<li>fix keys bug when list has nulls <a href="https://github.com/lhorie/mithril.js/issues/299">#299</a></li>
|
||||
<li>make sure empty value in option tag creates attribute <a href="https://github.com/lhorie/mithril.js/issues/380">#380</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.26">v0.1.26</a> - maintenance</p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>make sure input[type] is CSS-targetable <a href="https://github.com/lhorie/mithril.js/issues/364">#364</a></li>
|
||||
<li>throw error if m.route.param is called before initializing routes <a href="https://github.com/lhorie/mithril.js/issues/361">#361</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.25">v0.1.25</a> - maintenance</p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fixed input cursor jumping regression</li>
|
||||
<li>fixed interop bug when QUnit and AMD are used at the same time <a href="https://github.com/lhorie/mithril.js/issues/355">#355</a></li>
|
||||
<li>fixed route arg duplication in edge case <a href="https://github.com/lhorie/mithril.js/issues/352">#352</a></li>
|
||||
<li>prevented meaningless error in Chrome edge case <a href="https://github.com/lhorie/mithril.js/issues/358">#358</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.24">v0.1.24</a> - maintenance</p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>Prevent rogue <code>is</code> attribute from being created in Chrome</li>
|
||||
<li>Fix <code>data</code> regression in <code>m.request</code></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.23">v0.1.23</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>There's now support for extended custom elements (e.g. <code>m("button[is=my-button]")</code>)</li>
|
||||
<li><code>m.request</code> now supports a <code>initialValue</code> option to help prevent type errors in views when using the <code>background</code> option</li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>docs now have anchor links for easier navigation</li>
|
||||
<li>fixed a bunch of IE8 issues <a href="https://github.com/lhorie/mithril.js/issues/298">#298</a></li>
|
||||
<li>fixed handling of <code>method</code> option in JSONP mode <a href="https://github.com/lhorie/mithril.js/issues/292">#292</a></li>
|
||||
<li>fixed source map files</li>
|
||||
<li>fixed handling of select[multiple]</li>
|
||||
<li>fixed template compiler edge case <a href="https://github.com/lhorie/mithril.js/issues/286">#286</a></li>
|
||||
<li>fixed pathname bug in m.route <a href="https://github.com/lhorie/mithril.js/issues/290">#290</a></li>
|
||||
<li>fixed pathname querystring bug in routed links <a href="https://github.com/lhorie/mithril.js/issues/304">#304</a></li>
|
||||
<li>fixed handling of value in inputs when model value is not in sync with input value <a href="https://github.com/lhorie/mithril.js/issues/336">#336</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.22">v0.1.22</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>docs now have anchor links for easier navigation</li>
|
||||
<li>there is more documentation for things that weren't that clear</li>
|
||||
<li>json-p support added</li>
|
||||
<li><code>m()</code> now supports splat for children (e.g. <code>m("div", m("a"), m("b"), m("i"))</code> for nicer Coffeescript syntax</li>
|
||||
<li>by popular demand, <code>m.module</code> now returns a controller instance</li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>gracefully degrade on IE exceptions when setting invalid values</li>
|
||||
<li>fixes for Typescript definition file</li>
|
||||
<li>fixed bug in keys algorithm when mixing keyed and unkeyed elements <a href="https://github.com/lhorie/mithril.js/issues/246">#246</a></li>
|
||||
<li>added promise exception monitor and reverted promise exception handling semantics to v0.1.19 semantics (see <a href="mithril.deferred.html#unchecked-error-handling">docs</a>)</li>
|
||||
<li>fixed redraw scheduling bug in old version of IE</li>
|
||||
<li>fixed incorrect diff when document is root, and html element is omitted</li>
|
||||
<li>fixed querystring clobbering in links w/ config:m.route <a href="https://github.com/lhorie/mithril.js/issues/261">#261</a></li>
|
||||
<li>fixed rare bug that made events get dropped <a href="https://github.com/lhorie/mithril.js/issues/214">#214</a></li>
|
||||
<li>don't send Content-Type header if there's no request data <a href="https://github.com/lhorie/mithril.js/issues/280">#280</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.21">v0.1.21</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>passing a promise to an <code>m.prop</code> now populates it with the resolved value upon resolution, and returns <code>undefined</code> otherwise</li>
|
||||
<li><code>m.redraw</code> can now be forced to called synchronously</li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fixed handling of <code>+</code> character in <code>m.route.param</code> <a href="https://github.com/lhorie/mithril.js/issues/204">#204</a></li>
|
||||
<li>fixed corner case for undefined children in diff <a href="https://github.com/lhorie/mithril.js/issues/206">#206</a></li>
|
||||
<li>fixed context.onunload for array items <a href="https://github.com/lhorie/mithril.js/issues/200">#200</a></li>
|
||||
<li>fixed handling on comments in HTML converter tool</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.20">v0.1.20</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>redraw strategy can now be modified via <code>m.redraw.strategy</code></li>
|
||||
<li><code>math</code> tags now automatically get created with the MathML namespace</li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fixed IE8 null reference exception in <code>m</code></li>
|
||||
<li>fixed IE8 empty-text-node-in-input issue <a href="https://github.com/lhorie/mithril.js/issues/195">#195</a></li>
|
||||
<li>fixed <code>m.sync</code> resolution when passed an empty array <a href="https://github.com/lhorie/mithril.js/issues/191">#191</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.19">v0.1.19</a> - maintenance</p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fixed double redraw when events fire simultaneously <a href="https://github.com/lhorie/mithril.js/issues/151">#151</a></li>
|
||||
<li>fixed node insertion bug when using document as root <a href="https://github.com/lhorie/mithril.js/issues/153">#153</a></li>
|
||||
<li>prevent routes from reverting to original route in some cases</li>
|
||||
<li>fixed nested array ordering <a href="https://github.com/lhorie/mithril.js/issues/156">#156</a></li>
|
||||
<li>fixed key ordering in interpolation case <a href="https://github.com/lhorie/mithril.js/issues/157">#157</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.18">v0.1.18</a> - maintenance</p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>routing now correctly clears diff cache <a href="https://github.com/lhorie/mithril.js/issues/148">#148</a></li>
|
||||
<li>fixed incorrect context unloading when reattaching a child to a new parent</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.17">v0.1.17</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>config contexts can now have an <code>onunload</code> property for clean up tasks after elements are detached from the document</li>
|
||||
<li>route changes now re-render from scratch, rather than attempting a virtual dom diff</li>
|
||||
<li>virtual elements that are children of an array can now accept a <code>key</code> attribute which maintains the identity of the underlying DOM elements when the array gets shuffled <a href="https://github.com/lhorie/mithril.js/issues/98">#98</a></li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>fixed a subtree directive bug that happened in inputs inside loops</li>
|
||||
<li>fixed select.value so that the correct option is displayed on first render</li>
|
||||
<li>in m.request, non-idempotent methods now automatically send appropriate Content-Type header if <code>serialize</code> is <code>JSON.stringify</code> <a href="https://github.com/lhorie/mithril.js/issues/139">#139</a></li>
|
||||
<li><code>m</code> selectors now correctly handle empty attribute values like <code>[href='']</code></li>
|
||||
<li>pre-existing nodes in a root element now get cleared if there's no cell cache associated with the element <a href="https://github.com/lhorie/mithril.js/issues/60">#60</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.16">v0.1.16</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>controller::onunload now receives an event parameter so that the unloading can be aborted <a href="https://github.com/lhorie/mithril.js/issues/135">#135</a></li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>prevent route change when only hash changes in non-hash mode <a href="https://github.com/lhorie/mithril.js/issues/107">#107</a></li>
|
||||
<li>config now always runs after template is attached to document <a href="https://github.com/lhorie/mithril.js/issues/109">#109</a></li>
|
||||
<li>fix null reference exception with Browserify <a href="https://github.com/lhorie/mithril.js/issues/110">#110</a></li>
|
||||
<li>fix nested array removal edge cases <a href="https://github.com/lhorie/mithril.js/issues/120">#120</a></li>
|
||||
<li>ignore redraw calls when controller is not ready <a href="https://github.com/lhorie/mithril.js/issues/127">#127</a></li>
|
||||
<li>fix null reference exception in nested array edge case <a href="https://github.com/lhorie/mithril.js/issues/129">#129</a></li>
|
||||
<li>fix a contenteditable null reference error <a href="https://github.com/lhorie/mithril.js/issues/134">#134</a></li>
|
||||
<li>fix textarea value diffing when value is a node inside an array <a href="https://github.com/lhorie/mithril.js/issues/136">#136</a></li>
|
||||
<li>fix diff bug with trusted strings <a href="https://github.com/lhorie/mithril.js/issues/138">#138</a></li>
|
||||
</ul>
|
||||
<h3 id="breaking-changes-">Breaking changes:</h3>
|
||||
<ul>
|
||||
<li>Due to the poor level of compatibility between XDomainRequest and XHR2, XDomainRequest is no longer called internally by Mithril. If you need to use CORS in IE9 or lower, you will need to return an XDomainRequest instance from <code>m.request</code>'s <code>config</code> method <a href="https://github.com/lhorie/mithril.js/issues/121">#121</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.15">v0.1.15</a> - maintenance</p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li><code>m.sync</code> now correctly passes arguments to resolver in same order as input arguments <a href="https://github.com/lhorie/mithril.js/issues/96">#96</a> </li>
|
||||
<li>fixed diff deletion bug <a href="https://github.com/lhorie/mithril.js/issues/99">#99</a> </li>
|
||||
<li>updating textarea attributes updates its value correctly <a href="https://github.com/lhorie/mithril.js/issues/100">#100</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.14">v0.1.14</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>The signature of <code>m</code> now accepts virtual elements as the second parameter of the function.</li>
|
||||
<li><code>m.route(path, params)</code> now accepts an argument that gets parsed as a querystring.</li>
|
||||
<li>routes now ignore trailing slashes <a href="https://github.com/lhorie/mithril.js/issues/88">#88</a></li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>Resolving promises early without a value now works <a href="https://github.com/lhorie/mithril.js/issues/85">#85</a></li>
|
||||
<li>Throwing exceptions within <code>m.request</code> now follow the same resolution procedure as <code>m.deferred</code> <a href="https://github.com/lhorie/mithril.js/issues/85">#86</a></li>
|
||||
<li>Promises now always update their <code>m.prop</code> on success (and leave the m.prop alone on error)</li>
|
||||
<li>Nested arrays no longer cause double removal of elements <a href="https://github.com/lhorie/mithril.js/issues/87">#87</a></li>
|
||||
<li>HTTP error codes now correctly reject promises</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.13">v0.1.13</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>m.module now runs clean-up code in root module controllers that implement an <code>onunload</code> instance method <a href="https://github.com/lhorie/mithril.js/issues/82">#82</a></li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>Removing CSS rules now diffs correctly <a href="https://github.com/lhorie/mithril.js/issues/79">#79</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.12">v0.1.12</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>It's now possible to define <a href="mithril.route.html#variadic-routes">variadic routes</a> <a href="https://github.com/lhorie/mithril.js/issues/70">#70</a></li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>Fix link location in links using <code>config: m.route</code> after redraws <a href="https://github.com/lhorie/mithril.js/issues/74">#74</a></li>
|
||||
<li>Fixed support for <code>list</code> attribute in inputs <a href="https://github.com/lhorie/mithril.js/issues/69">#69</a></li>
|
||||
<li>Fixed URL decoding in route params <a href="https://github.com/lhorie/mithril.js/issues/75">#75</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.11">v0.1.11</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>Added <code>m.route()</code> overload to allow reading of current route <a href="https://github.com/lhorie/mithril.js/issues/61">#61</a></li>
|
||||
<li>Added <code>background</code> option to <code>m.request</code> to allow requests that don't affect rendering <a href="https://github.com/lhorie/mithril.js/issues/62">#62</a></li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>Links using <code>config: m.route</code> can now be opened in new tab correctly <a href="https://github.com/lhorie/mithril.js/issues/64">#64</a></li>
|
||||
<li>Fixed diff within contenteditable areas <a href="https://github.com/lhorie/mithril.js/issues/65">#65</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.10">v0.1.10</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>Added social buttons to homepage</li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>Bi-directional bindings no longer wipe out cursor position in Chrome <a href="https://github.com/lhorie/mithril.js/issues/58">#58</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.9">v0.1.9</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>Added comparison with React to homepage</li>
|
||||
<li>Added support for multi-island apps <a href="https://github.com/lhorie/mithril.js/issues/34">#34</a></li>
|
||||
<li>m.prop is now JSON-serializable <a href="https://github.com/lhorie/mithril.js/issues/54">#54</a></li>
|
||||
<li>Added <code>extract</code> option to <code>m.request</code> to allow access to response metadata <a href="https://github.com/lhorie/mithril.js/issues/53">#53</a></li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>Fixed node index displacement by null/undefined nodes <a href="https://github.com/lhorie/mithril.js/issues/56">#56</a></li>
|
||||
<li>Fixed mock's insertBefore and appendChild when dealing w/ reattachments</li>
|
||||
</ul>
|
||||
<h3 id="breaking-changes-">Breaking changes:</h3>
|
||||
<ul>
|
||||
<li>changing an id in a virtual element now recreates the element, instead of recycling it <a href="https://github.com/lhorie/mithril.js/issues/55">#55</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.8">v0.1.8</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>Mock now contains a basic <code>insertAdjacentHTML</code> implementation to enable better testing of <code>m.trust</code> / <code>m.render</code> interactions</li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>Fixed ordering bug in deep subchildren <a href="https://github.com/lhorie/mithril.js/issues/51">#51</a></li>
|
||||
<li>Fixed ordering bug with trusted strings <a href="https://github.com/lhorie/mithril.js/issues/51">#51</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.7">v0.1.7</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>Mithril will be on a accelerated release cycle for the rest of the v0.1.x series. This means CDNs may lag behind in versions, so it's recommended that you either use one of the supported NodeJS package managers or fork from the Github repo directly. More information can be found <a href="https://groups.google.com/forum/#!msg/mithriljs/mc0qTgFTlgs/OD7Mc7_2Wa4J">here</a></li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>Fixed ordering bug when virtual element is preceded by array <a href="https://github.com/lhorie/mithril.js/issues/50">#50</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.6">v0.1.6</a> - maintenance</p>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>Fixed serious bug when mixing cached text nodes with new virtual elements <a href="https://github.com/lhorie/mithril.js/issues/49">#49</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.5">v0.1.5</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>Launched the <a href="http://lhorie.github.io/mithril-blog">Mithril Blog</a></li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>Fixed serious ordering problem when mixing arrays with virtual elements <a href="https://github.com/lhorie/mithril.js/issues/48">#48</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.4">v0.1.4</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>added regression tests for reported bugs</li>
|
||||
<li>added support for SVG</li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>URLs with port numbers are now handled correctly <a href="https://github.com/lhorie/mithril.js/issues/40">#40</a></li>
|
||||
<li>NPM package now contains unminified version for map files <a href="https://github.com/lhorie/mithril.js/issues/39">#39</a></li>
|
||||
<li>fixed ordering issue when mixing newly created virtual elements with elements from cache <a href="https://github.com/lhorie/mithril.js/issues/44">#44</a></li>
|
||||
<li>fixed caching bug in links w/ config option attached <a href="https://github.com/lhorie/mithril.js/issues/43">#43</a></li>
|
||||
<li>fixed attribute update bug when an element has both <code>oninput</code> and <code>onkeydown</code> handlers <a href="https://github.com/lhorie/mithril.js/issues/36">#36</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.3">v0.1.3</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>Mithril is now available via <a href="http://component.io">Component</a></li>
|
||||
<li>There's now an extra low-level optimization hook called a SubtreeDirective, which allows implementing plugins that only create virtual trees if necessary.</li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li>diff no longer touch the DOM when processing <code>style</code> attributes and event handlers</li>
|
||||
<li>returning a thennable to a resolution callback in <code>m.deferred().promise</code> now causes the promise to adopt its state </li>
|
||||
<li>diff now correctly clears subtree if null or undefined is passed as a node</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.2">v0.1.2</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>There's now a <a href="mailto:mithriljs@googlegroups.com">community mailing list</a>. There's also a <a href="https://groups.google.com/forum/#!forum/mithriljs">web interface</a></li>
|
||||
<li>Mithril is now on Travis CI. The build status can be found in the <a href="https://github.com/lhorie/mithril.js">project homepage</a></li>
|
||||
<li>Mithril is now available via the CommonJS and AMD API</li>
|
||||
<li>Mithril can now <a href="installation.html">be installed via npm and bower</a></li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li><code>m.render</code> now correctly reattaches reused DOM elements to replaced parent nodes <a href="https://github.com/lhorie/mithril.js/issues/31">#31</a></li>
|
||||
<li>UI actions that can potentially de-synchronize the DOM from cache now force synchronization <a href="https://github.com/lhorie/mithril.js/issues/29">#29</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1.1">v0.1.1</a> - maintenance</p>
|
||||
<h3 id="news-">News:</h3>
|
||||
<ul>
|
||||
<li>Mithril is now available at <a href="http://cdnjs.com/librarieshttp://mithril.js.org/">cdnjs</a> and <a href="http://www.jsdelivr.com/#!mithril">jsdelivr</a></li>
|
||||
</ul>
|
||||
<h3 id="bug-fixes-">Bug Fixes:</h3>
|
||||
<ul>
|
||||
<li><code>m.route.param</code> now resets on route change correctly <a href="https://github.com/lhorie/mithril.js/issues/15">#15</a></li>
|
||||
<li><code>m.render</code> now correctly ignores undefined values in the virtual tree<a href="https://github.com/lhorie/mithril.js/issues/16">#16</a></li>
|
||||
<li>errors thrown in promises now cause downstreams to be rejected <a href="https://github.com/lhorie/mithril.js/issues/1">#1</a></li>
|
||||
</ul>
|
||||
<h3 id="breaking-changes-">Breaking changes:</h3>
|
||||
<ul>
|
||||
<li><p>changed default value for <code>xhr.withCredentials</code> from <code>true</code> to <code>false</code> for <code>m.request</code>, since public APIs are more common than auth-walled ones. <a href="https://github.com/lhorie/mithril.js/issues/14">#14</a></p>
|
||||
<p>In order to configure this flag, the following configuration should be used:</p>
|
||||
<pre><code class="lang-javascript">var privateAPI = function(xhr) {xhr.withCredentials = true};
|
||||
|
||||
m.request({method: "GET", url: "http://foo.com/api", config: privateAPI});
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="http://mithril.js.org/archive/v0.1">v0.1</a> - Initial release</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
89
archive/v0.2.4/community.html
Normal file
89
archive/v0.2.4/community.html
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Community
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="community">Community</h2>
|
||||
<h3 id="learn-mithril">Learn Mithril</h3>
|
||||
<p>Read Mithril tutorials and articles about web app development.</p>
|
||||
<p>Go to the <a href="http://lhorie.github.io/mithril-blog">Learn Mithril site</a></p>
|
||||
<hr>
|
||||
<h3 id="gitter">Gitter</h3>
|
||||
<p>There's a <a href="https://gitter.im/lhorie/mithril.js">Gitter chat room</a>. This is a great place to ask questions, discuss ideas and connect with Mithril users.</p>
|
||||
<p>You can sign in with your Github credentials. </p>
|
||||
<hr>
|
||||
<h3 id="mailing-list">Mailing List</h3>
|
||||
<p>Looking for a place to talk about Mithril? Suggestions?</p>
|
||||
<p>Feel free to post on the <a href="https://groups.google.com/forum/#!forum/mithriljs">mailing list</a></p>
|
||||
<hr>
|
||||
<h3 id="stackoverflow">StackOverflow</h3>
|
||||
<p>Looking for help on StackOverflow? Tag your questions with <code>mithril.js</code>.</p>
|
||||
<p>Want to help fellow Mithril developers and gain karma while at it? <a href="http://stackoverflow.com/questions/tagged/mithril.js">Keep an eye on the tagged questions</a></p>
|
||||
<hr>
|
||||
<h3 id="irc">IRC</h3>
|
||||
<p>Join the #mithriljs IRC channel on <a href="http://webchat.freenode.net">Freenode</a>.</p>
|
||||
<hr>
|
||||
<h3 id="github-wiki">Github Wiki</h3>
|
||||
<p>A collection of community projects, tutorials, starter kits and snippets created by Mithril users. A great place to find useful tools.</p>
|
||||
<p>Go to the <a href="https://github.com/lhorie/mithril.js/wiki">Mithril wiki</a></p>
|
||||
<hr>
|
||||
<h3 id="bug-tracker">Bug Tracker</h3>
|
||||
<p>You can file bugs in the <a href="https://github.com/lhorie/mithril.js/issues?state=open">issues page on Github</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
130
archive/v0.2.4/comparison.html
Normal file
130
archive/v0.2.4/comparison.html
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>How is Mithril Different from Other Frameworks
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="how-is-mithril-different-from-other-frameworks">How is Mithril Different from Other Frameworks</h2>
|
||||
<p>There are a lot of different Javascript frameworks and evaluating their merits and shortcomings can be a daunting task.</p>
|
||||
<p>This page aims to provide a comparison between Mithril and some of the most widely used frameworks, as well as some of the younger, but relevant ones.</p>
|
||||
<h3 id="code-size">Code Size</h3>
|
||||
<p>One of the most obvious differences between Mithril and most frameworks is in file size: Mithril is around 7.8 kB gzipped and has no dependencies on other libraries.</p>
|
||||
<p>Note that while a small gzipped size can look appealing, that number is often used to "hide the weight" of the uncompressed code: remember that the decompressed Javascript still needs to be parsed and evaluated on every page load, and this cost (which can be in the dozens of milliseconds range for some frameworks in some browsers) cannot be cached.</p>
|
||||
<p>This cost might be less of a concern in single page apps, but not necessarily if the app is typically opened simultaneously in multiple tabs, or run on less powerful devices.</p>
|
||||
<p>The performance tests in the homepage show execution times for parsing and evaluation of Mithril's code, compared to some popular frameworks. As you can see, it paints a much less flattering picture for some frameworks than when we look at gzipped size alone.</p>
|
||||
<h3 id="documentation">Documentation</h3>
|
||||
<p>Another point of comparison is documentation. Most of the popular frameworks have at least a bare minimum amount of documentation nowadays, but many leave a bit to be desired: some lack usage examples, and some frameworks' communities need to rely heavily on third party sites for explanations of more advanced topics, and sometimes even for learning the basics.</p>
|
||||
<p>This is a problem particularly for frameworks that had breaking changes in the past: It's common to find answers in StackOverflow that are out-of-date and no longer work with the latest version of said frameworks.</p>
|
||||
<p>Mithril has more documentation in its Github repo than source code, and none of the documentation is auto-generated.</p>
|
||||
<p>All API points are explained in prose, and have code examples. Because the entire documentation is hand-crafted, you get the benefit of actually having explanations for things that documentation-generator tools don't support well (for example, interfaces and callback parameter documentation).</p>
|
||||
<p>In addition, this guide section covers topics related to how to fit all the pieces together.</p>
|
||||
<p>From the get-go, Mithril's build system produces archived versions of the code and documentation so that you'll never be stuck without docs for out-of-date versions.</p>
|
||||
<p>Given how young Mithril is, hopefully you can appreciate the level of commitment for providing good documentation.</p>
|
||||
<h3 id="architecture">Architecture</h3>
|
||||
<p>In terms of architecture, one of Mithril's main differences is that it does not provide base classes to extend from.</p>
|
||||
<p>It's often said that <em>frameworks</em>, in contrast to <em>libraries</em>, dictate how code should be written. In this sense, one could argue that Mithril isn't really a framework.</p>
|
||||
<p>Instead of locking developers down to very specific implementations of design patterns, Mithril's approach is to provide an idiomatic pattern to follow, and tools to aid the developer when required. This approach means that developers can get discoverable codebases without necessarily getting locked into the framework.</p>
|
||||
<p>One related difference is that other frameworks often have hard-coded base classes where every conceivable convenience method gets inherited by the developer's classes (remember, in Javascript, this can mean copying all of the utility methods over to the child class, regardless of whether they're going to be used or not).</p>
|
||||
<p>Mithril's on-demand tooling approach means there are no hidden performance costs when implementing core MVC patterns, and there's also no extra learning curve for framework-specific syntax for those patterns.</p>
|
||||
<h3 id="view-layer-paradigm">View Layer Paradigm</h3>
|
||||
<p>Some of the older frameworks among the popular ones (out-of-the-box jQuery and Backbone, specifically) take a more procedural paradigm when it comes to the view layer; this means every action requires the developer to write custom view-level code to handle it.</p>
|
||||
<p>This can get noticeably bulky when you look at thing like collections: you often need to implement insertion code and deletion code, in addition to a "draw everything" routine for performance. And this is for every list that needs to be displayed in some way.</p>
|
||||
<p>Mithril's view layer paradigm is designed to be <strong>declarative</strong>, much like HTML, such that the same code implicitly does everything it needs to. As it turns out, this design decision is actually a compromise: it offers the benefit of decreased application code complexity at the cost of some performance loss. However, as the performance tests in the homepage show, this does not necessarily hurt Mithril in a meaningful way.</p>
|
||||
<hr>
|
||||
<h2 id="specific-framework-comparisons">Specific Framework Comparisons</h2>
|
||||
<p>Warning: this section is likely biased. Take it with a grain of salt.</p>
|
||||
<h3 id="jquery">jQuery</h3>
|
||||
<p>jQuery is ubiquitous and has a large ecosystem, but it's not an MVC framework.</p>
|
||||
<p>There's no idiomatic way to organize jQuery code in an MVC pattern and many frameworks were created specifically to overcome that shortcoming.</p>
|
||||
<p>As summarized above, Mithril differs from jQuery by allowing DOM-related code to be written largely in a declarative style (thereby decreasing code complexity), in addition to providing an idiomatic way to structure applications.</p>
|
||||
<p>One other difference that is extremely clear is the treatment of data. In jQuery it's common to use the DOM as a data storage mechanism, whereas Mithril encourages data to exist in an isolated model layer.</p>
|
||||
<h3 id="backbone">Backbone</h3>
|
||||
<p>Backbone was originally designed as a way to structure jQuery-based applications. One of its selling points is that it allows developers to leverage their existing jQuery knowledge, while providing some "walls" to organize the code in a more structured manner.</p>
|
||||
<p>As with jQuery, Mithril differs from Backbone by enforcing view code to be written in a declarative style.</p>
|
||||
<p>Another marking difference is that Backbone is workflow agnostic, providing no idiomatic way to organize applications. This is good for framework adoption, but not necessarily ideal for team scalability and codebase discoverability.</p>
|
||||
<p>In contrast, Mithril encourages you to develop applications using the patterns found throughout this guide, and discourages the use of "bastardized" MVC pattern variations.</p>
|
||||
<p>One technical aspect that is also different is that Backbone is heavily event-oriented. Mithril, on the other hand, purposely avoids the observer pattern in an attempt to abolish "come-from hell", a class of debugging problems where you don't know what triggers some code because of a long chain of events triggering other events.</p>
|
||||
<p>A particularly nasty instance of this problem that sometimes occurs in "real-time" applications is when event triggering chains become circular due to a conditional statement bug, causing infinite loops and browser crashes.</p>
|
||||
<p>Another significant difference between Backbone and Mithril is in their approach to familiarity: Backbone appeals to people familiar w/ jQuery; Mithril is designed to be familiar to people with server-side MVC framework experience.</p>
|
||||
<h3 id="angular">Angular</h3>
|
||||
<p>Angular is an MVC framework maintained by Google, and it provides a declarative view layer and an emphasis on testability. It leverages developer experience with server-side MVC frameworks, and in many ways, is very similar in scope to Mithril.</p>
|
||||
<p>The main difference between Angular templates and Mithril templates is that Angular templates follow the tradition of being defined in HTML. This has the benefit of cleaner syntax for writing static text, but it comes with the disadvantage of features getting awkwardly tied to HTML syntax, as well as providing poor debugging support.</p>
|
||||
<p>One thing you may have noticed on the <a href="http://lhorie.github.io/mithril/index.html#performance">Mithril homepage</a> is that, out of the box, Angular is not as performant as other frameworks. Steep performance degradation is a notoriously common issue in non-trivial Angular applications and there are several third party libraries which attempt to get around performance problems. Speaking from experience, it's generally difficult to reason about performance in Angular.</p>
|
||||
<p>Mithril takes some learnings from that and implements a templating redrawing system that renders less aggressively, is less complex and is easier to profile.</p>
|
||||
<p>A noteworthy difference between Angular and Mithril is in framework complexity: Angular implements several subsystems that would seem more logical in programming language implementations (e.g. a parser, a dynamic scoping mechanism, decorators, etc). Mithril, on the other hand, tries to provide only features that support a more classic MVC paradigm.</p>
|
||||
<h3 id="ember">Ember</h3>
|
||||
<p>Ember is a highly comprehensive MVC framework, providing a large API that covers not only traditional MVC patterns, but also a vast range of helper utilities as well.</p>
|
||||
<p>The biggest difference between Ember and Mithril is summarized in the Architecture section above: Ember's comprehensiveness comes at the cost of a steep learning curve and a high degree of vendor lock-in.</p>
|
||||
<p>Ember is also more opinionated in terms of how application architecture should look, and as a result, tends to be less transparent in terms of what is actually happening under the hood.</p>
|
||||
<h3 id="react">React</h3>
|
||||
<p>React is a component engine developed by Facebook. It's relevant for comparison because it uses the same architecture as Mithril's templating engine: i.e. it acknowledges that DOM operations are the bottleneck of templating systems, and implements a virtual DOM tree which keeps track of changes and only applies diffs to the real DOM where needed.</p>
|
||||
<p>The most visible difference between React and Mithril is that React's <em>JSX</em> syntax does not run natively in the browser, whereas Mithril's uncompiled templates do. Both can be compiled, but React's compiled code still has function calls for each virtual DOM element; Mithril templates compile into static Javascript data structures.</p>
|
||||
<p>Another difference is that Mithril, being an MVC framework, rather than a templating engine, provides an auto-redrawing system that is aware of network asynchrony and that can render views efficiently without cluttering application code with redraw calls, and without letting the developer unintentionally bleed out of the MVC pattern.</p>
|
||||
<p>Note also that, despite having a bigger scope, Mithril has a smaller file size than React.</p>
|
||||
<h3 id="knockout">Knockout</h3>
|
||||
<p>Knockout is a library focused on data binding. It is not an MVC framework in the traditional sense, but idiomatic Knockout code uses the similar concept of view models.</p>
|
||||
<p>A Knockout view model is an amalgamation of model and controller layers in a single class. In contrast, Mithril separates the two layers more distinctly.</p>
|
||||
<p>As with Angular, Knockout templates are written in HTML, and therefore have the same pros and cons as Angular templates.</p>
|
||||
<h3 id="vue">Vue</h3>
|
||||
<p>Vue is a relatively new templating engine, but it boasts impressive results in its performance benchmark.</p>
|
||||
<p>It is not a full MVC framework, but it is similar to Angular templates, and uses the same terminology for its features (e.g. directives and filters).</p>
|
||||
<p>The most relevant difference is that Vue uses <a href="https://github.com/yyx990803/vue/wiki/FAQ">browser features that don't work (and cannot be made to work) in Internet Explorer 8 and lower</a>. Mithril does not rely on unpolyfillable features, so developers can support browsers all the way back to IE6 and Blackberry by using shims if support for those older browsers is required.</p>
|
||||
<p>Vue's implementation cleverly hijacks array methods, but it should be noted that Javascript Arrays cannot be truly subclassed and as such, Vue suffers from abstraction leaks.</p>
|
||||
<p>In contrast, Mithril avoids "magic" types.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
2
archive/v0.2.4/comparisons/angular.parsing.html
Normal file
2
archive/v0.2.4/comparisons/angular.parsing.html
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular.min.js"></script>
|
||||
To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)
|
||||
14
archive/v0.2.4/comparisons/angular.rendering.html
Normal file
14
archive/v0.2.4/comparisons/angular.rendering.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<html ng-app>
|
||||
<head><script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular.min.js"></script></head>
|
||||
<body ng-controller="TestCtrl">
|
||||
<p>To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)</p>
|
||||
<div id="container">
|
||||
<span ng-repeat="item in items"><input value="{{item.name}}"></span>
|
||||
</div>
|
||||
<script>
|
||||
function TestCtrl($scope) {
|
||||
$scope.items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
13
archive/v0.2.4/comparisons/angular.safety.html
Normal file
13
archive/v0.2.4/comparisons/angular.safety.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<html ng-app>
|
||||
<head><script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular.min.js"></script></head>
|
||||
<body ng-controller="TestCtrl">
|
||||
<div id="container">
|
||||
<span ng-repeat="item in items"><input value="{{item.name}}"></span>
|
||||
</div>
|
||||
<script>
|
||||
function TestCtrl($scope) {
|
||||
$scope.items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a\"><img src='javascript:;' onerror=\"alert('alert box should not appear')\">"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
4
archive/v0.2.4/comparisons/backbone.parsing.html
Normal file
4
archive/v0.2.4/comparisons/backbone.parsing.html
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone-min.js"></script>
|
||||
To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)
|
||||
30
archive/v0.2.4/comparisons/backbone.rendering.html
Normal file
30
archive/v0.2.4/comparisons/backbone.rendering.html
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone-min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)</p>
|
||||
<div id="container"></div>
|
||||
<script type="text/template" id="template">
|
||||
<% _.each(items, function( item ){ %><input value="<%= item.name %>" /><% })%>
|
||||
</script>
|
||||
<script>
|
||||
var View = Backbone.View.extend({
|
||||
el: $("#container"),
|
||||
template: _.template(document.getElementById("template").innerHTML),
|
||||
initialize: function() {
|
||||
this.render()
|
||||
},
|
||||
render: function() {
|
||||
this.$el.html(this.template({items: this.model}))
|
||||
}
|
||||
})
|
||||
new View({
|
||||
model: [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}]
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
29
archive/v0.2.4/comparisons/backbone.safety.html
Normal file
29
archive/v0.2.4/comparisons/backbone.safety.html
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone-min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script type="text/template" id="template">
|
||||
<% _.each(items, function( item ){ %><input value="<%= item.name %>" /><% })%>
|
||||
</script>
|
||||
<script>
|
||||
var View = Backbone.View.extend({
|
||||
el: $("#container"),
|
||||
template: _.template(document.getElementById("template").innerHTML),
|
||||
initialize: function() {
|
||||
this.render()
|
||||
},
|
||||
render: function() {
|
||||
this.$el.html(this.template({items: this.model}))
|
||||
}
|
||||
})
|
||||
new View({
|
||||
model: [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a\"><img src='javascript:;' onerror=\"alert('alert box should not appear')\">"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}]
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
2
archive/v0.2.4/comparisons/jquery.parsing.html
Normal file
2
archive/v0.2.4/comparisons/jquery.parsing.html
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
|
||||
To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)
|
||||
17
archive/v0.2.4/comparisons/jquery.rendering.html
Normal file
17
archive/v0.2.4/comparisons/jquery.rendering.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<html>
|
||||
<head><script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script></head>
|
||||
<body>
|
||||
<p>To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)</p>
|
||||
<div id="container"></div>
|
||||
<script>
|
||||
var items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
|
||||
var view = function() {
|
||||
var container = $("#container").html("");
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
container.append("<input value=\"" + items[i].name + "\" />");
|
||||
}
|
||||
}
|
||||
$(view);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
16
archive/v0.2.4/comparisons/jquery.safety.html
Normal file
16
archive/v0.2.4/comparisons/jquery.safety.html
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<html>
|
||||
<head><script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script></head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script>
|
||||
var items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a\"><img src='javascript:;' onerror=\"alert('alert box should not appear')\">"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
|
||||
var view = function() {
|
||||
var container = $("#container").html("");
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
container.append("<input value=\"" + items[i].name + "\" />");
|
||||
}
|
||||
}
|
||||
$(view);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
2
archive/v0.2.4/comparisons/mithril.parsing.html
Normal file
2
archive/v0.2.4/comparisons/mithril.parsing.html
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<script src="../mithril.min.js"></script>
|
||||
To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)
|
||||
20
archive/v0.2.4/comparisons/mithril.rendering.html
Normal file
20
archive/v0.2.4/comparisons/mithril.rendering.html
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<html>
|
||||
<head><script src="../mithril.min.js"></script></head>
|
||||
<body>
|
||||
<p>To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)</p>
|
||||
<div id="container"></div>
|
||||
<script>
|
||||
var app = {}
|
||||
app.controller = function() {
|
||||
this.items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
|
||||
}
|
||||
app.view = function(ctrl) {
|
||||
return ctrl.items
|
||||
.map(function(item) {
|
||||
return m("input", {value: item.name})
|
||||
});
|
||||
}
|
||||
m.mount(document.getElementById("container"), app);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
19
archive/v0.2.4/comparisons/mithril.safety.html
Normal file
19
archive/v0.2.4/comparisons/mithril.safety.html
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<html>
|
||||
<head><script src="../mithril.min.js"></script></head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script>
|
||||
var app = {}
|
||||
app.controller = function() {
|
||||
this.items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a\"><img src='javascript:;' onerror=\"alert('alert box should not appear')\">"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
|
||||
}
|
||||
app.view = function(ctrl) {
|
||||
return ctrl.items
|
||||
.map(function(item) {
|
||||
return m("input", {value: item.name})
|
||||
});
|
||||
}
|
||||
m.mount(document.getElementById("container"), app);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
2
archive/v0.2.4/comparisons/react.parsing.html
Normal file
2
archive/v0.2.4/comparisons/react.parsing.html
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<script src="http://cdnjs.cloudflare.com/ajax/libs/react/0.9.0/react.min.js"></script>
|
||||
To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)
|
||||
24
archive/v0.2.4/comparisons/react.rendering.html
Normal file
24
archive/v0.2.4/comparisons/react.rendering.html
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<html>
|
||||
<head><script src="http://cdnjs.cloudflare.com/ajax/libs/react/0.9.0/react.min.js"></script></head>
|
||||
<body>
|
||||
<p>To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)</p>
|
||||
<div id="container">
|
||||
</div>
|
||||
<script>
|
||||
|
||||
var items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
|
||||
|
||||
var d = React.DOM;
|
||||
var Main = React.createClass({
|
||||
render: function () {
|
||||
return d.div({}, this.props.data.map(function (item) {
|
||||
return d.input({value: item.name})
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(Main({data: items}), document.getElementById('container'))
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
24
archive/v0.2.4/comparisons/react.safety.html
Normal file
24
archive/v0.2.4/comparisons/react.safety.html
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<html>
|
||||
<head><script src="http://cdnjs.cloudflare.com/ajax/libs/react/0.9.0/react.min.js"></script></head>
|
||||
<body>
|
||||
<p>To run an execution time test on this page, run the profiler from your browser's developer tools and measure the running time of a page refresh. (Lower is better)</p>
|
||||
<div id="container">
|
||||
</div>
|
||||
<script>
|
||||
|
||||
var items = [{name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a\"><img src='javascript:;' onerror=\"alert('alert box should not appear')\">"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}, {name: "a"}, {name: "b"}, {name: "c"}];
|
||||
|
||||
var d = React.DOM;
|
||||
var Main = React.createClass({
|
||||
render: function () {
|
||||
return d.div({}, this.props.data.map(function (item) {
|
||||
return d.input({value: item.name})
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(Main({data: items}), document.getElementById('container'))
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
10
archive/v0.2.4/component.json
Normal file
10
archive/v0.2.4/component.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "mithril",
|
||||
"description": "A Javascript framework for building brilliant applications",
|
||||
"keywords": ["mvc", "framework"],
|
||||
"repository": "lhorie/mithril",
|
||||
"main": "mithril.js",
|
||||
"scripts": ["mithril.js"],
|
||||
"version": "0.2.4",
|
||||
"license": "MIT"
|
||||
}
|
||||
561
archive/v0.2.4/components.html
Normal file
561
archive/v0.2.4/components.html
Normal file
|
|
@ -0,0 +1,561 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Components
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="components">Components</h2>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#application-architecture-with-components">Application architecture with components</a><ul>
|
||||
<li><a href="#aggregation-of-responsibility">Aggregation of responsibility</a></li>
|
||||
<li><a href="#distribution-of-concrete-responsibilities">Distribution of concrete responsibilities</a></li>
|
||||
<li><a href="#cross-communication-in-single-purpose-components">Cross-communication in single-purpose components</a></li>
|
||||
<li><a href="#the-observer-pattern">The observer pattern</a></li>
|
||||
<li><a href="#hybrid-architecture">Hybrid architecture</a></li>
|
||||
<li><a href="#classic-mvc">Classic MVC</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#example-html5-drag-n-drop-file-uploader-component">Example: HTML5 drag-n-drop file uploader component</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="application-architecture-with-components">Application architecture with components</h2>
|
||||
<p>Components are versatile tools to organize code and can be used in a variety of ways.</p>
|
||||
<p>Let's create a simple model entity which we'll use in a simple application, to illustrate different usage patterns for components:</p>
|
||||
<pre><code class="lang-javascript">var Contact = function(data) {
|
||||
data = data || {}
|
||||
this.id = m.prop(data.id || "")
|
||||
this.name = m.prop(data.name || "")
|
||||
this.email = m.prop(data.email || "")
|
||||
}
|
||||
Contact.list = function(data) {
|
||||
return m.request({method: "GET", url: "/api/contact", type: Contact})
|
||||
}
|
||||
Contact.save = function(data) {
|
||||
return m.request({method: "POST", url: "/api/contact", data: data})
|
||||
}
|
||||
</code></pre>
|
||||
<p>Here, we've defined a class called <code>Contact</code>. A contact has an id, a name and an email. There are two static methods: <code>list</code> for retrieving a list of contacts, and <code>save</code> to save a single contact. These methods assume that the AJAX responses return contacts in JSON format, containing the same fields as the class.</p>
|
||||
<h3 id="aggregation-of-responsibility">Aggregation of responsibility</h3>
|
||||
<p>One way of organizing components is to use component parameter lists to send data downstream, and to define events to bubble data back upstream to a centralized module who is responsible for interfacing with the model layer.</p>
|
||||
<pre><code class="lang-javascript">var ContactsWidget = {
|
||||
controller: function update() {
|
||||
this.contacts = Contact.list()
|
||||
this.save = function(contact) {
|
||||
Contact.save(contact).then(update.bind(this))
|
||||
}.bind(this)
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return [
|
||||
m.component(ContactForm, {onsave: ctrl.save}),
|
||||
m.component(ContactList, {contacts: ctrl.contacts})
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
var ContactForm = {
|
||||
controller: function(args) {
|
||||
this.contact = m.prop(args.contact || new Contact())
|
||||
},
|
||||
view: function(ctrl, args) {
|
||||
var contact = ctrl.contact()
|
||||
|
||||
return m("form", [
|
||||
m("label", "Name"),
|
||||
m("input", {oninput: m.withAttr("value", contact.name), value: contact.name()}),
|
||||
|
||||
m("label", "Email"),
|
||||
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
||||
|
||||
m("button[type=button]", {onclick: args.onsave.bind(this, contact)}, "Save")
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
var ContactList = {
|
||||
view: function(ctrl, args) {
|
||||
return m("table", [
|
||||
args.contacts().map(function(contact) {
|
||||
return m("tr", [
|
||||
m("td", contact.id()),
|
||||
m("td", contact.name()),
|
||||
m("td", contact.email())
|
||||
])
|
||||
})
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, ContactsWidget)
|
||||
</code></pre>
|
||||
<p>In the example above, there are 3 components. <code>ContactsWidget</code> is the top level module being rendered to <code>document.body</code>, and it is the module that has the responsibility of talking to our Model entity <code>Contact</code>, which we defined earlier.</p>
|
||||
<p>The <code>ContactForm</code> component is, as its name suggests, a form that allows us to edit the fields of a <code>Contact</code> entity. It exposes an event called <code>onsave</code> which is fired when the Save button is pressed on the form. In addition, it stores the unsaved contact entity internally within the component (<code>this.contact = m.prop(args.contact || new Contact())</code>).</p>
|
||||
<p>The <code>ContactList</code> component displays a table showing all the contact entities that are passed to it via the <code>contacts</code> argument.</p>
|
||||
<p>The most interesting component is <code>ContactsWidget</code>:</p>
|
||||
<ol>
|
||||
<li><p>on initialization, it fetches the list of contacts (<code>this.contacts = Contact.list</code>)</p>
|
||||
</li>
|
||||
<li><p>when <code>save</code> is called, it saves a contact (<code>Contact.save(contact)</code>)</p>
|
||||
</li>
|
||||
<li><p>after saving the contact, it reloads the list (<code>.then(update.bind(this))</code>)</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p><code>update</code> is the controller function itself, so defining it as a promise callback simply means that the controller is re-initialized after the previous asynchronous operation (<code>Contact.save()</code>)</p>
|
||||
<p>Aggregating responsibility in a top-level component allows the developer to manage multiple model entities easily: any given AJAX request only needs to be performed once regardless of how many components need its data, and refreshing the data set is simple.</p>
|
||||
<p>In addition, components can be reused in different contexts. Notice that the <code>ContactList</code> does not care about whether <code>args.contacts</code> refers to all the contacts in the database, or just contacts that match some criteria. Similarly, <code>ContactForm</code> can be used to both create new contacts as well as edit existing ones. The implications of saving are left to the parent component to handle.</p>
|
||||
<p>This architecture can yield highly flexible and reusable code, but flexibility can also increase the cognitive load of the system (for example, you need to look at both the top-level module and <code>ContactList</code> in order to know what is the data being displayed (and how it's being filtered, etc). In addition, having a deeply nested tree of components can result in a lot of intermediate "pass-through" arguments and event handlers.</p>
|
||||
<hr>
|
||||
<h3 id="distribution-of-concrete-responsibilities">Distribution of concrete responsibilities</h3>
|
||||
<p>Another way of organizing code is to distribute concrete responsibilities across multiple modules.</p>
|
||||
<p>Here's a refactored version of the sample app above to illustrate:</p>
|
||||
<pre><code class="lang-javascript">var ContactForm = {
|
||||
controller: function() {
|
||||
this.contact = m.prop(new Contact())
|
||||
this.save = function(contact) {
|
||||
Contact.save(contact)
|
||||
}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
var contact = ctrl.contact()
|
||||
|
||||
return m("form", [
|
||||
m("label", "Name"),
|
||||
m("input", {oninput: m.withAttr("value", contact.name), value: contact.name()}),
|
||||
|
||||
m("label", "Email"),
|
||||
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
||||
|
||||
m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save")
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
var ContactList = {
|
||||
controller: function() {
|
||||
this.contacts = Contact.list()
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return m("table", [
|
||||
ctrl.contacts().map(function(contact) {
|
||||
return m("tr", [
|
||||
m("td", contact.id()),
|
||||
m("td", contact.name()),
|
||||
m("td", contact.email())
|
||||
])
|
||||
})
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
m.route(document.body, "/", {
|
||||
"/list": ContactList,
|
||||
"/create": ContactForm
|
||||
})
|
||||
</code></pre>
|
||||
<p>Notice that now each component is self-contained: each has a separate route, and each component does exactly one thing. These components are designed to not interface with other components. On the one hand, it's extremely easy to reason about the behavior of the components since they only serve a single purpose, but on the other hand they don't have the flexibility that the previous example did (e.g. in this iteration, <code>ContactList</code> can only list all of the contacts in the database, not an arbitrary subset.</p>
|
||||
<p>Also, notice that since these components are designed to encapsulate their behavior, they cannot easily affect other components. In practice, this means that if the two components were in a <code>ContactsWidget</code> component as before, saving a contact would not update the list without some extra code.</p>
|
||||
<h4 id="cross-communication-in-single-purpose-components">Cross-communication in single-purpose components</h4>
|
||||
<p>Here's one way to implement cross-communication between single purpose components:</p>
|
||||
<pre><code class="lang-javascript">var Observable = function() {
|
||||
var controllers = []
|
||||
return {
|
||||
register: function(controller) {
|
||||
return function() {
|
||||
var ctrl = new controller
|
||||
ctrl.onunload = function() {
|
||||
controllers.splice(controllers.indexOf(ctrl), 1)
|
||||
}
|
||||
controllers.push({instance: ctrl, controller: controller})
|
||||
return ctrl
|
||||
}
|
||||
},
|
||||
trigger: function() {
|
||||
controllers.map(function(c) {
|
||||
ctrl = new c.controller
|
||||
for (var i in ctrl) c.instance[i] = ctrl[i]
|
||||
})
|
||||
}
|
||||
}
|
||||
}.call()
|
||||
|
||||
|
||||
var ContactsWidget = {
|
||||
view: function(ctrl) {
|
||||
return [
|
||||
ContactForm,
|
||||
ContactList
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
var ContactForm = {
|
||||
controller: function() {
|
||||
this.contact = m.prop(new Contact())
|
||||
this.save = function(contact) {
|
||||
Contact.save(contact).then(Observable.trigger)
|
||||
}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
var contact = ctrl.contact()
|
||||
|
||||
return m("form", [
|
||||
m("label", "Name"),
|
||||
m("input", {oninput: m.withAttr("value", contact.name), value: contact.name()}),
|
||||
|
||||
m("label", "Email"),
|
||||
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
||||
|
||||
m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save")
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
var ContactList = {
|
||||
controller: Observable.register(function() {
|
||||
this.contacts = Contact.list()
|
||||
}),
|
||||
view: function(ctrl) {
|
||||
return m("table", [
|
||||
ctrl.contacts().map(function(contact) {
|
||||
return m("tr", [
|
||||
m("td", contact.id()),
|
||||
m("td", contact.name()),
|
||||
m("td", contact.email())
|
||||
])
|
||||
})
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, ContactsWidget)
|
||||
</code></pre>
|
||||
<p>In this iteration, both the <code>ContactForm</code> and <code>ContactList</code> components are now children of the <code>ContactsWidget</code> component and they appear simultaneously on the same page.</p>
|
||||
<p>The <code>Observable</code> object exposes two methods: <code>register</code> which marks a controller as a Observable entity, and <code>trigger</code> which reloads controllers marked by <code>register</code>. Controllers are deregistered when their <code>onunload</code> event is triggered.</p>
|
||||
<p>The <code>ContactList</code> component's controller is marked as Observable, and the <code>save</code> event handler in <code>ContactForm</code> calls <code>Observable.trigger</code> after saving.</p>
|
||||
<p>This mechanism allows multiple components to be reloaded in response to non-idempotent operations.</p>
|
||||
<p>One extremely important aspect of this architecture is that since components encapsulate their internal state, then by definition it's harder to reason about AJAX request redundancy (i.e. how to prevent two identical AJAX requests originating from two different components).</p>
|
||||
<h3 id="the-observer-pattern">The observer pattern</h3>
|
||||
<p>The <code>Observable</code> object can be further refactored so that <code>trigger</code> broadcasts to "channels", which controllers can subscribe to. This is known, appropriately, as the <a href="http://en.wikipedia.org/wiki/Observer_pattern">observer pattern</a>.</p>
|
||||
<pre><code class="lang-javascript">var Observable = function() {
|
||||
var channels = {}
|
||||
return {
|
||||
register: function(subscriptions, controller) {
|
||||
return function self() {
|
||||
var ctrl = new controller
|
||||
var reload = controller.bind(ctrl)
|
||||
Observable.on(subscriptions, reload)
|
||||
ctrl.onunload = function() {
|
||||
Observable.off(reload)
|
||||
}
|
||||
return ctrl
|
||||
}
|
||||
},
|
||||
on: function(subscriptions, callback) {
|
||||
subscriptions.forEach(function(subscription) {
|
||||
if (!channels[subscription]) channels[subscription] = []
|
||||
channels[subscription].push(callback)
|
||||
})
|
||||
},
|
||||
off: function(callback) {
|
||||
for (var channel in channels) {
|
||||
var index = channels[channel].indexOf(callback)
|
||||
if (index > -1) channels[channel].splice(index, 1)
|
||||
}
|
||||
},
|
||||
trigger: function(channel, args) {
|
||||
console.log("triggered: " + channel)
|
||||
channels[channel].map(function(callback) {
|
||||
callback(args)
|
||||
})
|
||||
}
|
||||
}
|
||||
}.call()
|
||||
</code></pre>
|
||||
<p>This pattern is useful to decouple chains of dependencies (however care should be taken to avoid "come-from hell", i.e. difficulty in following a chains of events because they are too numerous and arbitrarily inter-dependent)</p>
|
||||
<h3 id="hybrid-architecture">Hybrid architecture</h3>
|
||||
<p>It's of course possible to use both aggregation of responsibility and the observer pattern at the same time.</p>
|
||||
<p>The example below shows a variation of the contacts app where <code>ContactForm</code> is responsible for saving.</p>
|
||||
<pre><code class="lang-javascript">var ContactsWidget = {
|
||||
controller: Observable.register(["updateContact"], function() {
|
||||
this.contacts = Contact.list()
|
||||
}),
|
||||
view: function(ctrl) {
|
||||
return [
|
||||
m.component(ContactForm),
|
||||
m.component(ContactList, {contacts: ctrl.contacts})
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
var ContactForm = {
|
||||
controller: function(args) {
|
||||
this.contact = m.prop(new Contact())
|
||||
this.save = function(contact) {
|
||||
Contact.save(contact).then(Observable.trigger("updateContact"))
|
||||
}
|
||||
},
|
||||
view: function(ctrl, args) {
|
||||
var contact = ctrl.contact()
|
||||
|
||||
return m("form", [
|
||||
m("label", "Name"),
|
||||
m("input", {oninput: m.withAttr("value", contact.name), value: contact.name()}),
|
||||
|
||||
m("label", "Email"),
|
||||
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
||||
|
||||
m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save")
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
var ContactList = {
|
||||
view: function(ctrl, args) {
|
||||
return m("table", [
|
||||
args.contacts().map(function(contact) {
|
||||
return m("tr", [
|
||||
m("td", contact.id()),
|
||||
m("td", contact.name()),
|
||||
m("td", contact.email())
|
||||
])
|
||||
})
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, ContactsWidget)
|
||||
</code></pre>
|
||||
<p>Here, the data fetching is still centralized in the top-level component, so that we can avoid duplicate AJAX requests when fetching data.</p>
|
||||
<p>And moving the responsibility of saving to the <code>ContactForm</code> component alleviates the need to send data back up the component tree, making the handling of non-idempotent operations less prone to pass-through argument noise.</p>
|
||||
<hr>
|
||||
<h3 id="classic-mvc">Classic MVC</h3>
|
||||
<p>Here's one last, but relevant variation of the pattern above.</p>
|
||||
<pre><code class="lang-javascript">//model layer observer
|
||||
Observable.on(["saveContact"], function(data) {
|
||||
Contact.save(data.contact).then(Observable.trigger("updateContact"))
|
||||
})
|
||||
|
||||
//ContactsWidget is the same as before
|
||||
var ContactsWidget = {
|
||||
controller: Observable.register(["updateContact"], function() {
|
||||
this.contacts = Contact.list()
|
||||
}),
|
||||
view: function(ctrl) {
|
||||
return [
|
||||
m.component(ContactForm),
|
||||
ctrl.contacts() === undefined
|
||||
? m("div", "loading contacts...") //waiting for promise to resolve
|
||||
: m.component(ContactList, {contacts: ctrl.contacts})
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
//ContactList no longer calls `Contact.save`
|
||||
var ContactForm = {
|
||||
controller: function(args) {
|
||||
var ctrl = this
|
||||
ctrl.contact = m.prop(new Contact())
|
||||
ctrl.save = function(contact) {
|
||||
Observable.trigger("saveContact", {contact: contact})
|
||||
ctrl.contact = m.prop(new Contact()) //reset to empty contact
|
||||
}
|
||||
return ctrl
|
||||
},
|
||||
view: function(ctrl, args) {
|
||||
var contact = ctrl.contact()
|
||||
|
||||
return m("form", [
|
||||
m("label", "Name"),
|
||||
m("input", {oninput: m.withAttr("value", contact.name), value: contact.name()}),
|
||||
|
||||
m("label", "Email"),
|
||||
m("input", {oninput: m.withAttr("value", contact.email), value: contact.email()}),
|
||||
|
||||
m("button[type=button]", {onclick: ctrl.save.bind(this, contact)}, "Save")
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
//ContactList is the same as before
|
||||
var ContactList = {
|
||||
view: function(ctrl, args) {
|
||||
return m("table", [
|
||||
args.contacts().map(function(contact) {
|
||||
return m("tr", [
|
||||
m("td", contact.id()),
|
||||
m("td", contact.name()),
|
||||
m("td", contact.email())
|
||||
])
|
||||
})
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, ContactsWidget)
|
||||
</code></pre>
|
||||
<p>Here we've moved <code>Contact.save(contact).then(Observable.trigger("updateContact"))</code> out of the <code>ContactForm</code> component and into the model layer. In its place, <code>ContactForm</code> merely emits an action, which is then handled by this model layer observer.</p>
|
||||
<p>This allows swapping the implementation of the <code>saveContact</code> handler without changing the <code>ContactForm</code> component.</p>
|
||||
<hr>
|
||||
<h3 id="example-html5-drag-n-drop-file-uploader-component">Example: HTML5 drag-n-drop file uploader component</h3>
|
||||
<p>Here's an example of a not-so-trivial component: a drag-n-drop file uploader. In addition to the <code>controller</code> and <code>view</code> properties that make the <code>Uploader</code> object usable as a component, it also has an <code>upload</code> convenience function that provides a basic upload model method, and a <code>serialize</code> function that allows files to be serialized as JSON in regular requests encoded as <code>application/x-www-form-urlencoded</code>.</p>
|
||||
<p>These two functions are here to illustrate the ability to expose APIs to component consumers that complement the component's user interface. By bundling model methods in the component, we avoid hard-coding how files are handled once they're dropped in, and instead, we provide a useful library of functions that can be consumed flexibly to meet the demands on an application.</p>
|
||||
<pre><code class="lang-javascript">var Uploader = {
|
||||
upload: function(options) {
|
||||
var formData = new FormData
|
||||
for (var key in options.data) {
|
||||
for (var i = 0; i < options.data[key].length; i++) {
|
||||
formData.append(key, options.data[key][i])
|
||||
}
|
||||
}
|
||||
|
||||
//simply pass the FormData object intact to the underlying XMLHttpRequest, instead of JSON.stringify'ing it
|
||||
options.serialize = function(value) {return value}
|
||||
options.data = formData
|
||||
|
||||
return m.request(options)
|
||||
},
|
||||
serialize: function(files) {
|
||||
var promises = files.map(function(file) {
|
||||
var deferred = m.deferred()
|
||||
|
||||
var reader = new FileReader
|
||||
reader.readAsDataURL()
|
||||
reader.onloadend = function(e) {
|
||||
deferred.resolve(e.result)
|
||||
}
|
||||
reader.onerror = deferred.reject
|
||||
return deferred.promise
|
||||
})
|
||||
return m.sync(promises)
|
||||
},
|
||||
controller: function(args) {
|
||||
this.noop = function(e) {
|
||||
e.preventDefault()
|
||||
}
|
||||
this.update = function(e) {
|
||||
e.preventDefault()
|
||||
if (typeof args.onchange == "function") {
|
||||
args.onchange([].slice.call((e.dataTransfer || e.target).files))
|
||||
}
|
||||
}
|
||||
},
|
||||
view: function(ctrl, args) {
|
||||
return m(".uploader", {ondragover: ctrl.noop, ondrop: ctrl.update})
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>Below are some examples of consuming the <code>Uploader</code> component:</p>
|
||||
<pre><code class="lang-javascript">//usage demo 1: standalone multipart/form-data upload when files are dropped into the component
|
||||
var Demo1 = {
|
||||
controller: function() {
|
||||
return {
|
||||
upload: function(files) {
|
||||
Uploader.upload({method: "POST", url: "/api/files", data: {files: files}}).then(function() {
|
||||
alert("uploaded!")
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return [
|
||||
m("h1", "Uploader demo"),
|
||||
m.component(Uploader, {onchange: ctrl.upload})
|
||||
]
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><a href="http://jsfiddle.net/vL22kjvs/5/">Demo</a></p>
|
||||
<pre><code class="lang-javascript">//usage demo 2: upload as base-64 encoded data url from a parent form
|
||||
var Demo2 = {
|
||||
Asset: {
|
||||
save: function(data) {
|
||||
return m.request({method: "POST", url: "/api/assets", data: data})
|
||||
}
|
||||
},
|
||||
|
||||
controller: function() {
|
||||
var files = m.prop([])
|
||||
return {
|
||||
files: files,
|
||||
save: function() {
|
||||
Uploader.serialize(files()).then(function(files) {
|
||||
Demo2.Asset.save({files: files}).then(function() {
|
||||
alert("Uploaded!")
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return [
|
||||
m("h1", "Uploader demo"),
|
||||
m("p", "Drag and drop a file below. An alert box will appear when the upload finishes"),
|
||||
m("form", [
|
||||
m.component(Uploader, {onchange: ctrl.files}),
|
||||
ctrl.files().map(function(file) {
|
||||
return file.name
|
||||
}).join(),
|
||||
m("button[type=button]", {onclick: ctrl.save}, "Upload")
|
||||
])
|
||||
]
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><a href="http://jsfiddle.net/vL22kjvs/6/">Demo</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
509
archive/v0.2.4/getting-started.html
Normal file
509
archive/v0.2.4/getting-started.html
Normal file
|
|
@ -0,0 +1,509 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Getting Started
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="getting-started">Getting Started</h2>
|
||||
<h3 id="what-is-mithril-">What is Mithril?</h3>
|
||||
<p>Mithril is a client-side Javascript MVC framework, i.e. it's a tool to make application code divided into a data layer (called <strong>M</strong>odel), a UI layer (called <strong>V</strong>iew), and a glue layer (called <strong>C</strong>ontroller)</p>
|
||||
<p>Mithril is around 7.8 kB gzipped thanks to its <a href="mithril.html">small, focused, API</a>. It provides a templating engine with a virtual DOM diff implementation for performant rendering, utilities for high-level modelling via functional composition, as well as support for routing and componentization.</p>
|
||||
<p>The goal of the framework is to make application code discoverable, readable and maintainable, and hopefully help you become an even better developer.</p>
|
||||
<p>Unlike some frameworks, Mithril tries very hard to avoid locking you into a web of dependencies: you can use as <em>little</em> of the framework as you need.</p>
|
||||
<p>However, using its entire toolset idiomatically can bring lots of benefits: learning to use functional programming in real world scenarios and solidifying good coding practices for OOP and MVC are just some of them.</p>
|
||||
<hr>
|
||||
<h2 id="a-simple-application">A Simple Application</h2>
|
||||
<p>Once you have a <a href="installation.html">copy of Mithril</a>, getting started is surprisingly boilerplate-free:</p>
|
||||
<pre><code class="lang-markup"><!doctype html>
|
||||
<title>Todo app</title>
|
||||
<script src="mithril.min.js"></script>
|
||||
<script>
|
||||
//app goes here
|
||||
</script>
|
||||
</code></pre>
|
||||
<p>Yes, this is valid HTML 5! According to the specs, the <code><html></code>, <code><head></code> and <code><body></code> tags can be omitted, but their respective DOM elements will still be there implicitly when a browser renders that markup.</p>
|
||||
<hr>
|
||||
<h3 id="model">Model</h3>
|
||||
<p>In Mithril, an application typically lives in a namespace and contains components. Components are merely structures that represent a viewable "page" or a part of a page. In addition, an application can be organizationally divided into three major layers: Model, Controller and View.</p>
|
||||
<p>For simplicity, our application will have only one component, and we're going to use it as the namespace for our application.</p>
|
||||
<p>In Mithril, a <em>component</em> is an object that contains a <code>view</code> function and optionally a <code>controller</code> function.</p>
|
||||
<pre><code>//an empty Mithril component
|
||||
var myComponent = {
|
||||
controller: function() {},
|
||||
view: function() {}
|
||||
}
|
||||
</code></pre><p>In addition to holding a controller and a view, a component can also be used to store data that pertains to it.</p>
|
||||
<p>Let's create a component.</p>
|
||||
<pre><code class="lang-markup"><script>
|
||||
//this application only has one component: todo
|
||||
var todo = {};
|
||||
</script>
|
||||
</code></pre>
|
||||
<p>Typically, model entities are reusable and live outside of components (e.g. <code>var User = ...</code>). In our example, since the whole application lives in one component, we're going to use the component as a namespace for our model entities.</p>
|
||||
<pre><code class="lang-javascript">var todo = {};
|
||||
|
||||
//for simplicity, we use this component to namespace the model classes
|
||||
|
||||
//the Todo class has two properties
|
||||
todo.Todo = function(data) {
|
||||
this.description = m.prop(data.description);
|
||||
this.done = m.prop(false);
|
||||
};
|
||||
|
||||
//the TodoList class is a list of Todo's
|
||||
todo.TodoList = Array;
|
||||
</code></pre>
|
||||
<p><a href="mithril.prop.html"><code>m.prop</code></a> is simply a factory for a getter-setter function. Getter-setters work like this:</p>
|
||||
<pre><code class="lang-javascript">//define a getter-setter with initial value `John`
|
||||
var a_name = m.prop("John");
|
||||
|
||||
//read the value
|
||||
var a = a_name(); //a == "John"
|
||||
|
||||
//set the value to `Mary`
|
||||
a_name("Mary"); //Mary
|
||||
|
||||
//read the value
|
||||
var b = a_name(); //b == "Mary"
|
||||
</code></pre>
|
||||
<p>Note that the <code>Todo</code> and <code>TodoList</code> classes we defined above are plain vanilla Javascript constructors. They can be initialized and used like this:</p>
|
||||
<pre><code class="lang-javascript">var myTask = new todo.Todo({description: "Write code"});
|
||||
|
||||
//read the description
|
||||
myTask.description(); //Write code
|
||||
|
||||
//is it done?
|
||||
var isDone = myTask.done(); //isDone == false
|
||||
|
||||
//mark as done
|
||||
myTask.done(true); //true
|
||||
|
||||
//now it's done
|
||||
isDone = myTask.done(); //isDone == true
|
||||
</code></pre>
|
||||
<p>The <code>TodoList</code> class is simply an alias of the native <code>Array</code> class.</p>
|
||||
<pre><code class="lang-javascript">var list = new todo.TodoList();
|
||||
list.length; //0
|
||||
</code></pre>
|
||||
<p>According to the classic definition of the MVC pattern, the model layer is responsible for data storage, state management and business logic.</p>
|
||||
<p>You can see that our classes above fit the criteria: they have all the methods and properties that they need to be assembled into a meaningful state. A <code>Todo</code> can be instantiated, and have its properties changed. The list can have todo items added to it via the <code>push</code> method. And so on.</p>
|
||||
<h4 id="view-model">View-Model</h4>
|
||||
<p>Our next step is to write a view-model that will use our model classes. A view-model is a model level entity that stores UI state. In many frameworks UI state is typically stored in a controller, but doing so makes the code harder to scale since controllers aren't designed to be data providers. In Mithril, UI state is understood to be model data, even though it doesn't necessarily map to a database ORM entity.</p>
|
||||
<p>View-models are also responsible for handling business logic that revolves around UI-specific restrictions. For example a form might have an input and a cancel button. In such a case, it's the view-model's responsibility to track the current state of the input vs the original state and to apply a cancellation, if required. In the event the form was saved, then view-model would delegate saving to a more appropriate ORM model entity.</p>
|
||||
<p>In the case of our todo application, the view-model needs a few things: it needs to track a running list of todos and a field for adding new todos, and it needs to handle the logic of adding to the todo and the implications of this action of the UI.</p>
|
||||
<pre><code class="lang-javascript">//define the view-model
|
||||
todo.vm = {
|
||||
init: function() {
|
||||
//a running list of todos
|
||||
todo.vm.list = new todo.TodoList();
|
||||
|
||||
//a slot to store the name of a new todo before it is created
|
||||
todo.vm.description = m.prop('');
|
||||
|
||||
//adds a todo to the list, and clears the description field for user convenience
|
||||
todo.vm.add = function(description) {
|
||||
if (description()) {
|
||||
todo.vm.list.push(new todo.Todo({description: description()}));
|
||||
todo.vm.description("");
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
</code></pre>
|
||||
<p>The code above defines a view-model object called <code>vm</code>. It is simply a javascript object that has an <code>init</code> function. This function initializes the <code>vm</code> object with three members: <code>list</code>, which is simply an array, <code>description</code>, which is an <code>m.prop</code> getter-setter function with an empty string as the initial value, and <code>add</code>, which is a method that adds a new Todo instance to <code>list</code> if an input description getter-setter is not an empty string.</p>
|
||||
<p>Later in this guide, we'll pass the <code>description</code> property as the parameter to this function. When we get there, I'll explain why we're passing description as an argument instead of simply using OOP-style member association.</p>
|
||||
<p>You can use the view-model like this:</p>
|
||||
<pre><code class="lang-javascript">//initialize our view-model
|
||||
todo.vm.init();
|
||||
|
||||
todo.vm.description(); //[empty string]
|
||||
|
||||
//try adding a to-do
|
||||
todo.vm.add(todo.vm.description);
|
||||
todo.vm.list.length; //0, because you can't add a to-do with an empty description
|
||||
|
||||
//add it properly
|
||||
todo.vm.description("Write code");
|
||||
todo.vm.add(todo.vm.description);
|
||||
todo.vm.list.length; //1
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="controller">Controller</h3>
|
||||
<p>In classic MVC, the role of the controller is to dispatch actions from the view to the model layer. In traditional server-side frameworks, the controller layer is of large significance because the nature of HTTP requests, responses and the framework abstractions that are exposed to developers require that the controller act as an adapter layer to transform the serialized data from HTTP requests to something that can be passed to ORM model methods.</p>
|
||||
<p>In client-side MVC, however, this dissonance doesn't exist, and controllers can be extremely simple. Mithril controllers can be stripped down to a bare minimum, so that they only perform a single essential role: to expose a scoped set of model-level functionality. As you may recall, models are responsible for encapsulating business logic, and view-models encapsulate logic that pertains specifically to UI state, so there's really nothing else for a controller to abstract away, and all it needs to do is expose a slice of the model layer that pertains to the UI that is currently in view.</p>
|
||||
<p>In other words, all our controller needs to do is this:</p>
|
||||
<pre><code class="lang-javascript">todo.controller = function() {
|
||||
todo.vm.init()
|
||||
}
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="view">View</h3>
|
||||
<p>The next step is to write a view so users can interact with the application. In Mithril, views are plain Javascript. This comes with several benefits (proper error reporting, proper lexical scoping, etc.), while still allowing <a href="https://github.com/insin/msx">HTML syntax to be used via a preprocessor tool</a></p>
|
||||
<pre><code class="lang-javascript">todo.view = function() {
|
||||
return m("html", [
|
||||
m("body", [
|
||||
m("input"),
|
||||
m("button", "Add"),
|
||||
m("table", [
|
||||
m("tr", [
|
||||
m("td", [
|
||||
m("input[type=checkbox]")
|
||||
]),
|
||||
m("td", "task description"),
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
};
|
||||
</code></pre>
|
||||
<p>The utility method <code>m()</code> creates virtual DOM elements. As you can see, you can use CSS selectors to specify attributes. You can also use the <code>.</code> syntax to add CSS classes and the <code>#</code> to add an id.</p>
|
||||
<p>In fact, when not using the <a href="https://github.com/insin/msx">MSX</a> HTML syntax preprocessor, it's recommended that you embrace using CSS selectors (e.g. <code>m(".modal-body")</code>) to really benefit from their inherent semantic expressiveness.</p>
|
||||
<p>For the purposes of testing out our code so far, the view can be rendered using the <code>m.render</code> method:</p>
|
||||
<pre><code class="lang-javascript">m.render(document, todo.view());
|
||||
</code></pre>
|
||||
<p>Notice that we pass a root DOM element to attach our template to, as well as the template itself.</p>
|
||||
<p>This renders the following markup:</p>
|
||||
<pre><code class="lang-markup"><html>
|
||||
<body>
|
||||
<input />
|
||||
<button>Add</button>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" /></td>
|
||||
<td>task description</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
</code></pre>
|
||||
<p>Note that <code>m.render</code> is a very low level method in Mithril that draws only once and doesn't attempt to run the auto-redrawing system. In order to enable auto-redrawing, the <code>todo</code> component must be initialized by either calling <code>m.mount</code> or by creating a route definition with <code>m.route</code>. Also note that, unlike observable-based frameworks like Knockout.js, setting a value in a <code>m.prop</code> getter-setter does NOT trigger redrawing side-effects in Mithril.</p>
|
||||
<hr>
|
||||
<h4 id="data-bindings">Data Bindings</h4>
|
||||
<p>Let's implement a <strong>data binding</strong> on the text input. Data bindings connect a DOM element to a Javascript variable so that updating one updates the other.</p>
|
||||
<pre><code class="lang-javascript">//binding a model value to an input in a template
|
||||
m("input", {value: todo.vm.description()})
|
||||
</code></pre>
|
||||
<p>This binds the <code>description</code> getter-setter to the text input. Updating the value of the description in the model updates the DOM input when Mithril redraws.</p>
|
||||
<pre><code class="lang-javascript">todo.vm.init();
|
||||
|
||||
todo.vm.description(); // empty string
|
||||
m.render(document, todo.view()); // input is blank
|
||||
|
||||
todo.vm.description("Write code"); //set the description in the controller
|
||||
m.render(document, todo.view()); // input now says "Write code"
|
||||
</code></pre>
|
||||
<p>At a glance it may seem like we're doing something very expensive by redrawing, but as it turns out, calling the <code>todo.view</code> method multiple times does not actually re-render the entire template. Internally, Mithril keeps a virtual representation of the DOM in cache, scans for changes, and then only modifies the absolute minimum required to apply the change to the DOM. In practice, this results in surprisingly fast re-rendering.</p>
|
||||
<p>In the case above, Mithril only touches the <code>value</code> attribute of the input.</p>
|
||||
<p>Note that the example above only <em>sets</em> the value of the input element in the DOM, but it never <em>reads</em> it. This means that typing something on the input and then re-rendering will clobber the text on screen.</p>
|
||||
<hr>
|
||||
<p>Fortunately, bindings can also be <strong>bi-directional</strong>: that is, they can be coded in such a way that, in addition to setting the DOM value, it's also possible to read it as a user types, and then update the <code>description</code> getter-setter in the view-model.</p>
|
||||
<p>Here's the most basic way of implementing the view-to-model part of the binding:</p>
|
||||
<pre><code class="lang-javascript">m("input", {onchange: m.withAttr("value", todo.vm.description), value: todo.vm.description()})
|
||||
</code></pre>
|
||||
<p>The code bound to the <code>onchange</code> can be read like this: "with the attribute value, set todo.vm.description".</p>
|
||||
<p>Note that Mithril does not prescribe how the binding updates: you can bind it to <code>onchange</code>, <code>onkeypress</code>, <code>oninput</code>, <code>onblur</code> or any other event that you prefer.</p>
|
||||
<p>You can also specify what attribute to bind. This means that just as you are able to bind the <code>value</code> attribute in an <code><select></code>, you are also able to bind the <code>selectedIndex</code> property, if needed for whatever reason.</p>
|
||||
<p>The <code>m.withAttr</code> utility is a functional programming tool provided by Mithril to minimize the need for anonymous functions in the view.</p>
|
||||
<p>The <code>m.withAttr("value", todo.vm.description)</code> call above returns a function that is the rough equivalent of this code:</p>
|
||||
<pre><code class="lang-javascript">onchange: function(e) {
|
||||
todo.vm.description(e.target["value"]);
|
||||
}
|
||||
</code></pre>
|
||||
<p>The difference, aside from avoiding an anonymous function, is that the <code>m.withAttr</code> idiom also takes care of catching the correct event target and selecting the appropriate source of the data - i.e. whether it should come from a Javascript property or from <code>DOMElement::getAttribute()</code></p>
|
||||
<hr>
|
||||
<p>In addition to bi-directional data binding, we can also bind parameterized functions to events:</p>
|
||||
<pre><code class="lang-javascript">var vm = todo.vm
|
||||
|
||||
m("button", {onclick: vm.add.bind(vm, vm.description)}, "Add")
|
||||
</code></pre>
|
||||
<p>In the code above, we are simply using the native Javascript <code>Function::bind</code> method. This creates a new function with the parameter already set. In functional programming, this is called <a href="http://en.wikipedia.org/wiki/Partial_application"><em>partial application</em></a>.</p>
|
||||
<p>The <code>vm.add.bind(vm, vm.description)</code> expression above returns a function that is equivalent to this code:</p>
|
||||
<pre><code class="lang-javascript">onclick: function(e) {
|
||||
todo.vm.add(todo.vm.description)
|
||||
}
|
||||
</code></pre>
|
||||
<p>Note that when we construct the parameterized binding, we are passing the <code>description</code> getter-setter <em>by reference</em>, and not its value. We only evaluate the getter-setter to get its value in the controller method. This is a form of <em>lazy evaluation</em>: it allows us to say "use this value later, when the event handler gets called".</p>
|
||||
<p>Hopefully by now, you're starting to see why Mithril encourages the usage of <code>m.prop</code>: Because Mithril getter-setters are functions, they naturally compose well with functional programming tools, and allow for some very powerful idioms. In this case, we're using them in a way that resembles C pointers.</p>
|
||||
<p>Mithril uses them in other interesting ways elsewhere.</p>
|
||||
<p>Clever readers will probably notice that we can refactor the <code>add</code> method to make it much simpler:</p>
|
||||
<pre><code class="lang-javascript">vm.add = function() {
|
||||
if (vm.description()) {
|
||||
vm.list.push(new todo.Todo({description: vm.description()}));
|
||||
vm.description("");
|
||||
}
|
||||
};
|
||||
</code></pre>
|
||||
<p>The difference with the modified version is that <code>add</code> no longer takes an argument.</p>
|
||||
<p>With this, we can make the <code>onclick</code> binding on the template <em>much</em> simpler:</p>
|
||||
<pre><code>m("button", {onclick: todo.vm.add}, "Add")
|
||||
</code></pre><p>The only reason I talked about partial application here was to make you aware of that technique, since it becomes useful when dealing with parameterized event handlers. In real life, given a choice, you should always pick the simplest idiom for your use case.</p>
|
||||
<hr>
|
||||
<p>To implement flow control in Mithril views, we simply use Javascript Array methods:</p>
|
||||
<pre><code class="lang-javascript">//here's the view
|
||||
m("table", [
|
||||
todo.vm.list.map(function(task, index) {
|
||||
return m("tr", [
|
||||
m("td", [
|
||||
m("input[type=checkbox]")
|
||||
]),
|
||||
m("td", task.description()),
|
||||
])
|
||||
})
|
||||
])
|
||||
</code></pre>
|
||||
<p>In the code above, <code>todo.vm.list</code> is an Array, and <code>map</code> is one of its native functional methods. It allows us to iterate over the list and merge transformed versions of the list items into an output array.</p>
|
||||
<p>As you can see, we return a partial template with two <code><td></code>'s. The second one has a data binding to the <code>description</code> getter-setter of the Todo class instance.</p>
|
||||
<p>You're probably starting to notice that Javascript has strong support for functional programming and that it allows us to naturally do things that can be clunky in other frameworks (e.g. looping inside a <code><dl>/<dt>/<dd></code> construct).</p>
|
||||
<hr>
|
||||
<p>The rest of the code can be implemented using idioms we already covered. The complete view looks like this:</p>
|
||||
<pre><code class="lang-javascript">todo.view = function() {
|
||||
return m("html", [
|
||||
m("body", [
|
||||
m("input", {onchange: m.withAttr("value", todo.vm.description), value: todo.vm.description()}),
|
||||
m("button", {onclick: todo.vm.add}, "Add"),
|
||||
m("table", [
|
||||
todo.vm.list.map(function(task, index) {
|
||||
return m("tr", [
|
||||
m("td", [
|
||||
m("input[type=checkbox]", {onclick: m.withAttr("checked", task.done), checked: task.done()})
|
||||
]),
|
||||
m("td", {style: {textDecoration: task.done() ? "line-through" : "none"}}, task.description()),
|
||||
])
|
||||
})
|
||||
])
|
||||
])
|
||||
]);
|
||||
};
|
||||
</code></pre>
|
||||
<p>Here are the highlights of the template above:</p>
|
||||
<ul>
|
||||
<li>The template is rendered as a child of the implicit <code><html></code> element of the document.</li>
|
||||
<li>The text input saves its value to the <code>todo.vm.description</code> getter-setter we defined earlier.</li>
|
||||
<li>The button calls the <code>todo.vm.add</code> method when clicked.</li>
|
||||
<li>The table lists all the existing to-dos, if any.</li>
|
||||
<li>The checkboxes save their value to the <code>task.done</code> getter setter.</li>
|
||||
<li>The description gets crossed out via CSS if the task is marked as done.</li>
|
||||
<li>When updates happen, the template is not wholly re-rendered - only the changes are applied.</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>So far, we've been using <code>m.render</code> to manually redraw after we made a change to the data. However, as I mentioned before, you can enable an <a href="auto-redrawing.html">auto-redrawing system</a>, by initializing the <code>todo</code> component via <code>m.mount</code>.</p>
|
||||
<pre><code class="lang-javascript">//render the todo component inside the document DOM node
|
||||
m.mount(document, {controller: todo.controller, view: todo.view});
|
||||
</code></pre>
|
||||
<p>Mithril's auto-redrawing system keeps track of controller stability, and only redraws the view once it detects that the controller has finished running all of its code, including asynchronous AJAX payloads. Likewise, it intelligently waits for asynchronous services inside event handlers to complete before redrawing. </p>
|
||||
<p>You can learn more about how redrawing heuristics work <a href="auto-redrawing.html">here</a>.</p>
|
||||
<hr>
|
||||
<h3 id="summary">Summary</h3>
|
||||
<p>Here's the application code in its entirety:</p>
|
||||
<pre><code class="lang-markup"><!doctype html>
|
||||
<script src="mithril.min.js"></script>
|
||||
<script>
|
||||
//this application only has one component: todo
|
||||
var todo = {};
|
||||
|
||||
//for simplicity, we use this component to namespace the model classes
|
||||
|
||||
//the Todo class has two properties
|
||||
todo.Todo = function(data) {
|
||||
this.description = m.prop(data.description);
|
||||
this.done = m.prop(false);
|
||||
};
|
||||
|
||||
//the TodoList class is a list of Todo's
|
||||
todo.TodoList = Array;
|
||||
|
||||
//the view-model tracks a running list of todos,
|
||||
//stores a description for new todos before they are created
|
||||
//and takes care of the logic surrounding when adding is permitted
|
||||
//and clearing the input after adding a todo to the list
|
||||
todo.vm = (function() {
|
||||
var vm = {}
|
||||
vm.init = function() {
|
||||
//a running list of todos
|
||||
vm.list = new todo.TodoList();
|
||||
|
||||
//a slot to store the name of a new todo before it is created
|
||||
vm.description = m.prop("");
|
||||
|
||||
//adds a todo to the list, and clears the description field for user convenience
|
||||
vm.add = function() {
|
||||
if (vm.description()) {
|
||||
vm.list.push(new todo.Todo({description: vm.description()}));
|
||||
vm.description("");
|
||||
}
|
||||
};
|
||||
}
|
||||
return vm
|
||||
}())
|
||||
|
||||
//the controller defines what part of the model is relevant for the current page
|
||||
//in our case, there's only one view-model that handles everything
|
||||
todo.controller = function() {
|
||||
todo.vm.init()
|
||||
}
|
||||
|
||||
//here's the view
|
||||
todo.view = function() {
|
||||
return m("html", [
|
||||
m("body", [
|
||||
m("input", {onchange: m.withAttr("value", todo.vm.description), value: todo.vm.description()}),
|
||||
m("button", {onclick: todo.vm.add}, "Add"),
|
||||
m("table", [
|
||||
todo.vm.list.map(function(task, index) {
|
||||
return m("tr", [
|
||||
m("td", [
|
||||
m("input[type=checkbox]", {onclick: m.withAttr("checked", task.done), checked: task.done()})
|
||||
]),
|
||||
m("td", {style: {textDecoration: task.done() ? "line-through" : "none"}}, task.description()),
|
||||
])
|
||||
})
|
||||
])
|
||||
])
|
||||
]);
|
||||
};
|
||||
|
||||
//initialize the application
|
||||
m.mount(document, {controller: todo.controller, view: todo.view});
|
||||
</script>
|
||||
</code></pre>
|
||||
<p>This example is also available as a <a href="http://jsfiddle.net/fbgypzbr/16/">jsFiddle</a>.
|
||||
There is also <a href="http://jsfiddle.net/glebcha/q7tvLxsa/">Extended example</a> available on jsfiddle.</p>
|
||||
<hr>
|
||||
<h2 id="notes-on-architecture">Notes on Architecture</h2>
|
||||
<p>Idiomatic Mithril code is meant to apply good programming conventions and be easy to refactor.</p>
|
||||
<p>In the application above, notice how the Todo class can easily be moved to a different component if code re-organization is required.</p>
|
||||
<p>Todos are self-contained and their data aren't tied to the DOM like in typical jQuery based code. The Todo class API is reusable and unit-test friendly, and in addition, it's a plain-vanilla Javascript class, and so has almost no framework-specific learning curve.</p>
|
||||
<p><a href="mithril.prop.html"><code>m.prop</code></a> is a simple but surprisingly versatile tool: it's functionally composable, it enables <a href="http://en.wikipedia.org/wiki/Uniform_data_access">uniform data access</a> and allows a higher degree of decoupling when major refactoring is required.</p>
|
||||
<p>When refactoring is unavoidable, the developer can simply replace the <code>m.prop</code> call with an appropriate getter-setter implementation, instead of having to grep for API usage across the entire application.</p>
|
||||
<p>For example, if todo descriptions needed to always be uppercased, one could simply change the <code>description</code> getter-setter:</p>
|
||||
<pre><code class="lang-javascript">this.description = m.prop(data.description)
|
||||
</code></pre>
|
||||
<p>becomes:</p>
|
||||
<pre><code class="lang-javascript">//private store
|
||||
var description;
|
||||
|
||||
//public getter-setter
|
||||
this.description = function(value) {
|
||||
if (arguments.length > 0) description = value.toUpperCase();
|
||||
return description;
|
||||
}
|
||||
|
||||
//make it serializable
|
||||
this.description.toJSON = function() {return description}
|
||||
|
||||
//set the value
|
||||
this.description(data.description)
|
||||
</code></pre>
|
||||
<p>In the view-model, we aliased the native Array class for <code>TodoList</code>. Be aware that by using the native Array class, we're making an implicit statement that we are going to support all of the standard Array methods as part of our API.</p>
|
||||
<p>While this decision allows better API discoverability, the trade-off is that we're largely giving up on custom constraints and behavior. For example, if we wanted to change the application to make the list be persisted, a native Array would most certainly not be a suitable class to use.</p>
|
||||
<p>In order to deal with that type of refactoring, one can explicitly decide to support only a subset of the Array API, and implement another class with the same interface as this subset API.</p>
|
||||
<p>Given the code above, the replacement class would only need to implement the <code>.push()</code> and <code>.map()</code> methods. By freezing APIs and swapping implementations, the developer can completely avoid touching other layers in the application while refactoring.</p>
|
||||
<pre><code class="lang-javascript">todo.TodoList = Array;
|
||||
</code></pre>
|
||||
<p>becomes:</p>
|
||||
<pre><code class="lang-javascript">todo.TodoList = function () {
|
||||
this.push = function() { /*...*/ },
|
||||
this.map = function() { /*...*/ }
|
||||
};
|
||||
</code></pre>
|
||||
<p>Hopefully these examples give you an idea of ways requirements can change over time and how Mithril's philosophy allows developers to use standard OOP techniques to refactor their codebases, rather than needing to modify large portions of the application.</p>
|
||||
<hr>
|
||||
<p>The first and most obvious thing you may have noticed in the view layer is that the view is not written in HTML.</p>
|
||||
<p>While superficially this may seem like an odd design, this actually has a lot of benefits:</p>
|
||||
<ul>
|
||||
<li><p>No flash-of-unbehaviored-content (FOUC). In fact, Mithril is able to render a fully functional application - with working event handlers - before the "DOM ready" event fires!</p>
|
||||
</li>
|
||||
<li><p>There's no need for a parse-and-compile pre-processing step to turn strings containing HTML + templating syntax into working DOM elements.</p>
|
||||
</li>
|
||||
<li><p>Mithril views can provide accurate and informative error reporting, with line numbers and meaningful stack traces.</p>
|
||||
</li>
|
||||
<li><p>You get the ability to automate linting, unit testing and minifying of the entire view layer.</p>
|
||||
</li>
|
||||
<li><p>It provides full Turing completeness: full control over evaluation eagerness/laziness and caching in templates. You can even build components that take other components as first-class-citizen parameters!</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>And if you really do want to use HTML syntax after all, <a href="https://github.com/insin/msx">you can use a package called MSX</a>.</p>
|
||||
<p>Views in Mithril use a virtual DOM diff implementation, which sidesteps performance problems related to opaque dirty-checking and excessive browser repaint that are present in some frameworks.</p>
|
||||
<p>Another feature - the optional <code>m()</code> utility - allows writing terse templates in a declarative style using CSS shorthands, similar to popular HTML preprocessors from server-side MVC frameworks.</p>
|
||||
<p>And because Mithril views are Javascript, the developer has full freedom to abstract common patterns - from bidirectional binding helpers to full blown components - using standard Javascript refactoring techniques.</p>
|
||||
<p>Mithril templates are also more collision-proof than other component systems since there's no way to pollute the HTML tag namespace by defining ad-hoc tag names.</p>
|
||||
<p>A more intellectually interesting aspect of the framework is that event handling is encouraged to be done via functional composition (i.e. by using tools like <a href="mithril.withAttr.html"><code>m.withAttr</code></a>, <a href="mithril.prop.html"><code>m.prop</code></a> and the native <code>.bind()</code> method for partial application).</p>
|
||||
<p>If you've been interested in learning or using Functional Programming in the real world, Mithril provides very pragmatic opportunities to get into it.</p>
|
||||
<hr>
|
||||
<h2 id="learn-more">Learn More</h2>
|
||||
<p>Mithril provides a few more facilities that are not demonstrated in this page. The following topics are good places to start a deeper dive.</p>
|
||||
<ul>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="optimizing-performance.html">Optimizing performance</a></li>
|
||||
<li><a href="auto-redrawing.html">Integrating with the Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
210
archive/v0.2.4/how-to-read-signatures.html
Normal file
210
archive/v0.2.4/how-to-read-signatures.html
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>How to Read Signatures
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="how-to-read-signatures">How to Read Signatures</h2>
|
||||
<p>Rather than providing concrete classes like other frameworks, Mithril provides methods that operate on plain old Javascript objects (POJOs) that match given signatures.</p>
|
||||
<p>A signature is a description of its static type. For functions, it shows the parameters of the function, its return value and their expected types. For objects and arrays, it shows the expected data structure and the expected types of their members.</p>
|
||||
<p>Method signatures in this documentation follow a syntax similar to Java syntax, with some extra additions:</p>
|
||||
<pre><code class="lang-clike">ReturnType methodName(ParameterType1 param1, ParameterType2 param2)
|
||||
</code></pre>
|
||||
<h3 id="optional-parameters">Optional Parameters</h3>
|
||||
<p>Square brackets denote optional parameters. In the example below, <code>param2</code> and <code>param3</code> can both be omitted, but passing a value to <code>param2</code> is required if also passing a value to <code>param3</code>:</p>
|
||||
<pre><code class="lang-clike">String test(String arg1 [, String arg2 [, String arg3]])
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">//examples of valid function calls
|
||||
test("first");
|
||||
test("first", "second");
|
||||
test("first", "second", "third");
|
||||
</code></pre>
|
||||
<h3 id="type-placeholders">Type Placeholders</h3>
|
||||
<p>The word <code>void</code> is used as a type when a function does not return a value (i.e. undefined):</p>
|
||||
<pre><code class="lang-clike">void test()
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">console.log(test()); // undefined
|
||||
</code></pre>
|
||||
<p>The word <code>any</code> is used as a type if there are no type restrictions on a parameter:</p>
|
||||
<pre><code class="lang-clike">void test(any value)
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">//examples of valid function calls
|
||||
test("hello");
|
||||
test(1);
|
||||
test(["hello", "world"]);
|
||||
</code></pre>
|
||||
<h3 id="arrays">Arrays</h3>
|
||||
<p>Arrays use Generics syntax to denote the expected type of array members:</p>
|
||||
<pre><code class="lang-clike">void test(Array<String> values)
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">//example of a valid function call
|
||||
test(["first", "second"]);
|
||||
</code></pre>
|
||||
<h3 id="objects-as-key-value-maps">Objects as Key-Value Maps</h3>
|
||||
<p>Objects also use Generics syntax when they are meant to be used as a key-value map. Keys are always strings and, in key-value maps, can have any name.</p>
|
||||
<pre><code class="lang-clike">void test(Object<Number> values)
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">//example of a valid function call
|
||||
test({first: 1, second: 2});
|
||||
</code></pre>
|
||||
<h3 id="objects-as-class-interfaces">Objects as Class Interfaces</h3>
|
||||
<p>Objects that require specific keys are denoted using curly brace syntax:</p>
|
||||
<pre><code class="lang-clike">void test(Object {String first, Number second} value)
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">//example of a valid function call
|
||||
test({first: "first", second: 2});
|
||||
</code></pre>
|
||||
<h3 id="type-aliasing">Type Aliasing</h3>
|
||||
<p>Some types are aliases of more complex types. For example, in the example below, we created an alias called <code>ComplexType</code> for the type from the previous example</p>
|
||||
<pre><code class="lang-clike">void test(ComplexType value)
|
||||
|
||||
where:
|
||||
ComplexType :: Object {String first, Number second}
|
||||
|
||||
//example of a valid function call
|
||||
test({first: "first", second: 2})
|
||||
</code></pre>
|
||||
<h3 id="mixin-types">Mixin Types</h3>
|
||||
<p>Curly brace syntax can also appear on other base types to denote that the value contains static members. For example, in the example below, a value of type <code>ComplexType</code> is a string, but it also has a boolean property called <code>flag</code>:</p>
|
||||
<pre><code class="lang-clike">ComplexType :: String { Boolean flag }
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">//an example
|
||||
var a = aComplexTypeValue
|
||||
typeof a == "string" // true
|
||||
"flag" in a // true
|
||||
a.flag = true
|
||||
</code></pre>
|
||||
<p>In the following example, a value of type <code>ComplexType</code> is a function, with a property called <code>label</code></p>
|
||||
<pre><code class="lang-clike">ComplexType :: void test() { String label }
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">//an example
|
||||
var a = aComplexTypeValue
|
||||
typeof a == "function" // true
|
||||
"label" in a // true
|
||||
a.label = "first"
|
||||
</code></pre>
|
||||
<h3 id="polymorphic-types">Polymorphic Types</h3>
|
||||
<p>When multiple (but not all) types are accepted, the pipe <code>|</code> is used to delimit the list of valid types</p>
|
||||
<pre><code class="lang-clike">void test(Children children, Value value)
|
||||
|
||||
where:
|
||||
Children :: Array<String text | Number number>
|
||||
Value :: String | Number
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">//examples of valid function calls
|
||||
test(["test", 2], "second")
|
||||
test([1, 2, 3], "second")
|
||||
test([1, "test", 3], 2)
|
||||
</code></pre>
|
||||
<p>Pipe syntax within Object curly brace syntax means that, for a specific key, name has specific type requirements.</p>
|
||||
<p>In the example below, the <code>value</code> parameter should be a key-value map. This map may contain a key called <code>config</code>, whose value should be a function.</p>
|
||||
<pre><code class="lang-clike">void test(Object { any | void config(DOMElement) } value)
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">//example of a valid function call
|
||||
test({ first: "first", config: function(element) { /*do stuff*/ } })
|
||||
</code></pre>
|
||||
<h3 id="nullable-types">Nullable Types</h3>
|
||||
<p>A question mark <code>?</code> after a type denotes that a value can be either of that type or <code>undefined</code>.</p>
|
||||
<pre><code class="lang-clink">XMLHttpRequest? config()
|
||||
</code></pre>
|
||||
<p>In the example above, the <code>config</code> function is expected to return either an instance of the XMLHttpRequest object or <code>undefined</code></p>
|
||||
<h3 id="splat">Splat</h3>
|
||||
<p>An ellipsis <code>...</code> means that an array argument can instead be a list of arguments</p>
|
||||
<pre><code class="lang-clike">VirtualElement m(String selector [, Attributes attributes] [, Children... children])
|
||||
</code></pre>
|
||||
<p>In the example above, we can define children as an array:</p>
|
||||
<pre><code class="lang-javascript">m("div", {title: "foo"}, ["child 1", "child 2", "child 3"])
|
||||
</code></pre>
|
||||
<p>And we also define it as a list of arguments (note that lack of square brackets)</p>
|
||||
<pre><code class="lang-javascript">m("div", {title: "foo"}, "child 1", "child 2", "child 3")
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
274
archive/v0.2.4/index.html
Normal file
274
archive/v0.2.4/index.html
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="cta">
|
||||
<div class="container">
|
||||
<h1 class="logo"><span>○</span> Mithril</h1>
|
||||
|
||||
<p>A Javascript Framework for Building Brilliant Applications</p>
|
||||
|
||||
<p>
|
||||
<a class="button" href="getting-started.html">Guide</a>
|
||||
<a class="button" href="installation.html">Download v0.2.4</a>
|
||||
</p>
|
||||
|
||||
<iframe src="ghbtns.html?user=lhorie&repo=mithril.js&type=watch&count=true" frameborder="0" scrolling="0" width="100" height="20"></iframe>
|
||||
|
||||
<a href="https://twitter.com/share" class="twitter-share-button" data-url="http://lhorie.github.io/mithril" data-via="LeoHorie">Tweet</a>
|
||||
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src='//platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
|
||||
|
||||
<a href="http://flattr.com/thing/2778375/lhoriemithril-js-on-GitHub" target="_blank"><img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr" title="Flattr" border="0" /></a>
|
||||
<a class="changelog" href="change-log.html"><small>Change Log</small></a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="features">
|
||||
<div class="container">
|
||||
<h2>What is Mithril?</h2>
|
||||
<p>Mithril is a client-side MVC framework - a tool to organize code in a way that is easy to think about and to maintain.</p>
|
||||
</div>
|
||||
<div class="container row">
|
||||
<div class="feature col(4,4,12)">
|
||||
<h2>Light-weight</h2>
|
||||
<ul>
|
||||
<li>Only 7.8 kB gzipped, no dependencies</li>
|
||||
<li>Small API, small learning curve</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="feature col(4,4,12)">
|
||||
<h2>Robust</h2>
|
||||
<ul>
|
||||
<li>Safe-by-default templates</li>
|
||||
<li>Hierarchical MVC via components</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="feature col(4,4,12)">
|
||||
<h2>Fast</h2>
|
||||
<ul>
|
||||
<li>Virtual DOM diffing and compilable templates</li>
|
||||
<li>Intelligent auto-redrawing system</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="sample">
|
||||
<div class="container">
|
||||
<div class="col(8,8,12)">
|
||||
<h2>Sample code</h2>
|
||||
|
||||
<pre><code class="language-javascript">
|
||||
//model
|
||||
var Page = {
|
||||
list: function() {
|
||||
return m.request({method: "GET", url: "pages.json"});
|
||||
}
|
||||
};
|
||||
|
||||
var Demo = {
|
||||
//controller
|
||||
controller: function() {
|
||||
var pages = Page.list();
|
||||
return {
|
||||
pages: pages,
|
||||
rotate: function() {
|
||||
pages().push(pages().shift());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//view
|
||||
view: function(ctrl) {
|
||||
return m("div", [
|
||||
ctrl.pages().map(function(page) {
|
||||
return m("a", {href: page.url}, page.title);
|
||||
}),
|
||||
m("button", {onclick: ctrl.rotate}, "Rotate links")
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//initialize
|
||||
m.mount(document.getElementById("example"), Demo);</code></pre>
|
||||
|
||||
</div>
|
||||
<div class="col(4,4,12)">
|
||||
<h2>Output</h2>
|
||||
<div id="example" class="example output">
|
||||
<script src="mithril.min.js"></script>
|
||||
<script>
|
||||
//model
|
||||
var Page = {
|
||||
list: function() {
|
||||
return m.request({method: "GET", url: "pages.json"});
|
||||
}
|
||||
};
|
||||
|
||||
var Demo = {
|
||||
//controller
|
||||
controller: function() {
|
||||
var pages = Page.list();
|
||||
return {
|
||||
pages: pages,
|
||||
rotate: function() {
|
||||
pages().push(pages().shift());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//view
|
||||
view: function(ctrl) {
|
||||
return m("div", [
|
||||
ctrl.pages().map(function(page) {
|
||||
return m("a", {href: page.url}, page.title);
|
||||
}),
|
||||
m("button", {onclick: ctrl.rotate}, "Rotate links")
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//initialize
|
||||
m.mount(document.getElementById("example"), Demo);
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="performance">
|
||||
<div class="container">
|
||||
<h2 id="performance">Performance</h2>
|
||||
<p>To run the execution time tests below, click on their respective links, run the profiler from your desired browser's developer tools and measure the running time of a page refresh (Lower is better). <a href="benchmarks.html">Read more</a></p>
|
||||
<div class="row">
|
||||
<div class="col(4,4,6)">
|
||||
<h3>Loading</h3>
|
||||
<table>
|
||||
<tr><td><a href="comparisons/mithril.parsing.html">Mithril</a></td><td><span class="bar" style="background:#161;width:1%;"></span> 0.28ms</td></tr>
|
||||
<tr><td><a href="comparisons/jquery.parsing.html">jQuery</a></td><td><span class="bar" style="background:#66c;width:26%;"></span> 13.11ms</td></tr>
|
||||
<tr><td><a href="comparisons/backbone.parsing.html">Backbone</a></td><td><span class="bar" style="background:#33c;width:37%;"></span> 18.54ms</td></tr>
|
||||
<tr><td><a href="comparisons/angular.parsing.html">Angular</a></td><td><span class="bar" style="background:#c33;width:14%;"></span> 7.49ms</td></tr>
|
||||
<tr><td><a href="comparisons/react.parsing.html">React</a></td><td><span class="bar" style="background:#6af;width:50%;"></span> 24.99ms</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col(8,8,12)">
|
||||
<h3>Rendering</h3>
|
||||
<table>
|
||||
<tr><td><a href="comparisons/mithril.rendering.html">Mithril</a></td><td><span class="bar" style="background:#161;width:4%;"></span> 9.44ms (uncompiled)</td></tr>
|
||||
<tr><td><a href="comparisons/jquery.rendering.html">jQuery</a></td><td><span class="bar" style="background:#66c;width:17%;"></span> 40.27ms</td></tr>
|
||||
<tr><td><a href="comparisons/backbone.rendering.html">Backbone</a></td><td><span class="bar" style="background:#33c;width:10%;"></span> 23.05ms</td></tr>
|
||||
<tr><td><a href="comparisons/angular.rendering.html">Angular</a></td><td><span class="bar" style="background:#c33;width:50%;"></span> 118.63ms</td></tr>
|
||||
<tr><td><a href="comparisons/react.rendering.html">React</a></td><td><span class="bar" style="background:#6af;width:33%;"></span> 79.65ms</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="security">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(8,8,12)">
|
||||
<h2>Safety</h2>
|
||||
<p>Mithril templates are safe by default, i.e. you can't unintentionally create security holes.</p>
|
||||
<p>To run the tests for each framework, click on the respective links. If you see an alert box, ensuring security with that framework is more work for you.</p>
|
||||
</div>
|
||||
<div class="col(4,4,12)">
|
||||
<h3>Test Summary</h3>
|
||||
<a href="comparisons/mithril.safety.html">Mithril</a> <em class="success">(pass) ✓</em><br />
|
||||
<a href="comparisons/jquery.safety.html">jQuery</a> <em class="error">(fail) ✗</em><br />
|
||||
<a href="comparisons/backbone.safety.html">Backbone</a> <em class="error">(fail) ✗</em><br />
|
||||
<a href="comparisons/angular.safety.html">Angular</a> <em class="success">(pass) ✓</em><br />
|
||||
<a href="comparisons/react.safety.html">React</a> <em class="success">(pass) ✓</em><br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="more">
|
||||
<div class="container row">
|
||||
<div class="col(3,6,12)">
|
||||
<div style="padding:0 2em 0 0;">
|
||||
<h2>Guide</h2>
|
||||
<p>Build a simple application. Learn the ropes</p>
|
||||
<p><a href="getting-started.html">Read Guide</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col(3,6,12)">
|
||||
<div style="padding:0 2em 0 0;">
|
||||
<h2>API</h2>
|
||||
<p>Docs and code samples for your reference</p>
|
||||
<p><a href="mithril.html">Read docs</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col(3,6,12)">
|
||||
<div style="padding:0 2em 0 0;">
|
||||
<h2>Learn Mithril</h2>
|
||||
<p>Weekly articles on how to use Mithril to its full potential.</p>
|
||||
<p><a href="http://lhorie.github.io/mithril-blog">Read articles</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col(3,6,12)">
|
||||
<div style="padding:0 2em 0 0;">
|
||||
<h2>Mailing List</h2>
|
||||
<p>Post questions and discuss Mithril related topics.</p>
|
||||
<p><a href="https://groups.google.com/forum/#!forum/mithriljs">Go to mailing list</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="socialmedia">
|
||||
<div class="container row">
|
||||
<h2>Social Media</h2>
|
||||
<div class="col(6,12,12)">
|
||||
<div style="padding:0 1em 0 0;">
|
||||
<blockquote class="twitter-tweet" lang="en"><p>Just converted the MELPA site from Angular to <a href="https://twitter.com/LeoHorie">@LeoHorie</a>'s tiny mithril.js: very pleasant. <a href="http://t.co/R7JKfo9vJ1">http://t.co/R7JKfo9vJ1</a> <a href="https://t.co/7IrR3L84OY">https://t.co/7IrR3L84OY</a></p>— Steve Purcell (@sanityinc) <a href="https://twitter.com/sanityinc/statuses/471358494101504000">May 27, 2014</a></blockquote>
|
||||
|
||||
<blockquote class="twitter-tweet" lang="en"><p><a href="https://twitter.com/LeoHorie">@<b>LeoHorie</b></a> I've long opposed JS frameworks, but I *really* dig Mithril! Major major kudos for making my life easier!</p>— i80and (@i80and) <a href="https://twitter.com/i80and/status/586170085691252736">Apr 9, 2015</a></blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col(6,12,12)">
|
||||
<blockquote class="twitter-tweet" lang="en"><p>Mithril.js m.prop feature is plain brilliant. I spent years yearning for an approach that lightweight <a href="http://t.co/KgzOxoa2WR">http://t.co/KgzOxoa2WR</a></p>— Xavier Via (@xaviervia) <a href="https://twitter.com/xaviervia/statuses/454635992360554497">April 11, 2014</a></blockquote>
|
||||
|
||||
<blockquote class="twitter-tweet" lang="en"><p>Ported my angularjs based WebGL ui editor to mithril and got a big speed boost. <a href="https://twitter.com/hashtag/mithriljs">#<b>mithriljs</b></a> love the small api and docs!</p> — geekrelief (@geekrelief) <a href="https://twitter.com/geekrelief/status/554214299460435969">January 11, 2015</a></blockquote>
|
||||
|
||||
<script async src="//platform.twitter.com/widgets.js"></script>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a><br />
|
||||
© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
125
archive/v0.2.4/installation.html
Normal file
125
archive/v0.2.4/installation.html
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Installation
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="installation">Installation</h2>
|
||||
<p>Mithril is available from a variety of sources:</p>
|
||||
<hr>
|
||||
<h3 id="direct-download">Direct download</h3>
|
||||
<p>You can <a href="mithril.min.zip">download a zip of the latest version here</a>.</p>
|
||||
<p>Links to older versions can be found in the <a href="change-log.html">change log</a>.</p>
|
||||
<p>In order to use Mithril, extract it from the zip file and point a script tag to the <code>.js</code> file:</p>
|
||||
<pre><code class="lang-markup"><script src="mithril.min.js"></script>
|
||||
</code></pre>
|
||||
<p>Note that in order to support older versions of IE, you need to include <a href="tools.html#internet-explorer-compatibility">some shims</a>.</p>
|
||||
<hr>
|
||||
<h3 id="cdns-content-delivery-networks-">CDNs (Content Delivery Networks)</h3>
|
||||
<p>You can also find Mithril in <a href="http://cdnjs.com/libraries/mithril/">cdnjs</a> and <a href="http://www.jsdelivr.com/#!mithril">jsDelivr</a>.</p>
|
||||
<p>Content delivery networks allow the library to be cached across different websites that use the same version of the framework, and help reduce latency by serving the files from a server that is physically near the user's location.</p>
|
||||
<h4 id="cdnjs">cdnjs</h4>
|
||||
<pre><code class="lang-markup"><script src="//cdnjs.cloudflare.com/ajax/libs/mithril/0.2.4/mithril.min.js"></script>
|
||||
</code></pre>
|
||||
<h4 id="jsdelivr">jsDelivr</h4>
|
||||
<pre><code class="lang-markup"><script src="//cdn.jsdelivr.net/mithril/0.2.4/mithril.min.js"></script>
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="npm">NPM</h3>
|
||||
<p>NPM is the default package manager for <a href="http://nodejs.org/">NodeJS</a>. If you're using NodeJS already or planning on using <a href="http://gruntjs.com/">Grunt</a> to create a build system, you can use NPM to conveniently keep up-to-date with Mithril versions.</p>
|
||||
<p>Assuming you have NodeJS installed, you can download Mithril by typing this:</p>
|
||||
<pre><code>npm install mithril
|
||||
</code></pre><p>Then, to use Mithril, point a script tag to the downloaded file:</p>
|
||||
<pre><code class="lang-markup"><script src="/node_modules/mithril/mithril.min.js"></script>
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="bower">Bower</h3>
|
||||
<p><a href="http://bower.io">Bower</a> is a frontend package manager built in <a href="http://nodejs.org/">NodeJS</a>. If you're using NodeJS already or planning on using <a href="http://gruntjs.com/">Grunt</a> to create a build system, you can use Bower to conveniently keep up-to-date with Mithril versions.</p>
|
||||
<p>Assuming you have NodeJS installed, you can install Bower by typing this in the command line:</p>
|
||||
<pre><code>npm install -g bower
|
||||
</code></pre><p>And you can download Mithril by typing this:</p>
|
||||
<pre><code>bower install mithril
|
||||
</code></pre><p>Then, to use Mithril, point a script tag to the downloaded file:</p>
|
||||
<pre><code class="lang-markup"><script src="/bower_components/mithril/mithril.min.js"></script>
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="component">Component</h3>
|
||||
<p><a href="http://component.github.io">Component</a> is another package manager for <a href="http://nodejs.org/">NodeJS</a>. If you're using NodeJS already or planning on using <a href="http://gruntjs.com/">Grunt</a> to create a build system, you can use Component to conveniently keep up-to-date with Mithril versions.</p>
|
||||
<p>Assuming you have NodeJS installed, you can install Component by typing this in the command line:</p>
|
||||
<pre><code>npm install -g component
|
||||
</code></pre><p>And you can download Mithril by typing this:</p>
|
||||
<pre><code>component install lhorie/mithril
|
||||
</code></pre><p>Then, to use Mithril, point a script tag to the downloaded file:</p>
|
||||
<pre><code class="lang-markup"><script src="/components/lhorie/mithril/master/mithril.js"></script>
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="rails">Rails</h3>
|
||||
<p>Jordan Humphreys created a gem to allow integration with Rails:</p>
|
||||
<p><a href="https://github.com/mrsweaters/mithril-rails">Mithril-Rails</a></p>
|
||||
<p>It includes support for the <a href="https://github.com/insin/msx">MSX</a> HTML templating syntax from Jonathan Buchanan.</p>
|
||||
<hr>
|
||||
<h3 id="github">Github</h3>
|
||||
<p>You can also fork the latest stable project <a href="https://github.com/lhorie/mithril">directly from Github</a>.</p>
|
||||
<p>If you want to use the bleeding edge version, you can <a href="https://github.com/lhorie/mithril.js">fork the development repository</a>.</p>
|
||||
<p>Be aware that even though Mithril has tests running in a continuous integration environment, the bleeding edge version might occasionally break. If you're interested in helping improve Mithril, you're welcome to use the bleeding edge version and report any bugs that you find.</p>
|
||||
<p>In order to update a forked version of Mithril, <a href="https://help.github.com/articles/syncing-a-fork">follow the instructions on this page</a>.</p>
|
||||
<h4 id="using-bleeding-edge-from-npm">Using bleeding edge from NPM</h4>
|
||||
<p>To use the bleeding edge version from npm, use the following command:</p>
|
||||
<pre><code>npm install git://github.com/lhorie/mithril.js#next --save
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
175
archive/v0.2.4/integration.html
Normal file
175
archive/v0.2.4/integration.html
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Integrating with Other Libraries
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="integrating-with-other-libraries">Integrating with Other Libraries</h2>
|
||||
<p>Integration with third party libraries or vanilla javascript code can be achieved via the <a href="mithril.html#accessing-the-real-dom"><code>config</code> attribute of virtual elements</a>.</p>
|
||||
<p>It's recommended that you encapsulate integration code in a component or a helper function.</p>
|
||||
<p>The example below shows a simple component that integrates with the <a href="http://ivaynberg.github.io/select2/">select2 library</a>.</p>
|
||||
<pre><code class="lang-javascript">var Select2 = {
|
||||
// Returns a select box
|
||||
view: function(ctrl, attrs) {
|
||||
var selectedId = attrs.value().id;
|
||||
//Create a Select2 progrssively enhanced SELECT element
|
||||
return m("select", {config: Select2.config(attrs)}, [
|
||||
attrs.data.map(function(item) {
|
||||
var args = {value: item.id};
|
||||
// Set selected option
|
||||
if(item.id == selectedId) {
|
||||
args.selected = "selected";
|
||||
}
|
||||
return m("option", args, item.name);
|
||||
})
|
||||
]);
|
||||
},
|
||||
/**
|
||||
Select2 config factory. The params in this doc refer to properties of the `ctrl` argument
|
||||
@param {Object} data - the data with which to populate the <option> list
|
||||
@param {prop} value - the prop of the item in `data` that we want to select
|
||||
@param {function(Object id)} onchange - the event handler to call when the selection changes.
|
||||
`id` is the the same as `value`
|
||||
*/
|
||||
// Note: The config is never run server side.
|
||||
config: function(ctrl) {
|
||||
return function(element, isInitialized) {
|
||||
if(typeof jQuery !== 'undefined' && typeof jQuery.fn.select2 !== 'undefined') {
|
||||
var el = $(element);
|
||||
if (!isInitialized) {
|
||||
el.select2()
|
||||
.on("change", function(e) {
|
||||
var id = el.select2("val");
|
||||
m.startComputation();
|
||||
//Set the value to the selected option
|
||||
ctrl.data.map(function(d){
|
||||
if(d.id == id) {
|
||||
ctrl.value(d);
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof ctrl.onchange == "function"){
|
||||
ctrl.onchange(el.select2("val"));
|
||||
}
|
||||
m.endComputation();
|
||||
});
|
||||
}
|
||||
el.val(ctrl.value().id).trigger("change");
|
||||
} else {
|
||||
console.warn('ERROR: You need jquery and Select2 in the page');
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var Dashboard = {
|
||||
controller: function() {
|
||||
var ctrl = this,
|
||||
//list of users to show
|
||||
data = [
|
||||
{id: 1, name: "John"},
|
||||
{id: 2, name: "Mary"},
|
||||
{id: 3, name: "Seniqua"}
|
||||
];
|
||||
|
||||
ctrl.data = data;
|
||||
// Has to use a prop for the current user
|
||||
ctrl.currentUser = m.prop(data[1]);
|
||||
ctrl.changeUser = function(id) {
|
||||
console.log(id);
|
||||
};
|
||||
},
|
||||
|
||||
view: function(ctrl) {
|
||||
return m("div", [
|
||||
m("label", "User:"),
|
||||
m.component(Select2, {
|
||||
data: ctrl.data,
|
||||
value: ctrl.currentUser,
|
||||
onchange: ctrl.changeUser
|
||||
})
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
m.mount(document.body, Dashboard);
|
||||
</code></pre>
|
||||
<p><code>select2.config</code> is a factory that creates a <code>config</code> function based on a given controller. We declare this outside of the <code>select2.view</code> function to avoid cluttering the template.</p>
|
||||
<p>The <code>config</code> function created by our factory only runs the initialization code if it hasn't already. This <code>if</code> statement is important, because this function may be called multiple times by Mithril's auto-redrawing system and we don't want to re-initialize select2 at every redraw.</p>
|
||||
<p>The initialization code defines a <code>change</code> event handler. Because this handler is not created using Mithril's templating engine (i.e. we're not defining an attribute in a virtual element), we must manually integrate it to the auto-redrawing system.</p>
|
||||
<p>This can be done by simply calling <code>m.startComputation</code> at the beginning, and <code>m.endComputation</code> at the end of the function. You must add a pair of these calls for each asynchronous execution thread, unless the thread is already integrated.</p>
|
||||
<p>For example, if you were to call a web service using <code>m.request</code>, you would not need to add more calls to <code>m.startComputation</code> / <code>m.endComputation</code> (you would still need the first pair in the event handler, though).</p>
|
||||
<p>On the other hand, if you were to call a web service using jQuery, then you would be responsible for adding a <code>m.startComputation</code> call before the jQuery ajax call, and for adding a <code>m.endComputation</code> call at the end of the completion callback, in addition to the calls within the <code>change</code> event handler. Refer to the <a href="auto-redrawing.html"><code>auto-redrawing</code></a> guide for an example.</p>
|
||||
<p>One important note about the <code>config</code> method is that you should avoid calling <code>m.redraw</code>, <code>m.startComputation</code> and <code>m.endComputation</code> in the <code>config</code> function's execution thread. (An execution thread is basically any amount of code that runs before other asynchronous threads start to run.)</p>
|
||||
<p>While Mithril technically does support this use case, relying on multiple redraw passes degrades performance and makes it possible to code yourself into an infinite execution loop situation, which is extremely difficult to debug.</p>
|
||||
<p>The <code>dashboard</code> component in the example shows how a developer would consume the select2 component.</p>
|
||||
<p>You should always document integration components so that others can find out what attribute parameters can be used to initialize the component.</p>
|
||||
<hr>
|
||||
<h2 id="integrating-to-legacy-code">Integrating to legacy code</h2>
|
||||
<p>If you need to add separate widgets to different places on a same page, you can simply initialize each widget as you would a regular Mithril application (i.e. use <code>m.render</code>, <code>m.mount</code> or <code>m.route</code>).</p>
|
||||
<p>There's just one caveat: while simply initializing multiple "islands" in this fashion works, their initialization calls are not aware of each other and can cause redraws too frequently. To optimize rendering, you should add a <code>m.startComputation</code> call before the first widget initialization call, and a <code>m.endComputation</code> after the last widget initialization call in each execution thread.</p>
|
||||
<pre><code class="lang-javascript">m.startComputation()
|
||||
|
||||
m.mount(document.getElementById("widget1-container"), Widget1)
|
||||
|
||||
m.mount(document.getElementById("widget2-container"), Widget2)
|
||||
|
||||
m.endComputation()
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
126
archive/v0.2.4/lib/prism/prism.css
Normal file
126
archive/v0.2.4/lib/prism/prism.css
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', monospace;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.builtin {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string,
|
||||
.token.variable {
|
||||
color: #a67f59;
|
||||
background: hsla(0,0%,100%,.5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
|
||||
.token.regex,
|
||||
.token.important {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
9
archive/v0.2.4/lib/prism/prism.js
Normal file
9
archive/v0.2.4/lib/prism/prism.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Prism: Lightweight, robust, elegant syntax highlighting
|
||||
* MIT license http://www.opensource.org/licenses/mit-license.php/
|
||||
* @author Lea Verou http://lea.verou.me
|
||||
*/(function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},clone:function(e){var n=t.util.type(e);switch(n){case"Object":var r={};for(var i in e)e.hasOwnProperty(i)&&(r[i]=t.util.clone(e[i]));return r;case"Array":return e.slice()}return e}},languages:{extend:function(e,n){var r=t.util.clone(t.languages[e]);for(var i in n)r[i]=n[i];return r},insertBefore:function(e,n,r,i){i=i||t.languages;var s=i[e],o={};for(var u in s)if(s.hasOwnProperty(u)){if(u==n)for(var a in r)r.hasOwnProperty(a)&&(o[a]=r[a]);o[u]=s[u]}return i[e]=o},DFS:function(e,n){for(var r in e){n.call(e,r,e[r]);t.util.type(e)==="Object"&&t.languages.DFS(e[r],n)}}},highlightAll:function(e,n){var r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');for(var i=0,s;s=r[i++];)t.highlightElement(s,e===!0,n)},highlightElement:function(r,i,s){var o,u,a=r;while(a&&!e.test(a.className))a=a.parentNode;if(a){o=(a.className.match(e)||[,""])[1];u=t.languages[o]}if(!u)return;r.className=r.className.replace(e,"").replace(/\s+/g," ")+" language-"+o;a=r.parentNode;/pre/i.test(a.nodeName)&&(a.className=a.className.replace(e,"").replace(/\s+/g," ")+" language-"+o);var f=r.textContent;if(!f)return;f=f.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ");var l={element:r,language:o,grammar:u,code:f};t.hooks.run("before-highlight",l);if(i&&self.Worker){var c=new Worker(t.filename);c.onmessage=function(e){l.highlightedCode=n.stringify(JSON.parse(e.data),o);t.hooks.run("before-insert",l);l.element.innerHTML=l.highlightedCode;s&&s.call(l.element);t.hooks.run("after-highlight",l)};c.postMessage(JSON.stringify({language:l.language,code:l.code}))}else{l.highlightedCode=t.highlight(l.code,l.grammar,l.language);t.hooks.run("before-insert",l);l.element.innerHTML=l.highlightedCode;s&&s.call(r);t.hooks.run("after-highlight",l)}},highlight:function(e,r,i){return n.stringify(t.tokenize(e,r),i)},tokenize:function(e,n,r){var i=t.Token,s=[e],o=n.rest;if(o){for(var u in o)n[u]=o[u];delete n.rest}e:for(var u in n){if(!n.hasOwnProperty(u)||!n[u])continue;var a=n[u],f=a.inside,l=!!a.lookbehind,c=0;a=a.pattern||a;for(var h=0;h<s.length;h++){var p=s[h];if(s.length>e.length)break e;if(p instanceof i)continue;a.lastIndex=0;var d=a.exec(p);if(d){l&&(c=d[1].length);var v=d.index-1+c,d=d[0].slice(c),m=d.length,g=v+m,y=p.slice(0,v+1),b=p.slice(g+1),w=[h,1];y&&w.push(y);var E=new i(u,f?t.tokenize(d,f):d);w.push(E);b&&w.push(b);Array.prototype.splice.apply(s,w)}}}return s},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[];r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(!r||!r.length)return;for(var i=0,s;s=r[i++];)s(n)}}},n=t.Token=function(e,t){this.type=e;this.content=t};n.stringify=function(e,r,i){if(typeof e=="string")return e;if(Object.prototype.toString.call(e)=="[object Array]")return e.map(function(t){return n.stringify(t,r,e)}).join("");var s={type:e.type,content:n.stringify(e.content,r,i),tag:"span",classes:["token",e.type],attributes:{},language:r,parent:i};s.type=="comment"&&(s.attributes.spellcheck="true");t.hooks.run("wrap",s);var o="";for(var u in s.attributes)o+=u+'="'+(s.attributes[u]||"")+'"';return"<"+s.tag+' class="'+s.classes.join(" ")+'" '+o+">"+s.content+"</"+s.tag+">"};if(!self.document){self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,i=n.code;self.postMessage(JSON.stringify(t.tokenize(i,t.languages[r])));self.close()},!1);return}var r=document.getElementsByTagName("script");r=r[r.length-1];if(r){t.filename=r.src;document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)}})();;
|
||||
Prism.languages.markup={comment:/<!--[\w\W]*?-->/g,prolog:/<\?.+?\?>/,doctype:/<!DOCTYPE.+?>/,cdata:/<!\[CDATA\[[\w\W]*?]]>/i,tag:{pattern:/<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|\w+))?\s*)*\/?>/gi,inside:{tag:{pattern:/^<\/?[\w:-]+/i,inside:{punctuation:/^<\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|>|"/g}},punctuation:/\/?>/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/&#?[\da-z]{1,8};/gi};Prism.hooks.add("wrap",function(e){e.type==="entity"&&(e.attributes.title=e.content.replace(/&/,"&"))});;
|
||||
Prism.languages.clike={comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,lookbehind:!0},string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/ig,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/ig,inside:{punctuation:/\(/}}, number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|(&){1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};
|
||||
;
|
||||
Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(var|let|if|else|while|do|for|return|in|instanceof|function|get|set|new|with|typeof|try|throw|catch|finally|null|break|continue)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g});Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}});Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/(<|<)script[\w\W]*?(>|>)[\w\W]*?(<|<)\/script(>|>)/ig,inside:{tag:{pattern:/(<|<)script[\w\W]*?(>|>)|(<|<)\/script(>|>)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript}}});;
|
||||
587
archive/v0.2.4/mithril.component.html
Normal file
587
archive/v0.2.4/mithril.component.html
Normal file
|
|
@ -0,0 +1,587 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.component
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-component">m.component</h2>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#rendering-components">Rendering components</a><ul>
|
||||
<li><a href="#optional-controller">Optional controller</a></li>
|
||||
<li><a href="#controller-as-a-class-constructor">Controller as a class constructor</a></li>
|
||||
<li><a href="#notes-on-the-view-function">Notes on the view function</a></li>
|
||||
<li><a href="#shorthand-syntax">Shorthand syntax</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#parameterized-components">Parameterized components</a></li>
|
||||
<li><a href="#nesting-components">Nesting components</a></li>
|
||||
<li><a href="#dealing-with-state">Dealing with state</a><ul>
|
||||
<li><a href="#stateless-components">Stateless components</a></li>
|
||||
<li><a href="#stateful-components">Stateful components</a></li>
|
||||
<li><a href="#parameterized-initial-state">Parameterized initial state</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#data-driven-component-identity">Data-driven component identity</a></li>
|
||||
<li><a href="#unloading-components">Unloading/Unmounting components</a></li>
|
||||
<li><a href="#nested-asynchronous-components">Nested asynchronous components</a></li>
|
||||
<li><a href="#limitations-and-caveats">Limitations and caveats</a></li>
|
||||
<li><a href="#opting-out-of-the-auto-redrawing-system">Opting out of the auto redrawing system</a></li>
|
||||
<li><a href="#signature">Signature</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>Components are building blocks for Mithril applications. They allow developers to encapsulate functionality into reusable units.</p>
|
||||
<hr>
|
||||
<h3 id="rendering-components">Rendering components</h3>
|
||||
<p>In Mithril, a component is nothing more than an object that has a <code>view</code> function and, optionally, a <code>controller</code> function.</p>
|
||||
<pre><code class="lang-javascript">var MyComponent = {
|
||||
controller: function(data) {
|
||||
return {greeting: "Hello"}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return m("h1", ctrl.greeting)
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, MyComponent) // renders <h1>Hello</h1> into <body>
|
||||
</code></pre>
|
||||
<p>The optional <code>controller</code> function creates an object that may be used in the following recommended ways:</p>
|
||||
<ul>
|
||||
<li>It can contain methods meant to be called by a <code>view</code>.</li>
|
||||
<li>It can call model methods directly or from methods inside the resulting object.</li>
|
||||
<li>It can store contextual data returned from model methods (i.e. a <a href="mithril.deferred.html">promise</a> from a <a href="mithril.request.html">request</a>).</li>
|
||||
<li>It can hold a reference to a view model.</li>
|
||||
</ul>
|
||||
<p>The <code>view</code> has access to methods and properties that the controller chooses to expose in the returned object. With those methods and properties, it creates a template that can consume model data and call controller methods to affect the model. This is the recommended way for views and models to exchange data.</p>
|
||||
<pre><code class="lang-javascript">//a simple MVC example
|
||||
|
||||
//a sample model that exposes a value
|
||||
var model = {count: 0}
|
||||
|
||||
var MyComponent = {
|
||||
controller: function(data) {
|
||||
return {
|
||||
increment: function() {
|
||||
//This is a simplication for the sake of the example.
|
||||
//Typically, values are modified via model methods,
|
||||
//rather than modified directly
|
||||
model.count++
|
||||
}
|
||||
}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return m("a[href=javascript:;]", {
|
||||
onclick: ctrl.increment //view calls controller method on click
|
||||
}, "Count: " + model.count)
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, MyComponent)
|
||||
|
||||
//renders:
|
||||
//<a href="javascript:;">Count: 0</a>
|
||||
//
|
||||
//the number increments when the link is clicked
|
||||
</code></pre>
|
||||
<p>Note that there is no requirement to tightly couple a controller and view while organizing code. It's perfectly valid to define controllers and views separately, and only bring them together when mounting them:</p>
|
||||
<pre><code class="lang-javascript">//controller.js
|
||||
var controller = function(data) {
|
||||
return {greeting: "Hello"}
|
||||
}
|
||||
|
||||
//view.js
|
||||
var view = function(ctrl) {
|
||||
return m("h1", ctrl.greeting)
|
||||
}
|
||||
|
||||
//render
|
||||
m.mount(document.body, {controller: controller, view: view}) // renders <h1>Hello</h1>
|
||||
</code></pre>
|
||||
<p>There are three ways to render a component:</p>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html"><code>m.route</code></a> (if you are building a single-page application that has multiple pages)</li>
|
||||
<li><a href="mithril.mount.html"><code>m.mount</code></a> (if your app only has one page)</li>
|
||||
<li><a href="mithril.render.html"><code>m.render</code></a> (if you are integrating Mithril's rendering engine into a larger framework and wish to manage redrawing yourself).</li>
|
||||
</ul>
|
||||
<p>The <code>controller</code> function is called <em>once</em> when the component is rendered. Subsequently, the <code>view</code> function is called and will be called again anytime a redraw is required. The return value of the <code>controller</code> function is passed to the <code>view</code> as its first argument.</p>
|
||||
<h4 id="optional-controller">Optional controller</h4>
|
||||
<p>The <code>controller</code> function is optional and defaults to an empty function <code>controller: function() {}</code></p>
|
||||
<pre><code class="lang-javascript">//a component without a controller
|
||||
var MyComponent = {
|
||||
view: function() {
|
||||
return m("h1", "Hello")
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, MyComponent) // renders <h1>Hello</h1>
|
||||
</code></pre>
|
||||
<h4 id="controller-as-a-class-constructor">Controller as a class constructor</h4>
|
||||
<p>A controller can also be used as a class constructor (i.e. it's possible to attach properties to the <code>this</code> object within the constructor, instead of returning a value).</p>
|
||||
<pre><code class="lang-javascript">var MyComponent = {
|
||||
controller: function(data) {
|
||||
this.greeting = "Hello"
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return m("h1", ctrl.greeting)
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, MyComponent) // renders <h1>Hello</h1>
|
||||
</code></pre>
|
||||
<h4 id="notes-on-the-view-function">Notes on the view function</h4>
|
||||
<p>The <code>view</code> function does not create a DOM tree when called. The return value of the view function is merely a plain Javascript data structure that represents a DOM tree. Internally, Mithril uses this data representation of the DOM to probe for data changes and update the DOM only where necessary. This rendering technique is known as <em>virtual DOM diffing</em>.</p>
|
||||
<p>The view function is run again whenever a redraw is required (i.e. whenever event handlers are triggered by user input). Its return value is used to diff against the previous virtual DOM tree.</p>
|
||||
<p>It may sound expensive to recompute an entire view any time there's a change to be displayed, but this operation actually turns out to be quite fast, compared to rendering strategies used by older frameworks. Mithril's diffing algorithm makes sure expensive DOM operations are performed only if absolutely necessary, and as an extra benefit, the global nature of the redraw makes it easy to reason about and troubleshoot the state of the application.</p>
|
||||
<h3 id="shorthand-syntax">Shorthand syntax</h3>
|
||||
<p>If the first argument to <code>m()</code> is a component, it acts as an alias of <code>m.component()</code></p>
|
||||
<pre><code class="lang-javascript">var MyComponent = {
|
||||
controller: function() {
|
||||
return {greeting: "hello"}
|
||||
},
|
||||
view: function(ctrl, args) {
|
||||
return m("h1", ctrl.greeting + " " + args.data)
|
||||
}
|
||||
}
|
||||
|
||||
m.render(document.body, [
|
||||
//the two lines below are equivalent
|
||||
m(MyComponent, {data: "world"}),
|
||||
m.component(MyComponent, {data: "world"})
|
||||
])
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="parameterized-components">Parameterized components</h3>
|
||||
<p>Components can have arguments "preloaded". In practice, this means that calling <code>m.component(MyComponent, {foo: "bar"})</code> will return a component that behaves exactly the same as <code>MyComponent</code>, but <code>{foo: "bar"}</code> will be bound as an argument to both the <code>controller</code> and <code>view</code> functions.</p>
|
||||
<pre><code class="lang-javascript">//declare a component
|
||||
var MyComponent = {
|
||||
controller: function(args, extras) {
|
||||
console.log(args.name, extras)
|
||||
return {greeting: "Hello"}
|
||||
},
|
||||
view: function(ctrl, args, extras) {
|
||||
return m("h1", ctrl.greeting + " " + args.name + " " + extras)
|
||||
}
|
||||
}
|
||||
|
||||
//create a component whose controller and view functions receive some arguments
|
||||
var component = m.component(MyComponent, {name: "world"}, "this is a test")
|
||||
|
||||
var ctrl = new component.controller() // logs "world", "this is a test"
|
||||
|
||||
m.render(document.body, component.view(ctrl)) // render the virtual DOM tree manually
|
||||
|
||||
//<body><h1>Hello world this is a test</h1></body>
|
||||
</code></pre>
|
||||
<p>The first parameter after the component object is meant to be used as an attribute map and should be an object (e.g. <code>{name: "world"}</code>). Subsequent parameters have no restrictions (e.g. <code>"this is a test"</code>)</p>
|
||||
<hr>
|
||||
<h3 id="nesting-components">Nesting components</h3>
|
||||
<p>Component views can include other components:</p>
|
||||
<pre><code class="lang-javascript">var App = {
|
||||
view: function() {
|
||||
return m(".app", [
|
||||
m("h1", "My App"),
|
||||
|
||||
//nested component
|
||||
m.component(MyComponent, {message: "Hello"})
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
var MyComponent = {
|
||||
controller: function(args) {
|
||||
return {greeting: args.message}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return m("h2", ctrl.greeting)
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, App)
|
||||
|
||||
// <div class="app">
|
||||
// <h1>My App</h1>
|
||||
// <h2>Hello</h2>
|
||||
// </div>
|
||||
</code></pre>
|
||||
<p>Components can be placed anywhere a regular element can. If you have components inside a sortable list, you should add <code>key</code> attributes to your components to ensure that DOM elements are not recreated from scratch, but merely moved when possible. Keys must be unique within a list of sibling DOM elements, and they must be either a string or a number:</p>
|
||||
<pre><code class="lang-javascript">var App = {
|
||||
controller: function() {
|
||||
return {data: [1, 2, 3]}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return m(".app", [
|
||||
//pressing the button reverses the list
|
||||
m("button[type=button]", {onclick: function() {ctrl.data.reverse()}}, "My App"),
|
||||
|
||||
ctrl.data.map(function(item) {
|
||||
//the key ensures the components aren't recreated from scratch, if they merely exchanged places
|
||||
return m.component(MyComponent, {message: "Hello " + item, key: item})
|
||||
})
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
var MyComponent = {
|
||||
controller: function(args) {
|
||||
return {greeting: args.message}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return m("h2", ctrl.greeting)
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, App)
|
||||
</code></pre>
|
||||
<h3 id="dealing-with-state">Dealing with state</h3>
|
||||
<h4 id="stateless-components">Stateless components</h4>
|
||||
<p>A component is said to be stateless when it does not store data internally. Instead, it's composed of <a href="http://en.wikipedia.org/wiki/Pure_function">pure functions</a>. It's a good practice to make components stateless because they are more predictable, and easier to reason about, test and troubleshoot.</p>
|
||||
<p>Instead of copying arguments to the controller object and then passing the controller object to the view (thereby creating internal state in the component), it is often desirable that views update based on the current value of arguments initially passed to a component.</p>
|
||||
<p>The following example illustrates this pattern:</p>
|
||||
<pre><code class="lang-javascript">var MyApp = {
|
||||
controller: function() {
|
||||
return {
|
||||
temp: m.prop(10) // kelvin
|
||||
}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return m("div", [
|
||||
m("input", {oninput: m.withAttr("value", ctrl.temp), value: ctrl.temp()}), "K",
|
||||
m("br"),
|
||||
m.component(TemperatureConverter, {value: ctrl.temp()})
|
||||
]);
|
||||
}
|
||||
};
|
||||
var TemperatureConverter = {
|
||||
controller: function() {
|
||||
//note how the controller does not handle the input arguments
|
||||
|
||||
//define some helper functions to be called from the view
|
||||
return {
|
||||
kelvinToCelsius: function(value) {
|
||||
return value - 273.15
|
||||
},
|
||||
kelvinToFahrenheit: function(value) {
|
||||
return (9 / 5 * (value - 273.15)) + 32
|
||||
}
|
||||
}
|
||||
},
|
||||
view: function(ctrl, args) {
|
||||
return m('div', [
|
||||
"celsius:", ctrl.kelvinToCelsius(args.value),
|
||||
m("br"),
|
||||
"fahrenheit:", ctrl.kelvinToFahrenheit(args.value),
|
||||
]);
|
||||
}
|
||||
};
|
||||
m.mount(document.body, MyApp);
|
||||
</code></pre>
|
||||
<p>In the example above, the text input is bi-directionally bound to a <code>temp</code> getter-setter. Changing the temperature value from the input updates the temperature value, which is passed to the TemperatureConverter view directly, and transformation functions are called from there. The TemperatureConverter controller never stores the value.</p>
|
||||
<p>Testing the various parts of the component is trivial:</p>
|
||||
<pre><code class="lang-javascript">//test a transformation function in the controller
|
||||
var ctrl = new TemperatureConverter.controller();
|
||||
assert(ctrl.kelvinToCelsius(273.15) == 0)
|
||||
|
||||
//test the template
|
||||
var tpl = TemperatureConverter.view(ctrl, {value: 273.15})
|
||||
assert(tpl.children[1] == 0)
|
||||
|
||||
//test with real DOM
|
||||
var testRoot = document.createElement("div")
|
||||
m.render(testRoot, TemperatureConverter.view(ctrl, {value: 273.15}))
|
||||
assert(testRoot.innerHTML.indexOf("celsius:0") > -1)
|
||||
</code></pre>
|
||||
<p>Note that the sample component above is illustrative. Ideally, temperature conversion functions (and any functions that deal strictly within the domain of the data) should go in the model layer, not in a component's controller.</p>
|
||||
<hr>
|
||||
<h3 id="stateful-components">Stateful components</h3>
|
||||
<p>Usually it's recommended that you store application state outside of components (either in a <a href="http://lhorie.github.io/mithril-blog/what-is-a-view-model.html">view-model</a> or in the top-level component in the case of nested components). Components <em>can</em> be stateful, but the purpose of component state is to prevent the pollution of the model layer with aspects that are inherently related to the component. For example, an autocompleter component may need to internally store a flag to indicate whether the dropdown is visible, but this kind of state is not relevant to an application's business logic.</p>
|
||||
<p>You might also elect to maintain component state when it's not meaningful outside the scope of a single component. For example, you might have a <code>UserForm</code> component that lives alongside other unrelated components on a bigger page, but it probably doesn't make sense for the parent page to be aware of the unsaved user data stored within the <code>UserForm</code> component.</p>
|
||||
<hr>
|
||||
<h4 id="parameterized-initial-state">Parameterized initial state</h4>
|
||||
<p>The ability to handle arguments in the controller is useful for setting up the initial state for a component whose state depends on input data:</p>
|
||||
<pre><code class="lang-javascript">var MyComponent = {
|
||||
controller: function(args) {
|
||||
//we only want to make this call once
|
||||
return {
|
||||
things: m.request({method: "GET", url: "/api/things/", data: args}) //slice the data in some way
|
||||
}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return m("ul", [
|
||||
ctrl.things().map(function(thing) {
|
||||
return m("li", thing.name)
|
||||
})
|
||||
]);
|
||||
}
|
||||
};
|
||||
</code></pre>
|
||||
<p>However, it's recommended that you aggregate all of your requests in a single place instead of scattering them across multiple components. Aggregating requests in a top-level component makes it easier to replay the request chain (i.e. fetching an updated list of items after you've saved something that changes that list), and it ensures the entire data set is loaded in memory before drilling down into nested components, avoiding redundant AJAX calls for sibling components that need the same data. Be sure to read the <a href="components.html#application-architecture-with-components">Application Architecture section</a> to learn more about organizing componentized code.</p>
|
||||
<hr>
|
||||
<h4 id="data-driven-component-identity">Data-driven component identity</h4>
|
||||
<p>A component can be re-initialized from scratch by changing the <code>key</code> associated with it. This is useful for re-running ajax calls for different model entities.</p>
|
||||
<p>Suppose we have a component called <code>ProjectList</code> and the following data:</p>
|
||||
<pre><code class="lang-javascript">
|
||||
var people = [
|
||||
{id: 1, name: "John"},
|
||||
{id: 2, name: "Mary"}
|
||||
]
|
||||
|
||||
//ajax and display a list of projects for John
|
||||
m.render(document.body, ProjectList({key: people[0].id, value: people[0]})
|
||||
|
||||
//ajax and display a list of projects for Mary
|
||||
m.render(document.body, ProjectList({key: people[1].id, value: people[1]})
|
||||
</code></pre>
|
||||
<p>In the example above, since the key is different, the ProjectList component is recreated from scratch. As a result, the controller runs again, the DOM is re-generated, and any applicable 3rd party plugins in configs are re-initialized.</p>
|
||||
<p>Remember that the rules for keys apply to components the same way they do to regular elements: it is not allowed to have duplicate keys on children of the same parent, and they must be either strings or numbers (or something with a <code>.toString()</code> implementation that makes the entity uniquely identifiable in the local scope when serialized). You can learn more about keys <a href="mithril.html#dealing-with-focus">here</a>.</p>
|
||||
<hr>
|
||||
<h3 id="unloading-components">Unloading components</h3>
|
||||
<p>If a component's controller contains the function <code>onunload</code>, it will be called under one of these circumstances:</p>
|
||||
<ul>
|
||||
<li>when a new call to <code>m.mount</code> updates the root DOM element of the component in question</li>
|
||||
<li>when a route changes (if you are using <a href="mithril.route.html"><code>m.route</code></a>)</li>
|
||||
</ul>
|
||||
<p>To unload/unmount a component without loading another component, you can simply call <code>m.mount</code> with a <code>null</code> as the component parameter:</p>
|
||||
<pre><code class="lang-javascript">m.mount(rootElement, null);
|
||||
</code></pre>
|
||||
<p>Often, you will want to do some work before the component is unloaded (i.e. clear timers or unsubscribe event handlers):</p>
|
||||
<pre><code class="lang-javascript">var MyComponent = {
|
||||
controller: function() {
|
||||
return {
|
||||
onunload: function() {
|
||||
console.log("unloading my component");
|
||||
}
|
||||
}
|
||||
},
|
||||
view: function() {
|
||||
return m("div", "test")
|
||||
}
|
||||
};
|
||||
|
||||
m.mount(document.body, MyComponent);
|
||||
|
||||
//...
|
||||
|
||||
var AnotherComponent = {
|
||||
view: function() {
|
||||
return m("div", "another")
|
||||
}
|
||||
};
|
||||
|
||||
// mount on the same DOM element, replacing MyComponent
|
||||
m.mount(document.body, AnotherComponent); // logs "unloading my component"
|
||||
</code></pre>
|
||||
<p>You can also use the <code>onunload</code> function to PREVENT a component from being unloaded in the context of a route change (i.e. to alert a user to save their changes before navigating away from a page)</p>
|
||||
<pre><code class="lang-javascript">var component = {
|
||||
controller: function() {
|
||||
var unsaved = m.prop(false)
|
||||
return {
|
||||
unsaved: unsaved,
|
||||
|
||||
onunload: function(e) {
|
||||
if (unsaved()) {
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
//...
|
||||
}
|
||||
</code></pre>
|
||||
<p>Normally, calling <code>m.mount</code> will return the controller instance for that component, but there's one corner case: if <code>e.preventDefault()</code> is called from a controller's <code>onunload</code> method, then the <code>m.mount</code> call will not instantiate the new controller, and will return <code>undefined</code>.</p>
|
||||
<p>Mithril does not hook into the browser's <code>onbeforeunload</code> event. To prevent unloading when attempting to navigate away from a page, you can check the return value of <code>m.mount</code></p>
|
||||
<pre><code class="lang-javascript">window.onbeforeunload = function() {
|
||||
if (!m.mount(rootElement, null)) {
|
||||
//onunload's preventDefault was called
|
||||
return "Are you sure you want to leave?"
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>Components that are nested inside other components can also call <code>onunload</code> and its <code>e.preventDefault()</code> like top-level components. The <code>onunload</code> event is called if an instantiated component is removed from a virtual element tree via a redraw.</p>
|
||||
<p>In the example below, clicking the button triggers the component's <code>onunload</code> event and logs "unloaded!".</p>
|
||||
<pre><code class="lang-javascript">var MyApp = {
|
||||
controller: function() {
|
||||
return {loaded: true}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return [
|
||||
m("button[type=button]", {onclick: function() {ctrl.loaded = false}}),
|
||||
ctrl.loaded ? MyComponent : ""
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
var MyComponent = {
|
||||
controller: function() {
|
||||
return {
|
||||
onunload: function() {
|
||||
console.log("unloaded!")
|
||||
}
|
||||
}
|
||||
},
|
||||
view: function() {
|
||||
return m("h1", "My component")
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, MyApp)
|
||||
</code></pre>
|
||||
<p>Calling <code>e.preventDefault()</code> from a component's <code>onunload</code> function aborts route changes, but it does not abort rollback or affect the current redraw in any way.</p>
|
||||
<hr>
|
||||
<h3 id="nested-asynchronous-components">Nested asynchronous components</h3>
|
||||
<p>Since controllers can call model methods, it's possible for nested components to encapsulate asynchronous behavior. When components aren't nested, Mithril waits for all asynchronous tasks to complete, but when components are nested, a component's parent view renders before the component completes its asynchronous tasks. The existence of the component only becomes known to the diff engine at the time when the template is rendered.</p>
|
||||
<p>When a component has asynchronous payloads and they are queued by the <a href="auto-redrawing.html">auto-redrawing system</a>, its view is NOT rendered until all asynchronous operations complete. When the component's asynchronous operations complete, another redraw is triggered and the entire template tree is evaluated again. This means that the virtual dom tree may take two or more redraws (depending on how many nested asynchronous components there are) to be fully rendered.</p>
|
||||
<p>There are <a href="components.html#application-architecture-with-components">different ways to organize components</a> that can side-step the need for multiple redraws. Regardless, you could also force multiple redraws to happen by using the <a href="mithril.request.html#rendering-before-web-service-requests-finish"><code>background</code></a> and <code>initialValue</code> options in <code>m.request</code>, or by manually calling <a href="mithril.redraw.html"><code>m.redraw()</code></a>.</p>
|
||||
<p>If a component A contains another component B that calls asynchronous services, when component A is rendered, a <code><placeholder></code> tag is rendered in place of component B until B's asynchronous services resolve. Once resolved, the placeholder is replaced with component B's view.</p>
|
||||
<hr>
|
||||
<h3 id="limitations-and-caveats">Limitations and caveats</h3>
|
||||
<p>One important limitation to be aware of when using components is that you cannot call Mithril's redrawing methods (<a href="mithril.computation.html"><code>m.startComputation</code> / <code>m.endComputation</code></a> and <a href="mithril.redraw.html"><code>m.redraw</code></a>) from templates.</p>
|
||||
<p>In addition, you cannot call <code>m.request</code> from templates. Doing so will trigger another redraw, which will result in an infinite loop.</p>
|
||||
<p>There are a few other technical caveats when nesting components:</p>
|
||||
<ol>
|
||||
<li><p>Nested component views must return either a virtual element or another component. Returning an array, a string, a number, boolean, falsy value, etc will result in an error.</p>
|
||||
</li>
|
||||
<li><p>Nested components cannot change <code>m.redraw.strategy</code> from the controller constructor (but they can from event handlers). It's recommended that you use the <a href="mithril.html#persisting-dom-elements-across-route-changes"><code>ctx.retain</code></a> flag instead of changing the redraw strategy in controller constructors.</p>
|
||||
</li>
|
||||
<li><p>The root DOM element in a component's view must not be changed during the lifecycle of the component, otherwise undefined behavior will occur. In other words, don't do this:</p>
|
||||
<pre><code class="lang-javascript">var MyComponent = {
|
||||
view: function() {
|
||||
return someCondition ? m("a") : m("b")
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
</li>
|
||||
<li><p>If a component's root element is a subtree directive on its first rendering pass, undefined behavior will occur.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<hr>
|
||||
<h3 id="opting-out-of-the-auto-redrawing-system">Opting out of the auto redrawing system</h3>
|
||||
<p>Components can be rendered without enabling the <a href="auto-redrawing.html">auto-redrawing system</a>, via <a href="mithril.render.html"><code>m.render</code></a>:</p>
|
||||
<pre><code class="lang-javascript">var MyComponent = {
|
||||
controller: function() {
|
||||
return {greeting: "Hello"}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return m("h1", ctrl.greeting)
|
||||
}
|
||||
}
|
||||
|
||||
m.render(document.body, MyComponent)
|
||||
</code></pre>
|
||||
<p>However, using <a href="mithril.render.html"><code>m.render</code></a> is only recommended if you want to use Mithril as part of a larger framework that manages the rendering lifecycle on its own. The vast majority of times, it's advisable to use <code>m.mount</code> instead.</p>
|
||||
<hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">Component component(Component component [, Object attributes [, any... args]])
|
||||
|
||||
where:
|
||||
Component :: Object { Controller, View }
|
||||
Controller :: SimpleController | UnloadableController
|
||||
SimpleController :: void controller([Object attributes [, any... args]])
|
||||
UnloadableController :: void controller([Object attributes [, any... args]]) { prototype: void unload(UnloadEvent e) }
|
||||
UnloadEvent :: Object {void preventDefault()}
|
||||
View :: void view(Object controllerInstance [, Object attributes [, any... args]])
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>Component component</strong></p>
|
||||
<p>A component is supposed to be an Object with two keys: <code>controller</code> and <code>view</code>. Each of these should point to a Javascript function. If a controller is not specified, Mithril will automatically create an empty controller function.</p>
|
||||
</li>
|
||||
<li><p><strong>Object attributes</strong></p>
|
||||
<p>A key/value map of attributes that gets bound as an argument to both the <code>controller</code> and <code>view</code> functions of the component.</p>
|
||||
</li>
|
||||
<li><p><strong>any... args</strong></p>
|
||||
<p>Other arguments to be bound as arguments to both the <code>controller</code> and <code>view</code> functions</p>
|
||||
</li>
|
||||
<li><p><strong>returns Component parameterizedComponent</strong></p>
|
||||
<p>A component with arguments bound</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
293
archive/v0.2.4/mithril.computation.html
Normal file
293
archive/v0.2.4/mithril.computation.html
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.startComputation / m.endComputation
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-startcomputation-m-endcomputation">m.startComputation / m.endComputation</h2>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#how-auto-redrawing-works">How auto-redrawing works</a></li>
|
||||
<li><a href="#difference-between-computation-methods-and-m-redraw">Difference between computation methods and m.redraw</a></li>
|
||||
<li><a href="#integrating-multiple-execution-threads">Integrating multiple execution threads</a></li>
|
||||
<li><a href="#integrating-to-legacy-code">Integrating to legacy code</a></li>
|
||||
<li><a href="#signature">Signature</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>Typically, <code>m.startComputation</code> / <code>m.endComputation</code> don't need to be called from application space. These methods are only intended to be used by people who are writing libraries that do things asynchronously, or when calling vanilla javascript asynchronous functions from template <a href="mithril.html#accessing-the-real-dom"><code>config</code></a> functions.</p>
|
||||
<p>If you need to do custom asynchronous calls without using Mithril's API, and find that your views are not redrawing, you should consider using <code>m.startComputation</code> / <code>m.endComputation</code> so that Mithril can intelligently auto-redraw once your custom code finishes running.</p>
|
||||
<p>In order to integrate an asynchronous code to Mithril's autoredrawing system, you should call <code>m.startComputation</code> BEFORE making an asynchronous call, and <code>m.endComputation</code> after the asynchronous callback completes.</p>
|
||||
<pre><code class="lang-javascript">//this service waits 1 second, logs "hello" and then notifies the view that
|
||||
//it may start redrawing (if no other asynchronous operations are pending)
|
||||
var doStuff = function() {
|
||||
m.startComputation(); //call `startComputation` before the asynchronous `setTimeout`
|
||||
|
||||
setTimeout(function() {
|
||||
console.log("hello");
|
||||
|
||||
m.endComputation(); //call `endComputation` at the end of the callback
|
||||
}, 1000);
|
||||
};
|
||||
</code></pre>
|
||||
<p>To integrate synchronous code, call <code>m.startComputation</code> at the beginning of the method, and <code>m.endComputation</code> at the end.</p>
|
||||
<pre><code class="lang-javascript">window.onfocus = function() {
|
||||
m.startComputation(); //call before everything else in the event handler
|
||||
|
||||
doStuff();
|
||||
|
||||
m.endComputation(); //call after everything else in the event handler
|
||||
}
|
||||
</code></pre>
|
||||
<p>For each <code>m.startComputation</code> call a library makes, it MUST also make one and ONLY one corresponding <code>m.endComputation</code> call.</p>
|
||||
<p>You should not use these methods if your code is intended to run repeatedly (e.g. by using <code>setInterval</code>). If you want to repeatedly redraw the view without necessarily waiting for user input, you should manually call <a href="mithril.redraw.html"><code>m.redraw</code></a> within the repeatable context.</p>
|
||||
<p>Note that failing to call <code>endComputation</code> after a respective <code>startComputation</code> call will halt the redrawing system. It's a good idea to wrap exception-prone code in a <code>try</code> block and call <code>m.endComputation</code> from within the respective <code>finally</code> block, in order to prevent rendering from halting.</p>
|
||||
<pre><code class="lang-javascript">window.onfocus = function() {
|
||||
m.startComputation();
|
||||
|
||||
try {
|
||||
doStuff();
|
||||
}
|
||||
finally {
|
||||
m.endComputation(); //redraw regardless of whether `doStuff` threw errors
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="how-auto-redrawing-works">How auto-redrawing works</h3>
|
||||
<p>The auto-redrawing system in Mithril is not affected by changes in values of <code>m.prop</code> getter-setters. Instead, Mithril relies on <code>m.startComputation</code> and <code>m.endComputation</code> calls to figure out when to redraw.</p>
|
||||
<p>Mithril has an internal counter, which is incremented every time <code>m.startComputation</code> is called, and decremented every time <code>m.endComputation</code> is called. Once the counter reaches zero, Mithril redraws. Mithril internally calls this pair of functions when you call <a href="mithril.mount.html"><code>m.mount</code></a>, <a href="mithril.route.html"><code>m.route</code></a>, <a href="mithril.request.html"><code>m.request</code></a>, and whenever an event defined with <a href="mithril.html"><code>m()</code></a> is triggered.</p>
|
||||
<p>So calling <code>m.request</code> multiple times from a controller context increments the internal counter. Once each request completes, the counter is decremented. The end result is that Mithril waits for all requests to complete before attempting to redraw. This also applies for asynchronous functions called from 3rd party libraries or from vanilla javascript, if they call this pair of functions.</p>
|
||||
<p>The reason Mithril waits for all asynchronous services to complete before redrawing is to avoid wasteful browser repaints, and to minimize the need for null reference checks in templates.</p>
|
||||
<p>It's possible to opt out of the redrawing schedule by using the <code>background</code> option for <code>m.request</code>, or by simply not calling <code>m.startComputation</code> / <code>m.endComputation</code> when calling non-Mithril asynchronous functions.</p>
|
||||
<pre><code class="lang-javascript">//`background` option example
|
||||
var component = m.component({
|
||||
controller: function() {
|
||||
//setting `background` allows the component to redraw immediately, without waiting for the request to complete
|
||||
m.request({method: "GET", url: "/foo", background: true})
|
||||
},
|
||||
//...
|
||||
})
|
||||
</code></pre>
|
||||
<p>It's also possible to modify the strategy that Mithril uses for any given redraw, by using <a href="mithril.redraw.html#changing-redraw-strategy"><code>m.redraw.strategy</code></a>. Note that changing the redraw strategy only affects the next scheduled redraw. After that, Mithril resets the <code>m.redraw.strategy</code> flag to either "all" or "diff" depending on whether the redraw was due to a route change or whether it was triggered by some other action.</p>
|
||||
<pre><code class="lang-javascript">//diff when routing, instead of redrawing from scratch
|
||||
//this preserves the `<input>` element and its 3rd party plugin after route changes, since the `<input>` doesn't change
|
||||
var Component1 = m.component({
|
||||
controller: function() {
|
||||
m.redraw.strategy("diff")
|
||||
},
|
||||
view: function() {
|
||||
return m("div", [
|
||||
m("h1", "Hello Foo"),
|
||||
m("input", {config: plugin}) //assuming `plugin` initializes a 3rd party library
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
var Component2 = m.component({
|
||||
controller: function() {
|
||||
m.redraw.strategy("diff")
|
||||
},
|
||||
view: function() {
|
||||
return m("div", [
|
||||
m("h1", "Hello Bar"),
|
||||
m("input", {config: plugin}) //assuming `plugin` initializes a 3rd party library
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
m.route(document.body, "/foo", {
|
||||
"/foo": Component1,
|
||||
"/bar": Component2,
|
||||
})
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">//model
|
||||
var saved = false
|
||||
function save(e) {
|
||||
if (e.keyCode == 13) {
|
||||
//this causes a redraw, since event handlers active auto-redrawing by default
|
||||
saved = true
|
||||
}
|
||||
else {
|
||||
//we don't care about other keys, so don't redraw
|
||||
m.redraw.strategy("none")
|
||||
}
|
||||
}
|
||||
|
||||
//view
|
||||
var view = function() {
|
||||
return m("div", [
|
||||
m("button[type=button]", {onkeypress: save}, "Save"),
|
||||
saved ? "Saved" : ""
|
||||
])
|
||||
}
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="difference-between-computation-methods-and-m-redraw">Difference between computation methods and m.redraw</h3>
|
||||
<p>The <code>m.startComputation</code> / <code>m.endComputation</code> pair is designed to be "stacked", i.e. multiple asynchronous services can each call this pair of functions to indicate that they want the redrawing algorithm to wait for them to finish before a redraw occurs. In contrast, <code>m.redraw</code> is "aggressive": it redraws as many times as it is called (with the caveat that redraws are batched if they occur less than one animation frame apart in time). In practice, this means that calling <code>m.redraw</code> may cause a redraw to happen before some AJAX calls have finished, which in turn, may cause null reference exceptions in templates that try to use the data from these requests without first checking that the data exists.</p>
|
||||
<p>Therefore, using the computation methods is recommended in order to reduce the amount of intermediate redraws that would otherwise occur as multiple asynchronous services are resolved.</p>
|
||||
<p>When computation methods are used dilligently and religiously, templates are never redrawn with incomplete data. However, it's important to always write conditional tests in templates to account for the possibility of nullables, because redraws may come to occur more aggressively than data is available (perhaps because a newly introduced 3rd party library calls <code>m.redraw</code>, or because you might want a more aggressive redraw policy to implement a specific feature down the road).</p>
|
||||
<p>Defending against nullables can typically be achieved via the <code>initialValue</code> option in <a href="mithril.request.html"><code>m.request</code></a> and basic null checks (e.g. <code>data ? m("div", data) : null</code>).</p>
|
||||
<hr>
|
||||
<h3 id="integrating-multiple-execution-threads">Integrating multiple execution threads</h3>
|
||||
<p>When <a href="integration.html">integrating with third party libraries</a>, you might find that you need to call asynchronous methods from outside of Mithril's API.</p>
|
||||
<p>In order to integrate non-trivial asynchronous code to Mithril's auto-redrawing system, you need to ensure all execution threads call <code>m.startComputation</code> / <code>m.endComputation</code>.</p>
|
||||
<p>An execution thread is basically any amount of code that runs before other asynchronous threads start to run.</p>
|
||||
<p>Integrating multiple execution threads can be done in a two different ways: in a layered fashion or in comprehensive fashion</p>
|
||||
<h4 id="layered-integration">Layered integration</h4>
|
||||
<p>Layered integration is recommended for modular code where many different APIs may be put together at the application level.</p>
|
||||
<p>Below is an example where various methods implemented with a third party library can be integrated in layered fashion: any of the methods can be used in isolation or in combination.</p>
|
||||
<p>Notice how <code>doBoth</code> repeatedly calls <code>m.startComputation</code> since that method calls both <code>doSomething</code> and <code>doAnother</code>. This is perfectly valid: there are three asynchronous computations pending after the <code>jQuery.when</code> method is called, and therefore, three pairs of <code>m.startComputation</code> / <code>m.endComputation</code> in play.</p>
|
||||
<pre><code class="lang-javascript">var doSomething = function(callback) {
|
||||
m.startComputation(); //call `startComputation` before the asynchronous AJAX request
|
||||
|
||||
return jQuery.ajax("/something").done(function() {
|
||||
if (callback) callback();
|
||||
|
||||
m.endComputation(); //call `endComputation` at the end of the callback
|
||||
});
|
||||
};
|
||||
var doAnother = function(callback) {
|
||||
m.startComputation(); //call `startComputation` before the asynchronous AJAX request
|
||||
|
||||
return jQuery.ajax("/another").done(function() {
|
||||
if (callback) callback();
|
||||
m.endComputation(); //call `endComputation` at the end of the callback
|
||||
});
|
||||
};
|
||||
var doBoth = function(callback) {
|
||||
m.startComputation(); //call `startComputation` before the asynchronous synchronization method
|
||||
|
||||
jQuery.when(doSomething(), doAnother()).then(function() {
|
||||
if (callback) callback();
|
||||
|
||||
m.endComputation(); //call `endComputation` at the end of the callback
|
||||
})
|
||||
};
|
||||
</code></pre>
|
||||
<h4 id="comprehensive-integration">Comprehensive integration</h4>
|
||||
<p>Comprehensive integration is recommended if integrating a monolithic series of asynchronous operations. In contrast to layered integration, it minimizes the number of <code>m.startComputation</code> / <code>m.endComputation</code> to avoid clutter.</p>
|
||||
<p>The example below shows a convoluted series of AJAX requests implemented with a third party library.</p>
|
||||
<pre><code class="lang-javascript">var doSomething = function(callback) {
|
||||
m.startComputation(); //call `startComputation` before everything else
|
||||
|
||||
jQuery.ajax("/something").done(function() {
|
||||
doStuff();
|
||||
jQuery.ajax("/another").done(function() {
|
||||
doMoreStuff();
|
||||
jQuery.ajax("/more").done(function() {
|
||||
if (callback) callback();
|
||||
|
||||
m.endComputation(); //call `endComputation` at the end of everything
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="integrating-to-legacy-code">Integrating to legacy code</h3>
|
||||
<p>If you need to add separate widgets to different places on a same page, you can simply initialize each widget as you would a regular Mithril application (i.e. use <code>m.render</code>, <code>m.mount</code> or <code>m.route</code>).</p>
|
||||
<p>There's just one caveat: while simply initializing multiple "islands" in this fashion works, their initialization calls are not aware of each other and can cause redraws too frequently. To optimize rendering, you should add a <code>m.startComputation</code> call before the first widget initialization call, and a <code>m.endComputation</code> after the last widget initialization call in each execution thread.</p>
|
||||
<pre><code>m.startComputation()
|
||||
|
||||
m.mount(document.getElementById("widget1-container"), Widget1)
|
||||
|
||||
m.mount(document.getElementById("widget2-container"), Widget2)
|
||||
|
||||
m.endComputation()
|
||||
</code></pre><hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">void startComputation()
|
||||
</code></pre>
|
||||
<pre><code class="lang-clike">void endComputation()
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
858
archive/v0.2.4/mithril.d.ts
vendored
Normal file
858
archive/v0.2.4/mithril.d.ts
vendored
Normal file
|
|
@ -0,0 +1,858 @@
|
|||
// Mithril type definitions for Typescript
|
||||
|
||||
/**
|
||||
* This is the module containing all the types/declarations/etc. for Mithril
|
||||
*/
|
||||
declare namespace Mithril {
|
||||
interface ChildArray extends Array<Children> {}
|
||||
type Children = Child | ChildArray;
|
||||
type Child = string | VirtualElement | Component<Controller>;
|
||||
|
||||
interface Static {
|
||||
/**
|
||||
* Creates a virtual element for use with m.render, m.mount, etc.
|
||||
*
|
||||
* @param selector A simple CSS selector. May include SVG tags. Nested
|
||||
* selectors are not supported.
|
||||
* @param attributes Attributes to add. Any DOM attribute may be used
|
||||
* as an attribute, although innerHTML and the like may be overwritten
|
||||
* silently.
|
||||
* @param children Child elements, components, and text to add.
|
||||
* @return A virtual element.
|
||||
*
|
||||
* @see m.render
|
||||
* @see m.mount
|
||||
* @see m.component
|
||||
*/
|
||||
(
|
||||
selector: string,
|
||||
...children: Children[]
|
||||
): VirtualElement;
|
||||
|
||||
/**
|
||||
* Creates a virtual element for use with m.render, m.mount, etc.
|
||||
*
|
||||
* @param selector A simple CSS selector. May include SVG tags. Nested
|
||||
* selectors are not supported.
|
||||
* @param attributes Attributes to add. Any DOM attribute may be used
|
||||
* as an attribute, although innerHTML and the like may be overwritten
|
||||
* silently.
|
||||
* @param children Child elements, components, and text to add.
|
||||
* @return A virtual element.
|
||||
*
|
||||
* @see m.render
|
||||
* @see m.mount
|
||||
* @see m.component
|
||||
*/
|
||||
(
|
||||
selector: string,
|
||||
attributes: Attributes,
|
||||
...children: Children[]
|
||||
): VirtualElement;
|
||||
|
||||
/**
|
||||
* Initializes a component for use with m.render, m.mount, etc.
|
||||
*
|
||||
* @param component A component.
|
||||
* @param args Arguments to optionally pass to the component.
|
||||
* @return A component.
|
||||
*
|
||||
* @see m.render
|
||||
* @see m.mount
|
||||
* @see m
|
||||
*/
|
||||
<T extends Controller>(
|
||||
component: Component<T>,
|
||||
...args: any[]
|
||||
): Component<T>;
|
||||
|
||||
/**
|
||||
* Creates a getter-setter function that wraps a Mithril promise. Useful
|
||||
* for uniform data access, m.withAttr, etc.
|
||||
*
|
||||
* @param promise A thennable to initialize the property with. It may
|
||||
* optionally be a Mithril promise.
|
||||
* @return A getter-setter function wrapping the promise.
|
||||
*
|
||||
* @see m.withAttr
|
||||
*/
|
||||
prop<T>(promise: Thennable<T>) : Promise<T>;
|
||||
|
||||
/**
|
||||
* Creates a getter-setter function that wraps a simple value. Useful
|
||||
* for uniform data access, m.withAttr, etc.
|
||||
*
|
||||
* @param value A value to initialize the property with
|
||||
* @return A getter-setter function wrapping the value.
|
||||
*
|
||||
* @see m.withAttr
|
||||
*/
|
||||
prop<T>(value: T): BasicProperty<T>;
|
||||
|
||||
/**
|
||||
* Creates a getter-setter function that wraps a simple value. Useful
|
||||
* for uniform data access, m.withAttr, etc.
|
||||
*
|
||||
* @return A getter-setter function wrapping the value.
|
||||
*
|
||||
* @see m.withAttr
|
||||
*/
|
||||
prop<T>(): BasicProperty<T>;
|
||||
|
||||
/**
|
||||
* Returns a event handler that can be bound to an element, firing with
|
||||
* the specified property.
|
||||
*
|
||||
* @param property The property to get from the event.
|
||||
* @param callback The handler to use the value from the event.
|
||||
* @return A function suitable for listening to an event.
|
||||
*/
|
||||
withAttr(
|
||||
property: string,
|
||||
callback: (value: any) => any,
|
||||
callbackThis?: any
|
||||
): (e: Event) => void;
|
||||
|
||||
/**
|
||||
* @deprecated Use m.mount instead
|
||||
*/
|
||||
module<T extends Controller>(
|
||||
rootElement: Node,
|
||||
component: Component<T>
|
||||
): T;
|
||||
|
||||
/**
|
||||
* Mounts a component to a base DOM node.
|
||||
*
|
||||
* @param rootElement The base node.
|
||||
* @param component The component to mount.
|
||||
* @return An instance of the top-level component's controller
|
||||
*/
|
||||
mount<T extends Controller>(
|
||||
rootElement: Node,
|
||||
component: Component<T>
|
||||
): T;
|
||||
|
||||
/**
|
||||
* Initializes a component for use with m.render, m.mount, etc.
|
||||
*
|
||||
* @param selector A component.
|
||||
* @param args Arguments to optionally pass to the component.
|
||||
* @return A component.
|
||||
*
|
||||
* @see m.render
|
||||
* @see m.mount
|
||||
* @see m
|
||||
*/
|
||||
component<T extends Controller>(
|
||||
component: Component<T>,
|
||||
...args: any[]
|
||||
): Component<T>;
|
||||
|
||||
/**
|
||||
* Trust this string of HTML.
|
||||
*
|
||||
* @param html The HTML to trust
|
||||
* @return A String object instance with an added internal flag to mark
|
||||
* it as trusted.
|
||||
*/
|
||||
trust(html: string): TrustedString;
|
||||
|
||||
/**
|
||||
* Render a virtual DOM tree.
|
||||
*
|
||||
* @param rootElement The base element/node to render the tree from.
|
||||
* @param children One or more child nodes to add to the tree.
|
||||
* @param forceRecreation If true, overwrite the entire tree without
|
||||
* diffing against it.
|
||||
*/
|
||||
render(
|
||||
rootElement: Element,
|
||||
children: VirtualElement|VirtualElement[],
|
||||
forceRecreation?: boolean
|
||||
): void;
|
||||
|
||||
redraw: {
|
||||
/**
|
||||
* Force a redraw the active component. It redraws asynchronously by
|
||||
* default to allow for simultaneous events to run before redrawing,
|
||||
* such as the event combination keypress + input frequently used for
|
||||
* input.
|
||||
*
|
||||
* @param force If true, redraw synchronously.
|
||||
*/
|
||||
(force?: boolean): void;
|
||||
|
||||
/**
|
||||
* Gets/sets the current redraw strategy, which returns one of the
|
||||
* following:
|
||||
*
|
||||
* "all" - recreates the DOM tree from scratch
|
||||
* "diff" - recreates the DOM tree from scratch
|
||||
* "none" - leaves the DOM tree intact
|
||||
*
|
||||
* This is useful for event handlers, which may want to cancel
|
||||
* the next redraw if the event doesn't update the UI.
|
||||
*
|
||||
* @return The current strategy
|
||||
*/
|
||||
strategy: BasicProperty<"all" | "diff" | "none">;
|
||||
}
|
||||
|
||||
route: {
|
||||
/**
|
||||
* Enable routing, mounting a controller based on the route. It
|
||||
* automatically mounts the components for you, starting with the one
|
||||
* specified by the default route.
|
||||
*
|
||||
* @param rootElement The element to mount the active controller to.
|
||||
* @param defaultRoute The route to start with.
|
||||
* @param routes A key-value mapping of pathname to controller.
|
||||
*/
|
||||
(
|
||||
rootElement: Element,
|
||||
defaultRoute: string,
|
||||
routes: Routes
|
||||
): void;
|
||||
|
||||
/**
|
||||
* This allows m.route to be used as the `config` attribute for a
|
||||
* virtual element, particularly useful for cases like this:
|
||||
*
|
||||
* ```ts
|
||||
* // Note that the '#' is not required in `href`, thanks to the
|
||||
* `config` setting.
|
||||
* m("a[href='/dashboard/alicesmith']", {config: m.route});
|
||||
* ```
|
||||
*/
|
||||
(
|
||||
element: Element,
|
||||
isInitialized: boolean,
|
||||
context?: Context,
|
||||
vdom?: VirtualElement
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Programmatically redirect to another route.
|
||||
*
|
||||
* @param path The route to go to.
|
||||
* @param params Parameters to pass as a query string.
|
||||
* @param shouldReplaceHistory Whether to replace the current history
|
||||
* instead of adding a new one.
|
||||
*/
|
||||
(path: string, params?: any, shouldReplaceHistory?: boolean): void;
|
||||
|
||||
/**
|
||||
* Gets the current route.
|
||||
*
|
||||
* @return The current route.
|
||||
*/
|
||||
(): string;
|
||||
|
||||
/**
|
||||
* Gets a route parameter.
|
||||
*
|
||||
* @param key The key to get.
|
||||
* @return The value associated with the parameter key.
|
||||
*/
|
||||
param(key: string): string;
|
||||
|
||||
/**
|
||||
* The current routing mode. This may be changed before calling
|
||||
* m.route to change the part of the URL used to perform the routing.
|
||||
*
|
||||
* The value can be set to one of the following, defaulting to
|
||||
* "hash":
|
||||
*
|
||||
* "search" - Uses the query string. This allows for named anchors to
|
||||
* work on the page, but changes cause IE8 and lower to refresh the
|
||||
* page.
|
||||
*
|
||||
* "hash" - Uses the hash. This is the only routing mode that does
|
||||
* not cause page refreshes on any browser, but it does not support
|
||||
* named anchors.
|
||||
*
|
||||
* "pathname" - Uses the URL pathname. This requires server-side
|
||||
* setup to support bookmarking and page refreshes. It always causes
|
||||
* page refreshes on IE8 and lower. Note that this requires that the
|
||||
* application to be run from the root of the URL.
|
||||
*/
|
||||
mode: "search" | "hash" | "pathname";
|
||||
|
||||
/**
|
||||
* Serialize an object into a query string.
|
||||
*
|
||||
* @param data The data to serialize.
|
||||
* @return The serialized string.
|
||||
*/
|
||||
buildQueryString(data: Object): string;
|
||||
|
||||
/**
|
||||
* Parse a query string into an object.
|
||||
*
|
||||
* @param data The data to parse.
|
||||
* @return The parsed object data.
|
||||
*/
|
||||
parseQueryString(data: string): Object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an XHR request to a server. Note that the `url` option is
|
||||
* required.
|
||||
*
|
||||
* @param options The options to use for the request.
|
||||
* @return A promise to the returned data, or void if not applicable.
|
||||
*
|
||||
* @see XHROptions for the available options.
|
||||
*/
|
||||
request(options: XHROptions): Promise<any>
|
||||
|
||||
/**
|
||||
* Send a JSONP request to a server. Note that the `url` option is
|
||||
* required.
|
||||
*
|
||||
* @param options The options to use
|
||||
* @return A promise to the returned data.
|
||||
*
|
||||
* @see JSONPOptions for the available options.
|
||||
*/
|
||||
request(options: JSONPOptions): Promise<any>;
|
||||
|
||||
deferred: {
|
||||
/**
|
||||
* Create a Mithril deferred object. It behaves synchronously if
|
||||
* possible, an intentional deviation from Promises/A+. Note that
|
||||
* deferreds are completely separate from the redrawing system, and
|
||||
* never trigger a redraw on their own.
|
||||
*
|
||||
* @return A new Mithril deferred instance.
|
||||
*
|
||||
* @see m.deferred.onerror for the error callback called for Error
|
||||
* subclasses
|
||||
*/
|
||||
<T>(): Deferred<T>;
|
||||
|
||||
/**
|
||||
* A callback for all uncaught native Error subclasses in deferreds.
|
||||
* This defaults to synchronously rethrowing all errors, a deviation
|
||||
* from Promises/A+, but the behavior is configurable. To restore
|
||||
* Promises/A+-compatible behavior. simply set this to a no-op.
|
||||
*/
|
||||
onerror(e: Error): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a list of promises or thennables and returns a Mithril promise
|
||||
* that resolves once all in the list are resolved, or rejects if any of
|
||||
* them reject.
|
||||
*
|
||||
* @param promises A list of promises to try to resolve.
|
||||
* @return A promise that resolves to all the promises if all resolve, or
|
||||
* rejects with the error contained in the first rejection.
|
||||
*/
|
||||
sync<T>(promises: Thennable<T>[]): Promise<T[]>;
|
||||
|
||||
/**
|
||||
* Use this and endComputation if your views aren't redrawing after
|
||||
* calls to third-party libraries. For integrating asynchronous code,
|
||||
* this should be called before any asynchronous work is done. For
|
||||
* synchronous code, this should be called at the beginning of the
|
||||
* problematic segment. Note that these calls must be balanced, much like
|
||||
* braces and parentheses. This is mostly used internally. Prefer
|
||||
* m.redraw where possible, especially when making repeated calls.
|
||||
*
|
||||
* @see endComputation
|
||||
* @see m.render
|
||||
*/
|
||||
startComputation(): void;
|
||||
|
||||
/**
|
||||
* Use startComputation and this if your views aren't redrawing after
|
||||
* calls to third-party libraries. For integrating asynchronous code,
|
||||
* this should be called after all asynchronous work completes. For
|
||||
* synchronous code, this should be called at the end of the problematic
|
||||
* segment. Note that these calls must be balanced, much like braces and
|
||||
* parentheses. This is mostly used internally. Prefer m.redraw where
|
||||
* possible, especially when making repeated calls.
|
||||
*
|
||||
* @see startComputation
|
||||
* @see m.render
|
||||
*/
|
||||
endComputation(): void;
|
||||
|
||||
/**
|
||||
* This overwrites the internal version of window used by Mithril.
|
||||
* It's mostly useful for testing, and is also used internally by
|
||||
* Mithril to test itself. By default Mithril uses `window` for the
|
||||
* dependency.
|
||||
*
|
||||
* @param mockWindow The mock to use for the window.
|
||||
* @return The mock that was passed in.
|
||||
*/
|
||||
deps(mockWindow: Window): Window;
|
||||
}
|
||||
|
||||
interface TrustedString extends String {
|
||||
/** @private Implementation detail. Don't depend on it. */
|
||||
$trusted: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface for a virtual element. It's best to consider this immutable
|
||||
* for most use cases.
|
||||
*
|
||||
* @see m
|
||||
*/
|
||||
interface VirtualElement {
|
||||
/**
|
||||
* The tag name of this element.
|
||||
*/
|
||||
tag: string;
|
||||
|
||||
/**
|
||||
* The attributes of this element.
|
||||
*/
|
||||
attrs: Attributes;
|
||||
|
||||
/**
|
||||
* The children of this element.
|
||||
*/
|
||||
children: Children[];
|
||||
}
|
||||
|
||||
/**
|
||||
* An event passed by Mithril to unload event handlers.
|
||||
*/
|
||||
interface Event {
|
||||
/**
|
||||
* Prevent the default behavior of scrolling the page and updating the
|
||||
* URL on next route change.
|
||||
*/
|
||||
preventDefault(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A context object for configuration functions.
|
||||
*
|
||||
* @see ElementConfig
|
||||
*/
|
||||
interface Context {
|
||||
/**
|
||||
* A function to call when the node is unloaded. Useful for cleanup.
|
||||
*/
|
||||
onunload?(): any;
|
||||
|
||||
/**
|
||||
* Set true if the backing DOM node needs to be retained between route
|
||||
* changes if possible. Set false if this node needs to be recreated
|
||||
* every single time, regardless of how "different" it is.
|
||||
*/
|
||||
retain?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a callback function for a virtual element's config
|
||||
* attribute. It's a low-level function useful for extra cleanup after
|
||||
* removal from the tree, storing instances of third-party classes that
|
||||
* need to be associated with the DOM, etc.
|
||||
*
|
||||
* @see Attributes
|
||||
* @see Context
|
||||
*/
|
||||
interface ElementConfig {
|
||||
/**
|
||||
* A callback function for a virtual element's config attribute.
|
||||
*
|
||||
* @param element The associated DOM element.
|
||||
* @param isInitialized Whether this is the first call for the virtual
|
||||
* element or not.
|
||||
* @param context The associated context for this element.
|
||||
* @param vdom The associated virtual element.
|
||||
*/
|
||||
(
|
||||
element: Element,
|
||||
isInitialized: boolean,
|
||||
context: Context,
|
||||
vdom: VirtualElement
|
||||
): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents the attributes available for configuring virtual elements,
|
||||
* beyond the applicable DOM attributes.
|
||||
*
|
||||
* @see m
|
||||
*/
|
||||
interface Attributes {
|
||||
/**
|
||||
* The class name(s) for this virtual element, as a space-separated list.
|
||||
*/
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* The class name(s) for this virtual element, as a space-separated list.
|
||||
*/
|
||||
class?: string;
|
||||
|
||||
/**
|
||||
* A custom, low-level configuration in case this element needs special
|
||||
* cleanup after removal from the tree.
|
||||
*
|
||||
* @see ElementConfig
|
||||
*/
|
||||
config?: ElementConfig;
|
||||
|
||||
/**
|
||||
* A key to optionally associate with this element.
|
||||
*/
|
||||
key?: string | number;
|
||||
|
||||
/**
|
||||
* Any other virtual element properties, including attributes and event
|
||||
* handlers.
|
||||
*/
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* The basis of a Mithril controller instance.
|
||||
*/
|
||||
interface Controller {
|
||||
/**
|
||||
* An optional handler to call when the associated virtual element is
|
||||
* destroyed.
|
||||
*
|
||||
* @param evt An associated event.
|
||||
*/
|
||||
onunload?(evt: Event): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a controller function.
|
||||
*
|
||||
* @see ControllerConstructor
|
||||
*/
|
||||
interface ControllerFunction<T extends Controller> {
|
||||
(...args: any[]): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a controller constructor.
|
||||
*
|
||||
* @see ControllerFunction
|
||||
*/
|
||||
interface ControllerConstructor<T extends Controller> {
|
||||
new (...args: any[]): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a Mithril component.
|
||||
*
|
||||
* @see m
|
||||
* @see m.component
|
||||
*/
|
||||
interface Component<T extends Controller> {
|
||||
/**
|
||||
* The component's controller.
|
||||
*
|
||||
* @see m.component
|
||||
*/
|
||||
controller: ControllerFunction<T> | ControllerConstructor<T>;
|
||||
|
||||
/**
|
||||
* Creates a view out of virtual elements.
|
||||
*
|
||||
* @see m.component
|
||||
*/
|
||||
view(ctrl?: T, ...args: any[]): VirtualElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the base interface for property getter-setters
|
||||
*
|
||||
* @see m.prop
|
||||
*/
|
||||
interface Property<T> {
|
||||
/**
|
||||
* Gets the contained value.
|
||||
*
|
||||
* @return The contained value.
|
||||
*/
|
||||
(): T;
|
||||
|
||||
/**
|
||||
* Sets the contained value.
|
||||
*
|
||||
* @param value The new value to set.
|
||||
* @return The newly set value.
|
||||
*/
|
||||
(value: T): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a non-promise getter-setter functions.
|
||||
*
|
||||
* @see m.prop which returns objects that implement this interface.
|
||||
*/
|
||||
interface BasicProperty<T> extends Property<T> {
|
||||
/**
|
||||
* Makes this serializable to JSON.
|
||||
*/
|
||||
toJSON(): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a key-value mapping linking routes to components.
|
||||
*/
|
||||
interface Routes {
|
||||
/**
|
||||
* The key represents the route. The value represents the corresponding
|
||||
* component.
|
||||
*/
|
||||
[key: string]: Component<Controller>;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a Mithril deferred object.
|
||||
*/
|
||||
interface Deferred<T> {
|
||||
/**
|
||||
* Resolve this deferred's promise with a value.
|
||||
*
|
||||
* @param value The value to resolve the promise with.
|
||||
*/
|
||||
resolve(value?: T): void;
|
||||
|
||||
/**
|
||||
* Reject this deferred with an error.
|
||||
*
|
||||
* @param value The reason for rejecting the promise.
|
||||
*/
|
||||
reject(reason?: any): void;
|
||||
|
||||
/**
|
||||
* The backing promise.
|
||||
*
|
||||
* @see Promise
|
||||
*/
|
||||
promise: Promise<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a thennable success callback.
|
||||
*/
|
||||
interface SuccessCallback<T, U> {
|
||||
(value: T): U | Thennable<U>;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a thennable error callback.
|
||||
*/
|
||||
interface ErrorCallback<T> {
|
||||
(value: Error): T | Thennable<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a thennable.
|
||||
*/
|
||||
interface Thennable<T> {
|
||||
then<U>(success: SuccessCallback<T, U>): Thennable<U>;
|
||||
then<U, V>(success: SuccessCallback<T, U>, error: ErrorCallback<V>): Thennable<U | V>;
|
||||
catch?(error: ErrorCallback<T>): Thennable<T>;
|
||||
catch?<U>(error: ErrorCallback<U>): Thennable<T | U>;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents a Mithril promise object.
|
||||
*/
|
||||
interface Promise<T> extends Thennable<T>, Property<T | Promise<T>> {
|
||||
/**
|
||||
* Chain this promise with a simple success callback, propogating
|
||||
* rejections.
|
||||
*
|
||||
* @param success The callback to call when the promise is resolved.
|
||||
* @return The chained promise.
|
||||
*/
|
||||
then<U>(success: SuccessCallback<T, U>): Promise<U>;
|
||||
|
||||
/**
|
||||
* Chain this promise with a success callback and error callback, without
|
||||
* propogating rejections.
|
||||
*
|
||||
* @param success The callback to call when the promise is resolved.
|
||||
* @param error The callback to call when the promise is rejected.
|
||||
* @return The chained promise.
|
||||
*/
|
||||
then<U, V>(success: SuccessCallback<T, U>, error: ErrorCallback<V>): Promise<U | V>;
|
||||
|
||||
/**
|
||||
* Chain this promise with a single error callback, without propogating
|
||||
* rejections.
|
||||
*
|
||||
* @param error The callback to call when the promise is rejected.
|
||||
* @return The chained promise.
|
||||
*/
|
||||
catch<U>(error: ErrorCallback<U>): Promise<T | U>;
|
||||
}
|
||||
|
||||
/**
|
||||
* These are the common options shared across normal and JSONP requests.
|
||||
*
|
||||
* @see m.request
|
||||
*/
|
||||
interface RequestOptions {
|
||||
/**
|
||||
* The data to be sent. It's automatically serialized in the right format
|
||||
* depending on the method (with exception of HTML5 FormData), and put in
|
||||
* the appropriate section of the request.
|
||||
*/
|
||||
data?: any;
|
||||
|
||||
/**
|
||||
* Whether to run it in the background, i.e. true if it doesn't affect
|
||||
* template rendering.
|
||||
*/
|
||||
background?: boolean;
|
||||
|
||||
/**
|
||||
* Set an initial value while the request is working, to populate the
|
||||
* promise getter-setter.
|
||||
*/
|
||||
initialValue?: any;
|
||||
|
||||
/**
|
||||
* An optional preprocessor function to unwrap a successful response, in
|
||||
* case the response contains metadata wrapping the data.
|
||||
*
|
||||
* @param data The data to unwrap.
|
||||
* @return The unwrapped result.
|
||||
*/
|
||||
unwrapSuccess?(data: any): any;
|
||||
|
||||
/**
|
||||
* An optional preprocessor function to unwrap an unsuccessful response,
|
||||
* in case the response contains metadata wrapping the data.
|
||||
*
|
||||
* @param data The data to unwrap.
|
||||
* @return The unwrapped result.
|
||||
*/
|
||||
unwrapError?(data: any): any;
|
||||
|
||||
/**
|
||||
* An optional function to serialize the data. This defaults to
|
||||
* `JSON.stringify`.
|
||||
*
|
||||
* @param dataToSerialize The data to serialize.
|
||||
* @return The serialized form as a string.
|
||||
*/
|
||||
serialize?(dataToSerialize: any): string;
|
||||
|
||||
/**
|
||||
* An optional function to deserialize the data. This defaults to
|
||||
* `JSON.parse`.
|
||||
*
|
||||
* @param dataToSerialize The data to parse.
|
||||
* @return The parsed form.
|
||||
*/
|
||||
deserialize?(dataToDeserialize: string): any;
|
||||
|
||||
/**
|
||||
* An optional function to extract the data from a raw XMLHttpRequest,
|
||||
* useful if the relevant data is in a response header or the status
|
||||
* field.
|
||||
*
|
||||
* @param xhr The associated XMLHttpRequest.
|
||||
* @param options The options passed to this request.
|
||||
* @return string The serialized format.
|
||||
*/
|
||||
extract?(xhr: XMLHttpRequest, options: this): string;
|
||||
|
||||
/**
|
||||
* The parsed data, or its children if it's an array, will be passed to
|
||||
* this class constructor if it's given, to parse it into classes.
|
||||
*
|
||||
* @param data The data to parse.
|
||||
* @return The new instance for the list.
|
||||
*/
|
||||
type?: new (data: any) => any;
|
||||
|
||||
/**
|
||||
* The URL to send the request to.
|
||||
*/
|
||||
url: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents the available options for configuring m.request for JSONP
|
||||
* requests.
|
||||
*
|
||||
* @see m.request
|
||||
*/
|
||||
interface JSONPOptions extends RequestOptions {
|
||||
/**
|
||||
* For JSONP requests, this must be the string "jsonp". Otherwise, it's
|
||||
* ignored.
|
||||
*/
|
||||
dataType: "jsonp";
|
||||
|
||||
/**
|
||||
* The querystring key for the JSONP request callback. This is useful for
|
||||
* APIs that don't use common conventions, such as
|
||||
* `www.example.com/?jsonpCallback=doSomething`. It defaults to
|
||||
* `callback`.
|
||||
*/
|
||||
callbackKey?: string;
|
||||
|
||||
/**
|
||||
* The data to send with the request. This is automatically serialized
|
||||
* to a querystring.
|
||||
*/
|
||||
data?: Object;
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents the available options for configuring m.request for
|
||||
* standard AJAX requests.
|
||||
*
|
||||
* @see m.request
|
||||
*/
|
||||
interface XHROptions extends RequestOptions {
|
||||
/**
|
||||
* This represents the HTTP method used, defaulting to "GET".
|
||||
*/
|
||||
method: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "OPTIONS";
|
||||
|
||||
/**
|
||||
* The username for HTTP authentication.
|
||||
*/
|
||||
user?: string;
|
||||
|
||||
/**
|
||||
* The password for HTTP authentication.
|
||||
*/
|
||||
password?: string;
|
||||
|
||||
/**
|
||||
* An optional function to run between `open` and `send`, useful for
|
||||
* adding request headers or using XHR2 features such as the `upload`
|
||||
* property. It is even possible to override the XHR altogether with a
|
||||
* similar object, such as an XDomainRequest instance.
|
||||
*
|
||||
* @param xhr The associated XMLHttpRequest.
|
||||
* @param options The options passed to this request.
|
||||
* @return The new XMLHttpRequest, or nothing if the same one is kept.
|
||||
*/
|
||||
config?(xhr: XMLHttpRequest, options: this): any;
|
||||
|
||||
/**
|
||||
* The data to send with the request.
|
||||
*/
|
||||
data?: Object;
|
||||
}
|
||||
}
|
||||
|
||||
declare const m: Mithril.Static;
|
||||
|
||||
declare module "mithril" {
|
||||
export = m;
|
||||
}
|
||||
281
archive/v0.2.4/mithril.deferred.html
Normal file
281
archive/v0.2.4/mithril.deferred.html
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.deferred
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-deferred">m.deferred</h2>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#usage">Usage</a></li>
|
||||
<li><a href="#retrieving-a-value-via-the-getter-setter-api">Retrieving a value via the getter-setter API</a></li>
|
||||
<li><a href="#integrating-to-the-mithril-redrawing-system">Integrating to the Mithril redrawing system</a></li>
|
||||
<li><a href="#differences-from-promises-a-">Differences from Promises/A+</a></li>
|
||||
<li><a href="#the-exception-monitor">The exception monitor</a></li>
|
||||
<li><a href="#signature">Signature</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>This is a low-level method in Mithril. It's a modified version of the Thenable API.</p>
|
||||
<p>A deferred is an asynchrony monad. It exposes a <code>promise</code> property which can <em>bind</em> callbacks to build a computation tree.</p>
|
||||
<p>The deferred object can then <em>apply</em> a value by calling either <code>resolve</code> or <code>reject</code>, which then dispatches the value to be processed to the computation tree.</p>
|
||||
<p>Each computation function takes a value as a parameter and is expected to return another value, which in turns is forwarded along to the next computation function (or functions) in the tree.</p>
|
||||
<p>The deferred object returned by <code>m.deferred</code> has two methods: <code>resolve</code> and <code>reject</code>, and one property called <code>promise</code>. The methods can be called to dispatch a value to the promise tree. The <code>promise</code> property is the root of the promise tree. It has a method <code>then</code> which takes a <code>successCallback</code> and a <code>errorCallback</code> callbacks. Calling the <code>then</code> method attaches the computations represented by <code>successCallback</code> and <code>errorCallback</code> to the promise, which will be called when either <code>resolve</code> or <code>reject</code> is called. The <code>then</code> method returns a child promise, which, itself, can have more child promises, recursively.</p>
|
||||
<p>The <code>promise</code> object is actually a function - specifically, it's an <a href="mithril.prop.html"><code>m.prop</code></a> getter-setter, which gets populated with the value returned by <code>successCallback</code> if the promise is resolved successfully.</p>
|
||||
<p>Note that Mithril promises are not automatically integrated to its automatic redrawing system. If you wish to use third party asynchronous libraries (for example, <code>jQuery.ajax</code>), you should also consider using <a href="mithril.computation.html"><code>m.startComputation</code> / <code>m.endComputation</code></a> if you want views to redraw after requests complete.</p>
|
||||
<hr>
|
||||
<h3 id="usage">Usage</h3>
|
||||
<pre><code class="lang-javascript">//standalone usage
|
||||
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
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h4 id="retrieving-a-value-via-the-getter-setter-api">Retrieving a value via the getter-setter API</h4>
|
||||
<p>The promise object is actually a getter-setter function that gets populated when the promise is fulfilled.</p>
|
||||
<pre><code class="lang-javascript">//asynchronous service
|
||||
var greetAsync = function() {
|
||||
var deferred = m.deferred();
|
||||
setTimeout(function() {
|
||||
deferred.resolve("hello");
|
||||
}, 1000);
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
//asynchronous consumer
|
||||
var greeting = greetAsync()
|
||||
var processed = greeting.then(function(value) {return value + " world"})
|
||||
|
||||
console.log(greeting()) // undefined - because `deferred.resolve` has not been called yet
|
||||
|
||||
setTimeout(function() {
|
||||
//now `deferred.resolve` has been called
|
||||
console.log(greeting()) // "hello"
|
||||
console.log(processed()) // "hello world"
|
||||
}, 2000)
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h4 id="integrating-to-the-mithril-redrawing-system">Integrating to the Mithril redrawing system</h4>
|
||||
<p>By default, promises are not integrated to the Mithril auto-redrawing system. When dealing with asynchronous functions, you must call [<code>m.startComputation</code> / <code>m.endComputation</code>] if you want the asynchronous payload to affect the view.</p>
|
||||
<pre><code class="lang-javascript">//asynchronous service
|
||||
var greetAsync = function() {
|
||||
//tell Mithril to wait for this service to complete before redrawing
|
||||
m.startComputation();
|
||||
|
||||
var deferred = m.deferred();
|
||||
setTimeout(function() {
|
||||
deferred.resolve("hello");
|
||||
|
||||
//the service is done, tell Mithril that it may redraw
|
||||
m.endComputation();
|
||||
}, 1000);
|
||||
return deferred.promise;
|
||||
};
|
||||
</code></pre>
|
||||
<p>Some cases may not require a redraw upon completion of the asynchronous callbacks. In such cases, simply omit the m.startComputation/m.endComputation calls.</p>
|
||||
<p>Some asynchronous operations might need to affect redrawing both before and after their completion. In those cases, you can call <a href="mithril.redraw.html"><code>m.redraw</code></a> instead of using m.startComputation/m.endComputation.</p>
|
||||
<pre><code class="lang-javascript">//asynchronous service
|
||||
var greetAsync = function() {
|
||||
//don't wait for this service; redraw right away
|
||||
|
||||
var deferred = m.deferred();
|
||||
setTimeout(function() {
|
||||
deferred.resolve("hello");
|
||||
|
||||
//redraw again
|
||||
m.redraw()
|
||||
}, 1000);
|
||||
return deferred.promise;
|
||||
};
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="differences-from-promises-a-">Differences from Promises/A+</h3>
|
||||
<p>For the most part, Mithril promises behave as you'd expect a <a href="http://promises-aplus.github.io/promises-spec/">Promise/A+</a> promise to behave, but have one difference: Mithril promises attempt to execute synchronously if possible.</p>
|
||||
<h4 id="synchronous-execution">Synchronous execution</h4>
|
||||
<p>Mithril promises attempt to execute synchronously if possible. To illustrate the difference between Mithril and A+ promises, consider the code below:</p>
|
||||
<pre><code class="lang-javascript">var deferred = m.deferred()
|
||||
|
||||
deferred.promise.then(function() {
|
||||
console.log(1)
|
||||
})
|
||||
|
||||
deferred.resolve("value")
|
||||
|
||||
console.log(2)
|
||||
</code></pre>
|
||||
<p>In the example above, A+ promises are required to log <code>2</code> before logging <code>1</code>, whereas Mithril logs <code>1</code> before <code>2</code>. Typically <code>resolve</code>/<code>reject</code> are called asynchronously after the <code>then</code> method is called, so normally this difference does not matter.</p>
|
||||
<p>There are a couple of reasons why Mithril runs callbacks synchronously. Conforming to the spec requires either a <code>setImmediate</code> polyfill (which is a significantly large library), or <code>setTimeout</code> (which is required to take at least 4 milliseconds per call, according to its specs). Neither of these trade-offs are acceptable, given Mithril's focus on nimbleness and performance.</p>
|
||||
<h4 id="unchecked-error-handling">Unchecked Error Handling</h4>
|
||||
<p>By default, Mithril does not swallow errors if these errors are subclasses of the Error class. Manually throwing an instance of the Error class itself (or any other objects or primitives) does trigger the rejection callback path as per the Promises/A+ spec.</p>
|
||||
<p>This deviation from the spec is there to make it easier for developers to find common logical errors such as typos that lead to null reference exceptions. By default, the spec requires that all thrown errors trigger rejection, which result in silent failures if the developer forgets to explicitly handle the failure case.</p>
|
||||
<p>For example, there is simply never a case where a developer would want to programmatically handle the error of accessing the property of a nullable entity without first checking for its existence. The only reasonable course of action to prevent the potential null reference exceptions in this case is to add the existence check in the source code. It is expected that such an error would bubble up to the console and display a developer-friendly error message and line number there.</p>
|
||||
<pre><code class="lang-javascript">m.request({method: "GET", url: "/things"})
|
||||
.then(function(items) {
|
||||
item.foreach(doSomething) //programmer error: typo will throw runtime error to the console
|
||||
})
|
||||
</code></pre>
|
||||
<p>The other side of the coin is still supported: if a developer needs to signal an exceptional condition within a promise callback, they can manually throw a <code>new Error</code> (for example, if a validation rule failed, and there should be an error message displayed to the user).</p>
|
||||
<pre><code class="lang-javascript">var error = m.prop()
|
||||
m.request({method: "GET", url: "/user/:id", data: {id: 1}})
|
||||
.then(function(user) {
|
||||
if (!user.isAdmin) throw new Error("Sorry, you don't have permissions")
|
||||
})
|
||||
.then(null, error) //handle the application error: bind to a getter-setter for diplaying it on the template
|
||||
</code></pre>
|
||||
<p>Note that the default promise exception handling semantics can be modified. See the next section.</p>
|
||||
<hr>
|
||||
<h3 id="the-exception-monitor">The exception monitor</h3>
|
||||
<p>Any time an exception is thrown inside a promise callback, Mithril calls <code>m.deferred.onerror(e)</code>.</p>
|
||||
<p>By default, this event handler rethrows the exception to the console if an error is a subclass of Error (but not an instance of Error itself). Otherwise it follows the Promises/A+ specifications. It does this because people expect unexpected errors like null reference exceptions to be thrown to the console for debugging purposes, and these errors are always subclasses of Error.</p>
|
||||
<p>On the other hand, javascript developers rarely ever throw errors that are subclasses of Error, and for the purposes of application error handling, the underlying prototypal chain of the error class is typically not relevant.</p>
|
||||
<p>The <code>onerror</code> function can be safely replaced if the default error monitoring semantics are not desired.</p>
|
||||
<pre><code>//swallow all errors
|
||||
m.deferred.onerror = function() {}
|
||||
|
||||
//only log errors
|
||||
m.deferred.onerror = function(e) {console.error(e)}
|
||||
</code></pre><hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">Deferred deferred() {void onerror(Error e)}
|
||||
|
||||
where:
|
||||
Deferred :: Object { Promise promise, void resolve(any value), void reject(any value) }
|
||||
Promise :: GetterSetter { Promise then(any successCallback(any value), any errorCallback(any value)), Promise catch(any errorCallback(any value)) }
|
||||
GetterSetter :: any getterSetter([any value])
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>GetterSetter { Promise then([any successCallback(any value) [, any errorCallback(any value)]]) } promise</strong></p>
|
||||
<p>A promise has a method called <code>then</code> which takes two computation callbacks as parameters.</p>
|
||||
<p>The <code>then</code> method returns another promise whose computations (if any) receive their inputs from the parent promise's computation.</p>
|
||||
<p>A promise is also a getter-setter (see <a href="mithril.prop.html"><code>m.prop</code></a>). After a call to either <code>resolve</code> or <code>reject</code>, it holds the result of the parent's computation (or the <code>resolve</code>/<code>reject</code> value, if the promise has no parent promises)</p>
|
||||
<p>Promises also have a method called <code>catch</code>, which is equivalent to calling <code>then(null, errorCallback)</code></p>
|
||||
<ul>
|
||||
<li><p><strong>Promise then([any successCallback(any value) [, any errorCallback(any value)]])</strong></p>
|
||||
<p>This method accepts two callbacks which process a value passed to the <code>resolve</code> and <code>reject</code> methods, respectively, and pass the processed value to the returned promise</p>
|
||||
<ul>
|
||||
<li><p><strong>any successCallback(any value)</strong> (optional)</p>
|
||||
<p>The <code>successCallback</code> is called if <code>resolve</code> is called in the root <code>deferred</code>.</p>
|
||||
<p>The default value (if this parameter is falsy) is the identity function <code>function(value) {return value}</code></p>
|
||||
<p>If this function returns undefined, then it passes the <code>value</code> argument to the next step in the thenable queue, if any</p>
|
||||
</li>
|
||||
<li><p><strong>any errorCallback(any value)</strong> (optional)</p>
|
||||
<p>The <code>errorCallback</code> is called if <code>reject</code> is called in the root <code>deferred</code>.</p>
|
||||
<p>The default value (if this parameter is falsy) is the identity function <code>function(value) {return value}</code></p>
|
||||
<p>If this function returns undefined, then it passes the <code>value</code> argument to the next step in the thenable queue, if any</p>
|
||||
</li>
|
||||
<li><p><strong>returns Promise promise</strong></p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><strong>void resolve(any value)</strong></p>
|
||||
<p>This method passes a value to the <code>successCallback</code> of the deferred object's child promise</p>
|
||||
</li>
|
||||
<li><p><strong>void reject(any value)</strong></p>
|
||||
<p>This method passes a value to the <code>errorCallback</code> of the deferred object's child promise</p>
|
||||
</li>
|
||||
<li><p><a name="onerror"></a></p>
|
||||
<h4 id="m-deferred-onerror">m.deferred.onerror</h4>
|
||||
<p><strong>void onerror(Error e)</strong></p>
|
||||
<p>This method gets called every time an exception is thrown inside a promise callback. By default, it rethrows to the console if an error is a subclass of Error (but not an instance of Error itself). Otherwise it follows the Promises/A+ specifications.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
141
archive/v0.2.4/mithril.deps.html
Normal file
141
archive/v0.2.4/mithril.deps.html
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.deps
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-deps">m.deps</h2>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#usage">Usage</a></li>
|
||||
<li><a href="#signature">Signature</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>This function overwrites the reference to the <code>window</code> object that is used internally by Mithril. It is useful for injecting a mock <code>window</code> dependency for the purposes of testing and for running Mithril in non-browser environments. The mock object used by Mithril for its own test suite <a href="https://github.com/lhorie/mithril.js/blob/next/test-deps/mock.js">can be found in the development repo</a>.</p>
|
||||
<p>By default, Mithril uses <code>window</code> itself as the dependency. Note that Mithril only uses the mock object for browser APIs such as the DOM API and <code>requestAnimationFrame</code>, but relies on the environment for ECMAScript features like <code>Object.keys</code>.</p>
|
||||
<hr>
|
||||
<h3 id="usage">Usage</h3>
|
||||
<p>Call it at the beginning of your test file to supply a mock <code>window</code>:</p>
|
||||
<pre><code class="lang-javascript">function testMithril(mockWindow) {
|
||||
window = m.deps(mockWindow);
|
||||
|
||||
// Your tests here...
|
||||
}
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">Window m.deps(Object window)
|
||||
|
||||
where:
|
||||
Window :: Object<any>
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>Object Window</strong></p>
|
||||
<p>This should be either <code>window</code> or a mock of the <code>window</code> object.</p>
|
||||
<p>Mithril uses certain <code>window</code> methods that will need to be made available for complete test coverage, depending on your application:</p>
|
||||
<ul>
|
||||
<li><code>window.document</code></li>
|
||||
<li>Mithril also uses certain methods on the DOM node object</li>
|
||||
<li><code>window.requestAnimationFrame</code>/<code>window.cancelAnimationFrame</code></li>
|
||||
<li>Falls back to <code>window.setTimeout</code>/<code>window.clearTimeout</code></li>
|
||||
<li><code>window.location</code></li>
|
||||
<li><code>window.history</code></li>
|
||||
<li><code>window.scrollTo</code></li>
|
||||
<li><code>window.XMLHttpRequest</code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><strong>returns</strong> Window</p>
|
||||
<p>The returned window is the same as what is passed in.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
578
archive/v0.2.4/mithril.html
Normal file
578
archive/v0.2.4/mithril.html
Normal file
|
|
@ -0,0 +1,578 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m">m</h2>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#usage">Usage</a></li>
|
||||
<li><a href="#binding-to-data">Binding to data</a></li>
|
||||
<li><a href="#using-html-entities">Using HTML entities</a></li>
|
||||
<li><a href="#accessing-the-real-dom-element">Accessing the real DOM element</a></li>
|
||||
<li><a href="#persisting-config-data">Persisting config data</a></li>
|
||||
<li><a href="#destructors">Destructors</a></li>
|
||||
<li><a href="#persisting-dom-elements-across-route-changes">Persisting DOM elements across route changes</a></li>
|
||||
<li><a href="#svg">SVG</a></li>
|
||||
<li><a href="#dealing-with-focus">Dealing with focus</a></li>
|
||||
<li><a href="#dealing-with-sorting-and-deleting-in-lists">Dealing with sorting and deleting in lists</a></li>
|
||||
<li><a href="#component-shorthand">Component shorthand</a></li>
|
||||
<li><a href="#signature">Signature</a></li>
|
||||
<li><a href="#the-config-attribute">The <code>config</code> attribute</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>This is a convenience method to compose virtual elements that can be rendered via <a href="mithril.render.html"><code>m.render()</code></a>.</p>
|
||||
<p>You are encouraged to use CSS selectors to define virtual elements. See "Signature" section for details.</p>
|
||||
<hr>
|
||||
<h3 id="usage">Usage</h3>
|
||||
<p>You can use simple tag selectors to make templates resemble HTML:</p>
|
||||
<pre><code class="lang-javascript">m("br"); //yields a virtual element that represents <br>
|
||||
|
||||
m("div", "Hello"); //yields <div>Hello</div>
|
||||
|
||||
m("div", {class: "container"}, "Hello"); //yields <div class="container">Hello</div>
|
||||
</code></pre>
|
||||
<p>Note that the output value from <code>m()</code> is not an actual DOM element. In order to turn the virtual element into a real DOM element, you must call <a href="mithril.render.html"><code>m.render()</code></a>.</p>
|
||||
<pre><code class="lang-javascript">m.render(document.body, m("br")); //puts a <br> in <body>
|
||||
</code></pre>
|
||||
<p>You can also use more complex CSS selectors:</p>
|
||||
<pre><code class="lang-javascript">m(".container"); //yields <div class="container"></div>
|
||||
|
||||
m("#layout"); //yields <div id="layout"></div>
|
||||
|
||||
m("a[name=top]"); //yields <a name="top"></a>
|
||||
|
||||
m("[contenteditable]"); //yields <div contenteditable></div>
|
||||
|
||||
m("a#google.external[href='http://google.com']", "Google"); //yields <a id="google" class="external" href="http://google.com">Google</a>
|
||||
</code></pre>
|
||||
<p>Each <code>m()</code> call creates a virtual DOM element, that is, a Javascript object that represents a DOM element, and which is eventually converted into one.</p>
|
||||
<p>You can, of course, nest virtual elements:</p>
|
||||
<pre><code class="lang-javascript">m("ul", [
|
||||
m("li", "item 1"),
|
||||
m("li", "item 2"),
|
||||
]);
|
||||
|
||||
/*
|
||||
yields
|
||||
<ul>
|
||||
<li>item 1</li>
|
||||
<li>item 2</li>
|
||||
</ul>
|
||||
*/
|
||||
</code></pre>
|
||||
<hr>
|
||||
<p>The CSS selector syntax (e.g. <code>a#google.external[href='http://google.com']</code>) is meant to be used for declaring static attributes in the element, i.e. attribute values that don't change dynamically when the user interacts with the app.</p>
|
||||
<p>The <code>attributes</code> argument (i.e. the second parameter in the <code>m("div", {class: "container"}, "Hello")</code> example) is meant to be used for attributes whose values we want to dynamically populate.</p>
|
||||
<p>For example, let's say that you're generating a link from an entry that comes from a web service:</p>
|
||||
<pre><code class="lang-javascript">//assume the variable `link` came from a web service
|
||||
var link = {url: "http://google.com", title: "Google"}
|
||||
|
||||
m("a", {href: link.url}, link.title); //yields <a href="http://google.com">Google</a>
|
||||
</code></pre>
|
||||
<p>Here's a less trivial example:</p>
|
||||
<pre><code class="lang-javascript">var links = [
|
||||
{title: "item 1", url: "/item1"},
|
||||
{title: "item 2", url: "/item2"},
|
||||
{title: "item 3", url: "/item3"}
|
||||
];
|
||||
|
||||
m.render(document.body, [
|
||||
m("ul.nav",
|
||||
links.map(function(link) {
|
||||
return m("li",
|
||||
m("a", {href: link.url}, link.title)
|
||||
);
|
||||
})
|
||||
)
|
||||
]);
|
||||
</code></pre>
|
||||
<p>yields:</p>
|
||||
<pre><code class="lang-markup"><body>
|
||||
<ul class="nav">
|
||||
<li><a href="/item1">item 1</a></li>
|
||||
<li><a href="/item2">item 2</a></li>
|
||||
<li><a href="/item3">item 3</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</code></pre>
|
||||
<p>As you can see, flow control is done with vanilla Javascript. This allows the developer to abstract away any aspect of the template at will.</p>
|
||||
<hr>
|
||||
<p>Note that you can use both Javascript property names and HTML attribute names to set values in the <code>attributes</code> argument, but you should pass a value of appropriate type. If an attribute has the same name in Javascript and in HTML, then Mithril assumes you're setting the Javascript property.</p>
|
||||
<pre><code class="lang-javascript">m("div", {class: "widget"}); //yields <div class="widget"></div>
|
||||
|
||||
m("div", {className: "widget"}); //yields <div class="widget"></div>
|
||||
|
||||
m("button", {onclick: alert}); //yields <button></button>, which alerts its event argument when clicked
|
||||
|
||||
//note this uses the Javascript syntax (uppercase "O") for `readonly`
|
||||
//in order to set the boolean javascript property instead of the HTML attribute
|
||||
m("input", {readOnly: true}); //yields <input readonly />
|
||||
|
||||
//using the HTML attribute name will call `setAttribute`, which may not be what you want
|
||||
m("input", {readonly: false}); //yields <input readonly="false" />, which is still readonly
|
||||
</code></pre>
|
||||
<hr>
|
||||
<p>Note that you can use JSON syntax if the attribute name you are setting has non-alphanumeric characters:</p>
|
||||
<pre><code class="lang-javascript">m("div", {"data-index": 1}); //yields <div data-index="1"></div>
|
||||
</code></pre>
|
||||
<p>You can set inline styles like this:</p>
|
||||
<pre><code class="lang-javascript">m("div", {style: {border: "1px solid red"}}); //yields <div style="border:1px solid red;"></div>
|
||||
</code></pre>
|
||||
<p>Note that in order to keep the framework lean, Mithril does not auto-append units like <code>px</code> or <code>%</code> to any values. Typically, you should not even be using inline styles to begin with (unless you are dynamically changing them).</p>
|
||||
<p>Mithril also does not auto-camel-case CSS properties on inline style attributes, so you should use the Javascript syntax when setting them via Javascript objects:</p>
|
||||
<pre><code class="lang-javascript">m("div", {style: {textAlign: "center"}}); //yields <div style="text-align:center;"></div>
|
||||
m("div", {style: {cssFloat: "left"}}); //yields <div style="float:left;"></div>
|
||||
|
||||
//this does not work
|
||||
m("div", {style: {"text-align": "center"}});
|
||||
m("div", {style: {float: "left"}});
|
||||
</code></pre>
|
||||
<p>You can find the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Properties_Reference">Javascript syntax for all the CSS rules here</a>.</p>
|
||||
<p>You can, however, use CSS syntax when defining style rules as inline strings:</p>
|
||||
<pre><code class="lang-javascript">m("div[style='text-align:center']"); //yields <div style="text-align:center;"></div>
|
||||
</code></pre>
|
||||
<p>One caveat of using the CSS syntax is that it clobbers the <code>style</code> attribute in the DOM element on redraws, so this syntax is not appropriate if you need to use it in conjunction with 3rd party tools that modify the element's style outside of Mithril's templates (e.g. via <code>config</code>, which is explained below)</p>
|
||||
<hr>
|
||||
<h3 id="binding-to-data">Binding to data</h3>
|
||||
<p>In order to stay flexible, Mithril doesn't provide helpers for bi-directional bindings out of the box. However, bindings can be implemented easily:</p>
|
||||
<pre><code class="lang-javascript">//a data store
|
||||
var name = m.prop("")
|
||||
|
||||
//binding the data store in a view
|
||||
m("input", {oninput: m.withAttr("value", name), value: name()})
|
||||
</code></pre>
|
||||
<p>In the code above, the <code>oninput</code> event handler updates the <code>name</code> getter-setter, and the Mithril auto-redrawing system redraws the template in order to update the displayed value. You can read more about the <a href="mithril.prop.html"><code>m.prop</code> getter-setter utility here</a> and the <a href="mithril.withAttr.html"><code>m.withAttr</code> event handler factory here</a>. You can also <a href="auto-redrawing.html">learn how the redrawing system works here</a>.</p>
|
||||
<p>Note that Mithril always considers the model layer data to be canonical. This means that in the code below, the input on screen will overwritten by the model data any time a redraw happens:</p>
|
||||
<pre><code class="lang-javascript">//note that we are not updating the value of the `name` getter-setter via an event handler
|
||||
//redraws will always overwrite the current UI value with the value of `name()`
|
||||
m("input", {value: name()})
|
||||
</code></pre>
|
||||
<p>Expressiveness can be achieved using standard refactoring techniques:</p>
|
||||
<pre><code class="lang-javascript">//refactor the binding to a simple helper
|
||||
var binds = function(prop) {
|
||||
return {oninput: m.withAttr("value", prop), value: prop()}
|
||||
}
|
||||
|
||||
//a data store
|
||||
var name = m.prop("")
|
||||
|
||||
//binding the data store in a view
|
||||
m("input", binds(name))
|
||||
</code></pre>
|
||||
<p>Here's an example of a more aggressive refactor:</p>
|
||||
<pre><code class="lang-javascript">//refactor the binding to a simple helper
|
||||
var input = function(prop) {
|
||||
return m("input", {oninput: m.withAttr("value", prop), value: prop()})
|
||||
}
|
||||
|
||||
//a data store
|
||||
var name = m.prop("")
|
||||
|
||||
//binding the data store in a view
|
||||
input(name)
|
||||
</code></pre>
|
||||
<p>Alternatively, you can also explore other techniques in order to achieve better <a href="http://lhorie.github.io/mithril-blog/asymmetrical-data-bindings.html">performance</a> and <a href="http://lhorie.github.io/mithril-blog/extending-the-view-language.html">expressiveness</a>.</p>
|
||||
<hr>
|
||||
<h3 id="using-html-entities">Using HTML entities</h3>
|
||||
<p>By default, Mithril escapes HTML strings in order to help prevent XSS attacks.</p>
|
||||
<pre><code class="lang-javascript">m("div", "&times;") //becomes <div>&amp;times;</div>
|
||||
</code></pre>
|
||||
<p>You can unescape trusted HTML strings by using <a href="mithril.trust.html"><code>m.trust</code></a></p>
|
||||
<pre><code class="lang-javascript">m("div", m.trust("&times;")) //becomes <div>&times;</div>
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h4 id="accessing-the-real-dom-element">Accessing the real DOM element</h4>
|
||||
<p>You can define a non-HTML-standard attribute called <code>config</code>. This special parameter allows you to call methods on the DOM element after it gets created.</p>
|
||||
<p>This is useful, for example, if you declare a <code>canvas</code> element and want to use the Javascript API to draw:</p>
|
||||
<pre><code class="lang-javascript">function draw(element, isInitialized, context) {
|
||||
//don't redraw if we did once already
|
||||
if (isInitialized) return;
|
||||
|
||||
var ctx = element.getContext("2d");
|
||||
/* draws stuff */
|
||||
}
|
||||
|
||||
var view = [
|
||||
m("canvas", {config: draw})
|
||||
]
|
||||
|
||||
//this creates the canvas element, and therefore, `isInitialized` is false
|
||||
m.render(document.body, view);
|
||||
|
||||
//here, isInitialized is `true`
|
||||
m.render(document.body, view);
|
||||
</code></pre>
|
||||
<p>One common way of using <code>config</code> is in conjunction with <a href="mithril.route.html"><code>m.route</code></a>, which is an unobtrusive extension to links that allow Mithril's routing system to work transparently regardless of which routing mode is used.</p>
|
||||
<pre><code class="lang-javascript">//this link can use any of Mithril's routing system modes
|
||||
//(i.e. it can use either the hash, the querystring or the pathname as the router implementation)
|
||||
//without needing to hard-code any syntax (`#` or `?`) in the `href` attribute.
|
||||
m("a[href='/dashboard']", {config: m.route}, "Dashboard");
|
||||
</code></pre>
|
||||
<p>The <code>config</code> mechanism can also be used to put focus on form inputs, and call methods that would not be possible to execute via the regular attribute syntax.</p>
|
||||
<p>Also note that the <code>config</code> callback only runs after a rendering lifecycle is done. Therefore, you should not use <code>config</code> to modify controller and model values, if you expect these changes to render immediately. Changes to controller and model values in this fashion will only render on the next <code>m.render</code> or <code>m.mount</code> call.</p>
|
||||
<p>You can use this mechanism to attach custom event listeners to controller methods (for example, when integrating with third party libraries), but you are responsible for making sure the integration with Mithril's autoredrawing system is in place. See the <a href="integration.html">integration guide</a> for more information.</p>
|
||||
<p>You can also use it to attach events to other elements (for example, <code>window.onresize</code>), but you should remove such event handlers via <code>ctx.onunload</code> to avoid surprises.</p>
|
||||
<hr>
|
||||
<h4 id="persisting-config-data">Persisting config data</h4>
|
||||
<p>The third argument for <code>config</code> allows you to map data to a virtual DOM element in a way that persists across redraws. This is useful when a <code>config</code> instantiates 3rd party classes and accesses the instance on redraws.</p>
|
||||
<p>The example below shows a contrived redraw counter. In it, the count is stored in the context object and re-accessed on each redraw.</p>
|
||||
<pre><code class="lang-javascript">function alertsRedrawCount(element, isInit, context) {
|
||||
if (!isInit) context.count = 0
|
||||
alert(++context.count)
|
||||
}
|
||||
|
||||
m("div", {config: alertsRedrawCount})
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h4 id="destructors">Destructors</h4>
|
||||
<p>If the <code>context</code> object that is passed to a <code>config</code> function has a property called <code>onunload</code>, this function will be called when the element gets detached from the document by Mithril's diff engine.</p>
|
||||
<p>This is useful if there are cleanup tasks that need to be run when an element is destroyed (e.g. clearing <code>setTimeout</code>'s, etc)</p>
|
||||
<pre><code class="lang-javascript">function unloadable(element, isInit, context) {
|
||||
context.timer = setTimeout(function() {
|
||||
alert("timed out!");
|
||||
}, 1000);
|
||||
|
||||
context.onunload = function() {
|
||||
clearTimeout(context.timer);
|
||||
console.log("unloaded the div");
|
||||
}
|
||||
};
|
||||
|
||||
m.render(document, m("div", {config: unloadable}));
|
||||
|
||||
m.render(document, m("a")); //logs `unloaded the div` and `alert` never gets called
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h4 id="persisting-dom-elements-across-route-changes">Persisting DOM elements across route changes</h4>
|
||||
<p>When using the <a href="mithril.route.html">router</a>, a route change recreates the DOM tree from scratch in order to unload plugins from the previous page. If you want to keep a DOM element intact across a route change, you can set the <code>retain</code> flag in the config's context object.</p>
|
||||
<p>In the example below, there are two routes, each of which loads a component when a user navigates to their respective URLs. Both components use a <code>menu</code> template, which contains links for navigation between the two components, and an expensive-to-reinitialize element. Setting <code>context.retain = true</code> in the element's config function allows the span to stay intact after a route change.</p>
|
||||
<pre><code class="lang-javascript">//a menu template
|
||||
var menu = function() {
|
||||
return m("div", [
|
||||
m("a[href='/']", {config: m.route}, "Home"),
|
||||
m("a[href='/contact']", {config: m.route}, "Contact"),
|
||||
//an expensive-to-initialize DOM element
|
||||
m("span", {config: persistent})
|
||||
])
|
||||
}
|
||||
//a configuration that persists across route changes
|
||||
function persistent(el, isInit, context) {
|
||||
context.retain = true
|
||||
|
||||
if (!isInit) {
|
||||
//only runs once, even if you move back and forth between `/` and `/contact`
|
||||
doSomethingExpensive(el)
|
||||
}
|
||||
}
|
||||
|
||||
//components that use the menu above
|
||||
var Home = {
|
||||
controller: function() {},
|
||||
view: function() {
|
||||
return m("div", [
|
||||
menu(),
|
||||
m("h1", "Home")
|
||||
])
|
||||
}
|
||||
}
|
||||
var Contact = {
|
||||
view: function() {
|
||||
return m("div", [
|
||||
menu(),
|
||||
m("h2", "Contact")
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
m.route(document.body, "/", {
|
||||
"/": Home,
|
||||
"/contact": Contact
|
||||
})
|
||||
</code></pre>
|
||||
<p>Note that even if you set <code>context.retain = true</code>, the element will still be destroyed and recreated if it is different enough from the existing element. An element is considered "different enough" if:</p>
|
||||
<ul>
|
||||
<li>the tag name changes, or</li>
|
||||
<li>the list of HTML attributes changes, or</li>
|
||||
<li>the value of the element's id attribute changes</li>
|
||||
</ul>
|
||||
<p>In addition, setting <code>context.retain = false</code> will also cause the element to be recreated, even if it is not considered different enough.</p>
|
||||
<hr>
|
||||
<h4 id="svg">SVG</h4>
|
||||
<p>You can use Mithril to create SVG documents (as long as you don't need to support browsers that don't support SVG natively).</p>
|
||||
<p>Mithril automatically figures out the correct XML namespaces when it sees an SVG island in the virtual DOM tree.</p>
|
||||
<pre><code class="lang-javascript">m("svg[height='200px'][width='200px']", [
|
||||
m("image[href='foo.jpg'][height='200px'][width='200px']")
|
||||
])
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h4 id="dealing-with-focus">Dealing with focus</h4>
|
||||
<p>The virtual DOM diffing algorithm has a weakness: a naive diff is not aware of the identity of DOM elements. In practice, this means performing operations like shifting an item from the beginning of a list would cause every element in the list to be diffed and potentially recreated. Another side-effect is that UI state like input focus is not tracked correctly if the focused element moves around, and likewise, state for 3rd party plugins that are added via <code>config</code> can also end up in the wrong element.</p>
|
||||
<p>Fortunately, with Mithril, it's possible for developers to attach an identity key to elements so that array operations like shift, splice and sort only affect the minimum amount of elements required, leaving the rest of the DOM elements untouched when a redraw happens. This allows us to maintain input focus and plugin state correctly.</p>
|
||||
<p>To maintain the identities of DOM elements, you need to add a <code>key</code> property to the direct children of the array that you're planning to modify. The key for each child must be unique among a list of sibling DOM elements, but it does not need to be globally unique. Also, keys must be either strings or numbers.</p>
|
||||
<pre><code class="lang-javascript">m("ul", [
|
||||
items.map(function(item) {
|
||||
return m("li", {key: item.id}, [
|
||||
m("input")
|
||||
]);
|
||||
})
|
||||
]);
|
||||
</code></pre>
|
||||
<p>In the example above, input focus would be maintained correctly after a redraw even if <code>items</code> got sorted or reversed. The key is defined in the <code>li</code>, which is the closest element to the <code>items</code> array, not directly on the <code>input</code>, even though we want to track focus on the input.</p>
|
||||
<p>Note that in addition to the presence of the <code>key</code> attribute, diffing rules also apply in determining whether an element is recreated. Elements are recreated if either their node name changes, or if the list of attribute names change, or if the ID attribute changes. To avoid surprises, be sure to change only attribute values, using <code>undefined</code> or <code>null</code> as values if appropriate, rather than conditionally substituting attribute dictionaries altogether.</p>
|
||||
<pre><code class="lang-javascript">//avoid using this idiom
|
||||
m("li", selected ? {class: "active"} : {})
|
||||
|
||||
//use this idiom instead
|
||||
m("li", {class: selected ? "active" : ""})
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="dealing-with-sorting-and-deleting-in-lists">Dealing with sorting and deleting in lists</h3>
|
||||
<p>As with input focus, we can maintain referential integrity between data in a list and the respective DOM representation by using keys.</p>
|
||||
<pre><code class="lang-javascript">m("ul", [
|
||||
items.map(function(item) {
|
||||
return m("li", {key: item.id}, [
|
||||
m("input")
|
||||
]);
|
||||
})
|
||||
]);
|
||||
</code></pre>
|
||||
<p>You should always use keys if you need to sort lists, remove items from them or splice them in any way.</p>
|
||||
<hr>
|
||||
<h3 id="component-shorthand">Component Shorthand</h3>
|
||||
<p>If the first argument to <code>m()</code> is a component, it acts as an alias of <code>m.component()</code></p>
|
||||
<pre><code class="lang-javascript">var MyComponent = {
|
||||
controller: function() {
|
||||
return {greeting: "hello"}
|
||||
},
|
||||
view: function(ctrl, args) {
|
||||
return m("h1", ctrl.greeting + " " + args.data)
|
||||
}
|
||||
}
|
||||
|
||||
m.render(document.body, [
|
||||
//the two lines below are equivalent
|
||||
m(component, {data: "world"}),
|
||||
m.component(component, {data: "world"})
|
||||
])
|
||||
</code></pre>
|
||||
<p>See <a href="mithril.component.html">components</a> for more information.</p>
|
||||
<hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">VirtualElement m(String selector [, Attributes attributes] [, Children... children])
|
||||
|
||||
where:
|
||||
VirtualElement :: Object { String tag, Attributes attributes, Children children }
|
||||
Attributes :: Object<any | void config(DOMElement element, Boolean isInitialized, Object context, VirtualElement vdom)>
|
||||
Children :: String text | VirtualElement virtualElement | Component | SubtreeDirective directive | Array<Children children>
|
||||
Component :: Object { Function? controller, Function view }
|
||||
SubtreeDirective :: Object { String subtree }
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>String selector</strong></p>
|
||||
<p>This string should be a CSS rule that represents a DOM element.</p>
|
||||
<p>Only tag, id, class and attribute selectors are supported.</p>
|
||||
<p>If the tag selector is omitted, it defaults to <code>div</code>.</p>
|
||||
<p>Note that if the same attribute is defined in the both <code>selector</code> and <code>attributes</code> parameters, the value in <code>attributes</code> is used.</p>
|
||||
<p>For developer convenience, Mithril makes an exception for the <code>class</code> attribute: if there are classes defined in both parameters, they are concatenated as a space separated list. It does not, however, de-dupe classes if the same class is declared twice.</p>
|
||||
<p><em>Examples:</em></p>
|
||||
<p><code>"div"</code></p>
|
||||
<p><code>"#container"</code></p>
|
||||
<p><code>".active"</code></p>
|
||||
<p><code>"[title='Application']"</code></p>
|
||||
<p><code>"div#container.active[title='Application']"</code></p>
|
||||
<p><code>".active#container"</code></p>
|
||||
</li>
|
||||
<li><p><strong>Attributes attributes</strong> (optional)</p>
|
||||
<p>This key-value map should define a list of HTML attributes and their respective values.</p>
|
||||
<p>You can use both HTML and Javascript attribute names. For example, both <code>class</code> and <code>className</code> are valid.</p>
|
||||
<p>Values' types should match the expected type for the respective attribute.</p>
|
||||
<p>For example, the value for <code>className</code> should be a string.</p>
|
||||
<p>When a attribute name expects different types for the value in HTML and Javascript, the Javascript type should be used.</p>
|
||||
<p>For example, the value for the <code>onclick</code> attribute should be a function.</p>
|
||||
<p>Similar, setting the value of attribute <code>readonly</code> to <code>false</code> is equivalent to removing the attribute in HTML.</p>
|
||||
<p>It's also possible to set values to Javascript-only properties, such as <code>hash</code> in a <code><a></code> element.</p>
|
||||
<p>Note that if the same attribute is defined in the both <code>selector</code> and <code>attributes</code> parameters, the value in <code>attributes</code> is used.</p>
|
||||
<p>For developer convenience, Mithril makes an exception for the <code>class</code> attribute: if there are classes defined in both parameters, they are concatenated as a space separated list. It does not, however, de-dupe classes if the same class is declared twice.</p>
|
||||
<p><em>Examples:</em></p>
|
||||
<p><code>{ title: "Application" }</code></p>
|
||||
<p><code>{ onclick: function(e) { /*do stuff*/ } }</code></p>
|
||||
<p><code>{ style: {border: "1px solid red"} }</code></p>
|
||||
</li>
|
||||
<li><h4 id="the-config-attribute">The <code>config</code> attribute</h4>
|
||||
<p><strong>void config(DOMElement element, Boolean isInitialized, Object context, VirtualElement vdom)</strong> (optional)</p>
|
||||
<p>You can define a non-HTML-standard attribute called <code>config</code>. This special parameter allows you to call methods on the DOM element after it gets created.</p>
|
||||
<p>This is useful, for example, if you declare a <code>canvas</code> element and want to use the Javascript API to draw:</p>
|
||||
<pre><code class="lang-javascript">function draw(element, isInitialized) {
|
||||
//don't redraw if we did once already
|
||||
if (isInitialized) return;
|
||||
|
||||
var ctx = element.getContext("2d");
|
||||
/* draws stuff */
|
||||
}
|
||||
|
||||
var view = [
|
||||
m("canvas", {config: draw})
|
||||
]
|
||||
|
||||
//this creates the canvas element, and therefore, `isInitialized` is false
|
||||
m.render(document.body, view);
|
||||
|
||||
//here, isInitialized is `true`
|
||||
m.render(document.body, view);
|
||||
</code></pre>
|
||||
<p>One common way of using <code>config</code> is in conjunction with <a href="mithril.route.html"><code>m.route</code></a>, which is an unobtrusive extension to links that allow Mithril's routing system to work transparently regardless of which routing mode is used.</p>
|
||||
<pre><code class="lang-javascript">//this link can use any of Mithril's routing system modes
|
||||
//(i.e. it can use either the hash, the querystring or the pathname as the router implementation)
|
||||
//without needing to hard-code any syntax (`#` or `?`) in the `href` attribute.
|
||||
m("a[href='/dashboard']", {config: m.route}, "Dashboard");
|
||||
</code></pre>
|
||||
<p>The <code>config</code> mechanism can also be used to put focus on form inputs, and call methods that would not be possible to execute via the regular attribute syntax.</p>
|
||||
<p>Also note that the <code>config</code> callback only runs after a rendering lifecycle is done. Therefore, you should not use <code>config</code> to modify controller and model values, if you expect these changes to render immediately. Changes to controller and model values in this fashion will only render on the next <code>m.render</code> or <code>m.mount</code> call.</p>
|
||||
<p>You can use this mechanism to attach custom event listeners to controller methods (for example, when integrating with third party libraries), but you are responsible for making sure the integration with Mithril's autoredrawing system is in place. See the <a href="integration.html">integration guide</a> for more information.</p>
|
||||
<p>You can also use it to attach events to other elements (for example, <code>window.onresize</code>), but you should remove such event handlers via <code>ctx.onunload</code> to avoid surprises.</p>
|
||||
<ul>
|
||||
<li><strong>DOMElement element</strong></li>
|
||||
</ul>
|
||||
<p>The DOM element that corresponds to virtual element defined by the <code>m()</code> call.</p>
|
||||
<ul>
|
||||
<li><strong>Boolean isInitialized</strong></li>
|
||||
</ul>
|
||||
<p>Whether this is the first time we are running this function on this element. This flag is false the first time it runs on an element, and true on redraws that happen after the element has been created.</p>
|
||||
<ul>
|
||||
<li><strong>Object context</strong></li>
|
||||
</ul>
|
||||
<p>An object that retains its state across redraws. It can be used to store instances of 3rd party classes that need to be accessed more than one time throughout the lifecycle of a page.</p>
|
||||
<p>The example below shows a contrived redraw counter. In it, the count is stored in the context object and re-accessed on each redraw.</p>
|
||||
<pre><code class="lang-javascript">function alertsRedrawCount(element, isInit, context) {
|
||||
if (!isInit) context.count = 0
|
||||
alert(++context.count)
|
||||
}
|
||||
|
||||
m("div", {config: alertsRedrawCount})
|
||||
</code></pre>
|
||||
<p>If the <code>context</code> object that is passed to a <code>config</code> function has a property called <code>onunload</code>, this function will be called when the element gets detached from the document by Mithril's diff engine.</p>
|
||||
<p>This is useful if there are cleanup tasks that need to be run when an element is destroyed (e.g. clearing <code>setTimeout</code>'s, etc)</p>
|
||||
<pre><code class="lang-javascript">function unloadable(element, isInit, context) {
|
||||
context.timer = setTimeout(function() {
|
||||
alert("timed out!");
|
||||
}, 1000);
|
||||
|
||||
context.onunload = function() {
|
||||
clearTimeout(context.timer);
|
||||
console.log("unloaded the div");
|
||||
}
|
||||
};
|
||||
|
||||
m.render(document, m("div", {config: unloadable}));
|
||||
|
||||
m.render(document, m("a")); //logs `unloaded the div` and `alert` never gets called
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><strong>VirtualElement vdom</strong></li>
|
||||
</ul>
|
||||
<p>The virtual DOM element to which the <code>config</code> function is attached</p>
|
||||
</li>
|
||||
<li><p><strong>Children children</strong> (optional)</p>
|
||||
<p>If this argument is a string, it will be rendered as a text node. To render a string as HTML, see <a href="mithril.trust.html"><code>m.trust</code></a></p>
|
||||
<p>If it's a VirtualElement, it will be rendered as a DOM Element.</p>
|
||||
<p>If it's a <a href="mithril.component.html">component</a>, the component will be instantiated and managed internally by Mithril</p>
|
||||
<p>If it's a list, its contents will recursively be rendered as appropriate and appended as children of the element being created.</p>
|
||||
<p>If it's a SubtreeDirective with the value "retain", it will retain the existing DOM tree in place, if any. See <a href="mithril.render.html#subtree-directives">subtree directives.html</a> for more information.</p>
|
||||
</li>
|
||||
<li><p><strong>returns</strong> VirtualElement</p>
|
||||
<p>The returned VirtualElement is a Javascript data structure that represents the DOM element to be rendered by <a href="mithril.render.html"><code>m.render</code></a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
2190
archive/v0.2.4/mithril.js
Normal file
2190
archive/v0.2.4/mithril.js
Normal file
File diff suppressed because it is too large
Load diff
8
archive/v0.2.4/mithril.min.js
vendored
Normal file
8
archive/v0.2.4/mithril.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
archive/v0.2.4/mithril.min.js.map
Normal file
1
archive/v0.2.4/mithril.min.js.map
Normal file
File diff suppressed because one or more lines are too long
BIN
archive/v0.2.4/mithril.min.zip
Normal file
BIN
archive/v0.2.4/mithril.min.zip
Normal file
Binary file not shown.
150
archive/v0.2.4/mithril.mount.html
Normal file
150
archive/v0.2.4/mithril.mount.html
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.mount
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-mount">m.mount</h2>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#rendering-components">Rendering Components</a></li>
|
||||
<li><a href="#signature">Signature</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>Mounting is the process of rendering a <a href="mithril.component.html">component</a> into a DOM element.</p>
|
||||
<p>The different between <code>m.mount</code> and <a href="mithril.render.html"><code>m.render</code></a> is that a component rendered via <code>m.mount</code> auto-redraws automatically when event handlers are triggered, whereas components rendered via <code>m.render</code> do not.</p>
|
||||
<p>In order to allow a user to navigate between different pages by loading and unloading components, consider using <a href="mithril.route.html"><code>m.route</code></a> instead.</p>
|
||||
<hr>
|
||||
<h2 id="rendering-components">Rendering Components</h2>
|
||||
<h3 id="usage">Usage</h3>
|
||||
<p>Calling <code>m.mount</code> with a DOM element as the first argument and a component as the second argument will call the component's controller function, and then call the component's view function. The return value of the controller function is passed to the view function as its first argument.</p>
|
||||
<pre><code class="lang-javascript">var MyComponent = {
|
||||
controller: function() {
|
||||
return {greeting: "Hello"}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
return m("h1", ctrl.greeting)
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, MyComponent)
|
||||
|
||||
//<body><h1>Hello</h1></body>
|
||||
</code></pre>
|
||||
<p>For more information on components, see <a href="mithril.component.html"><code>m.component</code></a>.</p>
|
||||
<hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">Object mount(DOMElement rootElement, Component component)
|
||||
|
||||
where:
|
||||
Component :: Object { Controller, View }
|
||||
Controller :: SimpleController | UnloadableController
|
||||
SimpleController :: void controller([Object attributes [, any... args]])
|
||||
UnloadableController :: void controller([Object attributes [, any... args]]) { prototype: void unload(UnloadEvent e) }
|
||||
UnloadEvent :: Object {void preventDefault()}
|
||||
View :: void view(Object controllerInstance [, Object attributes [, any... args]])
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>DOMElement rootElement</strong></p>
|
||||
<p>A DOM element which will contain the view's template.</p>
|
||||
</li>
|
||||
<li><p><strong>Component component</strong></p>
|
||||
<p>A component is supposed to be an Object with two keys: <code>controller</code> and <code>view</code>. Each of those should point to a Javascript function. If the <code>controller</code> is omitted, Mithril will provide one, pointing to an empty function.</p>
|
||||
<p>When <code>m.mount</code> is called, the controller function runs, and its return value is returned by the <code>m.mount</code> call.</p>
|
||||
<p>Once the controller code finishes executing (and this may include waiting for AJAX requests to complete), the view class is instantiated, and the instance of the controller is passed as an argument to the view's constructor.</p>
|
||||
</li>
|
||||
<li><p><strong>returns Object controllerInstance</strong></p>
|
||||
<p>An instance of the controller constructor</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
202
archive/v0.2.4/mithril.prop.html
Normal file
202
archive/v0.2.4/mithril.prop.html
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.prop
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-prop">m.prop</h2>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#usage">Usage</a></li>
|
||||
<li><a href="#third-party-promise-library-support">Third-party promise library support</a></li>
|
||||
<li><a href="#serializing-getter-setters">Serializing getter-setters</a></li>
|
||||
<li><a href="#signature">Signature</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>This is a getter-setter factory utility. It returns a function that stores information.</p>
|
||||
<p>Note that modifying the values of <code>m.prop</code> getter-setters does not trigger redrawing. Instead, Mithril's redrawing system relies on <a href="mithril.computation.html"><code>m.startComputation</code> and <code>m.endComputation</code></a>. These functions are internally called by Mithril when you initialize a component via <a href="mithril.mount.html"><code>m.mount</code></a> or <a href="mithril.route.html"><code>m.route</code></a>, and when you trigger event handlers that were created within templates with <a href="mithril.html"><code>m()</code></a>. </p>
|
||||
<hr>
|
||||
<h3 id="usage">Usage</h3>
|
||||
<pre><code class="lang-javascript">//define a getter-setter with initial value `John`
|
||||
var name = m.prop("John");
|
||||
|
||||
//read the value
|
||||
var a = name(); //a == "John"
|
||||
|
||||
//set the value to `Mary`
|
||||
name("Mary"); //Mary
|
||||
|
||||
//read the value
|
||||
var b = name(); //b == "Mary"
|
||||
</code></pre>
|
||||
<p>It can be used in conjunction with <a href="mithril.withAttr.html"><code>m.withAttr</code></a> to implement data binding in the view-to-model direction and to provide uniform data access for model entity properties.</p>
|
||||
<pre><code class="lang-javascript">//a contrived example of bi-directional data binding
|
||||
var User = {
|
||||
model: function(name) {
|
||||
this.name = m.prop(name);
|
||||
},
|
||||
controller: function() {
|
||||
this.user = new User.model("John Doe");
|
||||
},
|
||||
view: function(controller) {
|
||||
m.render("body", [
|
||||
m("input", {onchange: m.withAttr("value", controller.user.name), value: controller.user.name()})
|
||||
]);
|
||||
}
|
||||
};
|
||||
</code></pre>
|
||||
<p>In the example above, the usage of <code>m.prop</code> allows the developer to change the implementation of the user name getter/setter without the need for code changes in the controller and view.</p>
|
||||
<p><code>m.prop</code> can also be used in conjunction with <a href="mithril.request.html"><code>m.request</code></a> and <a href="mithril.deferred.html"><code>m.deferred</code></a> to bind data on completion of an asynchronous operation.</p>
|
||||
<pre><code class="lang-javascript">var users = m.prop([]);
|
||||
var error = m.prop("");
|
||||
|
||||
m.request({method: "GET", url: "/users"})
|
||||
.then(users, error); //on success, `users` will be populated, otherwise `error` will be populated
|
||||
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
|
||||
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of User instances
|
||||
//i.e. users()[0].name() == "John"
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="third-party-promise-library-support">Third-party promise library support</h3>
|
||||
<p>If a promise is passed into <code>m.prop()</code>, a Mithril promise is returned. Mithril promises are also getter-setter functions, which are populated with the resolved value if the promise is fulfilled successfully.</p>
|
||||
<p>Until the promise is resolved, the value of the prop will resolve to <code>undefined</code></p>
|
||||
<p>Here's an example using the <a href="https://github.com/kriskowal/q">Q</a> promise library:</p>
|
||||
<pre><code class="lang-javascript">var deferred = Q.defer()
|
||||
var users = m.prop(deferred.promise)
|
||||
|
||||
users() // undefined
|
||||
|
||||
deferred.resolve("Hello")
|
||||
|
||||
//wait for next tick for Q's A+ compliant promise to actually resolve
|
||||
setTimeout(function() {
|
||||
|
||||
users() // Hello
|
||||
users.then(function(value) {
|
||||
console.log(value) //Hello
|
||||
})
|
||||
|
||||
}, 1000)
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="serializing-getter-setters">Serializing getter-setters</h3>
|
||||
<p>Getter-setters are JSON-serializable:</p>
|
||||
<pre><code class="lang-javascript">var data = {foo: m.prop("bar")};
|
||||
JSON.stringify(data); // '{"foo": "bar"}'
|
||||
</code></pre>
|
||||
<p>This allows getter-setters to be passed directly as parameters to <a href="mithril.request.html"><code>m.request</code></a>, for example.</p>
|
||||
<hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">GetterSetter prop([any initialValue])
|
||||
|
||||
where:
|
||||
GetterSetter :: any getterSetter([any value])
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>any initialValue</strong> (optional)</p>
|
||||
<p>An initialization value. If not provided, the value of the getter-setter's internal store defaults to <code>undefined</code>.</p>
|
||||
</li>
|
||||
<li><p><strong>returns any getterSetter([any value])</strong></p>
|
||||
<p>A getter-setter method.</p>
|
||||
<ul>
|
||||
<li><p><strong>any value</strong> (optional)</p>
|
||||
<p>If provided, it updates the getter-setter's internal store to the provided value.</p>
|
||||
<p>If not provided, return the current internally stored value.</p>
|
||||
</li>
|
||||
<li><p><strong>returns any value</strong></p>
|
||||
<p>This method always returns the value of the internal store, regardless of whether it was updated or not.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
243
archive/v0.2.4/mithril.redraw.html
Normal file
243
archive/v0.2.4/mithril.redraw.html
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.redraw
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-redraw">m.redraw</h2>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#changing-redraw-strategy">Changing redraw strategy</a></li>
|
||||
<li><a href="#difference-between-computation-methods-and-m-redraw">Difference between computation methods and m.redraw</a></li>
|
||||
<li><a href="#preventing-redraws-on-events">Preventing redraws on events</a></li>
|
||||
<li><a href="#forcing-redraw">Forcing redraw</a></li>
|
||||
<li><a href="#signature">Signature</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>Redraws the view. Use <a href="mithril.mount.html"><code>m.mount()</code></a> or <a href="mithril.route.html"><code>m.route()</code></a> to activate a component.</p>
|
||||
<p>Calling <code>m.redraw</code> triggers a redraw regardless of whether AJAX requests (and other asynchronous services) are completed. Therefore, you should ensure that templates have null checks in place to account for the possibility of variables being uninitialized when the forced redraw occurs.</p>
|
||||
<p>If you are developing an asynchronous model-level service and finding that Mithril is not redrawing the view after your code runs, you should consider using <a href="mithril.computation.html"><code>m.startComputation</code> and <code>m.endComputation</code></a> to integrate with Mithril's auto-redrawing system instead.</p>
|
||||
<p>Assuming your templates have appropriate null checks in place, <code>m.redraw</code> is useful for transient DOM state such as loading indicators and to commit state to the DOM for the purposes of reading back computed values (for example, <code>offsetWidth</code> or <code>scrollHeight</code>)</p>
|
||||
<hr>
|
||||
<h3 id="difference-between-computation-methods-and-m-redraw">Difference between computation methods and m.redraw</h3>
|
||||
<p>The <a href="mithril.computation.html"><code>m.startComputation</code> / <code>m.endComputation</code> pair</a> is designed to be "stacked", i.e. multiple asynchronous services can each call this pair of functions to indicate that they want the redrawing algorithm to wait for them to finish before a redraw occurs. In contrast, <code>m.redraw</code> is "aggressive": it redraws as many times as it is called (with the caveat that redraws are batched if they occur less than one animation frame apart in time). In practice, this means that calling <code>m.redraw</code> may cause a redraw to happen before some AJAX calls have finished, which in turn, may cause null reference exceptions in templates that try to use the data from these requests without first checking that the data exists.</p>
|
||||
<p>Therefore, using the computation methods is recommended in order to reduce the amount of intermediate redraws that would otherwise occur as multiple asynchronous services are resolved.</p>
|
||||
<p>When computation methods are used dilligently and religiously, templates are never redrawn with incomplete data. However, it's important to always write conditional tests in templates to account for the possibility of nullables, because redraws may come to occur more aggressively than data is available (perhaps because a newly introduced 3rd party library calls <code>m.redraw</code>, or because you might want a more aggressive redraw policy to implement a specific feature down the road).</p>
|
||||
<p>Defending against nullables can typically be achieved via the <code>initialValue</code> option in <a href="mithril.request.html"><code>m.request</code></a> and basic null checks (e.g. <code>data ? m("div", data) : null</code>).</p>
|
||||
<hr>
|
||||
<h3 id="changing-redraw-strategy">Changing redraw strategy</h3>
|
||||
<p>If you need to change how Mithril performs a redraw, you can change the value of the <code>m.redraw.strategy</code> getter-setter to either <code>"all"</code>, <code>"diff"</code> or <code>"none"</code>. The new strategy will apply to the next scheduled redraw, if any. By default, Mithril sets this value to <code>"all"</code> before running controller constructors, and it sets it to <code>"diff"</code> before event handlers are triggered.</p>
|
||||
<p>After the redraw, Mithril resets the value of the flag to either "all" or "diff", depending on whether the redraw was due to a route change or not.</p>
|
||||
<p>Changing the flag outside of a redrawable context does nothing since the flag gets reset when entering one of the documented redrawable contexts above.</p>
|
||||
<p>When the flag is set to "all", Mithril throws away the current view and redraws from scratch. This is the default for going from one route to another.</p>
|
||||
<p>When the flag is set to "diff", Mithril performs a diff between the old view and the new view and applies patches to the DOM only where needed.</p>
|
||||
<p>When the flag is set to "none", Mithril skips the next redraw. You don't need to change this flag to something else again later, since Mithril does that for you.</p>
|
||||
<pre><code class="lang-javascript">var Component1 = {
|
||||
controller: function() {
|
||||
//this component will attempt to diff its template when routing, as opposed to re-creating the view from scratch.
|
||||
//this allows config contexts to live across route changes, if its element does not need to be recreated by the diff
|
||||
m.redraw.strategy("diff")
|
||||
},
|
||||
view: function() {
|
||||
return m("h1", {config: Component1.config}, "test") //assume all routes display the same thing
|
||||
},
|
||||
config: function(el, isInit, ctx) {
|
||||
if (!isInit) ctx.data = "foo" //we wish to initialize this only once, even if the route changes
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>Common reasons why one might need to change redraw strategy are:</p>
|
||||
<ul>
|
||||
<li><p>in order to prevent redraw when dealing with <code>keypress</code> events where the event's keyCode is not of interest</p>
|
||||
<pre><code class="lang-javascript">//model
|
||||
var saved = false
|
||||
function save(e) {
|
||||
if (e.keyCode == 13) {
|
||||
//this causes a redraw, since event handlers active auto-redrawing by default
|
||||
saved = true
|
||||
}
|
||||
else {
|
||||
//we don't care about other keys, so don't redraw
|
||||
m.redraw.strategy("none")
|
||||
}
|
||||
}
|
||||
|
||||
//view
|
||||
var view = function() {
|
||||
return m("div", [
|
||||
m("button[type=button]", {onkeypress: save}, "Save"),
|
||||
saved ? "Saved" : ""
|
||||
])
|
||||
}
|
||||
</code></pre>
|
||||
</li>
|
||||
<li><p>in order to avoid the full-page recreation when changing routes, for the sake of performance of global 3rd party components.</p>
|
||||
<pre><code class="lang-javascript">//diff when routing, instead of redrawing from scratch
|
||||
//this preserves the `<input>` element and its 3rd party plugin after route changes, since the `<input>` doesn't change
|
||||
var Component1 = {
|
||||
controller: function() {
|
||||
m.redraw.strategy("diff")
|
||||
},
|
||||
view: function() {
|
||||
return m("div", [
|
||||
m("h1", "Hello Foo"),
|
||||
m("input", {config: plugin}) //assuming `plugin` initializes a 3rd party library
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
var Component2 = {
|
||||
controller: function() {
|
||||
m.redraw.strategy("diff")
|
||||
},
|
||||
view: function() {
|
||||
return m("div", [
|
||||
m("h1", "Hello Bar"),
|
||||
m("input", {config: plugin}) //assuming `plugin` initializes a 3rd party library
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
m.route(document.body, "/foo", {
|
||||
"/foo": Component1,
|
||||
"/bar": Component2,
|
||||
})
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Note that the redraw strategy is a global setting that affects the entire template trees of all components on the page. In order to prevent redraws in <em>some parts</em> of an application, but not others, see <a href="mithril.render.html#subtree-directives">subtree directives</a></p>
|
||||
<p>You can also configure individual elements to always be diffed, instead of recreated from scratch (even across route changes), by using the <a href="mithril.html#persisting-dom-elements-across-route-changes"><code>ctx.retain</code> flag</a>. If you need to persist DOM state across route changes, it's recommended that you use the <code>ctx.retain</code> flag instead of <code>m.redraw.strategy("diff")</code>.</p>
|
||||
<hr>
|
||||
<h3 id="preventing-redraws-on-events">Preventing redraws on events</h3>
|
||||
<p>Sometimes you only care about a particular condition in an event and want the event to not trigger a redraw if this condition is not met.
|
||||
For example, you might only be interested in running a redraw if a user presses the space bar, and you might not want to waste a redraw if the user presses any other key. In that case, it's possible to skip redrawing altogether by calling <code>m.redraw.strategy("none")</code></p>
|
||||
<pre><code class="lang-javascript">m("input", {onkeydown: function(e) {
|
||||
if (e.keyCode == 13) vm.save() //do things and re-render only if the `enter` key was pressed
|
||||
else m.redraw.strategy("none") //otherwise, ignore
|
||||
}})
|
||||
</code></pre>
|
||||
<p>There are some important semantic caveats for <code>m.redraw.strategy("none")</code> that you should be aware of: Setting the strategy to <code>"none"</code> only affects <strong>synchronous</strong> redraws. As soon as the event handler returns, the strategy is set back to "diff".</p>
|
||||
<p>If you set strategy to <code>"none"</code> but then proceed to trigger a redraw asynchronously, either via <code>start/endComputation</code>, <code>m.redraw</code> or <code>m.request</code>, a redraw <em>will</em> occur, using the <code>"diff"</code> strategy.</p>
|
||||
<p>Additionally, calling <code>m.redraw</code> synchronously after calling <code>m.redraw.strategy("none")</code> resets the strategy to <code>"diff"</code>.</p>
|
||||
<p>Lastly, be aware that if a user action triggers more than one event handler (for example, oninput and onkeypress, or an event bubbling up to event handlers in multiple ancestor elements), every event triggers a redraw by default. Setting strategy to none in any one of those handlers will not affect the redrawing strategy of other handlers (and remember that <code>strategy("none")</code> has no effect on asynchronous redraws).</p>
|
||||
<hr>
|
||||
<h3 id="forcing-redraw">Forcing redraw</h3>
|
||||
<p>If you find yourself needing to redraw before the browsers normal redraw cycle, you can force a synchronous redraw by passing a boolean <code>true</code> as a parameter to <code>m.redraw</code>.</p>
|
||||
<pre><code class="lang-javascript">m.redraw(true) // force
|
||||
</code></pre>
|
||||
<p>Normally, you should only do this if you need to synchronously read a value from the DOM that requires a browser repaint (e.g. <code>offsetTop</code> or a CSS rule). If you need to read DOM values, try to read them all at once, because alternating reading and writing to the DOM causes multiple browser repaints, and repaints are expensive.</p>
|
||||
<hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">void redraw([Boolean forceSync]) { GetterSetter strategy }
|
||||
|
||||
where:
|
||||
GetterSetter :: String getterSetter([String value])
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>Boolean forceSync</strong> (optional)</p>
|
||||
<p>If set to true, forces the redraw to be synchronous. By default, event handlers schedule redraws to be done asynchronously in order to allow simultaneous events to run before redrawing (for example, the keypress and input are often used together for inputs). Defaults to <code>false</code></p>
|
||||
</li>
|
||||
<li><p><a name="strategy"></a></p>
|
||||
<h3 id="m-redraw-strategy">m.redraw.strategy</h3>
|
||||
<p><strong>GetterSetter strategy</strong></p>
|
||||
<p>The <code>m.redraw.strategy</code> getter-setter indicates how the next component redraw will occur. It can be one of three values:</p>
|
||||
<ul>
|
||||
<li><code>"all"</code> - recreates the DOM tree from scratch</li>
|
||||
<li><code>"diff"</code> - updates only DOM elements if needed</li>
|
||||
<li><code>"none"</code> - leaves the DOM tree intact</li>
|
||||
</ul>
|
||||
<p>This value can be programmatically changed in controllers and event handlers to modify the next redrawing strategy. It is modified internally by Mithril to the value <code>"all"</code> before running controller constructors, and to the value <code>"diff"</code> after all redraws.</p>
|
||||
<p>Calling this function without arguments returns the currently assigned redraw strategy.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
192
archive/v0.2.4/mithril.render.html
Normal file
192
archive/v0.2.4/mithril.render.html
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.render
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-render">m.render</h2>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#usage">Usage</a></li>
|
||||
<li><a href="#subtree-directives">Subtree directives</a></li>
|
||||
<li><a href="#signature">Signature</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>This method generates a DOM tree inside of a given HTML element.</p>
|
||||
<p>If the method is run more than once with the same root element, it diffs the new tree against the existing one and intelligently modifies only the portions that have changed.</p>
|
||||
<p>Note that, unlike many templating engines, this "smart diff" feature does not affect things like cursor placement in inputs and focus, and is therefore safe to call during user interactions. There are, however, some limitations to the diff algorithm that require you to add <a href="mithril.html#dealing-with-focus">key attributes</a> in some edge cases.</p>
|
||||
<hr>
|
||||
<h3 id="usage">Usage</h3>
|
||||
<p>Assuming a document has an empty <code><body></code> element, the code below:</p>
|
||||
<pre><code class="lang-javascript">var links = [
|
||||
{title: "item 1", url: "/item1"}
|
||||
];
|
||||
|
||||
m.render(document.body, [
|
||||
m("ul.nav", [
|
||||
m("li", links.map(function(link) {
|
||||
return m("a", {href: link.url, config: m.route}, link.title)
|
||||
}))
|
||||
])
|
||||
]);
|
||||
</code></pre>
|
||||
<p>yields:</p>
|
||||
<pre><code class="lang-markup"><body>
|
||||
<ul class="nav">
|
||||
<li>
|
||||
<a href="/item1">item 1</a>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="subtree-directives">Subtree Directives</h3>
|
||||
<p><code>m.render</code> accepts a special low level SubtreeDirective object as a node in a virtual DOM tree: if a tree contains a node that looks exactly like the object below, Mithril will abort the diff algorithm for that node. This allows you to implement optimizations that avoid creating virtual DOM trees in favor of their cached counterparts, if you know they have not changed between redraws. Note that using this feature is discouraged if you don't have visible performance problems.</p>
|
||||
<pre><code class="lang-javascript">{subtree: "retain"}
|
||||
</code></pre>
|
||||
<p>This mechanism is only intended to be used as a last resort optimization tool. If you do use it, you are responsible for determining what constitutes a scenario where the virtual DOM tree is changed/unchanged.</p>
|
||||
<p>The example below shows how to use a SubtreeDirective object to create a static header that doesn't incur diff costs once it has been rendered. This means that we are avoiding the creation of the header subtree (and therefore skipping the diff algorithm) altogether, but it also means that dynamic variables will NOT be updated within the header.</p>
|
||||
<pre><code class="lang-javascript">var app = {}
|
||||
|
||||
//here's an example plugin that determines whether data has changes.
|
||||
//in this case, it simply assumes data has changed the first time, and never changes after that.
|
||||
app.bindOnce = (function() {
|
||||
var cache = {}
|
||||
return function(view) {
|
||||
if (!cache[view.toString()]) {
|
||||
cache[view.toString()] = true
|
||||
return view()
|
||||
}
|
||||
else return {subtree: "retain"}
|
||||
}
|
||||
}())
|
||||
|
||||
//here's the view
|
||||
app.view = function() {
|
||||
return m(".layout", [
|
||||
app.bindOnce(function() {
|
||||
//this only runs once in order to boost performance
|
||||
//dynamic variables are not updated here
|
||||
return m("header", [
|
||||
m("h1", "this never changes")
|
||||
])
|
||||
}),
|
||||
//dynamic variables here still update on every redraw
|
||||
m("main", "rest of app goes here")
|
||||
])
|
||||
}
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">void render(DOMElement rootElement, Children children [, Boolean forceRecreation])
|
||||
|
||||
where:
|
||||
Children :: String text | VirtualElement virtualElement | SubtreeDirective directive | Array<Children children>
|
||||
VirtualElement :: Object { String tag, Attributes attributes, Children children }
|
||||
Attributes :: Object<Any | void config(DOMElement element)>
|
||||
SubtreeDirective :: Object { String subtree }
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>DOMElement rootElement</strong></p>
|
||||
<p>A DOM element which will contain the template represented by <code>children</code>.</p>
|
||||
</li>
|
||||
<li><p><strong>Children children</strong></p>
|
||||
<p>If this argument is a string, it will be rendered as a text node. To render a string as HTML, see <a href="mithril.trust.html"><code>m.trust</code></a></p>
|
||||
<p>If it's a VirtualElement, it will be rendered as a DOM Element.</p>
|
||||
<p>If it's a list, its contents will recursively be rendered as appropriate and appended as children of the <code>root</code> element.</p>
|
||||
</li>
|
||||
<li><p><strong>Boolean forceRecreation</strong></p>
|
||||
<p>If set to true, rendering a new virtual tree will completely overwrite an existing one without attempting to diff against it</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
607
archive/v0.2.4/mithril.request.html
Normal file
607
archive/v0.2.4/mithril.request.html
Normal file
|
|
@ -0,0 +1,607 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.request
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-request">m.request</h2>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#basic-usage">Basic usage</a></li>
|
||||
<li><a href="#processing-web-service-data">Processing-web-service-data</a></li>
|
||||
<li><a href="#bind-redirection-code">Bind redirection code</a></li>
|
||||
<li><a href="#binding-errors">Binding errors</a></li>
|
||||
<li><a href="#queuing-operations">Queuing operations</a></li>
|
||||
<li><a href="#casting-the-response-data-to-a-class">Casting the Response Data to a Class</a></li>
|
||||
<li><a href="#unwrapping-response-data">Unwrapping Response Data</a></li>
|
||||
<li><a href="#using-different-data-transfer-formats">Using Different Data Transfer Formats</a></li>
|
||||
<li><a href="#file-uploads-with-formdata">File uploads with FormData</a></li>
|
||||
<li><a href="#using-variable-data-formats">Using variable data formats</a></li>
|
||||
<li><a href="#extracting-metadata-from-the-response">Extracting Metadata from the Response</a></li>
|
||||
<li><a href="#custom-request-rejections">Custom request rejections</a></li>
|
||||
<li><a href="#configuring-the-underlying-xmlhttprequest">Configuring the underlying XMLHttpRequest</a></li>
|
||||
<li><a href="#aborting-a-request">Aborting a request</a></li>
|
||||
<li><a href="#using-json-p">Using JSON-P</a></li>
|
||||
<li><a href="#rendering-before-web-service-requests-finish">Rendering before web service requests finish</a></li>
|
||||
<li><a href="#signature">Signature</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>This is a high-level utility for working with web services, which allows writing asynchronous code relatively procedurally.</p>
|
||||
<p>By default, it assumes server responses are in JSON format and optionally instantiates a class with the response data.</p>
|
||||
<p>It provides a number of useful features out of the box:</p>
|
||||
<ul>
|
||||
<li>The ability to get an early reference to a container that will hold the asynchronous response</li>
|
||||
<li>The ability to queue operations to be performed after the asynchronous request completes</li>
|
||||
<li>The ability to "cast" the response to a class of your choice</li>
|
||||
<li>The ability to unwrap data in a response that includes metadata properties</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h3 id="basic-usage">Basic usage</h3>
|
||||
<p>The basic usage pattern for <code>m.request</code> returns an <a href="mithril.prop.html"><code>m.prop</code></a> getter-setter, which is populated when the AJAX request completes.</p>
|
||||
<p>The returned getter-setter can be thought of as a box: you can pass this reference around cheaply, and you can "unwrap" its value when needed.</p>
|
||||
<pre><code class="lang-javascript">var users = m.request({method: "GET", url: "/user"});
|
||||
|
||||
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
|
||||
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
|
||||
//i.e. users() //[{name: "John"}, {name: "Mary"}]
|
||||
</code></pre>
|
||||
<p>Note that this getter-setter holds an <em>undefined</em> value until the AJAX request completes. Attempting to unwrap its value early will likely result in errors.</p>
|
||||
<p>The returned getter-setter also implements the <a href="mithril.deferred.html">promise</a> interface (also known as a <em>thennable</em>): this is the mechanism you should always use to queue operations to be performed on the data from the web service.</p>
|
||||
<p>The simplest use case of this feature is to implement functional value assignment via <code>m.prop</code> (i.e. the same thing as above). You can bind a pre-existing getter-setter by passing it in as a parameter to a <code>.then</code> method:</p>
|
||||
<pre><code class="lang-javascript">var users = m.prop([]); //default value
|
||||
|
||||
m.request({method: "GET", url: "/user"}).then(users)
|
||||
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
|
||||
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
|
||||
//i.e. users() //[{name: "John"}, {name: "Mary"}]
|
||||
</code></pre>
|
||||
<p>This syntax allows you to bind intermediate results before piping them down for further processing, for example:</p>
|
||||
<pre><code class="lang-javascript">var users = m.prop([]); //default value
|
||||
var doSomething = function() { /*...*/ }
|
||||
|
||||
m.request({method: "GET", url: "/user"}).then(users).then(doSomething)
|
||||
</code></pre>
|
||||
<p>While both basic assignment syntax and thennable syntax can be used to the same effect, typically it's recommended that you use the assignment syntax whenever possible, as it's easier to read.</p>
|
||||
<p>The thennable mechanism is intended to be used in three ways:</p>
|
||||
<ul>
|
||||
<li>in the model layer: to process web service data in transformative ways (e.g. filtering a list based on a parameter that the web service doesn't support)</li>
|
||||
<li>in the controller layer: to bind redirection code upon a condition</li>
|
||||
<li>in the controller layer: to bind error messages</li>
|
||||
</ul>
|
||||
<h4 id="processing-web-service-data">Processing web service data</h4>
|
||||
<p>This step is meant to be done in the model layer. Doing it in the controller level is also possible, but philosophically not recommended, because by tying logic to a controller, the code becomes harder to reuse due to unrelated controller dependencies.</p>
|
||||
<p>In the example below, the <code>listEven</code> method returns a getter-setter that resolves to a list of users containing only users whose id is even.</p>
|
||||
<pre><code class="lang-javascript">//model
|
||||
var User = {}
|
||||
|
||||
User.listEven = function() {
|
||||
return m.request({method: "GET", url: "/user"}).then(function(list) {
|
||||
return list.filter(function(user) {return user.id % 2 == 0});
|
||||
});
|
||||
}
|
||||
|
||||
//controller
|
||||
var controller = function() {
|
||||
return {users: User.listEven()}
|
||||
}
|
||||
</code></pre>
|
||||
<h4 id="bind-redirection-code">Bind redirection code</h4>
|
||||
<p>This step is meant to be done in the controller layer. Doing it in the model level is also possible, but philosophically not recommended, because by tying redirection to the model, the code becomes harder to reuse due to overly tight coupling.</p>
|
||||
<p>In the example below, we use the previously defined <code>listEven</code> model method and queue a controller-level function that redirects to another page if the user list is empty.</p>
|
||||
<pre><code class="lang-javascript">//controller
|
||||
var controller = function() {
|
||||
return {
|
||||
users: User.listEven().then(function(users) {
|
||||
if (users.length == 0) m.route("/add");
|
||||
})
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h4 id="binding-errors">Binding errors</h4>
|
||||
<p>Mithril thennables take two functions as optional parameters: the first parameter is called if the web service request completes successfully. The second one is called if it completes with an error.</p>
|
||||
<p>Error binding is meant to be done in the controller layer. Doing it in the model level is also possible, but generally leads to more code in order to connect all the dots.</p>
|
||||
<p>In the example below, we bind an error getter-setter to our previous controller so that the <code>error</code> variable gets populated if the server throws an error.</p>
|
||||
<pre><code class="lang-javascript">//controller
|
||||
var controller = function() {
|
||||
this.error = m.prop("")
|
||||
|
||||
this.users = User.listEven().then(function(users) {
|
||||
if (users.length == 0) m.route("/add");
|
||||
}, this.error)
|
||||
}
|
||||
</code></pre>
|
||||
<p>If the controller doesn't already have a success callback to run after a request resolves, you can still bind errors like this:</p>
|
||||
<pre><code class="lang-javascript">//controller
|
||||
var controller = function() {
|
||||
this.error = m.prop("")
|
||||
|
||||
this.users = User.listEven().then(null, this.error)
|
||||
}
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="queuing-operations">Queuing Operations</h3>
|
||||
<p>As you saw, you can chain operations that act on the response data. Typically this is required in three situations:</p>
|
||||
<ul>
|
||||
<li>in model-level methods if client-side processing is needed to make the data useful for a controller or view.</li>
|
||||
<li>in the controller, to redirect after a model service resolves.</li>
|
||||
<li>in the controller, to bind error messages</li>
|
||||
</ul>
|
||||
<p>In the example below, we take advantage of queuing to debug the AJAX response data prior to doing further processing on the user list</p>
|
||||
<pre><code class="lang-javascript">//a FP-friendly console.log
|
||||
var log = function(value) {
|
||||
console.log(value)
|
||||
return value
|
||||
}
|
||||
|
||||
var users = m.request({method: "GET", url: "/user"})
|
||||
.then(log)
|
||||
.then(function(users) {
|
||||
//add one more user to the response
|
||||
return users.concat({name: "Jane"})
|
||||
})
|
||||
|
||||
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
|
||||
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
|
||||
//i.e. users() //[{name: "John"}, {name: "Mary"}, {name: "Jane"}]
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="casting-the-response-data-to-a-class">Casting the Response Data to a Class</h3>
|
||||
<p>It's possible to auto-cast a JSON response to a class. This is useful when we want to control access to certain properties in an object, as opposed to exposing all the fields in POJOs (plain old Javascript objects) for arbitrary processing.</p>
|
||||
<p>In the example below, <code>User.list</code> returns a list of <code>User</code> instances.</p>
|
||||
<pre><code class="lang-javascript">var User = function(data) {
|
||||
this.name = m.prop(data.name);
|
||||
}
|
||||
|
||||
User.list = function() {
|
||||
return m.request({method: "GET", url: "/user", type: User});
|
||||
}
|
||||
|
||||
var users = User.list();
|
||||
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
|
||||
//then when resolved (e.g. in a view), `users` will contain a list of User instances
|
||||
//i.e. users()[0].name() == "John"
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="unwrapping-response-data">Unwrapping Response Data</h3>
|
||||
<p>Often, web services return the relevant data wrapped in objects that contain metadata.</p>
|
||||
<p>Mithril allows you to unwrap the relevant data, by providing two callback hooks: <code>unwrapSuccess</code> and <code>unwrapError</code>.</p>
|
||||
<p>These hooks allow you to unwrap different parts of the response data depending on whether it succeed or failed.</p>
|
||||
<pre><code class="lang-javascript">var users = m.request({
|
||||
method: "GET",
|
||||
url: "/user",
|
||||
unwrapSuccess: function(response) {
|
||||
return response.data;
|
||||
},
|
||||
unwrapError: function(response) {
|
||||
return response.error;
|
||||
}
|
||||
});
|
||||
|
||||
//assuming the response is: `{data: [{name: "John"}, {name: "Mary"}], count: 2}`
|
||||
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
|
||||
//i.e. users() //[{name: "John"}, {name: "Mary"}]
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="using-different-data-transfer-formats">Using Different Data Transfer Formats</h3>
|
||||
<p>By default, <code>m.request</code> uses JSON to send and receive data to web services. You can override this by providing <code>serialize</code> and <code>deserialize</code> options:</p>
|
||||
<pre><code class="lang-javascript">var users = m.request({
|
||||
method: "GET",
|
||||
url: "/user",
|
||||
serialize: mySerializer,
|
||||
deserialize: myDeserializer
|
||||
});
|
||||
</code></pre>
|
||||
<p>One typical way to override this is to receive as-is responses. The example below shows how to receive a plain string from a txt file.</p>
|
||||
<pre><code class="lang-javascript">var file = m.request({
|
||||
method: "GET",
|
||||
url: "myfile.txt",
|
||||
deserialize: function(value) {return value;}
|
||||
});
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="file-uploads-with-formdata">File uploads with FormData</h3>
|
||||
<p>To use the HTML5 FormData object as the payload for a request, you need to override the <code>serialize</code> option. By default, <code>serialize</code> converts an object to JSON, but in the case of a FormData payload, you want to pass the object intact.</p>
|
||||
<pre><code class="lang-javascript">//assume the file comes from an HTML5 drag-n-drop event
|
||||
var file = e.dataTransfer.files[0]
|
||||
|
||||
var data = new FormData();
|
||||
data.append("file", file)
|
||||
|
||||
m.request({
|
||||
method: "POST",
|
||||
url: "/upload",
|
||||
serialize: function(data) {return data}
|
||||
})
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="using-variable-data-formats">Using variable data formats</h3>
|
||||
<p>By default, Mithril assumes both success and error responses are in JSON format, but some servers may not return JSON responses when returning HTTP error codes (e.g. 404)</p>
|
||||
<p>You can get around this issue by using <code>extract</code></p>
|
||||
<pre><code class="lang-javascript">var nonJsonErrors = function(xhr) {
|
||||
return xhr.status > 200 ? JSON.stringify(xhr.responseText) : xhr.responseText
|
||||
}
|
||||
|
||||
m.request({method: "GET", url: "/foo/bar.x", extract: nonJsonErrors})
|
||||
.then(function(data) {}, function(error) {console.log(error)})
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="extracting-metadata-from-the-response">Extracting Metadata from the Response</h3>
|
||||
<p>The <code>extract</code> method can be used to read metadata from HTTP response headers or the status field of an XMLHttpRequest.</p>
|
||||
<pre><code class="lang-javascript">var extract = function(xhr, xhrOptions) {
|
||||
if (xhrOptions.method == "HEAD") return xhr.getResponseHeader("x-item-count")
|
||||
else return xhr.responseText
|
||||
}
|
||||
|
||||
m.request({method: "POST", url: "/foo", extract: extract});
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="custom-request-rejections">Custom request rejections</h3>
|
||||
<p>If you want to be able to handle a condition as an error in a promise rejection handler, you can throw an <code>Error</code> from <code>extract</code> to reject the promise.</p>
|
||||
<p>This is useful, for example, if you received invalid JSON from the server in production and you want to display a message to the user saying that the server is offline.</p>
|
||||
<pre><code class="lang-javascript">var extract = function(xhr, xhrOptions) {
|
||||
try {
|
||||
return JSON.stringify(xhr.responseText)
|
||||
}
|
||||
catch (e) {
|
||||
//e instanceof SyntaxError == true
|
||||
//by default `e` would be caught by Mithril's promise exception monitor and rethrown to the console
|
||||
//this new error follows Promises/A+ specifications and triggers a rejection in the downstream promises without hitting the console.
|
||||
throw new Error("Server is offline")
|
||||
}
|
||||
}
|
||||
|
||||
m.request({method: "POST", url: "/foo", extract: extract});
|
||||
</code></pre>
|
||||
<p>You can read more about the <a href="mithril.deferred.html#unchecked-error-handling">promise exception monitor here</a>.</p>
|
||||
<hr>
|
||||
<h3 id="configuring-the-underlying-xmlhttprequest">Configuring the underlying XMLHttpRequest</h3>
|
||||
<p>The <code>config</code> option can be used to arbitrarily configure the native XMLHttpRequest instance and to access properties that would not be accessible otherwise.</p>
|
||||
<p>The example below shows how to configure a request where the server expects requests to have a <code>Content-Type: application/json</code> header</p>
|
||||
<pre><code class="lang-javascript">var xhrConfig = function(xhr) {
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
}
|
||||
|
||||
m.request({method: "POST", url: "/foo", config: xhrConfig});
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="aborting-a-request">Aborting a request</h3>
|
||||
<p>The <code>config</code> option can also be used to retrieve the <code>XMLHttpRequest</code> instance for aborting the request. This idiom can also be used to attach <code>onprogress</code> event handlers.</p>
|
||||
<pre><code class="lang-javascript">var transport = m.prop();
|
||||
|
||||
m.request({method: "POST", url: "/foo", config: transport});
|
||||
|
||||
//the `transport` getter-setter contains an instance of XMLHttpRequest
|
||||
transport().abort();
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="using-json-p">Using JSON-P</h3>
|
||||
<p>To make JSON-P requests, add the <code>dataType</code> option instead of <code>method</code>. You should not add the <code>callback</code> querystring parameter; Mithril already does that internally.</p>
|
||||
<pre><code class="lang-javascript">m.request({dataType: "jsonp", url: "/api/User"});
|
||||
</code></pre>
|
||||
<p>Some services (e.g. Flickr) don't follow the convention of calling the <code>callback</code> parameter <code>callback</code>. In order to specify the name of the querystring parameter that indicates the callback function, use the <code>callbackKey</code> option:</p>
|
||||
<pre><code class="lang-javascript">m.request({
|
||||
dataType: "jsonp",
|
||||
callbackKey: "jsoncallback",
|
||||
url: "http://api.flickr.com/services/feeds/photos_public.gne?tags=monkey&tagmode=any&format=json"
|
||||
});
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="rendering-before-web-service-requests-finish">Rendering before web service requests finish</h3>
|
||||
<p>By default, Mithril waits for web service requests to complete before attempting a redraw. This ensures that data being accessed in the view isn't nullable as a result of asynchronous data not being available yet.</p>
|
||||
<p>However, sometimes we do want to be able to redraw before a web service request completes, either because one web service out of many is slow, or because we don't need its response in order to redraw.</p>
|
||||
<p>Setting the <code>background</code> option to <code>true</code> prevents a request from affecting redrawing. This means it's possible for a view to attempt to use data before it is available. You can specify an initial value for the <code>m.request</code> getter-setter in order to avoid having to write defensive code against potential null reference exceptions:</p>
|
||||
<pre><code class="lang-javascript">var demo = {}
|
||||
|
||||
demo.controller = function() {
|
||||
return {
|
||||
users: m.request({method: "GET", url: "/api/user", background: true, initialValue: []})
|
||||
}
|
||||
}
|
||||
|
||||
//in the view
|
||||
demo.view = function(ctrl) {
|
||||
//This view gets rendered before the request above completes
|
||||
//Calling .map doesn't throw an error because we defined the initial value to be an empty array, instead of undefined
|
||||
return ctrl.users().map(function(user) {
|
||||
return m("div", user.name)
|
||||
})
|
||||
}
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">Promise request(Options options)
|
||||
|
||||
where:
|
||||
Promise :: GetterSetter { Promise then(any successCallback(any value), any errorCallback(any value)) }
|
||||
GetterSetter :: any getterSetter([any value])
|
||||
Options :: XHROptions | JSONPOptions
|
||||
XHROptions :: Object {
|
||||
String method,
|
||||
String url,
|
||||
[String user,]
|
||||
[String password,]
|
||||
[Object<any> data,]
|
||||
[Boolean background,]
|
||||
[any initialValue,]
|
||||
[any unwrapSuccess(any data, XMLHttpRequest xhr),]
|
||||
[any unwrapError(any data, XMLHttpRequest xhr),]
|
||||
[String serialize(any dataToSerialize),]
|
||||
[any deserialize(String dataToDeserialize),]
|
||||
[any extract(XMLHttpRequest xhr, XHROptions options),]
|
||||
[void type(Object<any> data),]
|
||||
[XMLHttpRequest? config(XMLHttpRequest xhr, XHROptions options)]
|
||||
}
|
||||
JSONPOptions :: Object {
|
||||
String dataType,
|
||||
String url,
|
||||
String callbackKey,
|
||||
Object<any> data
|
||||
}
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>XHROptions options</strong></p>
|
||||
<p>A map of options for the XMLHttpRequest</p>
|
||||
<ul>
|
||||
<li><p><strong>String method</strong></p>
|
||||
<p>The HTTP method. Must be either <code>"GET"</code>, <code>"POST"</code>, <code>"PUT"</code>, <code>"DELETE"</code>, <code>"HEAD"</code> or <code>"OPTIONS"</code></p>
|
||||
</li>
|
||||
<li><p><strong>String url</strong></p>
|
||||
<p>The URL to request. If the URL is not in the same domain as the application, the target server must be configured to accept cross-domain requests from the application's domain, i.e. its responses must include the header <code>Access-Control-Allow-Origin: *</code>.</p>
|
||||
</li>
|
||||
<li><p><strong>String user</strong> (optional)</p>
|
||||
<p>A user for HTTP authentication. Defaults to <code>undefined</code></p>
|
||||
</li>
|
||||
<li><p><strong>String password</strong> (optional)</p>
|
||||
<p>A password for HTTP authentication. Defaults to <code>undefined</code></p>
|
||||
</li>
|
||||
<li><p><strong>Object<any> data</strong> (optional)</p>
|
||||
<p>Data to be sent. It's automatically placed in the appropriate section of the request with the appropriate serialization based on <code>method</code></p>
|
||||
</li>
|
||||
<li><p><strong>Boolean background</strong> (optional)</p>
|
||||
<p>Determines whether the <code>m.request</code> can affect template rendering. Defaults to false.</p>
|
||||
<p>If this option is set to true, then the request does NOT call <a href="mithril.computation.html"><code>m.startComputation</code> / <code>m.endComputation</code></a> internally, and therefore the completion of the request does not trigger an update of the view, even if data has been changed. This option is useful for running operations in the background (i.e. without user intervention).</p>
|
||||
<p>In order to force a redraw after a background request, use <a href="mithril.redraw.html"><code>m.redraw</code></a>, or <code>m.startComputation</code> / <code>m.endComputation</code>.</p>
|
||||
<pre><code class="lang-javascript">var demo = {}
|
||||
|
||||
demo.controller = function() {
|
||||
var users = m.request({method: "GET", url: "/api/users", background: true, initialValue: []})
|
||||
users.then(m.redraw)
|
||||
return {users: users}
|
||||
}
|
||||
|
||||
demo.view = function(ctrl) {
|
||||
//this view renders twice (once immediately, and once after the request above completes)
|
||||
return m("div", [
|
||||
ctrl.users().map(function(user) {
|
||||
return m("div", user.name)
|
||||
})
|
||||
])
|
||||
}
|
||||
</code></pre>
|
||||
<p>It's strongly recommended that you set an <code>initialValue</code> option in ALL requests if you set the <code>background</code> option to true.</p>
|
||||
<p>When calling multiple background AJAX requests, it's recommended that you use <a href="mithril.sync.html"><code>m.sync</code></a> to batch redraw once at the end of all requests, as opposed to repeatedly redrawing after every request:</p>
|
||||
<pre><code class="lang-javascript">var demo = {}
|
||||
|
||||
demo.controller = function() {
|
||||
var users = m.request({method: "GET", url: "/api/users", background: true, initialValue: []})
|
||||
var projects = m.request({method: "GET", url: "/api/projects", background: true, initialValue: []})
|
||||
|
||||
m.sync([users, projects]).then(m.redraw)
|
||||
|
||||
return {users: users, projects: projects}
|
||||
}
|
||||
</code></pre>
|
||||
<p>Make sure to add null checks if your request value can be null</p>
|
||||
<pre><code class="lang-javascript">var demo = {}
|
||||
|
||||
demo.controller = function() {
|
||||
var user = m.request({method: "GET", url: "/api/users/1", background: true, initialValue: null})
|
||||
user.then(m.redraw)
|
||||
return {user: user}
|
||||
}
|
||||
|
||||
demo.view = function(ctrl) {
|
||||
return m("div", [
|
||||
//in the first redraw, there's no user, so ensure we don't throw an error
|
||||
ctrl.user ? ctrl.user.name : "no user"
|
||||
])
|
||||
}
|
||||
</code></pre>
|
||||
</li>
|
||||
<li><p><strong>any initialValue</strong> (optional)</p>
|
||||
<p>The value that populates the returned getter-setter before the request completes. This is useful when using the <code>background</code> option, in order to avoid the need for null checks in views that may be attempting to access the returned getter-setter before the asynchronous request resolves.</p>
|
||||
<p>It is strongly recommended that you always set this option to avoid future surprises.</p>
|
||||
</li>
|
||||
<li><p><strong>any unwrapSuccess(any data, XMLHttpRequest xhr)</strong> (optional)</p>
|
||||
<p>A preprocessor function to unwrap the data from a success response in case the response contains metadata wrapping the data.</p>
|
||||
<p>The default value (if this parameter is falsy) is the identity function <code>function(value) {return value}</code></p>
|
||||
<p>For example, if the response is <code>{data: [{name: "John"}, {name: "Mary"}]}</code> and the unwrap function is <code>function(response) {return response.data}</code>, then the response will be considered to be <code>[{name: "John"}, {name: "Mary"}]</code> when processing the <code>type</code> parameter</p>
|
||||
<ul>
|
||||
<li><p><strong>Object<any> | Array<any> data</strong></p>
|
||||
<p>The data to unwrap</p>
|
||||
</li>
|
||||
<li><p><strong>returns Object<any> | Array<any> unwrappedData</strong></p>
|
||||
<p>The unwrapped data</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><strong>any unwrapError(any data, XMLHttpRequest xhr)</strong> (optional)</p>
|
||||
<p>A preprocessor function to unwrap the data from an error response in case the response contains metadata wrapping the data.</p>
|
||||
<p>The default value (if this parameter is falsy) is the identity function <code>function(value) {return value}</code></p>
|
||||
<ul>
|
||||
<li><p><strong>Object<any> | Array<any> data</strong></p>
|
||||
<p>The data to unwrap</p>
|
||||
</li>
|
||||
<li><p><strong>returns Object<any> | Array<any> unwrappedData</strong></p>
|
||||
<p>The unwrapped data</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><strong>String serialize(any dataToSerialize)</strong> (optional)</p>
|
||||
<p>Method to use to serialize the request data</p>
|
||||
<p>The default value (if this parameter is falsy) is <code>JSON.stringify</code></p>
|
||||
<ul>
|
||||
<li><p><strong>any dataToSerialize</strong></p>
|
||||
<p>Data to be serialized</p>
|
||||
</li>
|
||||
<li><p><strong>returns String serializedData</strong></p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><strong>any deserialize(String dataToDeserialize)</strong> (optional)</p>
|
||||
<p>Method to use to deserialize the response data</p>
|
||||
<p>The default value (if this parameter is falsy) is <code>JSON.parse</code></p>
|
||||
<ul>
|
||||
<li><p><strong>String dataToDeserialize</strong></p>
|
||||
<p>Data to be deserialized</p>
|
||||
</li>
|
||||
<li><p><strong>returns any deserializedData</strong></p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><strong>any extract(XMLHttpRequest xhr, XHROptions options)</strong> (optional)</p>
|
||||
<p>Method to use to extract the data from the raw XMLHttpRequest. This is useful when the relevant data is either in a response header or the status field.</p>
|
||||
<p>If this parameter is falsy, the default value is a function that returns <code>xhr.responseText</code>.</p>
|
||||
</li>
|
||||
<li><p><strong>void type(Object<any> data)</strong> (optional)</p>
|
||||
<p>The response object (or the child items if this object is an Array) will be passed as a parameter to the class constructor defined by <code>type</code></p>
|
||||
<p>If this parameter is falsy, the deserialized data will not be wrapped.</p>
|
||||
<p>For example, if <code>type</code> is the following class:</p>
|
||||
<pre><code class="lang-javascript">var User = function(data) {
|
||||
this.name = m.prop(data.name);
|
||||
}
|
||||
</code></pre>
|
||||
<p>And the data is <code>[{name: "John"}, {name: "Mary"}]</code>, then the response will contain an array of two User instances.</p>
|
||||
</li>
|
||||
<li><p><strong>XMLHttpRequest? config(XMLHttpRequest xhr, XHROptions options)</strong> (optional)</p>
|
||||
<p>An initialization function that runs after <code>open</code> and before <code>send</code>. Useful for adding request headers and when using XHR2 features, such as the XMLHttpRequest's <code>upload</code> property.</p>
|
||||
<ul>
|
||||
<li><p><strong>XMLHttpRequest xhr</strong></p>
|
||||
<p>The XMLHttpRequest instance.</p>
|
||||
</li>
|
||||
<li><p><strong>XHROptions options</strong></p>
|
||||
<p>The <code>options</code> parameter that was passed into <code>m.request</code> call</p>
|
||||
</li>
|
||||
<li><p><strong>returns XMLHttpRequest? xhr</strong></p>
|
||||
<p>You may return an XHR-like object (e.g. a XDomainRequest instance) to override the provided XHR instance altogether.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><strong>returns Promise promise</strong></p>
|
||||
<p>returns a promise that can bind callbacks which get called on completion of the AJAX request.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><p><strong>JSONPOptions options</strong></p>
|
||||
<p>A map of options for JSONP requests</p>
|
||||
<ul>
|
||||
<li><p><strong>String dataType</strong></p>
|
||||
<p>Must be the string "jsonp"</p>
|
||||
</li>
|
||||
<li><p><strong>String url</strong></p>
|
||||
<p>The URL to request. If the URL is not in the same domain as the application, the target server must be configured to accept cross-domain requests from the application's domain, i.e. its responses must include the header <code>Access-Control-Allow-Origin: *</code>.</p>
|
||||
</li>
|
||||
<li><p><strong>String callbackKey</strong></p>
|
||||
<p>The name of the querystring key that defines the name of the callback function to be called by the response. Defaults to "callback"</p>
|
||||
<p>This option is useful for web services that use uncommon conventions for defining jsonp callbacks (e.g. foo.com/?jsonpCallback=doSomething)</p>
|
||||
</li>
|
||||
<li><p><strong>Object<any> data</strong> (optional)</p>
|
||||
<p>Data to be sent. It's automatically placed in the appropriate section of the request with the appropriate serialization based on <code>method</code></p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
392
archive/v0.2.4/mithril.route.html
Normal file
392
archive/v0.2.4/mithril.route.html
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.route
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-route">m.route</h2>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="#defining-routes">Defining routes</a></li>
|
||||
<li><a href="#variadic-routes">Variadic routes</a></li>
|
||||
<li><a href="#routes-with-querystrings">Routes with querystrings</a></li>
|
||||
<li><a href="#running-clean-up-code-on-route-change">Running clean up code on route change</a></li>
|
||||
<li><a href="#redirecting">Redirecting</a></li>
|
||||
<li><a href="#reading-the-currently-active-route">Reading the currently active route</a></li>
|
||||
<li><a href="#mode-abstraction">Mode abstraction</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>Routing is a system that allows creating Single-Page-Applications (SPA), i.e. applications that can go from a page to another without causing a full browser refresh.</p>
|
||||
<p>It enables seamless navigability while preserving the ability to bookmark each page individually, and the ability to navigate the application via the browser's history mechanism.</p>
|
||||
<p>This method overloads four different units of functionality:</p>
|
||||
<ul>
|
||||
<li><p><code>m.route(rootElement, defaultRoute, routes)</code> - defines the available URLs in an application, and their respective components</p>
|
||||
</li>
|
||||
<li><p><code>m.route(path)</code> - redirects to another route</p>
|
||||
</li>
|
||||
<li><p><code>m.route()</code> - returns the currently active route</p>
|
||||
</li>
|
||||
<li><p><code>m.route(element)</code> - an extension to link elements that unobtrusively abstracts away the routing mode</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Routing is single-page-application (SPA) friendly, and can be implemented using either <code>location.hash</code>, HTML5 URL rewriting or <code>location.querystring</code>. See <a href="#mode"><code>m.route.mode</code></a> for the caveats of each implementation.</p>
|
||||
<hr>
|
||||
<h3 id="defining-routes">Defining routes</h3>
|
||||
<h4 id="usage">Usage</h4>
|
||||
<p>To define a list of routes, you need to specify a host DOM element, a default route and a key-value map of possible routes and respective <a href="mithril.component.html">components</a> to be rendered. You don't need to call <a href="mithril.mount.html"><code>m.mount</code></a> to initialize your components if you define a list of routes - <code>m.route</code> calls it for you.</p>
|
||||
<p>The example below defines three routes, to be rendered in <code><body></code>. <code>Home</code>, <code>Login</code> and <code>Dashboard</code> are components. We'll see how to define a component in a bit.</p>
|
||||
<pre><code class="lang-javascript">m.route(document.body, "/", {
|
||||
"/": Home,
|
||||
"/login": Login,
|
||||
"/dashboard": Dashboard,
|
||||
});
|
||||
</code></pre>
|
||||
<p>Routes can take arguments, by prefixing words with a colon <code>:</code></p>
|
||||
<p>The example below shows a route that takes an <code>userID</code> parameter</p>
|
||||
<pre><code class="lang-javascript">//a sample component
|
||||
var Dashboard = {
|
||||
controller: function() {
|
||||
return {id: m.route.param("userID")}
|
||||
},
|
||||
view: function(controller) {
|
||||
return m("div", controller.id);
|
||||
}
|
||||
}
|
||||
|
||||
//setup routes to start w/ the `#` symbol
|
||||
m.route.mode = "hash";
|
||||
|
||||
//define a route
|
||||
m.route(document.body, "/dashboard/johndoe", {
|
||||
"/dashboard/:userID": Dashboard
|
||||
});
|
||||
</code></pre>
|
||||
<p>This redirects to the URL <code>http://server/#/dashboard/johndoe</code> and yields:</p>
|
||||
<pre><code class="lang-markup"><body><div>johndoe</div></body>
|
||||
</code></pre>
|
||||
<p>Above, <code>dashboard</code> is a component. It contains a <code>controller</code> and a <code>view</code> properties. When the URL matches a route, the respective component's controller is instantiated and passed as a parameter to the view.</p>
|
||||
<p>In this case, since there's only one route, the app redirects to the default route <code>"/dashboard/johndoe"</code>.</p>
|
||||
<p>The string <code>johndoe</code> is bound to the <code>:userID</code> parameter, which can be retrieved programmatically in the controller via <code>m.route.param("userID")</code>.</p>
|
||||
<p>The <code>m.route.mode</code> defines which part of the URL to use for routing.</p>
|
||||
<hr>
|
||||
<h4 id="variadic-routes">Variadic routes</h4>
|
||||
<p>We can append an ellipsis (<code>...</code>) to the name of a route argument to allow it to match URL snippets that contain slashes:</p>
|
||||
<pre><code class="lang-javascript">m.route(document.body, "/files/pictures/pic1.jpg", {
|
||||
"/files/:file...": gallery
|
||||
});
|
||||
|
||||
m.route.param("file") === "pictures/pic1.jpg"
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">m.route(document.body, "/blog/2014/01/20/articles", {
|
||||
"/blog/:date.../articles": articleList
|
||||
});
|
||||
|
||||
m.route.param("date") === "2014/01/20"
|
||||
</code></pre>
|
||||
<p>Note that Mithril checks for route matches in the order the routes are defined, so you should put variadic routes at the bottom of the list to prevent them from matching other more specific routes.</p>
|
||||
<pre><code>m.route(document.body, "/blog/archive/2014", {
|
||||
"/blog/:date...": Component1, //for the default path in the line above, this route matches first!
|
||||
"/blog/archive/:year": Component2
|
||||
});
|
||||
|
||||
m.route.param("date") === "archive/2014"
|
||||
|
||||
//the routes should be flipped around to get `m.route.param("year") == "2014"`
|
||||
</code></pre><hr>
|
||||
<h4 id="routes-with-querystrings">Routes with querystrings</h4>
|
||||
<p>In addition to route parameters, it's possible to pass arbitrary data to <code>m.route.param</code> using the querystring</p>
|
||||
<pre><code class="lang-javascript">m.route("/grid?sortby=date&dir=desc")
|
||||
|
||||
var sortBy = m.route.param("sortby") // "date"
|
||||
var dir = m.route.param("dir") // "desc"
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h4 id="running-clean-up-code-on-route-change">Running clean up code on route change</h4>
|
||||
<p>If a component's controller implements an instance method called <code>onunload</code>, this method will be called when a route changes.</p>
|
||||
<pre><code class="lang-javascript">var Home = {
|
||||
controller: function() {
|
||||
return {
|
||||
onunload: function() {
|
||||
console.log("unloading home component");
|
||||
}
|
||||
};
|
||||
},
|
||||
view: function() {
|
||||
return m("div", "Home")
|
||||
}
|
||||
};
|
||||
|
||||
var Dashboard = {
|
||||
controller: function() {},
|
||||
view: function() {}
|
||||
};
|
||||
|
||||
//go to the default route (home)
|
||||
m.route(document.body, "/", {
|
||||
"/": Home,
|
||||
"/dashboard": Dashboard,
|
||||
});
|
||||
|
||||
//re-route to dashboard
|
||||
m.route("/dashboard"); // logs "unloading home component"
|
||||
</code></pre>
|
||||
<p>This mechanism is useful to clear timers and unsubscribe event handlers. If you have a hierarchy of components, you can recursively call <code>onunload</code> on all the components in the tree or use a <a href="http://microjs.com/#pubsub">pubsub</a> library to unload specific components on demand.</p>
|
||||
<hr>
|
||||
<h4 id="signature">Signature</h4>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">void route(DOMElement rootElement, String defaultRoute, Object<Component> routes) { String mode, String param(String key), String buildQueryString(Object data), Object parseQueryString(String data) }
|
||||
|
||||
where:
|
||||
Component :: Object { void controller(), void view(Object controllerInstance) }
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>DOMElement root</strong></p>
|
||||
<p>A DOM element which will contain the view's template.</p>
|
||||
</li>
|
||||
<li><p><strong>String defaultRoute</strong></p>
|
||||
<p>The route to redirect to if the current URL does not match any of the defined routes</p>
|
||||
</li>
|
||||
<li><p><strong>Object<Component> routes</strong></p>
|
||||
<p>A key-value map of possible routes and their respective components. Keys are expected to be absolute pathnames, but can include dynamic parameters. Dynamic parameters are words preceded by a colon <code>:</code></p>
|
||||
<p><code>{'/path/to/page/': pageComponent}</code> - a route with a basic pathname</p>
|
||||
<p><code>{'/path/to/page/:id': pageComponent}</code> - a route with a pathname that contains a dynamic parameter called <code>id</code>. This route would be selected if the URL was <code>/path/to/page/1</code>, <code>/path/to/page/test</code>, etc</p>
|
||||
<p><code>{'/user/:userId/book/:bookId': userBookComponent}</code> - a route with a pathname that contains two parameters</p>
|
||||
<p>Dynamic parameters are wild cards that allow selecting a component based on a URL pattern. The values that replace the dynamic parameters in a URL are available via <code>m.route.param()</code></p>
|
||||
<p>Note that the URL component used to resolve routes is dependent on <code>m.route.mode</code>. By default, the querystring is considered the URL component to test against the routes collection</p>
|
||||
<p>If the current page URL matches a route, its respective component is activated. See <a href="mithril.component.html"><code>m.component</code></a> for information on components.</p>
|
||||
</li>
|
||||
<li><p><a name="mode"></a></p>
|
||||
<h4 id="m-route-mode">m.route.mode</h4>
|
||||
<p><strong>String mode</strong></p>
|
||||
<p>The <code>m.route.mode</code> property defines which URL portion is used to implement the routing mechanism. Its value can be set to either "search", "hash" or "pathname". Default value is "search". Note that if you're changing this configuration value, you should change it <strong>before</strong> calling <code>m.route</code>.</p>
|
||||
<ul>
|
||||
<li><p><code>search</code> mode uses the querystring (i.e. <code>?</code>). This allows named anchors (i.e. <code><a href="#top">Back to top</a></code>, <code><a name="top"></a></code>) to work on the page, but routing changes causes page refreshes in IE8, due to its lack of support for <code>history.pushState</code>.</p>
|
||||
<p>Example URL: <code>http://server/?/path/to/page</code></p>
|
||||
</li>
|
||||
<li><p><code>hash</code> mode uses the hash (i.e. <code>#</code>). It's the only mode in which routing changes do not cause page refreshes in any browser. However, this mode does not support named anchors.</p>
|
||||
<p>Example URL: <code>http://server/#/path/to/page</code></p>
|
||||
</li>
|
||||
<li><p><code>pathname</code> mode allows routing URLs that contains no special characters, however this mode requires server-side setup in order to support bookmarking and page refreshes. It always causes page refreshes in IE8.</p>
|
||||
<p>Example URL: <code>http://server/path/to/page</code></p>
|
||||
<p>The simplest server-side setup possible to support pathname mode is to serve the same content regardless of what URL is requested. In Apache, this URL rewriting can be achieved using ModRewrite.</p>
|
||||
<p>Note that in order to use the <code>pathname</code> mode, the application must be run from the root URL.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><a name="param"></a></p>
|
||||
<h4 id="m-route-param">m.route.param</h4>
|
||||
<p><strong>String param(String key)</strong></p>
|
||||
<p>Route parameters are dynamic values that can be extracted from the URL based on the signature of the currently active route.</p>
|
||||
<p>A route without parameters looks like this:</p>
|
||||
<p><code>"/path/to/page/"</code></p>
|
||||
<p>A route with parameters might look like this:</p>
|
||||
<p><code>"/path/to/page/:id"</code> - here, <code>id</code> is the name of the route parameter</p>
|
||||
<p>If the currently active route is <code>/dashboard/:userID</code> and the current URL is <code>/dashboard/johndoe</code>, then calling <code>m.route.param("userID")</code> returns <code>"johndoe"</code></p>
|
||||
<p>Querystring parameters in a route are also available in this collection automatically.</p>
|
||||
<p><code>"/grid?sortby=date"</code> - here, <code>m.route.param("sortby")</code> returns <code>"date"</code></p>
|
||||
<ul>
|
||||
<li><p><strong>String key</strong></p>
|
||||
<p>The name of a route parameter.</p>
|
||||
</li>
|
||||
<li><p><strong>returns String value</strong></p>
|
||||
<p>The value that maps to the parameter specified by <code>key</code></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p><strong>Object param()</strong></p>
|
||||
<ul>
|
||||
<li><p><strong>returns Object params</strong></p>
|
||||
<p>An object containing all the route parameters</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><a name="buildQueryString"></a></p>
|
||||
<h4 id="m-route-buildquerystring">m.route.buildQueryString</h4>
|
||||
<p><strong>String buildQueryString(Object data)</strong></p>
|
||||
<p>Serializes an object into its URI encoded querystring representation, following the same serialization conventions as <a href="https://medialize.github.io/URI.js/">URI.js</a></p>
|
||||
<ul>
|
||||
<li><p><strong>Object data</strong></p>
|
||||
<p>An object to be serialized</p>
|
||||
</li>
|
||||
<li><p><strong>returns String querystring</strong></p>
|
||||
<p>The serialized representation of the input data</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><a name="parseQueryString"></a></p>
|
||||
<h4 id="m-route-parsequerystring">m.route.parseQueryString</h4>
|
||||
<p><strong>Object parseQueryString(String querystring)</strong></p>
|
||||
<p>Deserializes an object from an URI encoded querystring representation, following the same deserialization conventions as <a href="https://medialize.github.io/URI.js/">URI.js</a></p>
|
||||
<ul>
|
||||
<li><p><strong>String querystring</strong></p>
|
||||
<p>An URI encoded querystring to be deserialized</p>
|
||||
</li>
|
||||
<li><p><strong>returns Object data</strong></p>
|
||||
<p>The deserialized object</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a name="redirecting"></a></p>
|
||||
<h3 id="redirecting">Redirecting</h3>
|
||||
<h4 id="usage">Usage</h4>
|
||||
<p>You can programmatically redirect to another page. Given the example in the "Defining Routes" section:</p>
|
||||
<pre><code class="lang-javascript">m.route("/dashboard/marysue");
|
||||
</code></pre>
|
||||
<p>redirects to <code>http://server/#/dashboard/marysue</code></p>
|
||||
<hr>
|
||||
<h4 id="signature">Signature</h4>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">void route(String path [, any params] [, Boolean shouldReplaceHistory])
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>String path</strong></p>
|
||||
<p>The route to redirect to. Note that to redirect to a different page outside of the scope of Mithril's routing, you should use <code>window.location</code></p>
|
||||
</li>
|
||||
<li><p><strong>any params</strong></p>
|
||||
<p>Parameters to pass as a querystring</p>
|
||||
</li>
|
||||
<li><p><strong>Boolean shouldReplaceHistory</strong></p>
|
||||
<p>If set to true, replaces the current history entry, instead of adding a new one. Defaults to false.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a name="reading-current-route"></a></p>
|
||||
<h3 id="reading-the-currently-active-route">Reading the currently active route</h3>
|
||||
<h4 id="usage">Usage</h4>
|
||||
<p>Mithril updates the native <code>location</code> object after rendering in order to allow the browser's <code>history.pushState</code> API to correctly show descriptive history entries (e.g. for Chrome's Ctrl+H page).</p>
|
||||
<p>In order to retrieve the currently active route in a controller, you can use <code>m.route()</code>. This returns the portion of the URL determined by <code>m.route.mode</code> (minus the <code>?</code> or <code>#</code> symbols for the <code>search</code> and <code>hash</code> modes, respectively).</p>
|
||||
<pre><code class="lang-javascript">//if the location bar is "http://example.com/?/foo/bar"
|
||||
//and m.route.mode is `search`
|
||||
//then `currentRoute == "/foo/bar"`
|
||||
var currentRoute = m.route();
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h4 id="signature">Signature</h4>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">String route()
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>returns String route</strong></p>
|
||||
<p>returns the currently active route</p>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a name="mode-abstraction"></a></p>
|
||||
<h3 id="mode-abstraction">Mode abstraction</h3>
|
||||
<h4 id="usage">Usage</h4>
|
||||
<p>This method is meant to be used with a virtual element's <code>config</code> attribute. For example:</p>
|
||||
<pre><code class="lang-javascript">//Note that the '#' is not required in `href`, thanks to the `config` setting.
|
||||
m("a[href='/dashboard/alicesmith']", {config: m.route});
|
||||
</code></pre>
|
||||
<p>This makes the href behave correctly regardless of which <code>m.route.mode</code> is selected. It's a good practice to always use the idiom above, instead of hardcoding <code>?</code> or <code>#</code> in the href attribute.</p>
|
||||
<p>See <a href="mithril.html"><code>m()</code></a> for more information on virtual elements.</p>
|
||||
<hr>
|
||||
<h4 id="signature">Signature</h4>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">void route(DOMElement element, Boolean isInitialized, Object context, Object vdom)
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>DOMElement element</strong></p>
|
||||
<p>an anchor element <code><a></code> with an <code>href</code> attribute that points to a route</p>
|
||||
</li>
|
||||
<li><p><strong>Boolean isInitialized</strong></p>
|
||||
<p>the method does not run if this flag is set to true. This is to make the method compatible with virtual DOM elements' <code>config</code> attribute (see <a href="mithril"><code>m()</code></a>)</p>
|
||||
</li>
|
||||
<li><p><strong>Object context</strong></p>
|
||||
<p>an object that retains its state across redraws</p>
|
||||
</li>
|
||||
<li><p><strong>Object vdom</strong></p>
|
||||
<p>The virtual DOM data structure to which the config is applied to</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
133
archive/v0.2.4/mithril.sync.html
Normal file
133
archive/v0.2.4/mithril.sync.html
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.sync
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-sync">m.sync</h2>
|
||||
<p>This method takes a list of promises and returns a promise that resolves when all promises in the input list have resolved. See <a href="mithril.deferred.html"><code>m.deferred</code></a> for more information on promises.</p>
|
||||
<hr>
|
||||
<h3 id="usage">Usage</h3>
|
||||
<pre><code class="lang-javascript">var greetAsync = function(delay) {
|
||||
var deferred = m.deferred();
|
||||
setTimeout(function() {
|
||||
deferred.resolve("hello");
|
||||
}, delay);
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
m.sync([
|
||||
greetAsync(1000),
|
||||
greetAsync(1500)
|
||||
]).then(function(args) {
|
||||
console.log(args); // ["hello", "hello"]
|
||||
});
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">Promise sync(Array<Promise> promises)
|
||||
|
||||
where:
|
||||
Promise :: GetterSetter { Promise then(any successCallback(any value), any errorCallback(any value)) }
|
||||
GetterSetter :: any getterSetter([any value])
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>Array<Promise> promises</strong></p>
|
||||
<p>A list of promises to synchronize</p>
|
||||
</li>
|
||||
<li><p><strong>return Promise promise</strong></p>
|
||||
<p>The promise of the deferred object that is resolved when all input promises have been resolved</p>
|
||||
<p>The callbacks for this promise receive as a parameter an Array containing the values of all the input promises</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
143
archive/v0.2.4/mithril.trust.html
Normal file
143
archive/v0.2.4/mithril.trust.html
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.trust
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-trust">m.trust</h2>
|
||||
<p>If you're writing a template for a view, use <code>m()</code> instead.</p>
|
||||
<p>This method flags a string as trusted HTML.</p>
|
||||
<p>Trusted HTML is allowed to render arbitrary, potentially invalid markup, as well as run arbitrary Javascript, and therefore the developer is responsible for either:</p>
|
||||
<ul>
|
||||
<li><p>sanitizing the markup contained in the string, or</p>
|
||||
</li>
|
||||
<li><p>acknowledging that the string is authorized to run any code that may be contained within it.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Note that browsers ignore <code><script></code> tags that have been inserted into the DOM via innerHTML. They do this because once the element is ready (and thus, has an accessible <code>innerHTML</code> property), their rendering engines cannot backtrack to the parsing-stage if the script calls something like <code>document.write("</body>")</code>.</p>
|
||||
<p>For this reason, <code>m.trust</code> will not auto-run <code><script></code> tags from trusted strings.</p>
|
||||
<p>Browsers do, however, allow scripts to be run asynchronously via a number of execution points, such as the <code>onload</code> or <code>onerror</code> attributes in <code><img></code> and <code><iframe></code>.</p>
|
||||
<p>IE also allows running of Javascript via CSS behaviors in <code><link></code>/<code><style></code> tags and <code>style</code> attributes.</p>
|
||||
<p>It's worth noting that the execution points listed above are commonly used for security attacks in combination with malformed markup, e.g. strings with mismatched attribute quotes like <code>" onload="alert(1)</code>.</p>
|
||||
<p>Mithril templates are defended against these attacks by default, except when markup is injected via <code>m.trust</code>.</p>
|
||||
<p>It is the developer's responsibility to ensure the input to <code>m.trust</code> cannot be maliciously modified by user-entered data.</p>
|
||||
<hr>
|
||||
<h3 id="usage">Usage</h3>
|
||||
<pre><code class="lang-javascript">//assume this content comes from the server
|
||||
var content = "<h1>Error: invalid user</h1>";
|
||||
|
||||
m.render("body", [
|
||||
m("div", m.trust(content))
|
||||
]);
|
||||
</code></pre>
|
||||
<p>yields:</p>
|
||||
<pre><code class="lang-markup"><body>
|
||||
<div>
|
||||
<h1>Error: invalid user</h1>
|
||||
</div>
|
||||
</body>
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">String trust(String html)
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>String html</strong></p>
|
||||
<p>A string containing HTML markup</p>
|
||||
</li>
|
||||
<li><p><strong>returns String trustedHtml</strong></p>
|
||||
<p>The returned string is a String object instance (as opposed to a string primitive) containing the same HTML content, and exposing a flag property for internal use within Mithril. Do not create or manipulate trust flags manually.</p>
|
||||
<p>Also note that concatenating or splitting a trusted string removes the trust flag. If doing such operations, the final string needs to be flagged as trusted.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
151
archive/v0.2.4/mithril.withAttr.html
Normal file
151
archive/v0.2.4/mithril.withAttr.html
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>m.withAttr
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="m-withattr">m.withAttr</h2>
|
||||
<p>This is an event handler factory. It returns a method that can be bound to a DOM element's event listener.</p>
|
||||
<p>Typically, it's used in conjunction with <a href="mithril.prop.html"><code>m.prop</code></a> to implement data binding in the view-to-model direction.</p>
|
||||
<p>This method is provided to decouple the browser's event model from the controller/logic model.</p>
|
||||
<p>You should use this method and implement similar ones when extracting values from a browser's Event object, instead of hard-coding the extraction code into controllers (or model methods).</p>
|
||||
<hr>
|
||||
<h3 id="usage">Usage</h3>
|
||||
<pre><code class="lang-javascript">//standalone usage
|
||||
document.body.onclick = m.withAttr("title", function(value) {
|
||||
//alerts the title of the body element when it's clicked
|
||||
alert(value);
|
||||
})
|
||||
</code></pre>
|
||||
<p>A contrived example of bi-directional data binding</p>
|
||||
<pre><code class="lang-javascript">var user = {
|
||||
model: function(name) {
|
||||
this.name = m.prop(name);
|
||||
},
|
||||
controller: function() {
|
||||
return {user: new user.model("John Doe")};
|
||||
},
|
||||
view: function(controller) {
|
||||
m.render("body", [
|
||||
m("input", {onchange: m.withAttr("value", controller.user.name), value: controller.user.name()})
|
||||
]);
|
||||
}
|
||||
};
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="signature">Signature</h3>
|
||||
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
|
||||
<pre><code class="lang-clike">EventHandler withAttr(String property, void callback(any value) [, any callbackThis])
|
||||
|
||||
where:
|
||||
EventHandler :: void handler(Event e)
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><p><strong>String property</strong></p>
|
||||
<p>Defines the property of the DOM element whose value will be passed to the callback.</p>
|
||||
</li>
|
||||
<li><p><strong>void callback(any value)</strong></p>
|
||||
<p>This function will be called with the value of the defined property as an argument.</p>
|
||||
<ul>
|
||||
<li><p><strong>any value</strong></p>
|
||||
<p>This is the value of the defined DOM element's property.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><strong>any callbackThis</strong></p>
|
||||
<p>The object which the <code>this</code> keyword points to for the callback</p>
|
||||
</li>
|
||||
<li><p><strong>returns EventHandler handler</strong></p>
|
||||
<p>This handler method can be assigned to properties like <code>onclick</code>, or passed as callbacks to <code>addEventListener</code>.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
96
archive/v0.2.4/mithril.xhr.html
Normal file
96
archive/v0.2.4/mithril.xhr.html
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
90
archive/v0.2.4/optimizing-performance.html
Normal file
90
archive/v0.2.4/optimizing-performance.html
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Optimizing Performance
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="optimizing-performance">Optimizing Performance</h2>
|
||||
<p>There's a number of ways to improve Mithril performance for the rare cases where pages are too complex for their own good.</p>
|
||||
<p>First and foremost, you should think hard about whether performance optimization is truly your last resort. By nature, optimizing performance make aggressive assumptions that can break in edge cases and it yields difficult to understand code. As a rule of thumb, you should <em>never</em> implement performance optimizations if another solution is available.</p>
|
||||
<p>For example, if you are building a table with thousands of rows and finding that the template is slow, you should first consider making the table show less items, because from a user experience perspective, no one is realistically going to scan through thousands of records. The effort to make the table paginated, searchable or filterable can improve the user experience in addition to solving the performance problem both on redraws and on initial page load.</p>
|
||||
<hr>
|
||||
<h2 id="compiling-templates">Compiling templates</h2>
|
||||
<p>You can optionally pre-compile templates that use <code>m()</code> by using <a href="https://github.com/tivac/mithril-objectify/">mithril-objectify</a>. This step isn't required in order to use Mithril, but it's an easy way to squeeze a little bit more performance out of an application, without the need for code changes.</p>
|
||||
<p>Compiling a template transforms the nested function calls of a template into a raw virtual DOM tree (which is merely a collection of native Javascript objects that is ready to be rendered via <a href="mithril.render.html"><code>m.render</code></a>). This means that compiled templates don't need to parse the string in <code>m("div#foo")</code> and they don't incur the cost of the function call.</p>
|
||||
<p>It's worth mentioning that Mithril has built-in mechanisms elsewhere that take care of real bottlenecks like browser repaint management and DOM updating. This optional compilation tool is merely "icing on the cake" that speeds up the Javascript run-time of templates (which is already fast, even without compilation - see the <a href="http://lhorie.github.io/mithril/index.html#performance">performance section on the homepage</a>).</p>
|
||||
<p>The tool takes regular Mithril templates like the one below:</p>
|
||||
<pre><code class="lang-javascript">var view = function() {
|
||||
return m("a", {href: "http://google.com"}, "test");
|
||||
}
|
||||
</code></pre>
|
||||
<p>It pre-processes the <code>m()</code> call and replaces it with its output:</p>
|
||||
<pre><code class="lang-javascript">var view = function() {
|
||||
return {tag: "a", attrs: {href: "http://google.com"}, children: "test"};
|
||||
}
|
||||
</code></pre>
|
||||
<p>Note that compiled templates are meant to be generated by an automated build process and are not meant to be human editable.</p>
|
||||
<hr>
|
||||
<h3 id="installing">Installing</h3>
|
||||
<p>Mithril-objectify requires a <a href="http://nodejs.org">NodeJS</a> environment. To install it, go to its website and use the installer provided.</p>
|
||||
<p>To install mithril-objectify, NodeJS provides a command-line package manager tool. In a command line, type:</p>
|
||||
<pre><code>npm install -g mithril-objectify
|
||||
</code></pre><p>Then, to compile a file, type:</p>
|
||||
<pre><code>mithril-objectify ./input-filename.js ./output-filename.js
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
11
archive/v0.2.4/package.json
Normal file
11
archive/v0.2.4/package.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "mithril",
|
||||
"description": "A Javascript Framework for building brilliant applications",
|
||||
"keywords": ["mvc", "framework"],
|
||||
"version": "0.2.4",
|
||||
"author": "Leo Horie <leohorie@hotmail.com>",
|
||||
"repository": {"type": "git", "url": "https://github.com/lhorie/mithril"},
|
||||
"main": "mithril.js",
|
||||
"licenses": [{"type": "MIT", "url": "http://opensource.org/licenses/MIT"}],
|
||||
"files": ["mithril.min.js", "mithril.min.js.map", "mithril.js", "README.*"]
|
||||
}
|
||||
6
archive/v0.2.4/pages.json
Normal file
6
archive/v0.2.4/pages.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[
|
||||
{"title": "Getting Started", "url": "getting-started.html"},
|
||||
{"title": "Documentation", "url": "mithril.html"},
|
||||
{"title": "Mithril Blog", "url": "http://lhorie.github.io/mithril-blog/"},
|
||||
{"title": "Mailing List", "url": "https://groups.google.com/forum/#!forum/mithriljs"}
|
||||
]
|
||||
119
archive/v0.2.4/practices.html
Normal file
119
archive/v0.2.4/practices.html
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>How Should Code Be Organized
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="how-should-code-be-organized">How Should Code Be Organized</h2>
|
||||
<p>While Mithril doesn't dictate how to organize your code, it does provide some recommendations for structuring it.</p>
|
||||
<p>As a rule of thumb, controllers should not change model entity properties on an individual basis.</p>
|
||||
<p>Data manipulation should be done in model classes, such that controllers never have entities lying around in temporarily invalid states.</p>
|
||||
<p>Mithril's design strongly encourages all entity logic to be handled in atomic model layer methods (in the sense of entity state stability).</p>
|
||||
<p>In fact, unavoidable abstraction leaks (such as network-bound asynchrony) are laid out in such a way as to make idiomatic code organization elegant, and conversely, to make it so that the abstraction leak problems themselves discourage attempts to misplace entity logic in the controller.</p>
|
||||
<p>This design decision comes from experience with <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> and the <a href="http://en.wikipedia.org/wiki/Bus_factor">"bus factor"</a> of large, highly relational model layers.</p>
|
||||
<p>This is in stark contrast to the ActiveRecord pattern of other frameworks, where model entities are largely object representations of database entities and these entities are manipulated in controllers in an ad-hoc field-by-field fashion, and then "committed" via a <code>save</code> method.</p>
|
||||
<p>Because Mithril encourages all entity logic to be done in the model layer, it's idiomatic to create modules with model-level classes that deal specifically with relationships between entities, when there isn't already a model entity that can logically hold the relational business logic.</p>
|
||||
<p>Models are also responsible for centralizing tasks such as filtering of entity lists and validation routines, so that access to these methods is available across the application.</p>
|
||||
<p>DOM manipulation should be done in the view via <a href="mithril"><code>m()</code> and <code>config</code></a>. Controllers may explicitly call <a href="mithril.redraw.html"><code>m.redraw</code></a>, but, if possible, it's preferable to abstract this into a service which integrates with Mithril's auto-redrawing system (see <a href="mithril.computation.html"><code>m.startComputation</code> / <code>m.endComputation</code></a>). You should avoid instantiating controller classes from views.</p>
|
||||
<hr>
|
||||
<h2 id="file-separation">File Separation</h2>
|
||||
<p>The examples in this site usually conflate different MVC layers together for the sake of readability, but normally it's recommended that each layer on a module be in different files. For example:</p>
|
||||
<pre><code class="lang-javascript">//app.model.js
|
||||
var app = {};
|
||||
|
||||
app.PageList = function() {
|
||||
return m.request({method: "GET", url: "pages.json"});
|
||||
};
|
||||
|
||||
app.vm = {};
|
||||
app.vm.init = function() {
|
||||
this.pages = new app.PageList();
|
||||
};
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">//app.controller.js
|
||||
app.controller = function() {
|
||||
app.vm.init();
|
||||
};
|
||||
</code></pre>
|
||||
<pre><code class="lang-javascript">//app.view.js
|
||||
app.view = function() {
|
||||
return app.vm.pages().map(function(page) {
|
||||
return m("a", {href: page.url}, page.title);
|
||||
});
|
||||
};
|
||||
</code></pre>
|
||||
<p>You can use task automation tools such as GruntJS to concatenate the files back together for production.</p>
|
||||
<p>Typically, when separating MVC layers, it's common that the namespace declaration be in the model layer, since this is usually the most used dependency for the other layers.</p>
|
||||
<p>You may choose to declare the namespace in a separate file or have the build system generate it on demand, instead.</p>
|
||||
<p>You should avoid grouping classes by the MVC layer they belong to, i.e. don't create three files called model.js, controllers.js and views.js.</p>
|
||||
<p>That organization pattern needlessly ties unrelated aspects of the application together and dilutes the clarity of modules.</p>
|
||||
<hr>
|
||||
<h2 id="usage-of-m-redraw">Usage of m.redraw</h2>
|
||||
<p><code>m.redraw</code> is a method that allows you to render a template outside the scope of Mithril's auto-redrawing system.</p>
|
||||
<p>Calling this method while using <code>m.mount</code> or <code>m.route</code> should only be done if you have recurring asynchronous view updates (i.e. something that uses setInterval).</p>
|
||||
<p>If you're integrating other non-recurring services (e.g. calling setTimeout), you should use <a href="mithril.computation.html"><code>m.startComputation</code> / <code>m.endComputation</code></a> instead.</p>
|
||||
<p>This is the most potentially expensive method in Mithril and should not be used at a rate faster than the rate at which the native <code>requestAnimationFrame</code> method fires (i.e. the rate at which browsers are comfortable calling recurring rendering-intensive code). Typically, this rate is around 60 calls per second.</p>
|
||||
<p>If you call this method more often than that, Mithril may ignore calls or defer them to the next browser repaint cycle.</p>
|
||||
<p>If calls are more expensive than a repaint window, the browser may drop frames, resulting in choppy animations. It's your responsibility to make sure single iterations of animation rendering code don't take longer than 16ms (for a frequency of 60 frames-per-second).</p>
|
||||
<p>In addition, note that template performance, both in Mithril templates as well as in general, is dependent on markup complexity. You are responsible for ensuring that templates aren't too big to render efficiently.</p>
|
||||
<hr>
|
||||
<h2 id="usage-of-keys">Usage of keys</h2>
|
||||
<p>If you need to sort lists, or delete items from them, or splice them in any way, you should <a href="mithril.html#dealing-with-sorting-and-deleting-in-lists">use the <code>key</code> attribute</a> to maintain referential integrity between the data and the DOM.</p>
|
||||
<p>Not using keys still works in some cases, but might trigger more expensive code paths within the redrawing algorithm.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
63
archive/v0.2.4/refactoring.html
Normal file
63
archive/v0.2.4/refactoring.html
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
123
archive/v0.2.4/roadmap.html
Normal file
123
archive/v0.2.4/roadmap.html
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Roadmap
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="api">API (v0.2.4)</h2>
|
||||
<h3 id="core">Core</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
|
||||
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
|
||||
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
|
||||
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
|
||||
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
|
||||
</ul>
|
||||
<h3 id="routing">Routing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
|
||||
<ul>
|
||||
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
|
||||
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
|
||||
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
|
||||
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
|
||||
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
|
||||
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
|
||||
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
|
||||
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="data">Data</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
|
||||
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
|
||||
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
|
||||
</ul>
|
||||
<h3 id="html">HTML</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
|
||||
</ul>
|
||||
<h3 id="rendering">Rendering</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
|
||||
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
|
||||
<ul>
|
||||
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
|
||||
</ul>
|
||||
<h3 id="data">Testing</h3>
|
||||
<ul>
|
||||
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="archive">History</h2>
|
||||
<ul>
|
||||
<li><a href="roadmap.html">Roadmap</a></li>
|
||||
<li><a href="change-log.html">Change log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="roadmap">Roadmap</h2>
|
||||
<p>Things that would be useful to have (though likely not as part of Mithril core)</p>
|
||||
<h3 id="utilities">Utilities</h3>
|
||||
<ul>
|
||||
<li>Formatters / parsers<ul>
|
||||
<li>i18n</li>
|
||||
<li>Date<ul>
|
||||
<li>Absolute (e.g. Jan 1, 1970 12:00 AM)</li>
|
||||
<li>Relative (e.g. 10 days ago)</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Number (e.g. 1,234.5)</li>
|
||||
<li>Currency (e.g. $1,000.00)</li>
|
||||
<li>Word wrap</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Dependency management</li>
|
||||
<li>Functional / relational tools</li>
|
||||
<li>Animation</li>
|
||||
</ul>
|
||||
<h3 id="components">Components</h3>
|
||||
<ul>
|
||||
<li>Autocompleter</li>
|
||||
<li>Date/time picker</li>
|
||||
<li>Swipe-to-show panel</li>
|
||||
<li>Tree</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
138
archive/v0.2.4/routing.html
Normal file
138
archive/v0.2.4/routing.html
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Routing
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="routing">Routing</h2>
|
||||
<p>Routing is a system that allows creating Single-Page-Applications (SPA), i.e. applications that can go from one page to another without causing a full browser refresh.</p>
|
||||
<p>It enables seamless navigability while preserving the ability to bookmark each page individually, and the ability to navigate the application via the browser's history mechanism.</p>
|
||||
<p>Mithril provides utilities to handle three different aspect of routing:</p>
|
||||
<ul>
|
||||
<li>defining a list of routes</li>
|
||||
<li>programmatically redirecting between routes</li>
|
||||
<li>making links in templates routed transparently and unobtrusively</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h3 id="defining-routes">Defining routes</h3>
|
||||
<p>To define a list of routes, you need to specify a host DOM element, a default route and a key-value map of possible routes and respective <a href="mithril.mount.html">modules</a> to be rendered.</p>
|
||||
<p>The example below defines three routes, to be rendered in <code><body></code>. <code>home</code>, <code>login</code> and <code>dashboard</code> are modules. We'll see how to define a module in a bit.</p>
|
||||
<pre><code class="lang-javascript">m.route(document.body, "/", {
|
||||
"/": home,
|
||||
"/login": login,
|
||||
"/dashboard": dashboard,
|
||||
});
|
||||
</code></pre>
|
||||
<p>Routes can take arguments, by prefixing words with a colon <code>:</code>.</p>
|
||||
<p>The example below shows a route that takes a <code>userID</code> parameter.</p>
|
||||
<pre><code class="lang-javascript">//a sample module
|
||||
var dashboard = {
|
||||
controller: function() {
|
||||
return {id: m.route.param("userID")};
|
||||
},
|
||||
view: function(controller) {
|
||||
return m("div", controller.id);
|
||||
}
|
||||
}
|
||||
|
||||
//setup routes to start w/ the `#` symbol
|
||||
m.route.mode = "hash";
|
||||
|
||||
//define a route
|
||||
m.route(document.body, "/dashboard/johndoe", {
|
||||
"/dashboard/:userID": dashboard
|
||||
});
|
||||
</code></pre>
|
||||
<p>This redirects to the URL <code>http://server/#/dashboard/johndoe</code> and yields:</p>
|
||||
<pre><code class="lang-markup"><body>johndoe</body>
|
||||
</code></pre>
|
||||
<p>Above, <code>dashboard</code> is a module. It contains <code>controller</code> and <code>view</code> properties. When the URL matches a route, the respective module's controller is instantiated and passed as a parameter to the view.</p>
|
||||
<p>In this case, since there's only one route, the app redirects to the default route <code>"/dashboard/johndoe"</code> and, under the hood, it calls <code>m.mount(document.body, dashboard)</code>.</p>
|
||||
<p>The string <code>johndoe</code> is bound to the <code>:userID</code> parameter, which can be retrieved programmatically in the controller via <code>m.route.param("userID")</code>.</p>
|
||||
<p>The <code>m.route.mode</code> property defines which URL portion is used to implement the routing mechanism. It should be set before any calls to <code>m.route</code>. Its value can be set to either "search", "hash" or "pathname". The default value is "search".</p>
|
||||
<ul>
|
||||
<li><p><code>search</code> mode uses the querystring. This allows named anchors (i.e. <code><a href="#top">Back to top</a></code>, <code><a name="top"></a></code>) to work on the page, but routing changes causes page refreshes in IE8, due to its lack of support for <code>history.pushState</code>.</p>
|
||||
<p>Example URL: <code>http://server/?/path/to/page</code></p>
|
||||
</li>
|
||||
<li><p><code>hash</code> mode uses the hash. It's the only mode in which routing changes do not cause page refreshes in any browser. However, this mode does not support named anchors and browser history lists.</p>
|
||||
<p>Example URL: <code>http://server/#/path/to/page</code></p>
|
||||
</li>
|
||||
<li><p><code>pathname</code> mode allows routing URLs that contain no special characters, however this mode requires server-side setup in order to support bookmarking and page refreshes. It also causes page refreshes in IE8.</p>
|
||||
<p>Example URL: <code>http://server/path/to/page</code></p>
|
||||
<p>The simplest server-side setup possible to support pathname mode is to serve the same content regardless of what URL is requested. In Apache, this URL rewriting can be achieved using <a href="https://httpd.apache.org/docs/current/mod/mod_rewrite.html">mod_rewrite</a>.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h3 id="redirecting">Redirecting</h3>
|
||||
<p>You can programmatically redirect to another page. Given the example in the "Defining Routes" section:</p>
|
||||
<pre><code class="lang-javascript">m.route("/dashboard/marysue");
|
||||
</code></pre>
|
||||
<p>redirects to <code>http://server/#/dashboard/marysue</code></p>
|
||||
<hr>
|
||||
<h3 id="mode-abstraction">Mode abstraction</h3>
|
||||
<p>This method is meant to be used with a virtual element's <code>config</code> attribute. For example:</p>
|
||||
<pre><code class="lang-javascript">//Note that the '#' is not required in `href`, thanks to the `config` setting.
|
||||
m("a[href='/dashboard/alicesmith']", {config: m.route});
|
||||
</code></pre>
|
||||
<p>This makes the href behave correctly regardless of which <code>m.route.mode</code> is selected. It's a good practice to always use the idiom above, instead of hardcoding <code>?</code> or <code>#</code> in the href attribute.</p>
|
||||
<p>See <a href="mithril.html"><code>m()</code></a> for more information on virtual elements.</p>
|
||||
<hr>
|
||||
<h3 id="redrawing-semantics-of-routing">Redrawing semantics of routing</h3>
|
||||
<p>By default, changing routes causes templates to be re-rendered from scratch. This behavior can be changed either via the <a href="mithril.html#persisting-dom-elements-across-route-changes">retain flag in a config's context object</a>, or the <a href="mithril.redraw.html#changing-redraw-strategy"><code>m.redraw.strategy("diff")</code> hint</a>.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
93
archive/v0.2.4/style.css
Normal file
93
archive/v0.2.4/style.css
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
.container {margin:auto;max-width:1000px;padding:0 20px;position:relative;}
|
||||
.container:after,.row:after {content:"";display:table;clear:both;}
|
||||
.container,.row,[class*='col('] {-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;}
|
||||
[class*='col('] {float:left;}
|
||||
[class*='(3,'] {width:25%;}
|
||||
[class*='(4,'] {width:33.33333%;}
|
||||
[class*='(6,'] {width:50%;}
|
||||
[class*='(8,'] {width:66.66667%;}
|
||||
[class*='(9,'] {width:75%;}
|
||||
@media (max-width:1000px) {
|
||||
[class*=',3,'] {width:25%;}
|
||||
[class*=',4,'] {width:33.33333%;}
|
||||
[class*=',6,'] {width:50%;}
|
||||
[class*=',8,'] {width:66.66667%;}
|
||||
[class*=',9,'] {width:75%;}
|
||||
}
|
||||
@media (max-width:750px) {
|
||||
[class*=',6)'] {width:50%;}
|
||||
[class*=',12)'] {width:100%;}
|
||||
}
|
||||
|
||||
html {background:#999;color:#222;font:14px/1.4 Helvetica;}
|
||||
html,body {margin:0;padding:0;}
|
||||
header,footer {background:#999;}
|
||||
nav {text-align:right;}
|
||||
nav a:first-child,nav a:first-child:visited {color:#fff;font-size:27px;float:left;line-height:1.3em;padding:0;text-decoration:none;}
|
||||
nav a {color:#fff;display:inline-block;padding:10px;}
|
||||
nav a:visited {color:#ddd;}
|
||||
footer {text-align:center;padding:10px 0;}
|
||||
footer,footer a,footer a:visited {color:#fff;}
|
||||
h1,h2 {font-family:Verdana;margin:0 0 10px;}
|
||||
h1 {font-size:3em;}
|
||||
h1 span {animation:logo 2s;display:inline-block;-webkit-animation:logo 2s;}
|
||||
h2 {color:#888;font-weight:normal;}
|
||||
h3 {margin:10px 0;}
|
||||
p {margin:15px 0;}
|
||||
ul {margin:15px 0;padding:0 0 0 1em;}
|
||||
li {margin:0 0 10px;}
|
||||
a {color:#161;}
|
||||
a:visited {color:#383;}
|
||||
a:hover {text-decoration:none;}
|
||||
pre {background:#ffe;border:1px solid #ddd;overflow:auto;margin:0 0 15px;padding:5px 10px;white-space:pre;-webkit-overflow-scrolling:touch;}
|
||||
pre[class*="language-"],code {background:#ffe;font:12px/15px Lucida Console,Monaco,monospace;}
|
||||
hr {border-top:1px solid #ccc;border-width:1px 0 0;margin:20px 0;}
|
||||
table {margin:0 0 10px;width:100%;}
|
||||
.cta {padding:30px 0 20px;text-align:center;}
|
||||
.cta {
|
||||
background:
|
||||
linear-gradient(27deg, #e5e5e5 5px, rgba(255,255,255,0) 5px) 0 5px,
|
||||
linear-gradient(207deg, #e5e5e5 5px, rgba(255,255,255,0) 5px) 10px 0px,
|
||||
linear-gradient(27deg, #f2f2f2 5px, rgba(255,255,255,0) 5px) 0px 10px,
|
||||
linear-gradient(207deg, #f2f2f2 5px, rgba(255,255,255,0) 5px) 10px 5px,
|
||||
linear-gradient(90deg, #ebebeb 10px, rgba(255,255,255,0) 10px),
|
||||
linear-gradient(#ededed 25%, #eaeaea 25%, #eaeaea 50%, rgba(255,255,255,0) 50%, rgba(255,255,255,0) 75%, #f4f4f4 75%, #f4f4f4);
|
||||
background-color: #e3e3e3;
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
.logo {color:#5a5;font-family:Georgia;font-style:italic;}
|
||||
.logo span {font-family:Arial;font-style:normal;}
|
||||
.logo :before {content:"\25CB";position:absolute;margin:-0.17em 0 0 -0.10em;}
|
||||
.logo :after {content:"\25CB";position:absolute;margin:-0.17em 0 0 -0.5em;}
|
||||
.button,.button:visited {background:#5a5;color:#fff;display:inline-block;font:normal bold 16px Helvetica;margin:0 10px 10px;padding:10px 30px;text-decoration:none;}
|
||||
.changelog {display:inline-block;margin:0 10px;vertical-align:7px;}
|
||||
.features {background:#fff;padding:30px 0 0;}
|
||||
.feature {margin:0 0 30px;padding:0 20px 0 0;}
|
||||
.sample {background:#f5f5f5;padding:30px 0 10px;}
|
||||
.example {background:#ffe;border:1px solid #ddd;display:block;font:Courier New;margin-bottom:20px;padding:5px 10px;}
|
||||
.example span {color:#383;font-weight:bold;}
|
||||
.example small {color:#888;font-size:1em;}
|
||||
.more {background:#ddd;padding:30px 0;}
|
||||
.output a,.more a {display:block;margin:0 0 10px;}
|
||||
.output a:after,.more a:after {content:" \bb";}
|
||||
.performance,.socialmedia {background:#fff;padding:30px 0;}
|
||||
.performance td:first-child {text-align:right;width:1%;}
|
||||
.bar {background:red;height:4px;float:left;margin:0.5em 1em 0 0;}
|
||||
.security {background:#f5f5f5;padding:30px 0;}
|
||||
.success {color:#383;}
|
||||
.callout {background:#84db84;padding:1em;}
|
||||
.error {color:#f00;}
|
||||
.content {background:#f5f5f5;padding:30px 0;}
|
||||
|
||||
@media (min-width:750px) {
|
||||
.sample pre {margin-right:20px;}
|
||||
}
|
||||
|
||||
@keyframes logo {
|
||||
from {opacity:0;transform:scale(2) rotate(359deg);}
|
||||
to {opacity:1;transform:scale(1) rotate(0deg);}
|
||||
}
|
||||
@-webkit-keyframes logo {
|
||||
from {opacity:0;-webkit-transform:scale(2) rotate(359deg);}
|
||||
to {opacity:1;-webkit-transform:scale(1) rotate(0deg);}
|
||||
}
|
||||
112
archive/v0.2.4/tools.html
Normal file
112
archive/v0.2.4/tools.html
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tools
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="tools">Tools</h2>
|
||||
<h3 id="html-to-mithril-template-converter">HTML-to-Mithril Template Converter</h3>
|
||||
<p>If you already have your HTML written and want to convert it into a Mithril template, you can use the tool below for one-off manual conversion.</p>
|
||||
<p><a href="http://arthurclemens.github.io/mithril-template-converter/index.html">Template Converter</a></p>
|
||||
<hr>
|
||||
<h3 id="automatic-html-to-mithril-template-converter">Automatic HTML-to-Mithril Template Converter</h3>
|
||||
<p>There's a tool called <a href="https://github.com/insin/msx">MSX by Jonathan Buchanan</a> that allows you to write templates using HTML syntax, and then automatically compile them to Javascript when files change.</p>
|
||||
<p>It is useful for teams where styling and functionality are done by different people, and for those who prefer to maintain templates in HTML syntax.</p>
|
||||
<p>The tool allows you to write code like this:</p>
|
||||
<pre><code class="lang-javascript">todo.view = function() {
|
||||
return <html>
|
||||
<body>
|
||||
<input onchange={m.withAttr("value", app.vm.description)} value={app.vm.description()}/>
|
||||
<button onclick={app.vm.add}>Add</button>
|
||||
</body>
|
||||
</html>
|
||||
};
|
||||
</code></pre>
|
||||
<p>Note, however, that since the code above is not valid Javascript, this syntax can only be used with a preprocessor build tool such as the provided <a href="http://gulpjs.com">Gulp.js</a> script.</p>
|
||||
<p>This tool is also available as a <a href="https://github.com/mrsweaters/mithril-rails">Rails gem</a>, created by Jordan Humphreys.</p>
|
||||
<hr>
|
||||
<h3 id="mithril-template-compiler">Mithril Template Compiler</h3>
|
||||
<p>You can pre-compile Mithril templates to make them run faster. For more information see this page:</p>
|
||||
<p><a href="optimizing-performance.html#compiling-templates">Compiling Templates</a></p>
|
||||
<hr>
|
||||
<h3 id="typescript-support">Typescript Support</h3>
|
||||
<p>There's a type definition file that you can use to add Mithril support to Typescript</p>
|
||||
<p><a href="mithril.d.ts">mithril.d.ts</a></p>
|
||||
<p>You can use it by adding a reference to your Typescript files. This will allow the compiler to type-check calls to the Mithril API.</p>
|
||||
<pre><code class="lang-javascript">/// <reference path="mithril.d.ts" />
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="internet-explorer-compatibility">Internet Explorer Compatibility</h3>
|
||||
<p>Mithril relies on some ECMAScript 5 features, namely: <code>Array::indexOf</code>, <code>Array::map</code> and <code>Object::keys</code>, as well as the <code>JSON</code> object. Internet Explorer 8 lacks native support for some of these features.</p>
|
||||
<p>The easiest way to polyfill these features is to include this script:</p>
|
||||
<pre><code class="lang-javascript"><script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.0.3/es5-shim.min.js"></script>
|
||||
</code></pre>
|
||||
<p>This will provide all the polyfills required for the browser. You can alternatively include only specific polyfills:</p>
|
||||
<pre><code class="lang-markup"><script src="http://cdn.polyfill.io/v1/polyfill.min.js?features=Array.prototype.indexOf,Object.keys,Function.prototype.bind,Array.prototype.forEach,JSON"></script>
|
||||
</code></pre>
|
||||
<p>You can also use other polyfills to support these features in IE7.</p>
|
||||
<ul>
|
||||
<li><p><a href="https://github.com/es-shims/es5-shim">ES5 Shim</a> or Mozilla.org's <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf">Array::indexOf</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map">Array::map</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys">Object::keys</a></p>
|
||||
</li>
|
||||
<li><p><a href="https://github.com/douglascrockford/JSON-js/blob/master/json2.js">JSON2.js</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Mithril also has a dependency on XMLHttpRequest. If you wish to support IE6, you'll need <a href="https://gist.github.com/Contra/2709462">a shim for it</a>. IE7 and lower do not support cross-domain AJAX requests.</p>
|
||||
<p>In addition, note that most <code>m.route</code> modes rely on <code>history.pushState</code> in order to allow moving from one page to another without a browser refresh. <a href="http://caniuse.com/#search=pushstate">IE9 and lower</a> do not support this feature and will gracefully degrade to page refreshes instead.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
84
archive/v0.2.4/tools/template-compiler.sjs
Normal file
84
archive/v0.2.4/tools/template-compiler.sjs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
Compiles Mithril templates
|
||||
|
||||
Requires sweet.js (https://github.com/mozilla/sweet.js)
|
||||
Installation: npm install -g sweet.js
|
||||
Usage: sjs --module /template-compiler.sjs --output <output-filename>.js <input-filename>.js
|
||||
*/
|
||||
|
||||
macro m_impl {
|
||||
case { _ ($selector, $dynAttrs ..., $children ...) } => {
|
||||
var selectorSyntax = #{$selector};
|
||||
var selector = unwrapSyntax(selectorSyntax);
|
||||
|
||||
var dynAttrsSyntax = #{$dynAttrs ...};
|
||||
try {
|
||||
var dynAttrs = unwrapSyntax(dynAttrsSyntax);
|
||||
|
||||
var parser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g;
|
||||
var attrParser = /\[(.+?)=("|'|)(.+?)\2\]/;
|
||||
var _match = null;
|
||||
var classes = [];
|
||||
var cell = {tag: "div", attrs: {}, children: []};
|
||||
|
||||
while (_match = parser.exec(selector)) {
|
||||
if (_match[1] == "") cell.tag = _match[2];
|
||||
else if (_match[1] == "#") cell.attrs.id = _match[2];
|
||||
else if (_match[1] == ".") classes.push(_match[2]);
|
||||
else if (_match[3][0] == "[") {
|
||||
var pair = attrParser.exec(_match[3]);
|
||||
cell.attrs[pair[1]] = pair[3];
|
||||
}
|
||||
}
|
||||
if (classes.length > 0) cell.attrs["class"] = classes.join(" ");
|
||||
|
||||
var tag = makeValue(cell.tag, #{here});
|
||||
var attrsBody = Object.keys(cell.attrs).reduce(function(memo, attrName) {
|
||||
return memo.concat([
|
||||
makeValue(attrName, #{here}),
|
||||
makePunc(":", #{here}),
|
||||
makeValue(cell.attrs[attrName], #{here}),
|
||||
makePunc(",", #{here})
|
||||
]);
|
||||
}, []).concat(dynAttrs.inner);
|
||||
var attrs = [makeDelim("{}", attrsBody, #{here})];
|
||||
letstx $tag = [tag], $attrs = attrs;
|
||||
|
||||
return #{ ({tag: $tag, attrs: $attrs , children: $children ...}) };
|
||||
}
|
||||
catch (e) {
|
||||
return #{ m_impl($tag, {}, [$dynAttrs ..., $children ...]) }
|
||||
}
|
||||
}
|
||||
case { _ ($selector, $partial ...) } => {
|
||||
var partialSyntax = #{$partial ...};
|
||||
try {
|
||||
var partial = unwrapSyntax(partialSyntax);
|
||||
var isTag = partial.inner && partial.inner.length > 2 && (partial.inner[0].token.value == "tag" && partial.inner[1].token.value == ":") || typeof partial != "object" || partial.value == "[]"
|
||||
return !isTag ? #{m_impl($selector, $partial ..., [])} : #{m_impl($selector, {}, $partial ...)};
|
||||
}
|
||||
catch (e) {
|
||||
return #{m_impl($selector, {}, $partial ...)}
|
||||
}
|
||||
}
|
||||
case { _ ($selector) } => {
|
||||
return #{m_impl($selector, {}, [])};
|
||||
}
|
||||
}
|
||||
|
||||
let m = macro {
|
||||
case { _ ($selector, $dynAttrs ..., $children ...) } => {
|
||||
return #{m_impl($selector, $dynAttrs ..., $children ...)}
|
||||
}
|
||||
case { _ ($selector, $partial ...) } => {
|
||||
return #{m_impl($selector, $partial ...)}
|
||||
}
|
||||
case { _ ($selector) } => {
|
||||
return #{m_impl($selector, {}, [])};
|
||||
}
|
||||
case { _ } => {
|
||||
return #{m};
|
||||
}
|
||||
}
|
||||
|
||||
export m;
|
||||
17
archive/v0.2.4/tools/template-converter.html
Normal file
17
archive/v0.2.4/tools/template-converter.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<!doctype html>
|
||||
<title>HTML to Mithril Template Converter</title>
|
||||
|
||||
<h1>HTML to Mithril Template Converter</h1>
|
||||
<p>
|
||||
If you have HTML you want to convert into a Mithril template, paste it below
|
||||
and press the "Convert" button. In case you're wondering, this itself is a
|
||||
Mithril app.
|
||||
</p>
|
||||
|
||||
<div id="converter"></div>
|
||||
|
||||
<script src="../mithril.min.js"></script>
|
||||
<script src="template-converter.js"></script>
|
||||
<script>
|
||||
m.mount(document.getElementById("converter"), templateConverter)
|
||||
</script>
|
||||
145
archive/v0.2.4/tools/template-converter.js
Normal file
145
archive/v0.2.4/tools/template-converter.js
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/* global m: false */
|
||||
// TODO: ensure this targets the current API.
|
||||
window.templateConverter = (function () {
|
||||
"use strict"
|
||||
|
||||
function each(list, f) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
f(list[i], i)
|
||||
}
|
||||
}
|
||||
|
||||
function createFragment(markup) {
|
||||
if (markup.indexOf("<!doctype") >= 0) {
|
||||
return [
|
||||
new DOMParser()
|
||||
.parseFromString(markup, "text/html")
|
||||
.childNodes[1]
|
||||
]
|
||||
}
|
||||
|
||||
var container = document.createElement("div")
|
||||
container.insertAdjacentHTML("beforeend", markup)
|
||||
return container.childNodes
|
||||
}
|
||||
|
||||
function createVirtual(fragment) {
|
||||
var list = []
|
||||
|
||||
each(fragment, function (el) {
|
||||
if (el.nodeType === 3) {
|
||||
list.push(el.nodeValue)
|
||||
} else if (el.nodeType === 1) {
|
||||
var attrs = {}
|
||||
|
||||
each(el.attributes, function (attr) {
|
||||
attrs[attr.name] = attr.value
|
||||
})
|
||||
|
||||
list.push({
|
||||
tag: el.nodeName.toLowerCase(),
|
||||
attrs: attrs,
|
||||
children: createVirtual(el.childNodes)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
function TemplateBuilder(virtual, level) {
|
||||
this.virtual = virtual
|
||||
this.level = level
|
||||
this.virtuals = []
|
||||
this.indented = false
|
||||
}
|
||||
|
||||
TemplateBuilder.prototype = {
|
||||
addVirtualString: function (el) {
|
||||
if (/\t| {2,}/.test(el) && /^\s*/.test(el)) {
|
||||
this.indented = true
|
||||
} else {
|
||||
this.virtuals.push('"' + el.replace(/(["\r\n])/g, "\\$1") + '"')
|
||||
}
|
||||
},
|
||||
|
||||
addVirtualAttrs: function (el) {
|
||||
var virtual = el.tag === "div" ? "" : el.tag
|
||||
|
||||
if (el.attrs.class) {
|
||||
virtual += "." + el.attrs.class.replace(/\s+/g, ".")
|
||||
delete el.attrs.class
|
||||
}
|
||||
|
||||
each(Object.keys(el.attrs).sort(), function (attrName) {
|
||||
if (attrName === "style") return
|
||||
virtual += "[" + attrName + "='"
|
||||
virtual += el.attrs[attrName].replace(/'/g, "\\'") + "']"
|
||||
})
|
||||
|
||||
if (virtual === "") virtual = "div"
|
||||
virtual = '"' + virtual + '"'
|
||||
|
||||
if (el.attrs.style) {
|
||||
var style = "{\"" + el.attrs.style
|
||||
.replace(/:/g, "\": \"")
|
||||
.replace(/;/g, "\", \"") + "}"
|
||||
virtual += ", {style: " + style.replace(/(, )"}/, "}") + "}"
|
||||
}
|
||||
|
||||
if (el.children.length !== 0) {
|
||||
var builder = new TemplateBuilder(el.children, this.level + 1)
|
||||
virtual += ", " + builder.complete()
|
||||
}
|
||||
|
||||
this.virtuals.push("m(" + virtual + ")")
|
||||
},
|
||||
|
||||
complete: function () {
|
||||
var tab = "\n"
|
||||
for (var i = 0; i <= this.level; i++) tab += "\t"
|
||||
|
||||
each(this.virtual, function (el) {
|
||||
if (typeof el === "string") {
|
||||
this.addVirtualString(el)
|
||||
} else {
|
||||
this.addVirtualAttrs(el)
|
||||
}
|
||||
}.bind(this))
|
||||
|
||||
if (!this.indented) tab = ""
|
||||
|
||||
if (this.virtuals.length === 1 && this.virtuals[0][0] === "\"") {
|
||||
return this.virtuals.join(", ")
|
||||
} else {
|
||||
var body = this.virtuals.join("," + tab)
|
||||
return "[" + tab + body + tab.slice(0, -1) + "]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
controller: function () {
|
||||
this.source = m.prop("")
|
||||
this.output = m.prop("")
|
||||
|
||||
this.convert = function () {
|
||||
var source = createVirtual(createFragment(this.source()))
|
||||
return this.output(new TemplateBuilder(source, 1).complete())
|
||||
}.bind(this)
|
||||
},
|
||||
|
||||
view: function (ctrl) {
|
||||
return m("div", [
|
||||
m("textarea", {
|
||||
autofocus: true,
|
||||
style: {width: "100%", height: "40%"},
|
||||
onchange: m.withAttr("value", ctrl.source)
|
||||
}, ctrl.source()),
|
||||
m("button", {onclick: ctrl.convert}, "Convert"),
|
||||
m("textarea", {style: {width: "100%", height: "40%"}},
|
||||
ctrl.output())
|
||||
])
|
||||
}
|
||||
}
|
||||
})()
|
||||
234
archive/v0.2.4/web-services.html
Normal file
234
archive/v0.2.4/web-services.html
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Web Services
- Mithril</title>
|
||||
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
|
||||
<link href="lib/prism/prism.css" rel="stylesheet" />
|
||||
<link href="style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="index.html" class="logo"><span>○</span> Mithril</a>
|
||||
<a href="getting-started.html">Guide</a>
|
||||
<a href="mithril.html">API</a>
|
||||
<a href="community.html">Community</a>
|
||||
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
|
||||
<a href="installation.html">Download</a>
|
||||
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
<section class="content">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col(3,3,12)">
|
||||
<h2 id="core-topics">Core Topics</h2>
|
||||
<ul>
|
||||
<li><a href="installation.html">Installation</a></li>
|
||||
<li><a href="getting-started.html">Getting Started</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
<li><a href="web-services.html">Web Services</a></li>
|
||||
<li><a href="components.html">Components</a></li>
|
||||
</ul>
|
||||
<h2 id="advanced-topics.html">Advanced Topics</h2>
|
||||
<ul>
|
||||
<li><a href="auto-redrawing.html">The Auto-Redrawing System</a></li>
|
||||
<li><a href="integration.html">Integrating with Other Libraries</a></li>
|
||||
<li><a href="optimizing-performance.html">Compiling Templates</a></li>
|
||||
</ul>
|
||||
<h2 id="misc">Misc</h2>
|
||||
<ul>
|
||||
<li><a href="comparison.html">Differences from Other MVC Frameworks</a></li>
|
||||
<li><a href="benchmarks.html">Benchmarks</a></li>
|
||||
<li><a href="practices.html">Good Practices</a></li>
|
||||
<li><a href="tools.html">Useful Tools</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col(9,9,12)">
|
||||
<h2 id="web-services">Web Services</h2>
|
||||
<p>Mithril allows writing asynchronous code in a procedural way through a high-level utility for working with web services.</p>
|
||||
<p>This utility provides a number of useful features out of the box:</p>
|
||||
<ul>
|
||||
<li>The ability to get an early reference to a container that will hold the asynchronous response</li>
|
||||
<li>The ability to queue operations to be performed after the asynchronous request completes</li>
|
||||
<li>The ability to "cast" the response to a class of your choice</li>
|
||||
<li>The ability to unwrap data in a response that includes metadata properties</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h3 id="basic-usage">Basic usage</h3>
|
||||
<p>The basic usage pattern for <code>m.request</code> returns an <a href="mithril.prop.html"><code>m.prop</code></a> getter-setter, which is populated when the AJAX request completes.</p>
|
||||
<p>The returned getter-setter can be thought of as a box: you can pass this reference around cheaply, and you can "unwrap" its value when needed.</p>
|
||||
<pre><code class="lang-javascript">var users = m.request({method: "GET", url: "/user"});
|
||||
|
||||
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
|
||||
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
|
||||
//i.e. users() //[{name: "John"}, {name: "Mary"}]
|
||||
</code></pre>
|
||||
<p>Note that this getter-setter holds an <em>undefined</em> value until the AJAX request completes. Attempting to unwrap its value early will likely result in errors.</p>
|
||||
<p>The returned getter-setter also implements the <a href="mithril.deferred.html">promise</a> interface (also known as a <em>thenable</em>): this is the mechanism you should always use to queue operations to be performed on the data from the web service.</p>
|
||||
<p>The simplest use case of this feature is to implement functional value assignment via <code>m.prop</code> (i.e. the same thing as above). You can bind a pre-existing getter-setter by passing it in as a parameter to a <code>.then</code> method:</p>
|
||||
<pre><code class="lang-javascript">var users = m.prop([]); //default value
|
||||
|
||||
m.request({method: "GET", url: "/user"}).then(users)
|
||||
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
|
||||
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
|
||||
//i.e. users() //[{name: "John"}, {name: "Mary"}]
|
||||
</code></pre>
|
||||
<p>This syntax allows you to bind intermediate results before piping them down for further processing, for example:</p>
|
||||
<pre><code class="lang-javascript">var users = m.prop([]); //default value
|
||||
var doSomething = function() { /*...*/ }
|
||||
|
||||
m.request({method: "GET", url: "/user"}).then(users).then(doSomething)
|
||||
</code></pre>
|
||||
<p>While both basic assignment syntax and thenable syntax can be used to the same effect, typically it's recommended that you use the assignment syntax in the first example whenever possible, as it's easier to read.</p>
|
||||
<p>The thenable mechanism is intended to be used in three ways:</p>
|
||||
<ul>
|
||||
<li>In the model layer: to process web service data in transformative ways (e.g. filtering a list based on a parameter that the web service doesn't support)</li>
|
||||
<li>In the controller layer: to bind redirection code upon a condition</li>
|
||||
<li>In the controller layer: to bind error messages</li>
|
||||
</ul>
|
||||
<h4 id="processing-web-service-data">Processing web service data</h4>
|
||||
<p>This step is meant to be done in the model layer. Doing it in the controller level is also possible, but philosophically not recommended, because by tying logic to a controller, the code becomes harder to reuse due to unrelated controller dependencies.</p>
|
||||
<p>In the example below, the <code>listEven</code> method returns a getter-setter that resolves to a list of users containing only users whose id is even.</p>
|
||||
<pre><code class="lang-javascript">//model
|
||||
var User = {}
|
||||
|
||||
User.listEven = function() {
|
||||
return m.request({method: "GET", url: "/user"}).then(function(list) {
|
||||
return list.filter(function(user) {return user.id % 2 == 0});
|
||||
});
|
||||
}
|
||||
|
||||
//controller
|
||||
var controller = function() {
|
||||
return {users: User.listEven()}
|
||||
}
|
||||
</code></pre>
|
||||
<h4 id="bind-redirection-code">Bind redirection code</h4>
|
||||
<p>This step is meant to be done in the controller layer. Doing it in the model level is also possible, but philosophically not recommended, because by tying redirection to the model, the code becomes harder to reuse due to overly tight coupling.</p>
|
||||
<p>In the example below, we use the previously defined <code>listEven</code> model method and queue a controller-level function that redirects to another page if the user list is empty.</p>
|
||||
<pre><code class="lang-javascript">//controller
|
||||
var controller = function() {
|
||||
return {
|
||||
users: User.listEven().then(function(users) {
|
||||
if (users.length == 0) m.route("/add");
|
||||
})
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h4 id="binding-errors">Binding errors</h4>
|
||||
<p>Mithril thenables take two functions as optional parameters: the first parameter is called if the web service request completes successfully. The second parameter is called if it completes with an error.</p>
|
||||
<p>Error binding is meant to be done in the controller layer. Doing it in the model level is also possible, but generally leads to more code in order to connect all the dots.</p>
|
||||
<p>In the example below, we bind an error getter-setter to our previous controller so that the <code>error</code> variable gets populated if the server throws an error.</p>
|
||||
<pre><code class="lang-javascript">//controller
|
||||
var controller = function() {
|
||||
this.error = m.prop("")
|
||||
|
||||
this.users = User.listEven().then(function(users) {
|
||||
if (users.length == 0) m.route("/add");
|
||||
}, this.error)
|
||||
}
|
||||
</code></pre>
|
||||
<p>If the controller doesn't already have a success callback to run after a request resolves, you can still bind errors like this:</p>
|
||||
<pre><code class="lang-javascript">//controller
|
||||
var controller = function() {
|
||||
this.error = m.prop("")
|
||||
|
||||
this.users = User.listEven().then(null, this.error)
|
||||
}
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="queuing-operations">Queuing Operations</h3>
|
||||
<p>As you saw, you can chain operations that act on the response data. Typically this is required in three situations:</p>
|
||||
<ul>
|
||||
<li>In model-level methods if client-side processing is needed to make the data useful for a controller or view</li>
|
||||
<li>In the controller, to redirect after a model service resolves</li>
|
||||
<li>In the controller, to bind error messages</li>
|
||||
</ul>
|
||||
<p>In the example below, we take advantage of queuing to debug the AJAX response data prior to doing further processing on the user list:</p>
|
||||
<pre><code class="lang-javascript">var users = m.request({method: "GET", url: "/user"})
|
||||
.then(log)
|
||||
.then(function(users) {
|
||||
//add one more user to the response
|
||||
return users.concat({name: "Jane"})
|
||||
})
|
||||
|
||||
function log(value) {
|
||||
console.log(value)
|
||||
return value
|
||||
}
|
||||
|
||||
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
|
||||
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
|
||||
//i.e. users() //[{name: "John"}, {name: "Mary"}, {name: "Jane"}]
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="casting-the-response-data-to-a-class">Casting the Response Data to a Class</h3>
|
||||
<p>It's possible to auto-cast a JSON response to a class. This is useful when we want to control access to certain properties in an object, as opposed to exposing all the fields in POJOs (plain old Javascript objects) for arbitrary processing.</p>
|
||||
<p>In the example below, <code>User.list</code> returns a list of <code>User</code> instances.</p>
|
||||
<pre><code class="lang-javascript">var User = function(data) {
|
||||
this.name = m.prop(data.name);
|
||||
}
|
||||
|
||||
User.list = function() {
|
||||
return m.request({method: "GET", url: "/user", type: User});
|
||||
}
|
||||
|
||||
var users = User.list();
|
||||
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
|
||||
//then when resolved (e.g. in a view), `users` will contain a list of User instances
|
||||
//i.e. users()[0].name() == "John"
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="unwrapping-response-data">Unwrapping Response Data</h3>
|
||||
<p>Often, web services return the relevant data wrapped in objects that contain metadata.</p>
|
||||
<p>Mithril allows you to unwrap the relevant data, by providing two callback hooks: <code>unwrapSuccess</code> and <code>unwrapError</code>.</p>
|
||||
<p>These hooks allow you to unwrap different parts of the response data depending on whether it succeed or failed.</p>
|
||||
<pre><code class="lang-javascript">var users = m.request({
|
||||
method: "GET",
|
||||
url: "/user",
|
||||
unwrapSuccess: function(response) {
|
||||
return response.data;
|
||||
},
|
||||
unwrapError: function(response) {
|
||||
return response.error;
|
||||
}
|
||||
});
|
||||
|
||||
//assuming the response is: `{data: [{name: "John"}, {name: "Mary"}], count: 2}`
|
||||
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
|
||||
//i.e. users() //[{name: "John"}, {name: "Mary"}]
|
||||
</code></pre>
|
||||
<hr>
|
||||
<h3 id="using-different-data-transfer-formats">Using Different Data Transfer Formats</h3>
|
||||
<p>By default, <code>m.request</code> uses JSON to send and receive data to web services. You can override this by providing <code>serialize</code> and <code>deserialize</code> options:</p>
|
||||
<pre><code class="lang-javascript">var users = m.request({
|
||||
method: "GET",
|
||||
url: "/user",
|
||||
serialize: mySerializer,
|
||||
deserialize: myDeserializer
|
||||
});
|
||||
</code></pre>
|
||||
<p>One typical way to override this is to receive as-is responses. The example below shows how to receive a plain string from a txt file.</p>
|
||||
<pre><code class="lang-javascript">var file = m.request({
|
||||
method: "GET",
|
||||
url: "myfile.txt",
|
||||
deserialize: function(value) {return value;}
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
|
||||
<br />© 2014 Leo Horie
|
||||
</div>
|
||||
</footer>
|
||||
<script src="lib/prism/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,5 +1,14 @@
|
|||
## Change Log
|
||||
|
||||
[v0.2.4](http://mithril.js.org/archive/v0.2.4)
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
- fix regression that caused errors to be swallowed in promises returned by m.request [#968](https://github.com/lhorie/mithril.js/issues/968)
|
||||
- fix ReferenceError when calling an event handler via mithril-query without an event argument
|
||||
|
||||
---
|
||||
|
||||
[v0.2.3](http://mithril.js.org/archive/v0.2.3)
|
||||
|
||||
### Bug Fixes:
|
||||
|
|
|
|||
4
mithril.min.js
vendored
4
mithril.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "mithril",
|
||||
"description": "Mithril.js - A Javascript Framework for Building Brilliant Applications",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.4",
|
||||
"homepage": "http://mithril.js.org",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue