Why is m.module not causing my view to rerender in Mithril?

I thought that given an object with controller & view properties, when the controller's state changed (or an m.prop property was changed) it would rerender the template. This isn't happening in my case (I have to use redraw directly). What am I doing wrong here? ////////app.js

define([ 'mithril', 'propertyPane'],
  function(m, propertyPane){
    //div#properties exists
    m.module(document.getElementById('properties'),propertyPane);
});

///////propertyPane.js

define([ 'mithril', 'emitter'],
  function(m, emitter) {

    //mithril "namespace"
    var propertyPane = {};

    ///////////////////////
    //    Models         //
    ///////////////////////
    propertyPane.PropertyList = Array;

    propertyPane.Property = function(name, data) {
      //the following reflects the format of the data obects as sent by the event emitter
      this.name        = m.prop(name);
      this.value       = m.prop(data.value);
      this.type        = m.prop(data.valueType);
      this.visible     = m.prop(data.userVisible);
      this.editable    = m.prop(data.userEditable);
    };

    ///////////////////////
    //    Controller     //
    ///////////////////////
    propertyPane.controller = function() {

      this.properties = propertyPane.PropertyList();

      this.updatePropertyPane = function(sender) {
        //destroy current list
        this.properties.length = 0;

        for( var key in sender.currentItem.tag){
          this.properties.push(new propertyPane.Property(key, sender.currentItem.tag[key]));
        }
//>>>This is required to make the property pane to rerender:<<<
//      m.redraw(); //why?
//why doesn't redraw happen automatically (because I'm using m.module)??
      }.bind(this);

      //this reliably updates the the properties list
      //but rerender does not happen
      emitter.addCurrentItemChangedListener(this.updatePropertyPane);

    };

    ///////////////////////
    //    View           //
    ///////////////////////
    propertyPane.view = function(ctrl){
      return m('ul',
        ctrl.properties.map(function(property){
          return m('li', property.name() + ' ' + property.value());
        })
      );
    };

    return propertyPane;
});

I also tried adding a property called foo that updated in updatePropertyPane thus:

//in controller
this.foo = m.prop(Date.now());
//in updatePropertyPane
  this.foo(Date.now());
//in view
m('li', ...+this.foo()) //to make sure the view accesses this property.

That didn't work either.

Upvotes: 2

Views: 966

Answers (2)

JohnSz
JohnSz

Reputation: 2049

Mithril automatically re-renders

  • After all event handlers, e.g. m(tag, {onclick:...}, foo), which have fired are terminated.
  • An AJAX request has terminated.
  • Routing occurs.

Data binding value changes themselves do not cause re-renders and there are several reasons for this. To be fair, most data changes in Mithril occur within event handlers or during AJAX processing.

You can however force a re-render on a value change by using a modified version of m.prop():

mprop: function(store, onchange) {
    var prop = function() {
        if (arguments.length) {
            store = arguments[0];
            if (typeof onchange == 'function') { onchange(store); }
        }
        return store;
    }
    prop.toJSON = function() { return store; }
    return prop;
}

this.name = mprop("John", function(value) {
    // value is the new value
    // do a m.redraw() rather than m.startComputation / m.endComputation
});

However you do it, you should prefer m.redraw() for async events over m.startComputation / m.endComputation. It redraws the view for the currently active module and its called internally by Mithril's auto-redrawing system.

Upvotes: 2

guillaume
guillaume

Reputation: 1380

Surprisingly, m.prop returns a getter/setter very much without side effects.

Mithril hooks itself in the event listeners of your view.

So in my very limited experience with Mithril, you should either use m to set your event listeners or use m.startComputation / m.endComputation which call m.redraw with the added benefit of allowing different parts of your app to only redraw once despite updating several things.

Upvotes: 3

Related Questions