Skip to content

Commit

Permalink
Handle files by default
Browse files Browse the repository at this point in the history
  • Loading branch information
cretueusebiu committed Apr 29, 2021
1 parent 1eda6e0 commit 76b76e6
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 21 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 2.0.3 - 2021-04-30

- Handle files with `FormData` by default.
- Added upload `progress` property to `Form`.

## 2.0.2 - 2021-04-29

- Fixed missing `Method` type for old axios versions.
Expand Down
22 changes: 8 additions & 14 deletions docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ form.successful: boolean
* The validation errors from the server.
*/
form.errors: Errors

/**
* The upload progress object.
*/
form.progress: { total: number, loaded: number, percentage: number } | undefined
```

#### Instance methods
Expand Down Expand Up @@ -403,12 +408,6 @@ export default {

### File Upload

To handle file uploads we need to install a npm pacakge:

```bash
npm install object-to-formdata
```

```html
<template>
<form @submit.prevent="submit" @keydown="form.onKeydown($event)">
Expand All @@ -418,13 +417,14 @@ npm install object-to-formdata
<input type="file" name="file" @change="handleFile">
<HasError :form="form" field="file" />

<div v-if="form.progress">Progress: {{ form.progress.percentage }}%</div>

<button type="submit">Submit</button>
</form>
</template>

<script>
import Form from 'vform'
import { serialize } from 'object-to-formdata'
import { HasError } from 'vform/src/components/bootstrap5'
export default {
Expand All @@ -449,13 +449,7 @@ export default {
async submit () {
const response = await this.form.post('/upload', {
transformRequest: [serialize],
onUploadProgress: e => {
// Handle the progress...
// console.log(e)
// See: https://github.com/axios/axios#request-config
}
// onUploadProgress: e => console.log(e) }
})
// ...
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vform",
"version": "2.0.2",
"version": "2.0.3",
"description": "Handle Laravel-Vue forms and validation with ease.",
"author": "Cretu Eusebiu",
"license": "MIT",
Expand Down
51 changes: 45 additions & 6 deletions src/Form.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { serialize } from 'object-to-formdata'
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
import Errors from './Errors'
import { deepCopy } from './util'
import { deepCopy, hasFiles } from './util'

interface Progress {
total: number
loaded: number
percentage: number
}

class Form {
[key: string]: any
Expand All @@ -21,10 +28,15 @@ class Form {
*/
errors: Errors = new Errors()

/**
* The upload progress object.
*/
progress: Progress | undefined = undefined

static axios: AxiosInstance
static routes: Record<string, string> = {}
static errorMessage = 'Something went wrong. Please try again.'
static ignore = ['busy', 'successful', 'errors', 'originalData']
static ignore = ['busy', 'successful', 'errors', 'progress', 'originalData']

/**
* Create a new form instance.
Expand Down Expand Up @@ -81,6 +93,7 @@ class Form {
this.errors.clear()
this.busy = true
this.successful = false
this.progress = undefined
}

/**
Expand All @@ -89,6 +102,7 @@ class Form {
finishProcessing () {
this.busy = false
this.successful = true
this.progress = undefined
}

/**
Expand All @@ -97,6 +111,7 @@ class Form {
clear () {
this.errors.clear()
this.successful = false
this.progress = undefined
}

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

config = {
data: {},
params: {},
url: this.route(url),
method: method as any,
onUploadProgress: this.handleUploadProgress.bind(this),
...config
}

if (method.toLowerCase() === 'get') {
config.params = { ...this.data(), ...(config.params || {}) }
config.params = { ...this.data(), ...config.params }
} else {
config.data = { ...this.data(), ...(config.data || {}) }
config.data = { ...this.data(), ...config.data }

if (hasFiles(config.data)) {
config.transformRequest = [data => serialize(data)]
}
}

return new Promise((resolve, reject) => {
// @ts-ignore
(Form.axios || axios).request({ url: this.route(url), method, ...config })
(Form.axios || axios).request(config)
.then((response: AxiosResponse) => {
this.finishProcessing()
resolve(response)
Expand All @@ -176,6 +203,7 @@ class Form {
*/
handleErrors (error: AxiosError) {
this.busy = false
this.progress = undefined

if (error.response) {
this.errors.set(this.extractErrors(error.response))
Expand All @@ -201,6 +229,17 @@ class Form {
return { ...response.data }
}

/**
* Handle the upload progress.
*/
handleUploadProgress (event: ProgressEvent) {
this.progress = {
total: event.total,
loaded: event.loaded,
percentage: Math.round((event.loaded * 100) / event.total)
}
}

/**
* @deprecated
*/
Expand Down
10 changes: 10 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,13 @@ export function deepCopy<T> (obj: T): T {
export function arrayWrap<T> (value: T): T[] {
return Array.isArray(value) ? value : [value]
}

/**
* Determine if the given data has files.
*/
export function hasFiles (data: File | Blob | FileList | Record<string, any>): boolean {
return data instanceof File ||
data instanceof Blob ||
data instanceof FileList ||
(typeof data === 'object' && data !== null && Object.values(data).find(value => hasFiles(value)) !== undefined)
}
13 changes: 13 additions & 0 deletions types/Form.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
import Errors from './Errors';
interface Progress {
total: number;
loaded: number;
percentage: number;
}
declare class Form {
[key: string]: any;
originalData: Record<string, any>;
Expand All @@ -15,6 +20,10 @@ declare class Form {
* The validation errors from the server.
*/
errors: Errors;
/**
* The upload progress object.
*/
progress: Progress | undefined;
static axios: AxiosInstance;
static routes: Record<string, string>;
static errorMessage: string;
Expand Down Expand Up @@ -91,6 +100,10 @@ declare class Form {
* Extract the errors from the response object.
*/
extractErrors(response: AxiosResponse): Record<string, any>;
/**
* Handle the upload progress.
*/
handleUploadProgress(event: ProgressEvent): void;
/**
* @deprecated
*/
Expand Down
4 changes: 4 additions & 0 deletions types/util.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ export declare function deepCopy<T>(obj: T): T;
* If the given value is not an array, wrap it in one.
*/
export declare function arrayWrap<T>(value: T): T[];
/**
* Determine if the given data has files.
*/
export declare function hasFiles(data: File | Blob | FileList | Record<string, any>): boolean;

0 comments on commit 76b76e6

Please sign in to comment.