Last active
November 1, 2020 23:09
-
-
Save lucafaggianelli/91b1ffa66aac733399888c4bdabf2410 to your computer and use it in GitHub Desktop.
Vue MC Firestore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import FirestoreCollection from './FirestoreCollection' | |
import Activity from './Activity' | |
export default class Activities extends FirestoreCollection { | |
model () { | |
return Activity | |
} | |
routes () { | |
return { | |
fetch: 'activities' | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import FirestoreModel from './FirestoreModel' | |
import { date, gte } from 'vue-mc/validation' | |
import moment from 'moment' | |
export default class Activity extends FirestoreModel { | |
modelName = 'Activity' | |
defaults () { | |
return { | |
id: null, | |
parentId: null, | |
title: '', | |
owner: '', | |
startDate: moment().toDate(), | |
dueDate: moment().add(1, 'week').toDate(), | |
completed: false, | |
effort: 0, | |
expense: '', | |
progress: 0 | |
} | |
} | |
mutations () { | |
return { | |
} | |
} | |
validation () { | |
return { | |
startDate: date, | |
dueDate: date, | |
type: (val) => val in [ 'task' ], | |
effort: gte(0) | |
} | |
} | |
routes () { | |
return { | |
fetch: 'activities', | |
save: 'activities', | |
delete: 'activities' | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import firebase from 'firebase/app' | |
import 'firebase/auth' | |
import 'firebase/database' | |
import 'firebase/firestore' | |
import 'firebase/storage' | |
import config from '@/config' | |
const firebaseApp = firebase.initializeApp(config.firebase) | |
const firestore = firebaseApp.firestore() | |
function createOrUpdate (collection, payload) { | |
let ref = firestore.collection(collection) | |
let id = payload.id | |
let data = Object.assign({}, payload) | |
delete data.id | |
if (id) { | |
ref = ref.doc(id).update(data) | |
} else { | |
ref = ref.add(data) | |
} | |
return ref | |
.then((docRef) => { | |
return docRef | |
}) | |
} | |
const FieldValue = firebase.firestore.FieldValue | |
export default firestore | |
export { | |
createOrUpdate, | |
firestore, | |
FieldValue | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { firestore } from 'firebase/app' | |
import { Collection } from 'vue-mc' | |
import { fetch } from './FirestoreCommon' | |
export default class FirestoreCollection extends Collection { | |
fetch (params) { return fetch.call(this, params) } | |
save (params) { /* save.call(this, params) */ console.warn('FirestoreCollection.save() not implemented') } | |
delete (params) { console.warn('FirestoreCollection.delete() Cannot be implemented') } | |
/** | |
* @param {Object} snapshot | |
* | |
* @returns {Array|null} Models from the response. | |
*/ | |
getModelsFromResponse (snapshot) { | |
let items = [] | |
snapshot.forEach(item => { | |
let attributes = item.data() | |
attributes.id = item.id | |
// Transform Firebase Timestamps objects to JS Date objects | |
for (let attr in attributes) { | |
if (attributes[attr] instanceof firestore.Timestamp) { | |
attributes[attr] = attributes[attr].toDate() | |
} | |
} | |
items.push(attributes) | |
}) | |
return items | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { firestore } from '@/database' | |
import store from '@/store' | |
function fetch (params) { | |
const url = this.getURL(this.getFetchRoute(), params) | |
this.onFetch() | |
let ref = firestore.collection(url) | |
if (params.where) { | |
for (let w of params.where) { | |
ref = ref.where(...w) | |
} | |
} | |
if (params.orderBy) { | |
ref = ref.orderBy(params.orderBy) | |
} | |
if (params.limit) { | |
ref = ref.limit(params.limit) | |
} | |
return new Promise((resolve, reject) => { | |
ref.onSnapshot( | |
(snapshot) => { | |
this.onFetchSuccess(snapshot) | |
resolve(snapshot) | |
}, | |
(err) => { | |
this.onFetchFailure() | |
reject(err) | |
}) | |
}) | |
} | |
async function save (params) { | |
const collection = this.getURL(this.getSaveRoute(), params) | |
try { | |
await this.onSave() | |
} catch (e) { | |
store.commit('notifyError', `Invalid ${this.getModelName()} data`) | |
console.error(e) | |
return | |
} | |
let ref = firestore.collection(collection) | |
if (this.isNew()) { | |
ref = ref.add(this.getSaveData()) | |
} else { | |
ref = ref.doc(this.identifier()) | |
.update(this.getSaveData()) | |
} | |
return ref | |
.then(() => { | |
this.onSaveSuccess(this._attributes) | |
store.commit('notify', `${this.getModelName()} successfully saved`) | |
}) | |
.catch(err => { | |
this.onSaveFailure(err) | |
store.commit('notifyError', `Can't save ${this.getModelName()}`) | |
}) | |
} | |
function _delete (params) { | |
const collection = this.getURL(this.getDeleteRoute(), params) | |
this.onDelete() | |
firestore.collection(collection) | |
.doc(this.identifier()) | |
.delete() | |
.then(() => { | |
this.onDeleteSuccess() | |
store.commit('notify', `${this.getModelName()} successfully deleted`) | |
}) | |
.catch(() => { | |
this.onDeleteFailure() | |
store.commit('notifyError', `Can't delete ${this.getModelName()}`) | |
}) | |
} | |
export { | |
fetch, | |
save, | |
_delete | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Vue from 'vue' | |
import { Model } from 'vue-mc' | |
import { fetch, save, _delete } from './FirebaseCommon' | |
export default class FirebaseModel extends Model { | |
fetch (params) { fetch.call(this, params) } | |
save (params) { save.call(this, params) } | |
delete (params) { _delete.call(this, params) } | |
onFetchSuccess (snapshot) { | |
let attributes = null | |
if (snapshot.exists()) { | |
attributes = snapshot.val() | |
attributes.id = snapshot.key | |
} else { | |
throw String('No data in fetch response') | |
} | |
this.assign(attributes) | |
Vue.set(this, 'fatal', false) | |
Vue.set(this, 'loading', false) | |
this.emit('fetch', { error: null }) | |
} | |
onSaveSuccess (data) { | |
// Clear errors because the request was successful. | |
this.clearErrors() | |
// Update this model with the data that was returned in the response. | |
if (data) { | |
this.update(data) | |
} | |
Vue.set(this, 'saving', false) | |
Vue.set(this, 'fatal', false) | |
// Automatically add to all registered collections. | |
this.addToAllCollections() | |
this.emit('save', { error: null }) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Vue from 'vue' | |
import Vuex from 'vuex' | |
import state from './state' | |
import actions from './actions' | |
import mutations from './mutations' | |
import getters from './getters' | |
import documents from './modules/documents' | |
import extensions from './modules/extensions' | |
import help from './modules/help' | |
import navigation from './modules/navigation' | |
import organizations from './modules/organizations' | |
import projects from './modules/projects' | |
import user from './modules/user' | |
Vue.use(Vuex) | |
const store = new Vuex.Store({ | |
state, | |
actions, | |
mutations, | |
getters, | |
modules: { | |
documents, | |
extensions, | |
help, | |
navigation, | |
organizations, | |
projects, | |
user | |
}, | |
strict: process.env.NODE_ENV !== 'production' | |
}) | |
Vue.prototype.$notify = function (message) { | |
store.commit('notify', message) | |
} | |
Vue.prototype.$notifyError = function (message, error) { | |
store.commit('notifyError', { message, error }) | |
} | |
// `what` arg is a boolean or a string <page name>:<type> | |
// eg: EAC:glossary | |
Vue.prototype.$openHelpMenu = function (what) { | |
store.commit('help/showContextualHelp', what) | |
} | |
Object.defineProperties(Vue.prototype, { | |
$currentUser: { | |
get () { | |
return store.getters['user/currentUser'] | |
} | |
} | |
}) | |
export default store |
Sure, I added database.js
and store/index.js
to the gist. The store is just a Vuex store with modules.
Thanks! That example answered a lot of questions on how to use a vuex store properly!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can you please show how you have implemented the store and database imports?
firestore looks like normal 'firestore', but I'm guessing with the config?