I am using Mithril as our MVC framework & I want to leverage of rich JQuery/Jquery UI functionalities. I would like to understand the \'Do\'s and Don\'t\'s\' when combining
Let's first figure out what each library is doing: Mithril is an MVC scaffold that's used to define the structure and lifecycle of your application. Mithril's views define the DOM, including what IDs the DOM elements will have, and will also dictate when these elements are removed or changed; jQuery UI is used to define the behaviour of widgets which sit within your wider view structure.
Mithril provides a config
attribute to expose a function that gives you access to the 'real DOM element' you're talking about. The function is executed whenever the Mithril view has rendered or changed: the first argument is the DOM element; the second argument is false
if the element has just been created and true
otherwise; the third argument is the context
– it allows you to define extra behaviour before the element is removed from the DOM.
config
will only execute when the element actually exists, and provides a reference for it. For this reason, your jQuery UI code should be inside this function. A benefit of this is that you never need a CSS selector style reference to the element, because config always provides a direct reference as the first argument. Let's rewrite your first snippet to work this way:
m.module( document.body, {
controller : function(){
},
// Because the view is generated by Mithril code
// (which could change the classes or IDs, or remove elements entirely...
view : function(){
return m( '.this',
m( '.is',
m( '.all',
m( '.rendered',
m( '.by',
m( '.mithril',
// ...None of this is referenced by jQuery.
m( 'input[placeholder=Rendering by Mithril!]', {
// All jQuery happens in external functions, attached like this:
config : configDatePicker
} ) ) ) ) ) ) );
}
} )
// ...Meanwhile...
function configDatePicker( element, init, context ){
// We don't want to add the class all the time, only the first time the element is created
if( !init ){
// Here we reference the element directly, and pass it to jQuery
$( element ).datepicker().on( 'click', function(){
$( element ).val( 'Behaviour by jQuery!' )
} );
// We can also bind an event to trigger behaviour when the element is destroyed
context.onunload = function(){
// …But this will never happen because our code doesn't do that ;)
alert( '.mydatepicker is going to be destroyed!' )
};
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://code.jquery.com/ui/1.11.1/themes/smoothness/jquery-ui.css" rel="stylesheet"/>
<script src="http://cdnjs.cloudflare.com/ajax/libs/mithril/0.1.24/mithril.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.2/jquery-ui.min.js"></script>
Probably the best way to think about this in a jQuery way is that config
is a bit like DOM ready:
$( document ).ready( function thisIsABitLikeAConfigFunction( element ){
// Except that DOM ready only works on the document element
} );
In a nutshell, all config
functions are guaranteed to run after the DOM tree has been created. so from within a config, you can call $(bla) without worrying whether the element has been drawn.
The caveat with Mithril (or for that matter, any system that allows subtemplates to be mounted and unmounted) is that elements may be removed from the DOM by conditional logic. Because of this, it's recommended that you either attach config
to the element that is going to be affected by the jQuery plugin, or wrap a subtree of elements in a function in order to make it more obvious that a config that uses querySelector applies to that specific range of elements.
For a large number of jQuery calls, it actually doesn't matter if the element being queried is there or not (e.g. $(".foo").hide()
simply does nothing if no .foo
's are present in the page).
The major thing to be concerned about is that you don't want to drive too much state from the DOM itself (which is somewhat idiomatic in jQuery). For example, toggling the visibility of a panel might be something that can be done quicker in jQuery, but it's harder to reach both visible and invisible states from, say, a page load, if the canonical data source is the CSS class in the DOM that is controlled by jQuery code, instead of a view-model flag that unidirectionally flows into the view.