Skip to content

Commit 76b76e6

Browse files
committed
Handle files by default
1 parent 1eda6e0 commit 76b76e6

File tree

7 files changed

+86
-21
lines changed

7 files changed

+86
-21
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 2.0.3 - 2021-04-30
4+
5+
- Handle files with `FormData` by default.
6+
- Added upload `progress` property to `Form`.
7+
38
## 2.0.2 - 2021-04-29
49

510
- Fixed missing `Method` type for old axios versions.

docs.md

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ form.successful: boolean
101101
* The validation errors from the server.
102102
*/
103103
form.errors: Errors
104+
105+
/**
106+
* The upload progress object.
107+
*/
108+
form.progress: { total: number, loaded: number, percentage: number } | undefined
104109
```
105110

106111
#### Instance methods
@@ -403,12 +408,6 @@ export default {
403408

404409
### File Upload
405410

406-
To handle file uploads we need to install a npm pacakge:
407-
408-
```bash
409-
npm install object-to-formdata
410-
```
411-
412411
```html
413412
<template>
414413
<form @submit.prevent="submit" @keydown="form.onKeydown($event)">
@@ -418,13 +417,14 @@ npm install object-to-formdata
418417
<input type="file" name="file" @change="handleFile">
419418
<HasError :form="form" field="file" />
420419

420+
<div v-if="form.progress">Progress: {{ form.progress.percentage }}%</div>
421+
421422
<button type="submit">Submit</button>
422423
</form>
423424
</template>
424425

425426
<script>
426427
import Form from 'vform'
427-
import { serialize } from 'object-to-formdata'
428428
import { HasError } from 'vform/src/components/bootstrap5'
429429
430430
export default {
@@ -449,13 +449,7 @@ export default {
449449
450450
async submit () {
451451
const response = await this.form.post('/upload', {
452-
transformRequest: [serialize],
453-
454-
onUploadProgress: e => {
455-
// Handle the progress...
456-
// console.log(e)
457-
// See: https://github.com/axios/axios#request-config
458-
}
452+
// onUploadProgress: e => console.log(e) }
459453
})
460454
461455
// ...

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vform",
3-
"version": "2.0.2",
3+
"version": "2.0.3",
44
"description": "Handle Laravel-Vue forms and validation with ease.",
55
"author": "Cretu Eusebiu",
66
"license": "MIT",

src/Form.ts

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1+
import { serialize } from 'object-to-formdata'
12
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
23
import Errors from './Errors'
3-
import { deepCopy } from './util'
4+
import { deepCopy, hasFiles } from './util'
5+
6+
interface Progress {
7+
total: number
8+
loaded: number
9+
percentage: number
10+
}
411

512
class Form {
613
[key: string]: any
@@ -21,10 +28,15 @@ class Form {
2128
*/
2229
errors: Errors = new Errors()
2330

31+
/**
32+
* The upload progress object.
33+
*/
34+
progress: Progress | undefined = undefined
35+
2436
static axios: AxiosInstance
2537
static routes: Record<string, string> = {}
2638
static errorMessage = 'Something went wrong. Please try again.'
27-
static ignore = ['busy', 'successful', 'errors', 'originalData']
39+
static ignore = ['busy', 'successful', 'errors', 'progress', 'originalData']
2840

2941
/**
3042
* Create a new form instance.
@@ -81,6 +93,7 @@ class Form {
8193
this.errors.clear()
8294
this.busy = true
8395
this.successful = false
96+
this.progress = undefined
8497
}
8598

8699
/**
@@ -89,6 +102,7 @@ class Form {
89102
finishProcessing () {
90103
this.busy = false
91104
this.successful = true
105+
this.progress = undefined
92106
}
93107

94108
/**
@@ -97,6 +111,7 @@ class Form {
97111
clear () {
98112
this.errors.clear()
99113
this.successful = false
114+
this.progress = undefined
100115
}
101116

102117
/**
@@ -151,15 +166,27 @@ class Form {
151166
submit (method: string, url: string, config: AxiosRequestConfig = {}): Promise<AxiosResponse> {
152167
this.startProcessing()
153168

169+
config = {
170+
data: {},
171+
params: {},
172+
url: this.route(url),
173+
method: method as any,
174+
onUploadProgress: this.handleUploadProgress.bind(this),
175+
...config
176+
}
177+
154178
if (method.toLowerCase() === 'get') {
155-
config.params = { ...this.data(), ...(config.params || {}) }
179+
config.params = { ...this.data(), ...config.params }
156180
} else {
157-
config.data = { ...this.data(), ...(config.data || {}) }
181+
config.data = { ...this.data(), ...config.data }
182+
183+
if (hasFiles(config.data)) {
184+
config.transformRequest = [data => serialize(data)]
185+
}
158186
}
159187

160188
return new Promise((resolve, reject) => {
161-
// @ts-ignore
162-
(Form.axios || axios).request({ url: this.route(url), method, ...config })
189+
(Form.axios || axios).request(config)
163190
.then((response: AxiosResponse) => {
164191
this.finishProcessing()
165192
resolve(response)
@@ -176,6 +203,7 @@ class Form {
176203
*/
177204
handleErrors (error: AxiosError) {
178205
this.busy = false
206+
this.progress = undefined
179207

180208
if (error.response) {
181209
this.errors.set(this.extractErrors(error.response))
@@ -201,6 +229,17 @@ class Form {
201229
return { ...response.data }
202230
}
203231

232+
/**
233+
* Handle the upload progress.
234+
*/
235+
handleUploadProgress (event: ProgressEvent) {
236+
this.progress = {
237+
total: event.total,
238+
loaded: event.loaded,
239+
percentage: Math.round((event.loaded * 100) / event.total)
240+
}
241+
}
242+
204243
/**
205244
* @deprecated
206245
*/

src/util.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,13 @@ export function deepCopy<T> (obj: T): T {
2121
export function arrayWrap<T> (value: T): T[] {
2222
return Array.isArray(value) ? value : [value]
2323
}
24+
25+
/**
26+
* Determine if the given data has files.
27+
*/
28+
export function hasFiles (data: File | Blob | FileList | Record<string, any>): boolean {
29+
return data instanceof File ||
30+
data instanceof Blob ||
31+
data instanceof FileList ||
32+
(typeof data === 'object' && data !== null && Object.values(data).find(value => hasFiles(value)) !== undefined)
33+
}

types/Form.d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
22
import Errors from './Errors';
3+
interface Progress {
4+
total: number;
5+
loaded: number;
6+
percentage: number;
7+
}
38
declare class Form {
49
[key: string]: any;
510
originalData: Record<string, any>;
@@ -15,6 +20,10 @@ declare class Form {
1520
* The validation errors from the server.
1621
*/
1722
errors: Errors;
23+
/**
24+
* The upload progress object.
25+
*/
26+
progress: Progress | undefined;
1827
static axios: AxiosInstance;
1928
static routes: Record<string, string>;
2029
static errorMessage: string;
@@ -91,6 +100,10 @@ declare class Form {
91100
* Extract the errors from the response object.
92101
*/
93102
extractErrors(response: AxiosResponse): Record<string, any>;
103+
/**
104+
* Handle the upload progress.
105+
*/
106+
handleUploadProgress(event: ProgressEvent): void;
94107
/**
95108
* @deprecated
96109
*/

types/util.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@ export declare function deepCopy<T>(obj: T): T;
66
* If the given value is not an array, wrap it in one.
77
*/
88
export declare function arrayWrap<T>(value: T): T[];
9+
/**
10+
* Determine if the given data has files.
11+
*/
12+
export declare function hasFiles(data: File | Blob | FileList | Record<string, any>): boolean;

0 commit comments

Comments
 (0)