Skip to content

Instantly share code, notes, and snippets.

@lucafaggianelli
Last active November 1, 2020 23:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lucafaggianelli/91b1ffa66aac733399888c4bdabf2410 to your computer and use it in GitHub Desktop.
Save lucafaggianelli/91b1ffa66aac733399888c4bdabf2410 to your computer and use it in GitHub Desktop.
Vue MC Firestore
import FirestoreCollection from './FirestoreCollection'
import Activity from './Activity'
export default class Activities extends FirestoreCollection {
model () {
return Activity
}
routes () {
return {
fetch: 'activities'
}
}
}
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'
}
}
}
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
}
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
}
}
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
}
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 })
}
}
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
@leifcr
Copy link

leifcr commented Oct 22, 2020

Can you please show how you have implemented the store and database imports?

import { firestore } from '@/database'
import store from '@/store'

firestore looks like normal 'firestore', but I'm guessing with the config?

@lucafaggianelli
Copy link
Author

Sure, I added database.js and store/index.js to the gist. The store is just a Vuex store with modules.

@leifcr
Copy link

leifcr commented Nov 1, 2020

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