firesync
firesync copied to clipboard
:fire: firesync is a library for seamless data synchronization between Firebase and your local data.
Firesync - Overview
firesync is a library for seamless data synchronization between Firebase your local data and optionally the DOM, without the need of a framework, also known as three-way-data-binding.
The illustration below should help you understand how firesync works.
#Three-way-data-binding
The three-way-data-binding is a binding where the model, view and database are all updated simulateniously without extra code. Firesync achieves this by using RactiveJS for its DOM binding. One should be familiar with RactiveJS when using the dom binding.
Requirements
firesync runs equally well in browsers and in node. Since it uses Object.observe
to watch for local changes a polyfill should be used in environments that do not support it.
Installation and usage
bower
bower install firesync
<script src="bower_components/firesync/dist/firesync.js"></script>
nodejs
npm install firesync-node
var firesync = require('firesync-node');
Examples
Firebase to Firesync
Basic
var ref = new Firebase('https://example.firebaseio.com/users/admin'); // null
ref.once('value', function (snap) {
var val = snap.val();
val.name === 'admin';
val.email === '[email protected]';
});
var admin = new firesync.FiresyncObject(ref);
admin.name = 'admin';
admin.dateCreated = new Date().valueOf().toString();
admin.email = '[email protected]';
//these changes will be automatically synced to the server
Firesync object synchronized from the database first
var ref = new Firebase('https://example.firebaseio.com/users/admin'); // {name: 'admin'}
var adminUser = new firesync.FiresyncObject(ref);
//the user might have already loaded
if (!adminUser.loaded()) {
//in most cases you would not need to explicitly attach to events
adminUser.once('loaded', function () {
var name = adminUser.name;
adminUser.once('synced', function () {
//https://example.firebaseio.com/users/admin now is - {name: '__invalid'}
});
adminUser.name = '__invalid';
});
}
Firesync object synchronized from local first
var ref = new Firebase('https://example.firebaseio.com/users/newUser'); // null
var newUser = new firesync.FiresyncObject(ref);
newUser.name = 'newUser';
newUser.once('synced', function () {
//https://example.firebaseio.com/users/newUser now is - {name: 'newUser'}
});
Firesync array synchronized from database first
var ref = new Firebase('https://example.firebaseio.com/users'); // [{name: 'admin'}, {name: 'newUser'}]
var users = new firesync.FiresyncArray(ref);
//the array can be already loaded
if (!users.loaded()) {
//we do not need to use loaded to add values
users.once('loaded', function () {
//users = [{name: 'admin'}, {name: 'newUser'}]
users[0].name === 'admin';
users[1].name === 'newUser';
users.add({
name: 'newUser2'
});
//this will immediately push newUser2 to the remote array
});
}
Firebase to Firesync to DOM
Binding to an input element with inline template:
<div id="container"></div>
var ref = new Firebase('https://example.firebaseio.com');
var bindChild = ref.child('currentItem'); // { value: 'test' }
var currentItem = new firesync.FiresyncObject(bindChild)
.bindTo({
el: '#container',
template: '<input type="text" value="{{value}}" />'
});
//this results in the following html <div id="container"><input type="text" value="test" /></div>
currentItem.value = 'test123';
//this results in the following html <div id="container"><input type="text" value="test123" /></div>
//writing in the input will also change the .value property
Binding to an input element with external template:
<div id="container"></div>
<script type="text/ractive" id="template">
<input type="text" value="{{value}}" />
</script>
var ref = new Firebase('https://example.firebaseio.com');
var bindChild = ref.child('currentItem'); // { value: 'test' }
var currentItem = new firesync.FiresyncObject(bindChild)
.bindTo({
el: '#container',
template: '#template'
});
//this results in the following html <div id="container"><input type="text" value="test" /></div>
currentItem.value = 'test123';
//this results in the following html <div id="container"><input type="text" value="test123" /></div>
//writing in the input will also change the .value property
Bind to a list.
<div id="container"></div>
<script type="text/ractive" id="template">
<ul>
{{#iterator}} //iterator is a built-in variable of FiresyncArray which is an iteratable array compatible with ractivejs since ractivejs does not support array mixins.
<li>{{value}}</li>
{{/iterator}}
</ul>
</script>
var ref = new Firebase('https://example.firebaseio.com');
var bindChild = ref.child('currentItem'); // { '-Ju5kIB-e3ZABIccrOjK': { value: 'test' }, '-Mb4kIBa-3ZABIaarOjK': { value: 'test2' } }
var array = new firesync.FiresyncArray(bindChild)
.bindTo({
el: '#container',
template: '#template'
});
/*
* this results in the following html:
* <div id="container">
* <ul>
* <li>test</li>
* <li>test2</li>
* </ul>
* </div>
*/
array.add({value: 'test3'});
/*
* this results in the following html:
* <div id="container">
* <ul>
* <li>test</li>
* <li>test2</li>
* <li>test3</li>
* </ul>
* </div>
*
* And to something like this in the firebase:
* { '-Ju5kIB-e3ZABIccrOjK': { value: 'test' }, '-Mb4kIBa-3ZABIaarOjK': { value: 'test2' }, '-Vb6kFBa-3ZABIaarOmH': { value: 'test2' } }
*/
Firesync utility methods
firesync.create
- Automatically creates a synchronized object or array based on the underlying firebase value. The created
objects are guaranteed to be loaded.
var ref = new Firebase('https://example.firebaseio.com/users/fred'); //{name: 'fred'}
firesync.create(ref)
.then(function (firesyncObj) {
firesyncObj.name === 'fred';
firesyncObj.loaded() === true;
});
var usersRef = new Firebase('https://example.firebaseio.com/users'); //[{name: 'admin'}, {name: 'fred'}];
firesync.create(usersRef)
.then(function (firesyncArr) {
firesyncArr.lenght() === 2;
firesyncArr.loaded() === true;
firesyncArr[0].name === 'admin';
});
firesync.map
- Returns a non-synchronized object or array, depending on the underlying firebase value. Each object in the
returned object/array is a synchronized FiresyncObject or FiresyncArray
depending on the underlying firebase value. The objects are guaranteed to be loaded.
var usersRef = new Firebase('https://example.firebaseio.com/users'); //[{name: 'admin'}, {name: 'fred'}];
firesync.map(usersRef)
.then(function (arr) {
Array.isArray(arr) === true;
arr[0] instanceof firesync.FiresyncObject === true;
arr[1] instanceof firesync.FiresyncObject === true;
//changes to arr will not reflect the changes to the remote data, but changing any of the inner objects will
});
var someRef = new Firebase('https://example.firebaseio.com/someRef'); //{someObj: {name: 'admin'}, someArr: [1, 2]};
firesync.map(someRef)
.then(function (obj) {
typeof obj === 'object';
obj.someObj instanceof firesync.FiresyncObject;
obj.someArr instanceof firesync.FiresyncArray;
//changes to obj will not reflect the changes to the remote data, but changing any of the inner objects will
});
Important !!!
Whenever you are done with any FiresyncObject or FiresyncArray make sure to call the detach()
method as it will unattach the inner firebase listeners.
API Reference
Classes
- firesync
The entry point of firesync.
- FirebaseRef
FirebaseRef object
- RactiveJs
RactiveJs object
Kind: global class
-
firesync
-
.FiresyncArray ⇐
FiresyncBase
-
.iterator :
Array
-
.key() ⇒
string
-
.update(value, identifier) ⇒
Promise
-
.add(value, [key], [index]) ⇒
Promise
-
.remove(identifier) ⇒
Promise
-
.move(oldIdentifier, newIdentifier) ⇒
Promise
-
.loaded() ⇒
boolean
-
.ref() ⇒
FirebaseRef
- .detach()
-
.bindTo(settings) ⇒
FiresyncBase
- "changed"
- "loaded"
- "synced" (err)
-
.iterator :
-
.FiresyncObject ⇐
FiresyncBase
-
.loaded() ⇒
boolean
-
.ref() ⇒
FirebaseRef
- .detach()
-
.bindTo(settings) ⇒
FiresyncBase
- "changed"
- "loaded"
- "synced" (err)
-
.loaded() ⇒
-
.create(ref) ⇒
Promise
-
.map(ref) ⇒
Promise
-
.FiresyncArray ⇐
firesync.FiresyncArray ⇐ FiresyncBase
An array which keeps its values synchronized with the remote. One should use the FiresyncArray methods to manipulate the values.
Kind: static class of firesync
Extends: FiresyncBase
Mixes: Array
Access: protected
-
.FiresyncArray ⇐
FiresyncBase
-
.iterator :
Array
-
.key() ⇒
string
-
.update(value, identifier) ⇒
Promise
-
.add(value, [key], [index]) ⇒
Promise
-
.remove(identifier) ⇒
Promise
-
.move(oldIdentifier, newIdentifier) ⇒
Promise
-
.loaded() ⇒
boolean
-
.ref() ⇒
FirebaseRef
- .detach()
-
.bindTo(settings) ⇒
FiresyncBase
- "changed"
- "loaded"
- "synced" (err)
-
.iterator :
firesyncArray.iterator : Array
A simple array which is in sync with the FiresyncArray.
Kind: instance property of FiresyncArray
Example
firesyncArray.bindTo({el: 'body', template: '{{#iterator}}<div>{{value}}</div>{{/iterator}}'});
firesyncArray.key() ⇒ string
Returns a key by a specified index.
Kind: instance method of FiresyncArray
Returns: string
- The key of the object at the specified index.
Example
firesyncArray.key(0) === '-Ju5kIB-e3ZABIccrOjK';
firesyncArray.update(value, identifier) ⇒ Promise
Update an object using a specified identifier. This is the only supported way to update an element inside {FiresyncArray}. Keeps the synchronization.
Kind: instance method of FiresyncArray
Returns: Promise
- For when the synchronization is complete.
Param | Type | Description |
---|---|---|
value | any |
The update value. |
identifier | Identifier |
The identifier to be used to find the element. |
firesyncArray.add(value, [key], [index]) ⇒ Promise
Adds an element to the {FiresyncArray}. By default generates Firebase arrays with standard Firebase-generated keys. This is the only supported way to add an element inside {FiresyncArray}. Keeps the synchronization.
Kind: instance method of FiresyncArray
Returns: Promise
- For when the synchronization is complete.
Param | Type | Default | Description |
---|---|---|---|
value | any |
The value to add to the arary. | |
[key] | string |
"ref.push().key()" |
The key to be used for the element. Default key is recommended. |
[index] | number |
last |
The index at which to add the element to the local array. |
firesyncArray.remove(identifier) ⇒ Promise
Removes an element from the array by an {Identifier}
Kind: instance method of FiresyncArray
Returns: Promise
- For when the synchronization is complete.
Param | Type | Description |
---|---|---|
identifier | Identifier |
The identifier to find the array by. |
firesyncArray.move(oldIdentifier, newIdentifier) ⇒ Promise
Moves an element from one index to another.
Kind: instance method of FiresyncArray
Returns: Promise
- For when the synchronization is complete.
Param | Type | Description |
---|---|---|
oldIdentifier | Identifier |
The identifier for the old object. |
newIdentifier | Identifier |
The identifier for the new object. |
firesyncArray.loaded() ⇒ boolean
Indicates whether the object has loaded its data from Firebase.
Kind: instance method of FiresyncArray
firesyncArray.ref() ⇒ FirebaseRef
Returns the ref set in the constructor
Kind: instance method of FiresyncArray
firesyncArray.detach()
Detaches from the subscribed Firebase and DOM events. Must be called if the object will no longer be used.
Kind: instance method of FiresyncArray
firesyncArray.bindTo(settings) ⇒ FiresyncBase
Binds to DOM templates using RactiveJs. The settings are passed directly to RactiveJs.
Kind: instance method of FiresyncArray
Returns: FiresyncBase
- The current instance.
Param | Type | Description |
---|---|---|
settings | Object |
The settings passed to RactiveJs. Refer to the RactiveJs docs for more details |
Example
new (FiresyncObject|FiresyncArray).bindTo({ el: 'body', template: '<input value={{value}}/>' })
"changed"
Fired the local object changes, regardless whether it is a result of direct local change or remote change.
Kind: event emitted by FiresyncArray
Example
firesyncObject.on('changed', function(){});
"loaded"
Fired when the initial value of the object is loaded from the remote.
Kind: event emitted by FiresyncArray
Example
firesyncObject.on('loaded', function(){});
"synced" (err)
Fired when the local object's value is sucesfully set to the remote.
Kind: event emitted by FiresyncArray
Param | Type | Description |
---|---|---|
err | Error |
Synchronization error |
Example
firesyncObject.on('synced', function(err){});
firesync.FiresyncObject ⇐ FiresyncBase
An object which keeps its values synchronized with the remote Firebase.
Kind: static class of firesync
Extends: FiresyncBase
Access: protected
-
.FiresyncObject ⇐
FiresyncBase
-
.loaded() ⇒
boolean
-
.ref() ⇒
FirebaseRef
- .detach()
-
.bindTo(settings) ⇒
FiresyncBase
- "changed"
- "loaded"
- "synced" (err)
-
.loaded() ⇒
firesyncObject.loaded() ⇒ boolean
Indicates whether the object has loaded its data from Firebase.
Kind: instance method of FiresyncObject
firesyncObject.ref() ⇒ FirebaseRef
Returns the ref set in the constructor
Kind: instance method of FiresyncObject
firesyncObject.detach()
Detaches from the subscribed Firebase and DOM events. Must be called if the object will no longer be used.
Kind: instance method of FiresyncObject
firesyncObject.bindTo(settings) ⇒ FiresyncBase
Binds to DOM templates using RactiveJs. The settings are passed directly to RactiveJs.
Kind: instance method of FiresyncObject
Returns: FiresyncBase
- The current instance.
Param | Type | Description |
---|---|---|
settings | Object |
The settings passed to RactiveJs. Refer to the RactiveJs docs for more details |
Example
new (FiresyncObject|FiresyncArray).bindTo({ el: 'body', template: '<input value={{value}}/>' })
"changed"
Fired the local object changes, regardless whether it is a result of direct local change or remote change.
Kind: event emitted by FiresyncObject
Example
firesyncObject.on('changed', function(){});
"loaded"
Fired when the initial value of the object is loaded from the remote.
Kind: event emitted by FiresyncObject
Example
firesyncObject.on('loaded', function(){});
"synced" (err)
Fired when the local object's value is sucesfully set to the remote.
Kind: event emitted by FiresyncObject
Param | Type | Description |
---|---|---|
err | Error |
Synchronization error |
Example
firesyncObject.on('synced', function(err){});
firesync.create(ref) ⇒ Promise
Creates a FiresyncObject or FiresyncArray from the specified ref depending on the underlying value. The returned object is guaranteed to be loaded.
Kind: static method of firesync
Param | Type | Description |
---|---|---|
ref | FirebaseRef |
from a specified ref |
Example
firesync.create(ref).then(function(firesyncObj) {}); //if ref's underlying value is array a FiresyncArray is returned
otherwise a FiresyncObject
firesync.map(ref) ⇒ Promise
Returns a non-synchronized array or an object of FiresyncObject or FiresyncArray objects. The objects are guaranteed to be loaded.
Kind: static method of firesync
Param | Type | Description |
---|---|---|
ref | FirebaseRef |
from a specified ref |
Example
firesync.map(ref).then(function(objOrArr){});
FirebaseRef
FirebaseRef object
Kind: global external
See: https://www.firebase.com/docs/web/api/firebase/child.html
RactiveJs
RactiveJs object
Kind: global external
See: http://docs.ractivejs.org/latest/get-started
Development and testing
The source code is located in the src
folder.
$ npm install
$ gulp build #builds the source code
$ gulp test #executes the tests
$ gulp watch #builds the source code as you change it
License
The MIT License (MIT)
Copyright (c) 2015 Firesync
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.