cheetah-framework
Version:
Cheetah Framework JS used in all our applications
175 lines (147 loc) • 4.04 kB
JavaScript
import Vue from 'vue'
import FormErrorHelper from './FormErrorHelper'
import FormErrors from './errors'
import LaravelModel from '@cheetah/models/LaravelModel'
Vue.config.optionMergeStrategies.form = function (toVal, fromVal) {
return _.defaults(fromVal, toVal)
}
const formDefaultOptions = {
/**
* Override method used to save instance.
* By default, store and update method will be used.
*/
storeMethodName: 'store',
updateMethodName: 'update'
}
export default {
form: formDefaultOptions,
provide () {
const _this = this
return {
formErrors: _this.formErrors
}
},
mixins: [FormErrorHelper],
props: {
instance: {
type: LaravelModel,
required: true
}
},
data () {
return {
loading: true,
saving: false,
formErrors: new FormErrors(),
localInstance: null,
savePromise: Promise.resolve()
}
},
computed: {
canNotSave () {
return this.loading || this.saving
},
canNotCancel () {
return this.saving
},
isPersisted () {
return this.instance.isPersisted
}
},
methods: {
handleError (error) {
if (!error) {
return
}
if (error.response) {
this.formErrors.set(error.response.data.errors)
this.$nextTick(() => {
if (!this.scrollToFirstError()) {
// Fallback error reporting when there are errors, but they are not visible.
RemoteErrors(error)
}
})
} else {
RemoteErrors(error)
}
throw error
},
scrollToFirstError,
/**
* Use it to bind save action on a click button event. Prevent,
* el-button to react with Promise and throw a weird error. Use
* saveInstance if you support Promise.
*/
callSaveMethod () {
this.saveInstance()
},
saveInstance () {
this.saving = true
this.formErrors.forget()
const calledMethod = this.$options.form[this.localInstance.isPersisted ? 'updateMethodName' : 'storeMethodName']
return (this.savePromise = this.localInstance[calledMethod]().then(response => {
this.$emit('saved', response.data)
return response
}).catch(this.handleError).finally(_ => {
this.saving = false
}))
},
load () {
const Constructor = this.instance.constructor
let promises = this.promisesToWaitingFor()
if (promises instanceof Promise) {
promises = [promises]
}
this.loading = true
this.localInstance = null
if (this.instance.isPersisted) {
promises.push(Constructor.get(this.instance[Constructor.idKey]).then(response => {
this.localInstance = new Constructor(response.data)
this.$emit('loaded', this.localInstance)
}))
} else {
this.localInstance = new Constructor(this.instance)
}
Promise.all(promises).finally(_ => {
this.afterLoad()
this.loading = false
})
},
/**
* Use it to return Promises your need to wait for
* to display your form.
* Can be a Promise or an array of Promises
* @returns {Promise<void>[]|Promise<void>}
*/
promisesToWaitingFor () {
return []
},
/**
* Override this function to do something after everything is loaded
*/
afterLoad () { },
/**
* Override this function to do something after save success
*/
afterSave () { }
},
created () {
this.load()
}
}
function scrollToFirstError () {
const firstError = document.querySelector('.is-error, .inlineError')
if (!firstError) {
return false
}
let errorInput = firstError.querySelector('input')
if (!errorInput) {
errorInput = firstError.querySelector('textarea')
}
if (errorInput) {
errorInput.focus()
}
firstError.scrollIntoView({ behavior: 'smooth', block: firstError.offsetHeight > window.innerHeight ? 'start' : 'center', inline: 'nearest' })
return true
}
export { scrollToFirstError }