-
-
Notifications
You must be signed in to change notification settings - Fork 344
[feature request] automatic rebind #208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
using a watcher is the way the go 🙂 Adding such feature would be reinventing the wheel |
IMO automatic re-binding would be more Vue-way. Vue is all about declarative programming. Even the Vue guide recommends using computed properties over watchers.
Can you explain, please? |
using a watcher is the vue way because we are doing a side effect 😉 computed: {
documentPromise() {
return this.$bind('document', db.collection('items').doc(this.documentId)
}
} And then you can consume use the promise state to display a loading, fulfilled or error state using something like vue-promised with the RTDB, you still have to do it a bit manually by creating the promise yourself and passing resolve, and reject as |
Thanks for the clarification. |
Here's the solution I came up with. I wrote a little Vue plugin that automatically rebinds Firestore queries. It's not very elegant because it creates intermediate computed properties prefixed with VueFirestoreAutoRebind plugin: export default {
install(Vue) {
Vue.mixin({
beforeCreate() {
if (!this.$options.db) {
return;
}
const keys = Object.keys(this.$options.db);
const computed = {};
// For $options.$db property register a computed property prefixed with _
keys.forEach((key) => {
computed[`_${key}`] = this.$options.db[key];
});
// Merge dynamically created computed properties with component's computed properties
this.$options.computed = {
...this.$options.computed,
...computed,
};
},
created() {
if (!this.$options.db) {
return;
}
const keys = Object.keys(this.$options.db);
keys.forEach((key) => {
// Bind the query when the component is created in order to run the query at least once
this.$bind(key, this[`_${key}`]);
// Re-bind properties when computed properties re-evaluate
this.$watch(`_${key}`, (query) => {
this.$bind(key, query);
});
});
},
});
},
}; Initialization: import Vue from 'vue';
import VueFire from 'vuefire';
import App from './App.vue';
import VueFirestoreAutoRebind from './plugins/VueFirestoreAutoRebind';
Vue.use(VueFire);
Vue.use(VueFirestoreAutoRebind);
new Vue({
render: h => h(App),
}).$mount('#js--app'); Usage: <template>
<div class="auto-rebind-test">
<input type="number" v-model.number="limit" min="1" max="100">
<ul>
<li v-for="{ key, name } in companies" :key="key">
{{ name }}
</li>
</ul>
</div>
</template>
<script>
import { db } from '../modules/firebase-client';
export default {
name: 'auto-rebind-test',
// The VueFirestoreAutoRebind plugin requires the `db` property
db: {
companies() {
return db.collection('companies')
.where('isActive', '==', true)
.limit(this.limit);
},
},
data: () => ({
companies: [], // Initial value is required
limit: 10,
}),
};
</script> |
I guess it's okay if you use it in your application but I will keep a
simple approach that is more flexible for vuefire :)
|
Update:I've added: The pluginimport isFunction from 'lodash/isFunction';
import isObject from 'lodash/isObject';
import noop from 'lodash/noop';
import has from 'lodash/has';
export default {
install(Vue) {
Vue.mixin({
beforeCreate() {
if (!this.$options.db) {
return;
}
const keys = Object.keys(this.$options.db);
const computed = {};
// For $options.$db property register a computed property prefixed with _
keys.forEach((key) => {
const settings = this.$options.db[key];
const computedProp = `${key}Query`;
if (isFunction(settings)) {
computed[computedProp] = settings;
}
if (isObject(settings)) {
if (has(settings, 'ref') && isFunction(settings.ref)) {
computed[computedProp] = settings.ref;
}
}
});
// Merge dynamically created computed properties with component's computed properties
this.$options.computed = {
...this.$options.computed,
...computed,
};
},
created() {
if (!this.$options.db) {
return;
}
// Apply vuefire bindings to all entries
Object.keys(this.$options.db)
.forEach((key) => {
const settings = this.$options.db[key];
const callbacks = {
before: isFunction(settings.before) ? settings.before : noop,
resolve: isFunction(settings.resolve) ? settings.resolve : noop,
reject: isFunction(settings.reject) ? settings.reject : noop,
};
const bind = (k, q) => {
callbacks.before.call(this);
// tempKey key used when settings.wait is `true`
// It's a workaround for: https://github.com/vuejs/vuefire/issues/83
const tempKey = `_temp_${k}`;
if (settings.wait) {
this[tempKey] = null;
}
const fieldToBindTo = settings.wait ? tempKey : k;
this.$bind(fieldToBindTo, q)
.then(() => {
if (settings.wait) {
this[k] = this[tempKey];
delete this[tempKey];
}
callbacks.resolve.call(this);
})
.catch(() => {
delete this[tempKey];
callbacks.reject.call(this);
});
};
// Bind the collection when the component is created
const query = this[`${key}Query`];
bind(key, query);
// Re-bind properties when computed properties re-evaluate
this.$watch(`${key}Query`, (q) => {
bind(key, q);
});
});
},
});
},
}; Initializationimport Vue from 'vue';
import VueFire from 'vuefire';
import App from './App.vue';
import VueFirestoreAutoRebind from './plugins/VueFirestoreAutoRebind';
Vue.use(VueFire);
Vue.use(VueFirestoreAutoRebind);
new Vue({
render: h => h(App),
}).$mount('#js--app'); Usage<template>
<div>
<input type="text" v-model="name">
<input type="text" v-model="city">
</div>
</template>
<script>
import { db } from '../modules/firebase-client';
export default {
name: 'vuefire-autobind-example',
db: {
// Basic usage
companiesByCity() {
return db.collection('companies')
.where('city', '==', this.city);
},
// Full example
companiesByName: {
ref() {
return db.collection('companies')
.where('name', '==', this.name);
},
before() {
this.loading = true;
},
resolve() {
this.loading = false;
},
reject() {
this.loading = false;
this.error = true;
},
wait: true,
},
},
data: () => ({
companiesByCity: [],
companiesByName: [],
name: 'My Company Name',
city: 'Boston',
error: false,
loading: false,
}),
};
</script> |
Currently, if we want to implement automatic rebind we have to use a watcher, unbind and then rebind firebase refs using
$bindAsArray
or$bindAsObject
methods.If such a feature was implemented then re-binding refs after instance properites change would be as easy as:
Is it possible to implement such a feature?
The text was updated successfully, but these errors were encountered: