mithril
Version:
A framework for building brilliant applications
144 lines (127 loc) • 7.28 kB
HTML
<html>
<head>
<meta charset="UTF-8" />
<title># Integrating with Other Libraries - Mithril.js</title>
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css' />
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<section>
<a class="hamburger" href="javascript:;">≡</a>
<h1><img src="logo.svg"> Mithril <small>1.0.0</small></h1>
<nav>
<a href="index.html">Guide</a>
<a href="api.html">API</a>
<a href="https://gitter.im/lhorie/mithril.js">Chat</a>
<a href="https://github.com/lhorie/mithril.js">Github</a>
</nav>
</section>
</header>
<main>
<section>
<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">// Component containing a Select that uses Select2 for functionality.
var S2Component = {
// Rendered view for S2
view: function(ctrl, attrs) {
var current = attrs.selectedUser
return m('select', {
class: 'select-field',
config: S2Component._configure
},
attrs.data.map(function(item){
return m('option', {
id: item.id,
value: item.value,
selected: (item.id === current) ? true : false
}, item.name);
})
)
},
// Configure function -- called from m('select') in the view
_configure: function(element, initialized) {
/*
Note: This function is being called from the 'config' attribute
in our mithril view.
Integration with third-party party DOM manipulation (jQuery) needs
{config: function(){}} because that's the attribute that exposes the real
DOM element (as opposed to the virtual DOM element) in the corresponding
function so you can access and manipulate it.
*/
// If this hasn't been initialized, we can do our setup
if(!initialized) {
$(element).select2({
// options
});
// Other logic pertaining to this select also goes here.
// e.g. Event handlers, etc.
}
}
}
// Primary component.
var MainComponent = {
controller: function() {
var ctrl = this;
// Some arbitrary data
// Aaron Burr is the initially selected user.
ctrl.selectedUser = 2;
ctrl.data = [
{id: 1, name: 'Alexander Hamilton'},
{id: 2, name: 'Aaron Burr'},
{id: 3, name: 'Thomas Jefferson'},
{id: 4, name: 'John Adams'},
{id: 5, name: 'James Madison'},
{id: 6, name: 'Elizabeth Schuyler'},
{id: 7, name: 'King George'},
{id: 8, name: 'Marquis de Lafayette'}
]
},
view: function(ctrl) {
return m('div', {class: 'select-container'}, [
m('label', 'Historical Figure: '),
m(S2Component, {
selectedUser: ctrl.selectedUser,
data: ctrl.data
})
])
}
}
m.mount(document.body, MainComponent)
</code></pre>
<p><a href="https://jsfiddle.net/11pz8afy/9/">Source code in JSFiddle</a></p>
<p><code>_configure</code> is a helper function that is called via the <code>config</code> attribute in the <code>select</code> we render in our <code>SC2Component.view</code></p>
<p>This <code>_configure</code> function has a guarded <code>if</code> statement: <code>if(!initialized)</code>. In the event this component is being instantiated for the first time, we're going to do all of the initial setup on the first render. Subsequent redraws will <strong>not</strong> run the initialization code again, making sure everything in the <code>_configure</code> function is only initialized once.</p>
<p>The initialization code is simply calling <code>$(element).select2()</code> on the exposed DOM element in order to initialize it. You can also addevent handlers to these elements. If you modify the DOM or any data that your component relies on inside of this function, you'll need to make sure the component knows to redraw itself by adding <code>m.redraw</code> as needed.</p>
<p><code>m.startComputation</code> and <code>m.endComputation</code> are used for asynchronous operations. 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 any event handlers. Refer to the <a href="auto-redrawing.html"><code>auto-redrawing</code></a> guide for an example.</p>
<p>Though possible, you should avoid calling <code>m.redraw</code>, <code>m.startComputation</code> and <code>m.endComputation</code> in the <code>_configure</code> function's execution thread. (An execution thread is basically any amount of code that runs before other asynchronous threads start to run.) 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 component in the example shows how a developer would consume the <code>SC2Component</code>.</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>
<hr />
<small>License: MIT. © Leo Horie.</small>
</section>
</main>
<script src="lib/prism/prism.js"></script>
<script>
document.querySelector(".hamburger").onclick = function() {
document.body.className = 'navigating'
document.querySelector("h1 + ul").onclick = function() {
document.body.className = ''
}
}
</script>
</body>
</html