# Extra features
# Variables for firestorePath or clauses
Besides '{userId}'
in your firestorePath
in the config or in where
clauses, you can also use any variable in the firestorePath
or the where
filter.
// your vuex module
SpecificGroupUserModule: {
firestorePath: 'groups/{groupId}/users/{userId}',
moduleName: 'groupUserData',
}
You can replace a path variable with the actual string by:
// 1. Passing it as a parameter to openDBChannel
dispatch('groupUserData/openDBChannel', {pathVariables: {groupId: 'group-A'}})
// 2. Passing it as a parameter to fetchAndAdd
dispatch('groupUserData/fetchAndAdd', {pathVariables: {groupId: 'group-A'}})
// 3. Dispatching setPathVars
dispatch('groupUserData/setPathVars', {groupId: 'group-A'})
Always clear out your Vuex module before you change a path variable. You can do so by:
dispatch('moduleName/closeDBChannel', {clearModule: true})
// or just
commit('moduleName/RESET_VUEX_EASY_FIRESTORE_STATE')
# Warning: Do not overuse path variables!
The original intend of Vuex Easy Firestore is to have 1 Firestore path in sync with 1 Vuex module. Changing a path variable means that each following patch/insert/deletion will use that path.
Only the last path variable you passed to a module is kept! So you cannot add documents from several different paths (eg. you call fetchAndAdd
three times with different path variables provided) and still expect to be able to modify all these docs.
# Use case: Retrieve a variable from the user's Data
Custom variables are useful however, if you need to first retrieve eg. a groupId
from the user's data also on firestore. You can do so by waiting for the Promise to resolve after openDBChannel
to retrieve the user's data (eg. another vuex-easy-firestore module called userData
):
// in this example the `userData` module will retrieve {userName: '', groupId: ''}
store.dispatch('userData/openDBChannel')
.then(_ => {
// Then we can get the groupId:
const userGroupId = store.state.userData.groupId
// Then we can pass it as variable to the next openDBChannel:
store.dispatch('groupUserData/openDBChannel', {pathVariables: {groupId: userGroupId}})
})
# Use case: Retrieve data based on the Vue Router path
This is a great use case! But it has a good and a bad implementation. I'll go over both so you can see what I mean:
# Bad implementation of Vue Router
Do not use use a path variable as last param of a FirestorePath in 'doc' mode! Eg:
// DO NOT DO THIS!
SpecificGroupUserModule: {
firestorePath: 'pages/{pageId}',
firestoreRefType: 'doc',
moduleName: 'page',
}
// in your Vue file (DO NOT DO THIS!)
export default {
created () {
const pageId = this.$route.params.id
this.$store.dispatch('page/fetchAndAdd', {pathVariables: {pageId}})
},
computed: {
openDoc () {
return this.$store.state.page
}
}
}
The above example shows a Vuex module linked to a single doc, but this path is changed every time the user opens a page and then the doc is retrieved. When opening a new page you will need to release the previous doc from memory every time, so when the user goes back you will be charged with a read again.
# Good implementation of Vue Router
Instead, use 'collection' mode! This way you can keep the pages that were opened already and opening those pages again is much faster. That implementation would look like this:
// MUCH BETTER:
SpecificGroupUserModule: {
firestorePath: 'pages',
firestoreRefType: 'collection',
moduleName: 'pageData',
statePropName: 'data',
}
// in your Vue file
export default {
created () {
const pageId = this.$route.params.id
this.$store.dispatch('pages/fetchById', pageId)
},
computed: {
openDoc () {
const pageId = this.$route.params.id
return this.$store.state.pages.data[pageId]
}
}
}
# Fillables and guard
You can prevent props on your docs to be synced to the firestore server. For this you should use either fillables
or guard
:
- Fillables: Array of keys - the props which may be synced to the server.
- 0 fillables = all props are synced
- 1 or more fillables = only those props are synced (any prop not in fillables is not synced)
- Guard: Array of keys - the props which should not be synced to the server.
- adding any prop here will prevent it from being synced
{
// your other vuex-easy-fire config...
sync: {
fillables: [], // array of keys
guard: [], // array of keys
}
}
# Example fillables
// settings:
fillables: ['name', 'age']
// insert new doc:
const newUser = {name: 'Ash', age: 10, email: 'ash@pokemon.com'}
dispatch('user/set', newUser)
// object which will be added to Vuex `user` module:
{name: 'Ash', age: 10, email: 'ash@pokemon.com'}
// object which will be synced to firestore:
{name: 'Ash', age: 10}
# Example guard
If you have only one prop you do not want to sync to firestore you can set guard
instead of fillables
.
// the same example as above can also be achieved by doing:
guard: ['email']
# Nested fillables/guard
In the example below we will prevent the nested field called notAllowed
from being synced:
const docToPatch = {nested: {allowed: true, notAllowed: true}}
// in your module config, either set the fillables like so:
fillables: ['nested.allowed']
// OR set your guard like so:
guard: ['nested.notAllowed']
# Wildcard fillables/guard
You can also use wildcards!
In this example you have a document with an object called lists
. The lists each have an id as the key, and a nested property you want to prevent from being synced:
const docToPatch = {
lists: {
'list-id1': {allowed: true, notAllowed: true},
'list-id2': {allowed: true, notAllowed: true}
}
}
// in your module config, either set the fillables like so:
fillables: ['lists.*.allowed']
// OR set your guard like so:
guard: ['lists.*.notAllowed']
# Default values
You can set up default values for your docs that will be added to the object on each insert.
In 'doc' mode this can just be done by adding those values to your module state. This is how's it's done regularly with Vuex.
In 'collection' mode the library takes care of applying these default values to each doc that's inserted. Default values should be set in your modules sync
config:
const pokemonBoxModule = {
// your other vuex-easy-firestore config...
sync: {
defaultValues: {
freed: false,
},
}
}
// Now, when you add a new pokemon, it will automatically have `freed`
dispatch('pokemonBox/insert', {name: 'Poliwag'})
// This will appear in your module like so:
// {name: 'Poliwag', freed: false}
Also, to make sure there are no vue reactivity issues, these default values are also applied to any doc that doesn't have them that's retrieved from the server.
# Firestore Timestamp conversion
Firestore works with special "timestamp" fields rather than with new Date()
. The general rule is: If you set or update a field to be new Date()
it will be converted to a special Timestamp field on the server automatically.
There is nothing you can do to prevent this, it's just how Firestore works. The problem is that next time a user opens your app and your documents are retrieved from the server, you will be getting Timestamps and have to call timestamp.toDate()
on each of these fields!
Luckily vuex-easy-firestore can do this for you! This library has date fields for created_at
and updated_at
that are already automatically converted to regular dates upon server retrieval. So we can easily extend this function to include other fields you want to use new Date()
.
You just have to specify the fields in a convertTimestamps
object in your module config like so:
const vuexModule = {
// your other vuex-easy-firestore config...
serverChange: {
convertTimestamps: {
created_at: '%convertTimestamp%', // default
updated_at: '%convertTimestamp%', // default
// define each field like so ↑ in an object here
},
}
}
The Timestamps of each of the fields defined like above will automatically trigger Timestamp.toDate()
before being added to your vuex store!
Eg.
dispatch('module/set', {timestampField: new Date()})
The above will be added as new Date()
in vuex but as a timestamp in Firestore.
# Pass Firebase dependency
Vuex Easy Firestore will automatically use Firebase as a peer dependency to access Firebase.auth()
etc. If you want to pass a Firebase instance you have instantiated yourself you can do so like this:
// make sure you import at least auth and firestore as well:
import * as Firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/firestore'
import createEasyFirestore from 'vuex-easy-firestore'
const easyFirestore = createEasyFirestore(
userDataModule,
{logging: true, FirebaseDependency: Firebase} // pass Firebase like this. Mind the Capital F!
)
# Custom sync debounce duration
Vuex easy firestore only makes one api call per 1000ms, no matter how many patches you make. This default debounce duration of 1000ms can be modified per module like so:
const vuexModule = {
// your other vuex-easy-firestore config...
sync: {
debounceTimerMs: 2000
// defaults to 1000
}
}