Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Mithril and jQuery interact with each other?

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 jQuery with Mithril

What I understand is, I can use Mithril config to access the real DOM element & bind to various jQuery functions safely.

Using jQuery UI functions with Mithril

But what about using jQuery selectors on classes or ids to locate the real DOM element, like

attaching a jQuery date picker

  beforeShow: function(input, inst) {
   $('#ui-datepicker-div').addClass("mydatepicker");
  },

or hiding a div

 $("#mydiv").hide();

What is the danger of inprogress m.render causing $('blah') === undefined.

Would really like to understand the how these 2 components could/should interact with each other.

like image 341
chifer Avatar asked Nov 26 '14 01:11

chifer


2 Answers

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.

like image 95
LeoHorie Avatar answered Oct 25 '22 18:10

LeoHorie


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
}  );
like image 30
Barney Avatar answered Oct 25 '22 20:10

Barney