Created
August 28, 2017 19:28
-
-
Save jdkanani/a81d09bad12f1bab028ab1e651c82803 to your computer and use it in GitHub Desktop.
vuexfire + vuefire
This file contains hidden or 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
import Vue from 'vue'; | |
import { VueFireMixin, firebaseMutations, bind, unbind } from '~/plugins/vuefire'; | |
// vuefire init | |
export default function ({ store }) { | |
// use object-based merge strategy | |
// TODO This makes impossible to merge functions | |
const mergeStrats = Vue.config.optionMergeStrategies; | |
mergeStrats.firebase = mergeStrats.methods; | |
// For normal vuefire: mutate vm's key directly while committing | |
function commit (type, ...args) { | |
firebaseMutations[type](this, ...args); | |
} | |
// extend instance methods | |
// bind as object | |
Vue.prototype.$bindAsObject = function (key, source, options = {}) { | |
options.asObject = true; | |
bind({ commit: commit.bind(this), state: this }, key, source, options); | |
}; | |
// bind as array | |
Vue.prototype.$bindAsArray = function (key, source, options = {}) { | |
bind({ commit: commit.bind(this), state: this }, key, source, options); | |
}; | |
// unbind | |
Vue.prototype.$unbind = function (key) { | |
unbind({ commit: commit.bind(this), state: this }, key); | |
}; | |
// mixin | |
Vue.mixin(VueFireMixin); | |
} |
This file contains hidden or 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
/* eslint-disable */ | |
import Vue from 'vue'; | |
// mutations | |
const VUEXFIRE_VALUE = 'fire:onValue'; | |
const VUEXFIRE_ARRAY_ADD = 'fire:onChildAdded'; | |
const VUEXFIRE_ARRAY_CHANGE = 'fire:onChildChanged'; | |
const VUEXFIRE_ARRAY_MOVE = 'fire:onChildMoved'; | |
const VUEXFIRE_ARRAY_REMOVE = 'fire:onChildRemoved'; | |
const commitOptions = { root: true }; | |
// check if object | |
const isObject = val => Object.prototype.toString.call(val) === '[object Object]'; | |
const normalizeMap = map => (Array.isArray(map) | |
? map.map(key => ({ key, val: key })) | |
: Object.keys(map).map(key => ({ key, val: map[key] }))); | |
// Get key and ref | |
const getKey = snapshot => (typeof snapshot.key === 'function' ? snapshot.key() : snapshot.key); | |
const getRef = (refOrQuery) => { | |
let result; | |
if (typeof refOrQuery.ref === 'function') { | |
result = refOrQuery.ref(); | |
} else if (typeof refOrQuery.ref === 'object') { | |
result = refOrQuery.ref; | |
} | |
return result; | |
}; | |
// create record | |
const createRecord = (snapshot, Model) => { | |
const value = snapshot.val(); | |
let result; | |
if (isObject(value)) { | |
result = Model ? new Model(value) : value; | |
} else { | |
result = { '.value': value }; | |
} | |
result['.key'] = getKey(snapshot); | |
return result; | |
}; | |
// get index for key | |
const indexForKey = (array, key) => { | |
let i; | |
for (i = 0; i < array.length; i += 1) { | |
if (array[i]['.key'] === key) { | |
return i; | |
} | |
} | |
return -1; | |
}; | |
export const bind = (vm, key, source, options) => { | |
if (!vm.state.$firebaseRefs) { | |
vm.state.$firebaseRefs = Object.create(null); | |
vm.state.$firebaseSources = Object.create(null); | |
vm.state.$firebaseListeners = Object.create(null); | |
} | |
const asObject = !!options.asObject; | |
const ref = getRef(source); | |
vm.state.$firebaseRefs[key] = ref; | |
vm.state.$firebaseSources[key] = source; | |
// bind based on initial value type | |
if (asObject) { | |
bindAsObject(vm, key, source, options); | |
} else { | |
bindAsArray(vm, key, source, options); | |
} | |
options.readyCallback && source.once('value', options.readyCallback.bind(vm)); | |
}; | |
export const unbind = (vm, key) => { | |
const source = vm.state.$firebaseSources && vm.state.$firebaseSources[key]; | |
if (!source) { | |
throw new Error(`VueFire: unbind failed: ${key} is not bound to a Firebase reference.`); | |
} | |
const listeners = vm.state.$firebaseListeners[key]; | |
Object.keys(listeners).forEach((event) => { | |
source.off(event, listeners[event]); | |
}); | |
// set value to vuex or simple component | |
vm.commit(VUEXFIRE_VALUE, { key, value: null }, commitOptions); | |
vm.state.$firebaseRefs[key] = null; | |
vm.state.$firebaseSources[key] = null; | |
vm.state.$firebaseListeners[key] = null; | |
}; | |
const bindAsObject = (vm, key, source, options) => { | |
// set value to vuex or simple component | |
if (!vm.state[key]) { | |
vm.commit(VUEXFIRE_VALUE, { key, value: {} }, commitOptions); | |
} | |
const cancelCallback = options.cancelCallback; | |
let func = options.once ? source.once : source.on; | |
func = func.bind(source); // need to bind with source object | |
const cb = func('value', (snapshot) => { | |
const value = createRecord(snapshot, options.model); | |
// set value | |
vm.commit(VUEXFIRE_VALUE, { key, value }, commitOptions); | |
// on change callback | |
options.onChange && options.onChange(value); | |
}, cancelCallback); | |
vm.state.$firebaseListeners[key] = { value: cb }; | |
}; | |
const bindAsArray = (vm, key, source, options) => { | |
if (!vm.state[key]) { | |
// set value to vuex or simple component | |
vm.commit(VUEXFIRE_VALUE, { key, value: [] }, commitOptions); | |
} | |
const array = vm.state[key]; | |
const cancelCallback = options.cancelCallback; | |
const onAdd = source.on('child_added', (snapshot, prevKey) => { | |
const index = prevKey ? indexForKey(array, prevKey) + 1 : array.length; | |
const record = createRecord(snapshot, options.model); | |
vm.commit(VUEXFIRE_ARRAY_ADD, { key, index, record, array }, commitOptions); | |
options.onChildAdded && options.onChildAdded(record); | |
}, cancelCallback); | |
const onRemove = source.on('child_removed', (snapshot) => { | |
const index = indexForKey(array, getKey(snapshot)); | |
const record = array[index]; | |
vm.commit(VUEXFIRE_ARRAY_REMOVE, { key, index, record, array }, commitOptions); | |
options.onChildRemoved && options.onChildRemoved(record); | |
}, cancelCallback); | |
const onChange = source.on('child_changed', (snapshot) => { | |
const index = indexForKey(array, getKey(snapshot)); | |
const record = createRecord(snapshot, options.model); | |
vm.commit(VUEXFIRE_ARRAY_CHANGE, { key, index, record, array }, commitOptions); | |
options.onChildChanged && options.onChildChanged(record); | |
}, cancelCallback); | |
const onMove = source.on('child_moved', (snapshot, prevKey) => { | |
const index = indexForKey(array, getKey(snapshot)); | |
const record = createRecord(snapshot, options.model); | |
let newIndex = prevKey ? indexForKey(array, prevKey) + 1 : 0; | |
// TODO refactor | |
newIndex += index < newIndex ? -1 : 0; | |
vm.commit(VUEXFIRE_ARRAY_MOVE, { key, index, record, newIndex, array }, commitOptions); | |
}, cancelCallback); | |
vm.state.$firebaseListeners[key] = { | |
child_added: onAdd, | |
child_removed: onRemove, | |
child_changed: onChange, | |
child_moved: onMove, | |
}; | |
}; | |
// mutations | |
export const firebaseMutations = { | |
[VUEXFIRE_VALUE] (state, { key, value }) { | |
state[key] = value; | |
}, | |
[VUEXFIRE_ARRAY_ADD] (state, { key, index, record, array }) { | |
array = array || state[key]; | |
array.splice(index, 0, record); | |
}, | |
[VUEXFIRE_ARRAY_CHANGE] (state, { key, index, record, array }) { | |
array = array || state[key]; | |
array.splice(index, 1, record); | |
}, | |
[VUEXFIRE_ARRAY_MOVE] (state, { key, index, record, newIndex, array }) { | |
array = array || state[key]; | |
array.splice(newIndex, 0, array.splice(index, 1)[0]); | |
}, | |
[VUEXFIRE_ARRAY_REMOVE] (state, { key, index, array }) { | |
array = array || state[key]; | |
array.splice(index, 1); | |
}, | |
}; | |
// actions | |
export const firebaseActions = { | |
bindFirebaseRef (context, { key, source, options = {} }) { | |
const { state, commit } = context; | |
bind({ state, commit }, key, source, options); | |
}, | |
unbindFirebaseRef (context, { key }) { | |
const { state, commit } = context; | |
unbind({ state, commit }, key); | |
}, | |
}; | |
// vue fire mixin | |
export const VueFireMixin = { | |
beforeDestroy () { | |
if (!this.$firebaseRefs) return; | |
Object.keys(this.$firebaseRefs).forEach((key) => { | |
if (this.$firebaseRefs[key]) { | |
this.$unbind(key); | |
} | |
}); | |
this.$firebaseRefs = null; | |
this.$firebaseSources = null; | |
this.$firebaseListeners = null; | |
}, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment