2FAuth/resources/js/components/formElements/Form.js
2023-12-01 15:29:26 +01:00

330 lines
7.7 KiB
JavaScript
Vendored

import { httpClientFactory } from '@/services/httpClientFactory'
import Errors from './FormErrors'
class Form {
/**
* Create a new form instance.
*
* @param {Object} data
*/
constructor (data = {}) {
this.axios = httpClientFactory('web')
this.isBusy = false
this.isDisabled = false
// this.successful = false
this.errors = new Errors()
this.originalData = this.deepCopy(data)
Object.assign(this, data)
}
/**
* Fill form data.
*
* @param {Object} data
*/
fill (data) {
this.keys().forEach(key => {
this[key] = data[key]
})
}
/**
* Update original form data.
*/
setOriginal () {
Object.keys(this)
.filter(key => !Form.ignore.includes(key))
.forEach(key => {
this.originalData[key] = this.deepCopy(this[key])
})
}
/**
* Fill form data.
*
* @param {Object} data
*/
hasChanged () {
return this.keys().some(key => this[key] !== this.originalData[key])
}
/**
* Fill form data.
*
* @param {Object} data
*/
fillWithKeyValueObject (data) {
this.keys().forEach(key => {
const keyValueObject = data.find(s => s.key === key.toString())
if(keyValueObject != undefined) {
this[key] = keyValueObject.value
}
})
}
/**
* Get the form data.
*
* @return {Object}
*/
data () {
return this.keys().reduce((data, key) => (
{ ...data, [key]: this[key] }
), {})
}
/**
* Get the form data keys.
*
* @return {Array}
*/
keys () {
return Object.keys(this)
.filter(key => !Form.ignore.includes(key))
}
/**
* Start processing the form.
*/
startProcessing () {
this.errors.clear()
this.isBusy = true
// this.successful = false
}
/**
* Finish processing the form.
*/
finishProcessing () {
this.isBusy = false
// this.successful = true
}
/**
* Clear the form errors.
*/
clear () {
this.errors.clear()
// this.successful = false
}
/**
* Reset the form fields.
*/
reset () {
Object.keys(this)
.filter(key => !Form.ignore.includes(key))
.forEach(key => {
this[key] = this.deepCopy(this.originalData[key])
})
}
/**
* Submit the form via a GET request.
*
* @param {String} url
* @param {Object} config (axios config)
* @return {Promise}
*/
get (url, config = {}) {
return this.submit('get', url, config)
}
/**
* Submit the form via a POST request.
*
* @param {String} url
* @param {Object} config (axios config)
* @return {Promise}
*/
post (url, config = {}) {
return this.submit('post', url, config)
}
/**
* Submit the form via a PATCH request.
*
* @param {String} url
* @param {Object} config (axios config)
* @return {Promise}
*/
patch (url, config = {}) {
return this.submit('patch', url, config)
}
/**
* Submit the form via a PUT request.
*
* @param {String} url
* @param {Object} config (axios config)
* @return {Promise}
*/
put (url, config = {}) {
return this.submit('put', url, config)
}
/**
* Submit the form via a DELETE request.
*
* @param {String} url
* @param {Object} config (axios config)
* @return {Promise}
*/
delete (url, config = {}) {
return this.submit('delete', url, config)
}
/**
* Submit the form data via an HTTP request.
*
* @param {String} method (get, post, patch, put)
* @param {String} url
* @param {Object} config (axios config)
* @return {Promise}
*/
submit (method, url, config = {}) {
this.startProcessing()
const data = method === 'get'
? { params: this.data() }
: this.data()
return new Promise((resolve, reject) => {
// (Form.axios || axios).request({ url: this.route(url), method, data, ...config })
this.axios.request({ url: this.route(url), method, data, ...config })
.then(response => {
this.finishProcessing()
resolve(response)
})
.catch(error => {
this.isBusy = false
if (error.response) {
this.errors.set(this.extractErrors(error.response))
}
if (error.response?.status != 422) {
reject(error)
}
})
})
}
/**
* Submit the form data via an HTTP request.
*
* @param {String} method (get, post, patch, put)
* @param {String} url
* @param {Object} config (axios config)
* @return {Promise}
*/
upload (url, config = {}) {
this.startProcessing()
return new Promise((resolve, reject) => {
// https://www.npmjs.com/package/axios#-automatic-serialization-to-formdata
this.axios.post(this.route(url), this.data(), { headers: {'Content-Type' : 'multipart/form-data'}, ...config })
.then(response => {
this.finishProcessing()
resolve(response)
})
.catch(error => {
this.isBusy = false
if (error.response) {
this.errors.set(this.extractErrors(error.response))
}
// if (error.response?.status != 422) {
reject(error)
// }
})
})
}
/**
* Extract the errors from the response object.
*
* @param {Object} response
* @return {Object}
*/
extractErrors (response) {
if (!response.data || typeof response.data !== 'object') {
return { error: Form.errorMessage }
}
if (response.data.errors) {
return { ...response.data.errors }
}
if (response.data.message) {
return { error: response.data.message }
}
return { ...response.data }
}
/**
* Get a named route.
*
* @param {String} name
* @return {Object} parameters
* @return {String}
*/
route (name, parameters = {}) {
let url = name
if (Form.routes.hasOwnProperty(name)) {
url = decodeURI(Form.routes[name])
}
if (typeof parameters !== 'object') {
parameters = { id: parameters }
}
Object.keys(parameters).forEach(key => {
url = url.replace(`{${key}}`, parameters[key])
})
return url
}
/**
* Clear errors on keydown.
*
* @param {KeyboardEvent} event
*/
onKeydown (event) {
if (event.target.name) {
this.errors.clear(event.target.name)
}
}
/**
* Deep copy the given object.
*
* @param {Object} obj
* @return {Object}
*/
deepCopy (obj) {
if (obj === null || typeof obj !== 'object') {
return obj
}
const copy = Array.isArray(obj) ? [] : {}
Object.keys(obj).forEach(key => {
copy[key] = this.deepCopy(obj[key])
})
return copy
}
}
Form.routes = {}
Form.errorMessage = 'Something went wrong. Please try again.'
Form.ignore = ['isBusy', 'isDisabled', 'errors', 'originalData', 'axios']
export default Form