Helpers and Extensions - MithrilJS/mithril.js GitHub Wiki
A collection of extensions to vanilla Mithril that you might find useful.
- View Helpers
- Component Helpers
- Network Extensions
- Database Connectivity
- Router Helpers
- Events
- Helpers for m.prop
- Isiah Meadows' collection of general helpers
View Helpers
- mithril-n - raw DOM nodes in Mithril templates
- Stephan Hoyer's collection of view helpers
m.withValue = function (callback) {
return m.withAttr('value', callback)
}
Component Helpers
m.initComponent
This helper allows you to create an instance of a component and manage it yourself. Useful for when you want explicit control over your component's lifecycle, e.g. you want to control when your controller gets initialized.
m.initComponent = function (component, defaultOptions, defaultContent) {
var ctrl = new component.controller(options)
ctrl.render = function (options, content) {
return component.view(
controller,
options || defaultOptions,
content || defaultContent)
}
return ctrl
}
Example use:
var UserList = {
controller: function () { /* ... */ },
view: function () { /* ... */ }
}
var App = {
controller: function () {
this.userList = m.initComponent(UserList, {users: [ /* ... */ ]})
},
view: function (ctrl) {
return m('.app', [
m('h1', "My App"),
ctrl.userList.render()
])
}
}
Network Extensions
Helpers that deal with AJAX requests and the like.
m.deferred.resolve
This simple helper allows you to mock a promise with a successful value. Useful for AJAX cache layers and mocking server responses.
m.deferred.resolve = function (value) {
var deferred = m.deferred()
deferred.resolve(value)
return deferred.promise
}
m.deferred.reject
This simple helper allows you to mock a rejected promise. Useful for mocking server responses.
m.deferred.reject = function (value) {
var deferred = m.deferred()
deferred.reject(value)
return deferred.promise
}
Router Helpers
Route Wrapper
function Route(module, name) {
return {
controller: function() {
// Do something generic like calling Google Analytics from here
console.log("Router", name)
return new module.controller()
},
view: module.view
}
}
m.route(document.getElementById("page"), "/", {
"/": Route(app, "app"),
"/project/:id": Route(project, "project")
})
Events
Pub/Sub mixin
See this gist.
Utilizes the browser's event system. Useful in cases where you need communication between independent components. Registered events are automatically removed with onunload while preserving any pre-existing onunload handler.
function eventsMixin(target) {
var subscriptions = []
target.broadcast = function(type, payload) {
document.dispatchEvent(new CustomEvent(type, {
detail: payload,
bubbles: true,
cancelable: true
}))
}
target.subscribe = function (type, callback, capture) {
capture = !!capture
subscriptions.push([type, callback, capture])
document.addEventListener(type, callback, capture)
}
target.ignore = function(type, callback, capture) {
capture = !!capture
for (var i = 0; i < subscriptions; i++) {
var sub = subscriptions[i]
if (sub[0] === type && sub[1] === callback && sub[2] === capture) {
subscriptions.splice(i, 1)
}
}
document.removeEventListener(type, callback, capture)
}
// save a reference to a possible present unload method
var savedUnload = target.onunload || null
target.onunload = function () {
for (var i = 0; i < subscriptions; i++) {
document.removeEventListener.apply(document, subscriptions[i])
}
subscriptions = []
if (savedUnload) savedUnload.call(target)
}
return target
}
Example Use:
var obervableObject = eventsMixin({})
var component1 = {
controller: function () {
observable.subscribe('myFancyEvent', function (e) { /* ... */ })
observable.broadcast('myOtherEvent', 'data')
}
}
var component2 = {
controller: function () {
observable.subscribe('myOtherEvent', function (e) { /* ... */ })
observable.broadcast('myFancyEvent', 'data')
}
}
Utilities for m.prop
m.prop with change callback
Do something when an m.prop changes
m.prop = (function (old) {
return function (store, callback) {
var wrapped = old(store)
if (!callback) return wrapped
return function (value) {
if (arguments.length) callback(value)
return wrapped.apply(null, arguments)
}
}
})(m.prop);
// Example use
var myDomDestroyer = m.prop("", function () {
// Mwahahaha!
var head = document.getElementsByTagName("body")[0]
head.parentElement.removeChild(head)
var body = document.getElementsByTagName("body")[0]
body.parentElement.removeChild(body)
})
Redraw on m.prop update
m.prop = (function (old) {
return function (store) {
var wrapped = old(store)
return function (value) {
var res = wrapped.apply(null, arguments)
if (arguments.length) m.redraw()
return res
}
}
})(m.prop);
Replace m.prop with a POJO
m.prop is sometimes difficult to debug. See this gist for a replacement.