Skip to content

Helpers and Extensions

Isiah Meadows edited this page Nov 26, 2015 · 24 revisions

A collection of extensions to vanilla Mithril that you might find useful.


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 - 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 = m.prop(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.

Clone this wiki locally