Skip to content

Instantly share code, notes, and snippets.

@webcss
Last active April 6, 2016 07:47
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save webcss/5d59d15fb07204c7d732 to your computer and use it in GitHub Desktop.
Save webcss/5d59d15fb07204c7d732 to your computer and use it in GitHub Desktop.
Firebase with mithril. Note that the gists provided here are deprecated and will soon be removed. You should use firebaseMixin (https://gist.github.com/webcss/e4aaa7d95342d107e1ce) instead.
var Dinosaurs = {
model: function() {
// return a firebase reference to our database
return new Firebase('https://dinosaur-facts.firebaseio.com');
},
controller: function() {
var ref = Dinosaurs.model();
this.facts = mithrilFire(ref.orderByChild('height'));
},
view: function(ctrl) {
return [
m('h1', 'Dinosaur facts'),
m('ul',
ctrl.facts.map(function(dino, id){
return m('li', [
m('h3', id + ' was ' + dino.height ' meters tall'),
m('input', { onchange: ctrl.facts.withValue('value', id, 'height'), value: dino.height })
]);
})
)
];
}
};
m.module(document.body, Dinosaurs);
/***************************************************************
* MithrilFire - m.firebase
* version 1.0.3
* make firebase play nice with Mithril
* param @pathRef string/object - a firebase url/reference to listen to
* return object - firebase object accessor
================================================================
This is a lightweight solution to handle firebase data with
mithril. It takes a firebase reference and returns an object which
provides access to the returned data in various ways.
Basic CRUD methods allow for add, update, remove and DataBinding
of snapshot values.
****************************************************************/
m.firebase = function(pathRef, options) {
var ref = (typeof pathRef === 'string') ? new Firebase(m.firebase.rootPath + '/' + pathRef): pathRef;
function typeOf(o) {
return Object.prototype.toString.call(o).slice(8, -1).toLowerCase();
}
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;
}
}
function __onadded(arr, deferred) {
return function(snapshot, prevChild) {
var pos, data,
idx = findIndex(arr, snapshot.key());
if (idx < 0) {
data = snapshot.val();
if(typeOf(data) !== 'object' || !data) {
data = { '.value': data };
}
data._id = snapshot.key();
pos = positionAfter(arr, prevChild);
arr.splice(pos, 0, data);
deferred.resolve(arr);
m.redraw();
}
};
}
function __onchanged(arr, deferred) {
return function(snapshot) {
var data,
idx = findIndex(arr, snapshot.key());
if (idx > -1) {
data = snapshot.val();
if(typeOf(data) !== 'object' || !data) {
data = { '.value': data };
}
data._id = snapshot.key();
arr[idx] = data;
deferred.resolve(arr);
m.redraw();
}
};
}
function __onremoved(arr, deferred) {
return function(snapshot) {
var idx = findIndex(arr, snapshot.key());
if (idx > -1) {
arr.splice(idx, 1);
deferred.resolve(arr);
m.redraw();
}
};
}
function __onmoved(arr, deferred) {
return function(snapshot, prevChild) {
var data, newpos,
idx = findIndex(arr, snapshot.key());
if (idx > -1) {
data = arr.splice(idx, 1)[0];
newpos = positionAfter(arr, prevChild);
arr.splice(newpos, 0, data);
deferred.resolve(arr);
m.redraw();
}
};
}
options = options || {};
if (options.orderBy) {
switch(options.orderBy.toLowerCase()) {
case '.key': ref = ref.orderByKey(); break;
case '.value': ref = ref.orderByValue(); break;
case '.priority': ref = ref.orderByPriority(); break;
default:
ref = ref.orderByChild(options.orderBy);
}
if (options.startAt) {
ref = ref.startAt(options.startAt);
}
if (options.endAt) {
ref = ref.endAt(options.endAt);
}
if (options.equalTo) {
ref = ref.equalTo(options.equalTo);
}
}
return {
/**
* asArray
* param @options object - define optional params like ordering, range queries
* return mithril promise resolving to array for the given firebase location
*
* applies listeners for live data changes
*/
asArray: function(options) {
var d = m.deferred(),
out = [];
ref.once('value', function(snapshot) {
m.startComputation();
snapshot.forEach(function(record) {
var data = record.val();
if(typeOf(data) !== 'object' || !data) {
data = { '.value': data };
}
data._id = record.key();
out.push(data);
});
d.resolve(out);
m.endComputation();
// m.redraw();
// make sure we unbind any previously added listeners to avoid multiplication of lissteners
ref.off();
// listen for changes
ref.on('child_added', __onadded(out, d));
ref.on('child_changed', __onchanged(out, d));
ref.on('child_removed', __onremoved(out, d));
ref.on('child_moved', __onmoved(out, d));
});
return d.promise;
},
/**
* asObject
* param @once bool - define wether or not to execute only once
* return mithril promise resolving to an object with the data at the given firebase location
*
* when once is false, updates live data changes
*/
asObject: function(once) {
var d = m.deferred();
// make sure we unbind any previously added listeners to avoid multiplication of lissteners
if(once) ref.off();
ref[once?'once':'on']('value', function(snapshot) {
d.resolve(snapshot.val());
m.redraw();
});
return d.promise;
},
/**
* selectedKeys
* param @keys array - keys for which to retrieve data
* return mithril promise resolving to an array with the data at the given firebase locations
*
* resolve master-detail where master holds an array of detail keys
*/
selectedKeys: function(keys) {
var d = m.deferred(),
out = [];
keys.forEach(function(key) {
m.startComputation();
ref.child(key).once('value', function(snapshot) {
var data = snapshot.val();
if (data) {
if(typeOf(data) !== 'object' || !data) {
data = { '.value': data };
}
data._id = snapshot.key();
out.push(data);
d.resolve(out);
// m.redraw();
} else {
console.info('value not found at: ' + ref.path.toString() + '/' + key);
}
m.endComputation();
});
});
// make sure we unbind any previously added listeners to avoid multiplication of lissteners
ref.off();
// only listen for changed or removed children
// ref.on('child_added', __onadded(out, d));
ref.on('child_changed', __onchanged(out, d));
ref.on('child_removed', __onremoved(out, d));
ref.on('child_moved', __onmoved(out, d));
return d.promise;
},
/**
* bindHelper
* param @key string - key index
* param @property string - key property to bind to
* param @attr string - HTML attribute to bind to
* return mithril withAttr function
*
*/
bindHelper: function(key, childProp, attr) {
return m.withAttr(attr, function(value) {
ref.child(key).child(childProp).set(value);
});
},
/**
* unbind
* remove any listeners applied
*
*/
unbind: function() {
ref.off();
},
/**
* add
* param @value any - value to add to this location
* param @priority any - optional priority for this value
* return key string - the firebasekey for the newly added value
*
*/
add: function(value, priority) {
var newRef = ref.push(value);
if(priority) {
newRef.setPriority(priority);
}
return newRef.key();
},
/**
* update
* param @key string - the key for the location to update
* param @newData any - the update value
* param @priority any - optional priority for this value
*
*/
update: function(key, newData, priority) {
// make sure we don't accidentally push our _id/key prop
if(newData.hasOwnProperty('_id')) {
delete newData._id;
}
if(newData.hasOwnProperty('.value')){
newData = newData['.value'];
}
if (priority) {
ref.child(key).setWithPriority(newData, priority);
} else {
ref.child(key).set(newData);
}
},
/**
* updatePriority
* param @key string - the key for the location
* param @priority any - optional priority for this location
*
*/
updatePriority: function(key, priority) {
ref.child(key).setPriority(priority);
},
/**
* remove
* param @key string - the key for the location to remove
*
*/
remove: function(key) {
ref.child(key).remove();
}
};
};
/**
* m.firebase.rootPath
* rootPath to your data
*
*/
m.firebase.rootPath = '';
/***************************************************************
* MithrilFire
* version 0.7
* make firebase play nice with Mithril
* param @ref object - a firebase reference to listen to
* param @bool boolean - optionally set to true to listen once
* return object - firebase object accessor
================================================================
This is a lightweight solution to handle firebase data with
mithril. It takes a firebase reference and returns an object which
provides access to the returned data.
since firebase stores data as objects only, extend it with some
useful array methods (forEach, filter, map, reduce) which work on
the returned snapshot.
Basic CRUD methods allow for add, update, remove and DataBinding
of snapshot values.
****************************************************************/
var mithrilFire = function mithrilFire(ref, once) {
var data = [];
var fbo = {
asArray: function asArray() {
var out = [];
data.forEach(function(item) {
var value = item.val();
if(typeof(value) !== 'object' || !value) {
value = { '.value': value };
}
value.$id = item.key();
out.push(value);
});
return out;
},
filter: function filter(fn) {
var out = [];
data.forEach(function(item) {
if (fn.call(item, item.val(), item.key(), data)) {
out.push(item);
}
});
return out;
},
forEach: function forEach(fn) {
data.forEach(fn);
},
map: function map(fn) {
var out = [];
data.forEach(function(item) {
out.push(fn.call(item, item.val(), item.key(), data));
});
return out;
},
reduce: function reduce(fn, initValue) {
data.forEach(function(item) {
fn.call(item, initValue, item.val(), item.key(), data);
});
return initValue;
},
snapshot: function snapshot() {
return data;
},
add: function add(value, priority) {
var key = data.push(value);
if(priority) {
data.setPriority(key, priority);
}
return key;
},
update: function update(key, newData, priority) {
var ref = data.child(key).ref();
if (priority) {
ref.setWithPriority(newData, priority);
} else {
ref.set(newData);
}
},
updatePriority: function updatePriority(key, priority) {
var ref = data.child(key).ref();
ref.setPriority(priority);
},
remove: function remove(key) {
var ref = data.child(key).ref();
ref.remove();
},
withValue: function widthValue(attr, key, snapChild) {
return m.withAttr(attr, function(value) {
var ref = data.child(key).child(snapChild).ref();
ref.set(value);
});
}
};
m.startComputation();
ref[(once === true)? 'once': 'on']('value', function(snap) {
data = snap;
m.endComputation();
});
return fbo;
};
@webcss
Copy link
Author

webcss commented Mar 25, 2015

note that the gists provided here are deprecated and will soon be removed.
You should use (firebaseMixin)[https://gist.github.com/webcss/e4aaa7d95342d107e1ce] instead.

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