Form.js 7.3 KB

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