Skip to content

Instantly share code, notes, and snippets.

@gilbert
Last active August 3, 2016 21:19
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gilbert/0bf02f1f21c72de9fb49 to your computer and use it in GitHub Desktop.
Save gilbert/0bf02f1f21c72de9fb49 to your computer and use it in GitHub Desktop.
Mithril.js - Avoiding m.props
// m.prop is a great pattern, but Plain-old JavaScript Objects (POJO) are
// much more pleasant to work with.
// Here's an example of using POJOs with one- and two-way data binding.
// First, the helper methods
m.setValue = function (obj, prop) {
return m.withAttr('value', function(value) { obj[prop] = value })
}
// This helper isn't necessary if you're using ES6
m.set = function (obj, prop, value) {
return function () { obj[prop] = value }
}
//
// Usage Example #1
//
var CommentBox = {}
CommentBox.controller = function () {
var ctrl = this
ctrl.userInput = '' // No m.prop!
}
CommentBox.view = function (ctrl) {
return m('.comment-box', [
m('h1', "Please enter your comment:"),
m('textarea', { onchange: m.setValue(ctrl, 'userInput') }, ctrl.userInput)
])
}
//
// Usage Example #2
//
var TabbedContent = {}
TabbedContent.controller = function () {
var ctrl = this
ctrl.tab = 'first' // No m.prop!
}
TabbedContent.view = function (ctrl) {
return m('.tabs', [
m('.tab', { onclick: m.set(ctrl, 'tab', 'first') }, "First Tab"),
m('.tab', { onclick: m.set(ctrl, 'tab', 'second') }, "Second Tab"),
// ES6 version
// m('.tab', { onclick: () => ctrl.tab = 'first') }, "First Tab"),
// m('.tab', { onclick: () => ctrl.tab = 'second') }, "Second Tab"),
ctrl.tab === 'first' ? m('h1', "Content 1") : null,
ctrl.tab === 'second' ? m('h1', "Content 2") : null
])
}
//
// Usage Example #3 - Sharing state between siblings
//
var Parent = {}
Parent.controller = function () {
this.names = ['Alice', 'Bob']
}
Parent.view = function (ctrl) {
return [ m(SiblingA, { names: ctrl.names }), m(SiblingB, { names: ctrl.names }) ]
}
var SiblingA = {
view: function (ctrl, options) {
return m('ul.sib-a', options.names.map(function (name) {
return m('li', name)
}))
}
}
var SiblingB = {
view: function (ctrl, options) {
return m('.sib-b', [
m('button', {
onclick: function (e) {
var newName = prompt("Please enter a new name:")
names.push(newName)
}
}, "Add name")
])
}
}
//
// Usage Example #4 - Sharing primitives between siblings
//
var Parent = {}
Parent.controller = function () {
this.data = { x: 10, y: 20 }
}
Parent.view = function (ctrl) {
return [ m(SiblingA, { shared: ctrl.data }), m(SiblingB, { shared: ctrl.data }) ]
}
var SiblingA = {
view: function (ctrl, options) {
return m('p', "x: " + options.shared.x, "y: " + options.shared.y)
}
}
var SiblingB = {
view: function (ctrl, options) {
return m('.sib-b', [
m('button', {
onclick: function (e) {
var newX = prompt("Please enter a new x value:")
options.shared.x = parseInt(newX, 10)
}
}, "Change x")
])
}
}
@panych
Copy link

panych commented Jun 27, 2016

As I know, Mithril uses m.prop not only to support getter/setter pattern, but for controlling auto redrawing system.

@gilbert
Copy link
Author

gilbert commented Aug 2, 2016

@panych That is actually a common misconception. m.prop is only a getter / setter; it has no ties to the redraw system.

@panych
Copy link

panych commented Aug 3, 2016

You are right, it's true for Mithril 0.2.x. But m.prop in the upcoming major version of Mithril ("rewrite" brunch) will produce a stream, which brings features, like computed values.

@gilbert
Copy link
Author

gilbert commented Aug 3, 2016

Yes, using the stream version of m.prop will be much more useful... but only when you need it. Always reach for simple before complex! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment