├── example ├── config │ ├── users.yml │ ├── permissions.yml │ └── config.yml ├── assets │ ├── img │ │ ├── rico.png │ │ ├── vdc.gif │ │ ├── mirko.png │ │ └── luke_vader.jpg │ ├── css │ │ └── chat.css │ └── js │ │ ├── bootstrap.min.js │ │ └── jquery.min.js ├── server.js ├── package.json └── index.html ├── test └── vdc.test.js ├── .travis.yml ├── src ├── process-method.js ├── vdc.js └── vdc-methods.js ├── package.json └── README.md /example/config/users.yml: -------------------------------------------------------------------------------- 1 | mirko: 2 | password: "" 3 | 4 | rico: 5 | password: "" 6 | -------------------------------------------------------------------------------- /example/assets/img/rico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecerroni/vue-deepstream-connector/HEAD/example/assets/img/rico.png -------------------------------------------------------------------------------- /example/assets/img/vdc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecerroni/vue-deepstream-connector/HEAD/example/assets/img/vdc.gif -------------------------------------------------------------------------------- /example/assets/img/mirko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecerroni/vue-deepstream-connector/HEAD/example/assets/img/mirko.png -------------------------------------------------------------------------------- /example/assets/img/luke_vader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ecerroni/vue-deepstream-connector/HEAD/example/assets/img/luke_vader.jpg -------------------------------------------------------------------------------- /test/vdc.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var vdc = require('./../src/vdc'); 3 | 4 | describe('void test', function(){ 5 | it('testing has yet to be implemented', function(){ 6 | expect(true).to.be.true; 7 | }) 8 | } 9 | ); 10 | -------------------------------------------------------------------------------- /example/config/permissions.yml: -------------------------------------------------------------------------------- 1 | record: 2 | "*": 3 | create: true 4 | write: true 5 | read: true 6 | delete: true 7 | listen: true 8 | event: 9 | "*": 10 | publish: true 11 | subscribe: true 12 | listen: true 13 | rpc: 14 | "*": 15 | provide: true 16 | request: true 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | directories: 5 | - node_modules 6 | notifications: 7 | email: false 8 | node_js: 9 | - '4' 10 | before_install: 11 | - npm i -g npm@^2.0.0 12 | before_script: 13 | - npm prune 14 | script: 15 | - npm run test:single 16 | after_success: 17 | - npm run semantic-release 18 | branches: 19 | except: 20 | - /^v\d+\.\d+\.\d+$/ 21 | -------------------------------------------------------------------------------- /example/server.js: -------------------------------------------------------------------------------- 1 | var Deepstream = require( 'deepstream.io' ); 2 | var http = require( 'http' ); 3 | var express = require( 'express' ); 4 | var deepstream = new Deepstream('./config/config.yml'); 5 | 6 | var app = express(); 7 | var server = http.createServer(app); 8 | 9 | app.use(express.static(__dirname + '/')); 10 | 11 | deepstream.set( 'httpServer', server ); 12 | 13 | deepstream.start(); 14 | server.listen( 6020, '0.0.0.0' ,function(){ 15 | console.log( 'HTTP server listening on 6020' ); 16 | }); -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-deepstream-connector-example", 3 | "version": "0.1.0", 4 | "description": "vue.js deepstream connector example", 5 | "main": "server.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/ecerroni/vue-deepstream-connector.git" 9 | }, 10 | "keywords": [ 11 | "vue.js", 12 | "deepstream.io" 13 | ], 14 | "author": "ric0", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/ecerroni/vue-deepstream-connector/issues" 18 | }, 19 | "homepage": "https://github.com/ecerroni/vue-deepstream-connector#readme", 20 | "dependencies": { 21 | "deepstream.io": "1.0.3", 22 | "express": "4.14.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/process-method.js: -------------------------------------------------------------------------------- 1 | function processMethod(methods) { 2 | if (!methods) { 3 | return {} 4 | } 5 | 6 | function process(method) { 7 | if (!method) { 8 | return null 9 | } 10 | if (typeof method === 'function') { 11 | return {priority: 100, fn: method} 12 | } 13 | 14 | if (!Vue.util.isObject(method)) { 15 | return null 16 | } 17 | if (typeof method.fn != "function") { 18 | return null 19 | } 20 | var priority = method.priority 21 | if (!priority) { 22 | return {priority: 100, fn: method.fn} 23 | } 24 | if (typeof priority != 'number' || priority < 1 || priority > 100) { 25 | return null 26 | } 27 | return {priority: priority, fn: method.fn} 28 | 29 | } 30 | 31 | var result = {} 32 | Object.keys(methods).forEach(function (key) { 33 | var value = methods[key] 34 | var method = process(value) 35 | if (!method) { 36 | 0 37 | throw "can not accept method \"" + key + "\"" 38 | } else { 39 | result[key] = method 40 | } 41 | }) 42 | return result 43 | } 44 | 45 | 46 | module.exports = { 47 | processMethod: processMethod 48 | } 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-deepstream-connector", 3 | "version": "0.0.0-semantically-released", 4 | "description": "vue.js deepstream connector for seamless integration", 5 | "main": "src/vdc.js", 6 | "directories": { 7 | "example": "example" 8 | }, 9 | "scripts": { 10 | "commit": "git-cz", 11 | "test": "mocha test/vdc.test.js -w", 12 | "test:single": "mocha test/vdc.test.js", 13 | "build:umd": "webpack --output-filename vdc.js", 14 | "build:test": "webpack --output-filename vdc.test.js", 15 | "build:min": "webpack --output-filename vdc.min.js -p", 16 | "semantic-release": "semantic-release pre && npm publish && semantic-release post" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/ecerroni/vue-deepstream-connector.git" 21 | }, 22 | "keywords": [ 23 | "vue.js", 24 | "deepstream.io" 25 | ], 26 | "author": "ric0", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/ecerroni/vue-deepstream-connector/issues" 30 | }, 31 | "homepage": "https://github.com/ecerroni/vue-deepstream-connector#readme", 32 | "dependencies": { 33 | "deepstream.io-client-js": "1.0.2", 34 | "underscore": "1.8.3" 35 | }, 36 | "devDependencies": { 37 | "babel": "6.5.2", 38 | "babel-cli": "6.10.1", 39 | "babel-core": "6.10.4", 40 | "babel-loader": "6.2.4", 41 | "babel-preset-es2015": "6.9.0", 42 | "babel-preset-stage-2": "6.11.0", 43 | "babel-register": "6.9.0", 44 | "chai": "3.5.0", 45 | "commitizen": "^2.8.2", 46 | "cz-conventional-changelog": "^1.1.6", 47 | "ghooks": "^1.3.2", 48 | "json-loader": "0.5.4", 49 | "mocha": "3.0.0", 50 | "semantic-release": "^4.3.5", 51 | "webpack": "1.12.0" 52 | }, 53 | "config": { 54 | "commitizen": { 55 | "path": "node_modules/cz-conventional-changelog" 56 | }, 57 | "ghooks": { 58 | "pre-commit": "npm run test:single" 59 | } 60 | }, 61 | "babel": { 62 | "presets": [ 63 | "es2015", 64 | "stage-2" 65 | ] 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/vdc.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | var dsMethods = require('./process-method') 3 | 4 | /** 5 | * 6 | * A good chunk is based on Zhuojie Zhou's awesome meteor-vue package @ https://github.com/zhouzhuojie/meteor-vue/blob/master/lib/main.js 7 | * Also re-used some code from the vue-verify package 8 | */ 9 | 10 | exports.install = function (Vue, options){ 11 | var Vue = Vue; 12 | 13 | options = options || {} 14 | var builtInDeepstream = Vue.util.extend(require("./vdc-methods.js"), dsMethods.processMethod(options.methods)) 15 | 16 | var p = Vue.prototype; 17 | 18 | p.__callHook = p._callHook; 19 | p._callHook = function (hook) { 20 | if(hook == 'created') { 21 | var self = this; 22 | 23 | // On every object use the $sync function to get the value 24 | _.each(this.$options.sync, function (rxFunc, key) { 25 | self.$sync(key, rxFunc); 26 | }); 27 | } 28 | this.__callHook(hook); 29 | } 30 | 31 | /** 32 | * "Overwrite" of the Vue _init function 33 | * @param Array option Vue options 34 | */ 35 | p.__init = p._init; 36 | p._init = function (option) { 37 | // Dict 38 | this.$$syncDict = {}; 39 | 40 | // Init data field to avoid warning 41 | option = option || {}; 42 | option.data = option.data || {}; 43 | option.sync = option.sync || {}; 44 | 45 | var sync = _.extend({}, this.constructor.options.sync || {}, option.sync); 46 | _.extend(option.data, sync); 47 | 48 | option.init = option.init 49 | ? [initDs].concat(option.init) 50 | : initDs 51 | 52 | // Default init 53 | this.__init(option); 54 | }; 55 | 56 | // Stop the key from syncDict 57 | p.$unsync = function (key) { 58 | var ref = this.$$syncDict[key]; 59 | 60 | if (ref && typeof ref.stop === 'function') { 61 | ref.stop(); 62 | } 63 | }; 64 | 65 | // Sync key in syncDict with value = rxFunc 66 | p.$sync = function (key, rxFunc) { 67 | this.$unsync(key); 68 | 69 | if (typeof rxFunc === 'function') { 70 | var self = this; 71 | 72 | 73 | 74 | this.$$syncDict[key] = (function () {//IIF 75 | var val; 76 | self._callingKey = key 77 | val = rxFunc.call(self); 78 | return self.$set(key, val); 79 | })() 80 | } 81 | }; 82 | 83 | Vue.config.optionMergeStrategies.sync = Vue.config.optionMergeStrategies.computed 84 | 85 | p.$dsListFetch = builtInDeepstream['vueListFetch'] 86 | p.$dsListFetchReadOnly = builtInDeepstream['vueListFetchReadOnly'] 87 | p.$dsConnect = builtInDeepstream['vDs'] 88 | p.$dsLogin = builtInDeepstream['vdsLogin'] 89 | p.$dsLogout = builtInDeepstream['vdsLogout'] 90 | p.$dsRecordFieldFetch = builtInDeepstream['vueRecordFieldFetch'] 91 | p.$dsRecordCreate = builtInDeepstream['vueRecordCreate'] 92 | p.$dsRecordFetch = builtInDeepstream['vueRecordFetch'] 93 | 94 | Vue.filter('ds-sync', { 95 | read: function(value, key) { 96 | if (value!=undefined) this.$emit('ds-sync', value, key); 97 | return value 98 | }, 99 | write: function(val, oldVal) { 100 | return val 101 | } 102 | }) 103 | 104 | function initDs () { 105 | 106 | // init deepstream 107 | if ((this.$options.ds) && (this.$root == this)) { 108 | this.$ds = p.$dsConnect(this.$options.ds).login() 109 | } else if (this.$root.$ds) { 110 | this.$ds = this.$root.$ds 111 | } 112 | 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /example/config/config.yml: -------------------------------------------------------------------------------- 1 | # General 2 | serverName: UUID # Each server within a cluster needs a unique name. Set to UUID to have deepstream autogenerate a unique id 3 | showLogo: true # Show the deepstream logo on startup (highly recommended) 4 | logLevel: INFO # Log messages with this level and above. Valid levels are DEBUG, INFO, WARN, ERROR, OFF 5 | #libDir: ../lib # Directory where all plugins reside 6 | 7 | # Connectivity 8 | webServerEnabled: true # accept incoming HTTP long-polling and websocket connections 9 | tcpServerEnabled: true # accept incoming TCP connections 10 | port: 6020 # port for the HTTP/websocket server 11 | host: 0.0.0.0 # host for the HTTP/websocket server 12 | tcpPort: 6021 # port for the TCP server 13 | tcpHost: 0.0.0.0 # host for the TCP server 14 | urlPath: /deepstream # url path HTTP/WEBSOCKET connections connect to 15 | 16 | # SSL Configuration 17 | sslKey: null 18 | sslCert: null 19 | sslCa: null 20 | 21 | # Logger Configuration 22 | # logger: 23 | # # use either the default logger 24 | # name: default 25 | # options: 26 | # colors: true 27 | # logLevel: INFO # value of logLevel (line 4) will always overwrite this value 28 | 29 | # # or the winston logger 30 | # name: winston 31 | # options: 32 | # # specify a list of transports (console, file, time) 33 | # - 34 | # type: console 35 | # options: 36 | # level: info # value of logLevel (line 4) will always overwrite this value 37 | # colorize: true 38 | # - 39 | # type: time 40 | # options: 41 | # filename: ../var/deepstream 42 | 43 | # # or a custom logger 44 | # path: ./my-custom-logger 45 | 46 | # Plugin Configuration 47 | plugins: 48 | # message: 49 | # name: redis 50 | # options: 51 | # host: localhost 52 | # port: 5672 53 | # 54 | # cache: 55 | # name: memcached 56 | # options: 57 | # serverLocation: 'localhost:11211' 58 | # 59 | # storage: 60 | # name: rethinkdb 61 | # options: 62 | # host: localhost 63 | # port: 28015 64 | 65 | # Storage options 66 | storageExclusion: null # a RegExp that matches recordNames. If it matches, the record's data won't be stored in the db 67 | 68 | # Security 69 | unauthenticatedClientTimeout: 180000 # amount of time a connection can remain open while not being logged in 70 | maxAuthAttempts: 3 # invalid login attempts before the connection is cut 71 | logInvalidAuthData: true # if true, the logs will contain the cleartext username / password of invalid login attempts 72 | maxMessageSize: 1048576 # maximum allowed size of an individual message in bytes 73 | 74 | #Authentication 75 | auth: 76 | # type: none 77 | 78 | # # reading users and passwords from a file 79 | type: file 80 | options: 81 | path: ./users.yml # Path to the user file. Can be json, js or yml 82 | #hash: 'md5' # the name of a HMAC digest algorithm 83 | iterations: 100 # the number of times the algorithm should be applied 84 | keyLength: 32 # the length of the resulting key 85 | 86 | # # getting permissions from a http webhook 87 | # type: http 88 | # options: 89 | # endpointUrl: https://someurl.com/validateLogin # a post request will be send to this url on every incoming connection 90 | # permittedStatusCodes: [ 200 ] # any of these will be treated as access granted 91 | # requestTimeout: 2000 # if the webhook didn't respond after this amount of milliseconds, the connection will be rejected 92 | 93 | # Permissioning 94 | permission: 95 | type: config # Only config or custom permissionHandler at the moment 96 | options: 97 | path: ./permissions.yml # Path to the permissionFile. Can be json, js or yml 98 | maxRuleIterations: 3 # Amount of times nested cross-references will be loaded. Avoids endless loops 99 | cacheEvacuationInterval: 60000 # PermissionResults are cached to increase performance. Lower number means more loading 100 | 101 | # Timeouts (in milliseconds) 102 | rpcProviderQueryTimeout: 1000 103 | rpcProviderCacheTime: 60000 104 | rpcAckTimeout: 1000 105 | rpcTimeout: 10000 106 | cacheRetrievalTimeout: 1000 107 | storageRetrievalTimeout: 2000 108 | dependencyInitialisationTimeout: 2000 -------------------------------------------------------------------------------- /example/assets/css/chat.css: -------------------------------------------------------------------------------- 1 | 2 | body{ 3 | margin-top:20px; 4 | background:#ebeef0; 5 | } 6 | [v-cloak] { 7 | display: none; 8 | } 9 | .panel { 10 | box-shadow: 0 2px 0 rgba(0,0,0,0.075); 11 | border-radius: 0; 12 | border: 0; 13 | margin-bottom: 24px; 14 | } 15 | .panel .panel-heading, .panel>:first-child { 16 | border-top-left-radius: 0; 17 | border-top-right-radius: 0; 18 | } 19 | .panel-heading { 20 | position: relative; 21 | height: 50px; 22 | padding: 0; 23 | border-bottom:1px solid #eee; 24 | } 25 | .panel-control { 26 | height: 100%; 27 | position: relative; 28 | float: right; 29 | padding: 0 15px; 30 | } 31 | .panel-title { 32 | font-weight: normal; 33 | padding: 0 20px 0 20px; 34 | font-size: 1.416em; 35 | line-height: 50px; 36 | white-space: nowrap; 37 | overflow: hidden; 38 | text-overflow: ellipsis; 39 | } 40 | .panel-control>.btn:last-child, .panel-control>.btn-group:last-child>.btn:first-child { 41 | border-bottom-right-radius: 0; 42 | } 43 | .panel-control .btn, .panel-control .dropdown-toggle.btn { 44 | border: 0; 45 | } 46 | .nano { 47 | position: relative; 48 | width: 100%; 49 | height: 100%; 50 | overflow: hidden; 51 | } 52 | .nano>.nano-content { 53 | position: absolute; 54 | overflow: scroll; 55 | overflow-x: hidden; 56 | top: 0; 57 | right: 0; 58 | bottom: 0; 59 | left: 0; 60 | } 61 | .pad-all { 62 | padding: 15px; 63 | } 64 | .mar-btm { 65 | margin-bottom: 15px; 66 | } 67 | .media-block .media-left { 68 | display: block; 69 | float: left; 70 | } 71 | .img-sm { 72 | width: 46px; 73 | height: 46px; 74 | } 75 | .media-block .media-body { 76 | display: block; 77 | overflow: hidden; 78 | width: auto; 79 | } 80 | .pad-hor { 81 | padding-left: 15px; 82 | padding-right: 15px; 83 | } 84 | .speech { 85 | position: relative; 86 | background: #b7dcfe; 87 | color: #317787; 88 | display: inline-block; 89 | border-radius: 0; 90 | padding: 12px 20px; 91 | } 92 | .speech:before { 93 | content: ""; 94 | display: block; 95 | position: absolute; 96 | width: 0; 97 | height: 0; 98 | left: 0; 99 | top: 0; 100 | border-top: 7px solid transparent; 101 | border-bottom: 7px solid transparent; 102 | border-right: 7px solid #b7dcfe; 103 | margin: 15px 0 0 -6px; 104 | } 105 | .speech-right>.speech:before { 106 | left: auto; 107 | right: 0; 108 | border-top: 7px solid transparent; 109 | border-bottom: 7px solid transparent; 110 | border-left: 7px solid #ffdc91; 111 | border-right: 0; 112 | margin: 15px -6px 0 0; 113 | } 114 | .speech .media-heading { 115 | font-size: 1.2em; 116 | color: #317787; 117 | display: block; 118 | border-bottom: 1px solid rgba(0,0,0,0.1); 119 | margin-bottom: 10px; 120 | padding-bottom: 5px; 121 | font-weight: 300; 122 | } 123 | .speech-time { 124 | margin-top: 20px; 125 | margin-bottom: 0; 126 | font-size: .8em; 127 | font-weight: 300; 128 | } 129 | .media-block .media-right { 130 | float: right; 131 | } 132 | .speech-right { 133 | text-align: right; 134 | } 135 | .pad-hor { 136 | padding-left: 15px; 137 | padding-right: 15px; 138 | } 139 | .speech-right>.speech { 140 | background: #ffda87; 141 | color: #a07617; 142 | text-align: right; 143 | } 144 | .speech-right>.speech .media-heading { 145 | color: #a07617; 146 | } 147 | .btn-primary, .btn-primary:focus, .btn-hover-primary:hover, .btn-hover-primary:active, .btn-hover-primary.active, .btn.btn-active-primary:active, .btn.btn-active-primary.active, .dropdown.open>.btn.btn-active-primary, .btn-group.open .dropdown-toggle.btn.btn-active-primary { 148 | background-color: #579ddb; 149 | border-color: #5fa2dd; 150 | color: #fff !important; 151 | } 152 | .btn { 153 | cursor: pointer; 154 | /* background-color: transparent; */ 155 | color: inherit; 156 | padding: 6px 12px; 157 | border-radius: 0; 158 | border: 1px solid 0; 159 | font-size: 11px; 160 | line-height: 1.42857; 161 | vertical-align: middle; 162 | -webkit-transition: all .25s; 163 | transition: all .25s; 164 | } 165 | .form-control { 166 | font-size: 11px; 167 | height: 100%; 168 | border-radius: 0; 169 | box-shadow: none; 170 | border: 1px solid #e9e9e9; 171 | transition-duration: .5s; 172 | } 173 | .nano>.nano-pane { 174 | background-color: rgba(0,0,0,0.1); 175 | position: absolute; 176 | width: 5px; 177 | right: 0; 178 | top: 0; 179 | bottom: 0; 180 | opacity: 0; 181 | -webkit-transition: all .7s; 182 | transition: all .7s; 183 | } 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-deepstream-connector 2 | 3 | [](https://travis-ci.org/ecerroni/vue-deepstream-connector/builds) 4 | [](https://github.com/ecerroni/vue-deepstream-connector/releases/tag/0.1.0) 5 | 6 | A bridge connector between [Vue](http://vuejs.org) and [Deepstream](http://deepstream.io). 7 | 8 | ## Get Started 9 | The __vue-deepstream-connector__ allows to intercepts vue's array change detection methods to trigger deepstream's API. It combines them to offer a ___bidirectional data-sync___ option. 10 | 11 | ## Demo 12 |  13 | 14 | ## Usage 15 | 16 | Load the library (either for browser and Node): 17 | 18 | Vue.use(VueDeepStreamConnector) 19 | 20 | Sync usage: 21 | 22 | sync: { 23 | 'some-key': function(){ 24 | // It returns a deepstream source 25 | } 26 | } 27 | 28 | 29 | 30 | ### Browser 31 | 32 | 33 | 34 | 35 | 36 | Connect to the deepstream server. Use a custom option named 'ds' always in the root component 37 | 38 | var demo = new Vue({ 39 | ds: {connectionUrl: 'localhost:6020', 40 | options: { 41 | //deepstream client connection options 42 | //find them here: https://deepstream.io/docs/client-js/options/ 43 | } 44 | }, // You may omit the option object completely 45 | el: '#demo', 46 | data: { 47 | // Your data here 48 | } 49 | }) 50 | 51 | Login to the deepstream server 52 | 53 | this.$dsLogin({username: 'uid', password: 'pwd'}, function(logged, data){ 54 | // logged is either false or true 55 | }) 56 | } 57 | 58 | 59 | Sync a single deepstream record: 60 | 61 | sync: { 62 | object: function(){ // params: recordName 63 | return this.$dsRecordFetch('welcome') 64 | } 65 | 66 | 67 | Sync a whole array/list: 68 | 69 | sync: { 70 | conversations: function(){ // params: listName 71 | return this.$dsListFetch('chats') 72 | } // to insert items in the list use this.conversations.push(object) 73 | // to remove items from the list use this.conversations.$remove(object) 74 | // for read only lists use: return this.$dsListFetchReadOnly(listName) 75 | 76 | Sync v-model: 77 | Use the ds-sync filter 78 | 79 | 80 | 81 | Retrieve the exact field: 82 | 83 | sync: { 84 | editable: function(){ // params: recordName, fieldName 85 | return this.$dsRecordFieldFetch('myRecord', 'myField') 86 | } 87 | // using the ds-sync filter will update real-time all the connected clients 88 | 89 | 90 | ### Node 91 | 92 | npm install deepstream.io // install the server 93 | npm install vue-deepstream-connector // install both client@1.x.x and the connector 94 | 95 | 96 | ### Example (Browser) 97 | 98 | Same as the animated gif example 99 | 100 | git clone https://github.com/ecerroni/vue-deepstream-connector.git 101 | cd vue-deepstream-connector 102 | cd example 103 | npm install 104 | node server 105 | 106 | Open the browser at http://localhost:6020 107 | 108 | ## All Methods 109 | 110 | - __this.$dsLogin__ // return a callback with the login result status 111 | - __this.$dsLogout__ // logouts the user closing the connection. You may now login with another user 112 | - __this.$dsRecordFieldFetch__ // reactive deepstream source: a specific record's field 113 | - __this.$dsListFetch__ // reactive deepstream source: a list that can be populate with Vue's array mutation functions 114 | - __this.$dsRecordCreate__ // create a record with or without a unique id 115 | - __this.$dsConnect__ //use the 'ds' customOption instead, see above 116 | - __this.$dsListFetchReadOnly__ // reactive deepstream source: a list that can be only read but not mutated (mutation stays only locally until next browser refresh) 117 | 118 | ## Caveats 119 | 120 | So far the vue-deepstream-connector works with: 121 | - Deepstream server 1.0.x 122 | - Deepstream client 1.0.x 123 | - Vue 1.0.x 124 | 125 | __Compatibility with vue 2.0 is in the works__ 126 | 127 | In order to make everything sync seamlessly a "_uid" property is added to every object inserted into the array 128 | This allows an optimistic UI where the new item triggers a View update on the client without waiting the response of the server 129 | 130 | ### Dynamic components 131 | The connector works well with dynamic components as long as you're working with props. 132 | 133 | __If a child component use a method to fetch anything from deepstream you must use the keep-alive param (either in vue-router or is:currentView) to preserve its state and avoid re-rendering__ 134 | 135 | ----- 136 | 137 | ## VUE-DEEPSTREAM INTEGRATION STATUS 138 | 139 | ### DONE: [VUE] 140 | - push() // adds "_uid" for you 141 | - splice() 142 | - $set 143 | - $remove 144 | - v-model ds-sync for text 145 | - v-model ds-sync for textarea 146 | 147 | ### TO DO: [VUE] 148 | - pop() 149 | - shift() 150 | - unshift() 151 | 152 | ### NOT PLANNED: [VUE] 153 | - sort() 154 | - reverse() 155 | - v-model ds-sync for number, select, checkbox and radio 156 | 157 | 158 | ### TO DO: [DS] 159 | - Create ds record (sugar syntax and auto "_uid") 160 | - Records 161 | - All Events 162 | - unsubscribe 163 | - discard 164 | - delete 165 | - Lists 166 | - All Events 167 | - unsubscribe 168 | - discard 169 | - delete 170 | 171 | ### PLANNED: [DS] 172 | - ~~ServerOptions~~ DONE 173 | - Connection States 174 | - ~~Login~~ DONE 175 | - ~~Auth & Permissions~~ DONE 176 | - Anonymous Record 177 | - RPC 178 | - ERRORS 179 | 180 | ### PLANNED: [OTHER] 181 | - free underscore dependency 182 | - ~~avoid the need to pass sync.key to the function call~~ DONE 183 | - ~~repo files with no console logs~~ DONE 184 | 185 | 186 | ## Thanks To 187 | - [Zhou's meteor-vue](https://github.com/zhouzhuojie/meteor-vue) 188 | - [PeakTai's vue-verify](https://github.com/PeakTai/vue-verify) 189 | 190 | 191 | ## LICENSE 192 | --- 193 | 194 | MIT -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 |Use either rico or mirko
SHARED PAD