Last active
April 5, 2016 09:36
-
-
Save webcss/e4aaa7d95342d107e1ce to your computer and use it in GitHub Desktop.
Mixin for mithril controllers to enable firebase livedata
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************** | |
* Firebase mixin | |
******************************************/ | |
export function mixinFirebase(target) { | |
var OBJECT = '[object Object]', ARRAY = '[object Array]', STRING = '[object String]', FUNCTION = '[object Function]'; | |
var type = {}.toString; | |
var _references = []; | |
function unify(key, value) { | |
if(type.call(value) !== OBJECT || !value) { | |
value = { '.value': value }; | |
} | |
value._id = key; | |
return value; | |
} | |
function findIndex(arr, key) { | |
for(var i = 0, l = arr.length; i < l; i++) { | |
if(arr[i]._id === key) { | |
return i; | |
} | |
} | |
return -1; | |
} | |
// using the Firebase API's prevChild behavior, we | |
// place each element in the list after it's prev | |
// sibling or, if prevChild is null, at the beginning | |
function positionAfter(arr, prevChild) { | |
var idx; | |
if (prevChild === null) { | |
return 0; | |
} else { | |
idx = findIndex(arr, prevChild); | |
return (idx === -1) ? arr.length: idx + 1; | |
} | |
} | |
/** | |
* getData - retrieve the data at the specified firebase location once | |
* param @reference firebaseReference - firebase reference to write to | |
* param @oncomplete function - callback function with fetched data | |
* | |
*/ | |
target.getData = function(reference, oncomplete) { | |
reference.once('value', function(snap) { | |
oncomplete.call(target, snap.val()); | |
m.redraw(); | |
}); | |
}; | |
/** | |
* onlivedata - listen to firebase live data changes | |
* param @reference firebaseReference - firebase reference to write to | |
* param @ondata function - callback function with updated data | |
* param @asObject boolean - optional, pass a truthy value to receive a | |
* complete snapshot.val() rather than an array | |
* | |
*/ | |
target.onlivedata = function(reference, ondata, asObject) { | |
// save the reference for later removal of eventlisteners | |
_references.push(reference); | |
// add eventlistener | |
if (asObject) { | |
reference.on('value', function(snap) { | |
ondata.call(target, snap.val()); | |
m.redraw(); | |
}); | |
} else { | |
var out = []; | |
reference.on('child_added', function(snap, prevChild) { | |
var pos, idx = findIndex(out, snap.key()); | |
if (idx < 0) { | |
pos = positionAfter(out, prevChild); | |
out.splice( pos, 0, unify( snap.key(), snap.val() ) ); | |
ondata.call(target, out); | |
m.redraw(); | |
} | |
}); | |
reference.on('child_changed', function(snap) { | |
var idx = findIndex(out, snap.key()); | |
if (idx > -1) { | |
out[idx] = unify( snap.key(), snap.val() ); | |
ondata.call(target, out); | |
m.redraw(); | |
} | |
}); | |
reference.on('child_removed', function(snap) { | |
var idx = findIndex(out, snap.key()); | |
out.splice(idx, 1); | |
ondata.call(target, out); | |
m.redraw(); | |
}); | |
reference.on('child_moved', function(snapshot, prevChild) { | |
var data, newpos, | |
idx = findIndex(out, snapshot.key()); | |
if (idx > -1) { | |
data = out.splice(idx, 1)[0]; | |
newpos = positionAfter(out, prevChild); | |
out.splice(newpos, 0, data); | |
ondata.call(target, out); | |
m.redraw(); | |
} | |
}); | |
} | |
}; | |
/** | |
* bindFirebase - helper function to bind an input value to a firebase location | |
* param @ref firebaseReference - firebase reference to write to | |
* param @key string - key index | |
* param @property string - key property to bind to | |
* param @attr string - HTML attribute to bind to | |
* return mithril m.withAttr function | |
* | |
*/ | |
target.bindFirebase = function(reference, key, childProp, attr) { | |
return m.withAttr(attr, function(value) { | |
reference.child(key).child(childProp).set(value); | |
}); | |
}; | |
// save a reference to a possible present unload method | |
var _savedUnload = (target.onunload)? target.onunload : null; | |
target.onunload = function() { | |
while (_references.length) { | |
_references.pop().off(); | |
} | |
_savedUnload && _savedUnload(); | |
}; | |
return target; | |
} | |
// usage: | |
var MyCustomers = new Firebase('https://<myfirebase>.firebaseio.com/customers') | |
var Customers = {}; | |
Customers.controller = function(args) { | |
var scope = mixinFirebase(this); | |
scope.onLivedata(args.firebase, function(data) { | |
scope.customers = data; | |
}); | |
}; | |
Customers.view = function(ctrl) { | |
return m('ul', ctrl.customers.map(function(customer) { | |
return m('li', { key: customer._id }, customer.name ); | |
})); | |
}; | |
m.module(document.body, Customers, { firebase: MyCustomers }); |
@kulakowka Would be nice if you'd at least mentioned me as the initial author in your repo's readme or within your license...
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I have created repo for this: https://github.com/kulakowka/mithril-firebase-mixin
And published it to npm with minimal changes: https://www.npmjs.com/package/mithril-firebase-mixin
PR Welcome...