Form.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. import { httpClientFactory } from '@/services/httpClientFactory'
  2. import Errors from './FormErrors'
  3. class Form {
  4. /**
  5. * Create a new form instance.
  6. *
  7. * @param {Object} data
  8. */
  9. constructor (data = {}) {
  10. this.axios = httpClientFactory('web')
  11. this.isBusy = false
  12. this.isDisabled = false
  13. // this.successful = false
  14. this.errors = new Errors()
  15. this.originalData = this.deepCopy(data)
  16. Object.assign(this, data)
  17. }
  18. /**
  19. * Fill form data.
  20. *
  21. * @param {Object} data
  22. */
  23. fill (data) {
  24. this.keys().forEach(key => {
  25. this[key] = data[key]
  26. })
  27. }
  28. /**
  29. * Update original form data.
  30. */
  31. setOriginal () {
  32. Object.keys(this)
  33. .filter(key => !Form.ignore.includes(key))
  34. .forEach(key => {
  35. this.originalData[key] = this.deepCopy(this[key])
  36. })
  37. }
  38. /**
  39. * Fill form data.
  40. *
  41. * @param {Object} data
  42. */
  43. fillWithKeyValueObject (data) {
  44. this.keys().forEach(key => {
  45. const keyValueObject = data.find(s => s.key === key.toString())
  46. if(keyValueObject != undefined) {
  47. this[key] = keyValueObject.value
  48. }
  49. })
  50. }
  51. /**
  52. * Get the form data.
  53. *
  54. * @return {Object}
  55. */
  56. data () {
  57. return this.keys().reduce((data, key) => (
  58. { ...data, [key]: this[key] }
  59. ), {})
  60. }
  61. /**
  62. * Get the form data keys.
  63. *
  64. * @return {Array}
  65. */
  66. keys () {
  67. return Object.keys(this)
  68. .filter(key => !Form.ignore.includes(key))
  69. }
  70. /**
  71. * Start processing the form.
  72. */
  73. startProcessing () {
  74. this.errors.clear()
  75. this.isBusy = true
  76. // this.successful = false
  77. }
  78. /**
  79. * Finish processing the form.
  80. */
  81. finishProcessing () {
  82. this.isBusy = false
  83. // this.successful = true
  84. }
  85. /**
  86. * Clear the form errors.
  87. */
  88. clear () {
  89. this.errors.clear()
  90. // this.successful = false
  91. }
  92. /**
  93. * Reset the form fields.
  94. */
  95. reset () {
  96. Object.keys(this)
  97. .filter(key => !Form.ignore.includes(key))
  98. .forEach(key => {
  99. this[key] = this.deepCopy(this.originalData[key])
  100. })
  101. }
  102. /**
  103. * Submit the form via a GET request.
  104. *
  105. * @param {String} url
  106. * @param {Object} config (axios config)
  107. * @return {Promise}
  108. */
  109. get (url, config = {}) {
  110. return this.submit('get', url, config)
  111. }
  112. /**
  113. * Submit the form via a POST request.
  114. *
  115. * @param {String} url
  116. * @param {Object} config (axios config)
  117. * @return {Promise}
  118. */
  119. post (url, config = {}) {
  120. return this.submit('post', url, config)
  121. }
  122. /**
  123. * Submit the form via a PATCH request.
  124. *
  125. * @param {String} url
  126. * @param {Object} config (axios config)
  127. * @return {Promise}
  128. */
  129. patch (url, config = {}) {
  130. return this.submit('patch', url, config)
  131. }
  132. /**
  133. * Submit the form via a PUT request.
  134. *
  135. * @param {String} url
  136. * @param {Object} config (axios config)
  137. * @return {Promise}
  138. */
  139. put (url, config = {}) {
  140. return this.submit('put', url, config)
  141. }
  142. /**
  143. * Submit the form via a DELETE request.
  144. *
  145. * @param {String} url
  146. * @param {Object} config (axios config)
  147. * @return {Promise}
  148. */
  149. delete (url, config = {}) {
  150. return this.submit('delete', url, config)
  151. }
  152. /**
  153. * Submit the form data via an HTTP request.
  154. *
  155. * @param {String} method (get, post, patch, put)
  156. * @param {String} url
  157. * @param {Object} config (axios config)
  158. * @return {Promise}
  159. */
  160. submit (method, url, config = {}) {
  161. this.startProcessing()
  162. const data = method === 'get'
  163. ? { params: this.data() }
  164. : this.data()
  165. return new Promise((resolve, reject) => {
  166. // (Form.axios || axios).request({ url: this.route(url), method, data, ...config })
  167. this.axios.request({ url: this.route(url), method, data, ...config })
  168. .then(response => {
  169. this.finishProcessing()
  170. resolve(response)
  171. })
  172. .catch(error => {
  173. this.isBusy = false
  174. if (error.response) {
  175. this.errors.set(this.extractErrors(error.response))
  176. }
  177. reject(error)
  178. })
  179. })
  180. }
  181. /**
  182. * Submit the form data via an HTTP request.
  183. *
  184. * @param {String} method (get, post, patch, put)
  185. * @param {String} url
  186. * @param {Object} config (axios config)
  187. * @return {Promise}
  188. */
  189. upload (url, formData, config = {}) {
  190. this.startProcessing()
  191. return new Promise((resolve, reject) => {
  192. // (Form.axios || axios).request({ url: this.route(url), method, data, ...config })
  193. this.axios.request({ url: this.route(url), method: 'post', data: formData, header: {'Content-Type' : 'multipart/form-data'}, ...config })
  194. .then(response => {
  195. this.finishProcessing()
  196. resolve(response)
  197. })
  198. .catch(error => {
  199. this.isBusy = false
  200. if (error.response) {
  201. this.errors.set(this.extractErrors(error.response))
  202. }
  203. reject(error)
  204. })
  205. })
  206. }
  207. /**
  208. * Extract the errors from the response object.
  209. *
  210. * @param {Object} response
  211. * @return {Object}
  212. */
  213. extractErrors (response) {
  214. if (!response.data || typeof response.data !== 'object') {
  215. return { error: Form.errorMessage }
  216. }
  217. if (response.data.errors) {
  218. return { ...response.data.errors }
  219. }
  220. if (response.data.message) {
  221. return { error: response.data.message }
  222. }
  223. return { ...response.data }
  224. }
  225. /**
  226. * Get a named route.
  227. *
  228. * @param {String} name
  229. * @return {Object} parameters
  230. * @return {String}
  231. */
  232. route (name, parameters = {}) {
  233. let url = name
  234. if (Form.routes.hasOwnProperty(name)) {
  235. url = decodeURI(Form.routes[name])
  236. }
  237. if (typeof parameters !== 'object') {
  238. parameters = { id: parameters }
  239. }
  240. Object.keys(parameters).forEach(key => {
  241. url = url.replace(`{${key}}`, parameters[key])
  242. })
  243. return url
  244. }
  245. /**
  246. * Clear errors on keydown.
  247. *
  248. * @param {KeyboardEvent} event
  249. */
  250. onKeydown (event) {
  251. if (event.target.name) {
  252. this.errors.clear(event.target.name)
  253. }
  254. }
  255. /**
  256. * Deep copy the given object.
  257. *
  258. * @param {Object} obj
  259. * @return {Object}
  260. */
  261. deepCopy (obj) {
  262. if (obj === null || typeof obj !== 'object') {
  263. return obj
  264. }
  265. const copy = Array.isArray(obj) ? [] : {}
  266. Object.keys(obj).forEach(key => {
  267. copy[key] = this.deepCopy(obj[key])
  268. })
  269. return copy
  270. }
  271. }
  272. Form.routes = {}
  273. Form.errorMessage = 'Something went wrong. Please try again.'
  274. Form.ignore = ['isBusy', 'isDisabled', 'errors', 'originalData']
  275. export default Form