Reputation: 2321
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
Reputation: 2049
Mithril automatically re-renders
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
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