Skip to content

Commit b38167b

Browse files
committed
init
0 parents  commit b38167b

File tree

5 files changed

+380
-0
lines changed

5 files changed

+380
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.DS_Store
2+
node_modules

README.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# VueFire
2+
3+
## Installation
4+
5+
1. If included as global `<script>`: will install automatically if global `Vue` is present.
6+
7+
2. In module environments, e.g CommonJS:
8+
9+
``` js
10+
var Vue = require('vue')
11+
var VueFire = require('vuefire')
12+
Vue.use(VueFire)
13+
```
14+
15+
## Usage
16+
17+
``` js
18+
var vm = new Vue({
19+
el: '#demo',
20+
firebase: {
21+
// bind as an object
22+
anObject: new Firebase('url/to/my/object'),
23+
// bind as an array
24+
anArray: {
25+
// the source can be either a ref or a query
26+
source: new Firebase('url/to/my/list').limitToLast(25),
27+
asArray: true,
28+
// optionally provide the cancelCallback
29+
cancelCallback: function () {}
30+
}
31+
}
32+
})
33+
```
34+
35+
``` html
36+
<div id="demo">
37+
<pre>{{ anObject | json }}</pre>
38+
<ul>
39+
<li v-for="item in anArray">{{ item.text }}</li>
40+
</ul>
41+
</div>
42+
```
43+
44+
The above will bind the Vue instance's `anObject` and `anArray` to the respective Firebase data sources. In addition, the instance also gets the `$firebaseRefs` property, which holds the refs for each binding:
45+
46+
``` js
47+
// add an item to the array
48+
vm.$firebaseRefs.anArray.push({
49+
text: 'hello'
50+
})
51+
```
52+
53+
## Data Normalization
54+
55+
### Array Bindings
56+
57+
Each record in the bound array will contain a `_key` property which specifies the key where the record is stored. So if you have data at `/items/-Jtjl482BaXBCI7brMT8/`, the record for that data will have a `_key` of `"-Jtjl482BaXBCI7brMT8"`.
58+
59+
If an individual record's value in the database is a primitive (boolean, string, or number), the value will be stored in the `_value` property. If the individual record's value is an object, each of the object's properties will be stored as properties of the bound record. As an example, let's assume the `/items/` node you bind to contains the following data:
60+
61+
``` json
62+
{
63+
"items": {
64+
"-Jtjl482BaXBCI7brMT8": 100,
65+
"-Jtjl6tmqjNeAnQvyD4l": {
66+
"first": "fred",
67+
"last": "Flintstone"
68+
},
69+
"-JtjlAXoQ3VAoNiJcka9": "foo"
70+
}
71+
}
72+
```
73+
74+
The resulting bound array stored in `vm.items` will be:
75+
76+
``` json
77+
[
78+
{
79+
"_key": "-Jtjl482BaXBCI7brMT8",
80+
"_value": 100
81+
},
82+
{
83+
"_key": "-Jtjl6tmqjNeAnQvyD4l",
84+
"first": "Fred",
85+
"last": "Flintstone"
86+
},
87+
{
88+
"_key": "-JtjlAXoQ3VAoNiJcka9",
89+
"_value": "foo"
90+
}
91+
]
92+
```

examples/todoApp/index.html

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>VueFire Todo App Demo</title>
6+
<script src="https://cdn.firebase.com/js/client/2.3.2/firebase.js"></script>
7+
<script src="https://cdn.jsdelivr.net/vue/1.0.13/vue.js"></script>
8+
<script src="../../src/vuefire.js"></script>
9+
</head>
10+
<body>
11+
12+
<div id="app">
13+
<ul>
14+
<li v-for="item in items" track-by="_key">
15+
{{ item.text }}
16+
<span @click="removeTodo(item._key)">X</span>
17+
</li>
18+
</ul>
19+
<form @submit.prevent="addTodo">
20+
<input v-model="newTodo">
21+
<button>Add #{{ items.length }}</button>
22+
</form>
23+
</div>
24+
25+
<script>
26+
var app = new Vue({
27+
el: '#app',
28+
data: {
29+
newTodo: ''
30+
},
31+
firebase: {
32+
items: {
33+
source: new Firebase('https://ReactFireTodoApp.firebaseio.com/items/').limitToLast(25),
34+
asArray: true
35+
}
36+
},
37+
methods: {
38+
removeTodo: function (key) {
39+
this.$firebaseRefs.items.child(key).remove()
40+
},
41+
addTodo: function () {
42+
if (this.newTodo.trim()) {
43+
this.$firebaseRefs.items.push({
44+
text: this.newTodo
45+
})
46+
this.newTodo = ''
47+
}
48+
}
49+
}
50+
})
51+
</script>
52+
</body>
53+
</html>

package.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "vuefire",
3+
"version": "1.0.0",
4+
"description": "Firebase mixin for Vue.js",
5+
"main": "src/vuefire.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/firebase/vuefire.git"
12+
},
13+
"keywords": [
14+
"vue",
15+
"mixin",
16+
"firebase",
17+
"realtime"
18+
],
19+
"author": "Evan You",
20+
"license": "MIT",
21+
"bugs": {
22+
"url": "https://github.com/firebase/vuefire/issues"
23+
},
24+
"homepage": "https://github.com/firebase/vuefire#readme"
25+
}

src/vuefire.js

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
(function (root, factory) {
2+
if (typeof exports === 'object' && typeof module === 'object')
3+
module.exports = factory()
4+
else if (typeof define === 'function' && define.amd)
5+
define([], factory)
6+
else if (typeof exports === 'object')
7+
exports["VueFire"] = factory()
8+
else
9+
root["VueFire"] = factory()
10+
})(this, function () {
11+
12+
// late bind
13+
var Vue
14+
15+
/**
16+
* Check if a value is an object.
17+
*
18+
* @param {*} val
19+
* @return {boolean}
20+
*/
21+
function isObject (val) {
22+
return Object.prototype.toString.call(val) === '[object Object]'
23+
}
24+
25+
/**
26+
* Convert firebase snapshot into a bindable data record.
27+
*
28+
* @param {FirebaseSnapshot} snapshot
29+
* @return {Object}
30+
*/
31+
function createRecord (snapshot) {
32+
var value = snapshot.val()
33+
var res = value && typeof value === 'object'
34+
? value
35+
: { _value: value }
36+
res._key = snapshot.key()
37+
return res
38+
}
39+
40+
/**
41+
* Find the index for an object with given key.
42+
*
43+
* @param {array} array
44+
* @param {string} key
45+
* @return {number}
46+
*/
47+
function indexForKey (array, key) {
48+
for (var i = 0; i < array.length; i++) {
49+
if (array[i]._key === key) {
50+
return i
51+
}
52+
}
53+
return -1
54+
}
55+
56+
/**
57+
* Bind a firebase data source to a key on a vm.
58+
*
59+
* @param {Vue} vm
60+
* @param {object} data
61+
* @param {string} key
62+
* @param {object} source
63+
*/
64+
function bind (vm, data, key, source) {
65+
if (!isObject(source)) {
66+
throw new Error('Invalid Firebase binding source');
67+
}
68+
var asArray = false
69+
var cancelCallback = null
70+
// check { source, asArray, cancelCallback } syntax
71+
if (isObject(source.source)) {
72+
asArray = source.asArray
73+
cancelCallback = source.cancelCallback
74+
source = source.source
75+
}
76+
// get the original ref for possible queries
77+
var ref = source
78+
if (typeof source.ref === 'function') {
79+
ref = source.ref()
80+
}
81+
vm.$firebaseRefs[key] = ref
82+
vm._firebaseSources[key] = source
83+
// bind based on initial value type
84+
if (asArray) {
85+
bindAsArray(vm, data, key, source, cancelCallback)
86+
} else {
87+
bindAsObject(vm, data, key, source, cancelCallback)
88+
}
89+
}
90+
91+
/**
92+
* Bind a firebase data source to a key on a vm as an Array.
93+
*
94+
* @param {Vue} vm
95+
* @param {object} data
96+
* @param {string} key
97+
* @param {object} source
98+
* @param {function|null} cancelCallback
99+
*/
100+
function bindAsArray (vm, data, key, source, cancelCallback) {
101+
var array = data[key] = []
102+
103+
var onAdd = source.on('child_added', function (snapshot, prevKey) {
104+
var index = prevKey ? indexForKey(array, prevKey) + 1 : 0
105+
array.splice(index, 0, createRecord(snapshot))
106+
}, cancelCallback)
107+
108+
var onRemove = source.on('child_removed', function (snapshot) {
109+
var index = indexForKey(array, snapshot.key())
110+
array.splice(index, 1)
111+
}, cancelCallback)
112+
113+
var onChange = source.on('child_changed', function (snapshot) {
114+
var index = indexForKey(array, snapshot.key())
115+
array.splice(index, 1, createRecord(snapshot))
116+
}, cancelCallback)
117+
118+
var onMove = source.on('child_moved', function (snapshot, prevKey) {
119+
var index = indexForKey(array, snapshot.key())
120+
var record = array.splice(index, 1)[0]
121+
var newIndex = prevKey ? indexForKey(array, prevKey) + 1 : 0
122+
array.splice(newIndex, 0, record)
123+
}, cancelCallback)
124+
125+
vm._firebaseListeners[key] = {
126+
child_added: onAdd,
127+
child_removed: onRemove,
128+
child_changed: onChange,
129+
child_moved: onMove
130+
}
131+
}
132+
133+
/**
134+
* Bind a firebase data source to a key on a vm as an Object.
135+
*
136+
* @param {Vue} vm
137+
* @param {object} data
138+
* @param {string} key
139+
* @param {Object} source
140+
* @param {function|null} cancelCallback
141+
*/
142+
function bindAsObject (vm, data, key, source, cancelCallback) {
143+
data[key] = {}
144+
var cb = source.on('value', function (snapshot) {
145+
vm[key] = snapshot.val()
146+
}, cancelCallback)
147+
vm._firebaseListeners[key] = { value: cb }
148+
}
149+
150+
/**
151+
* Unbind a firebase-bound key from a vm.
152+
*
153+
* @param {Vue} vm
154+
* @param {string} key
155+
*/
156+
function unbind (vm, key) {
157+
var source = vm._firebaseSources[key]
158+
var listeners = vm._firebaseListeners[key]
159+
for (var event in listeners) {
160+
source.off(event, listeners[event])
161+
}
162+
}
163+
164+
var VueFireMixin = {
165+
init: function () {
166+
var bindings = this.$options.firebase
167+
if (!bindings) return
168+
this.$firebaseRefs = Object.create(null)
169+
this._firebaseSources = Object.create(null)
170+
this._firebaseListeners = Object.create(null)
171+
// wrap data fn
172+
var vm = this
173+
var getData = this.$options.data
174+
this.$options.data = function () {
175+
var data = getData()
176+
for (var key in bindings) {
177+
bind(vm, data, key, bindings[key])
178+
}
179+
return data
180+
}
181+
},
182+
beforeDestroy: function () {
183+
for (var key in this.$firebaseRefs) {
184+
unbind(this, key)
185+
}
186+
this.$firebaseRefs =
187+
this._firebaseSources =
188+
this._firebaseListeners = null
189+
}
190+
}
191+
192+
/**
193+
* Install function passed to Vue.use() in manual installation.
194+
*
195+
* @param {function} _Vue
196+
*/
197+
function install (_Vue) {
198+
Vue = _Vue
199+
Vue.mixin(VueFireMixin)
200+
}
201+
202+
// auto install
203+
if (typeof window !== 'undefined' && window.Vue) {
204+
install(window.Vue)
205+
}
206+
207+
return install
208+
})

0 commit comments

Comments
 (0)