antd 上传控件需要 action 函数,但我不需要它
action function is required with antd upload control, but I dont need it
我正在使用 ant design 组件并且我有一个上传输入:
https://ant.design/components/upload/
根据文档,需要对道具进行操作。
但是我不需要在上传时将文件发布到 url,我需要将整个 FORM 提交到 rest 端点(检查 handlesubmit 函数)
尝试浏览文档,我使用 handlechange 事件将文件添加到状态,但 STATUS 从未完成,因此永远不会命中该行。
我在这里错过了什么?
import React, { Component } from 'react';
import { Input, Upload , Icon, message} from 'antd';
import Form from '../../components/uielements/form';
import Checkbox from '../../components/uielements/checkbox';
import Button from '../../components/uielements/button';
import Notification from '../../components/notification';
import { adalApiFetch } from '../../adalConfig';
const FormItem = Form.Item;
class RegisterTenantForm extends Component {
constructor(props) {
super(props);
this.state = {TenantId: '', TenantUrl: '', CertificatePassword: '', confirmDirty: false, loading: false, buttondisabled: true};
this.handleChangeTenantUrl = this.handleChangeTenantUrl.bind(this);
this.handleChangeCertificatePassword = this.handleChangeCertificatePassword.bind(this);
this.handleChangeTenantId= this.handleChangeTenantId.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleupload = this.handleupload.bind(this);
this.handleTenantIdValidation = this.handleTenantIdValidation.bind(this);
this.handleTenantAdminUrl = this.handleTenantAdminUrl.bind(this);
};
handleChangeTenantUrl(event){
this.setState({TenantUrl: event.target.value});
}
handleChangeCertificatePassword(event){
this.setState({CertificatePassword: event.target.value});
}
handleChangeTenantId(event){
this.setState({TenantId: event.target.value});
}
beforeUpload(file) {
const isJPG = file.type === 'image/jpeg';
if (!isJPG) {
message.error('You can only upload JPG file!');
}
}
handleupload(info){
//let files = e.target.files;
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
this.setState({ loading: false });
this.setState({ 'selectedFile': info.file });
}
}
handleTenantIdValidation(rule, value, callback){
const form = this.props.form;
const str = form.getFieldValue('tenantid');
var re = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
if (str && !str.match(re)) {
this.setState({buttondisabled: true});
callback('Tenant id is not correctly formated id');
}
else {
this.setState({buttondisabled: false});
callback();
}
}
handleTenantAdminUrl(rule, value, callback){
const form = this.props.form;
const str = form.getFieldValue('tenantadminurl');
var re = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!$&'\(\)\*\+,;=.]+$/i;
if (str && !str.match(re)) {
this.setState({buttondisabled: true});
callback('Tenant Url is not correctly formated id');
}
else {
this.setState({buttondisabled: false});
callback();
}
}
handleSubmit(e){
e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
/*Notification(
'success',
'Received values of form',
JSON.stringify(values)
);*/
let data = new FormData();
//Append files to form data
data.append("model", JSON.stringify({ "TenantId": this.state.TenantId, "TenantUrl": this.state.TenantUrl, "CertificatePassword": this.state.CertificatePassword }));
//data.append("model", {"TenantId": this.state.TenantId, "TenantUrl": this.state.TenantUrl, "TenantPassword": this.state.TenantPassword });
let files = this.state.selectedFile;
for (let i = 0; i < files.length; i++) {
data.append("file", files[i], files[i].name);
}
const options = {
method: 'put',
body: data,
config: {
headers: {
'Content-Type': 'multipart/form-data'
}
}
};
adalApiFetch(fetch, "/Tenant", options)
.then(response => response.json())
.then(responseJson => {
if (!this.isCancelled) {
this.setState({ data: responseJson });
}
})
.catch(error => {
console.error(error);
});
}
});
}
render() {
const { getFieldDecorator } = this.props.form;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 14 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 14,
offset: 6,
},
},
};
return (
<Form onSubmit={this.handleSubmit}>
<FormItem {...formItemLayout} label="Tenant Id" hasFeedback>
{getFieldDecorator('tenantid', {
rules: [
{
required: true,
message: 'Please input your tenant id',
},
{
validator: this.handleTenantIdValidation
}],
})(<Input name="tenantid" id="tenantid" onChange={this.handleChangeTenantId}/>)}
</FormItem>
<FormItem {...formItemLayout} label="Certificate Password" hasFeedback>
{getFieldDecorator('certificatepassword', {
rules: [
{
required: true,
message: 'Please input your password!',
}
],
})(<Input type="password" name="certificatepassword" id="certificatepassword" onChange={this.handleChangeCertificatePassword}/>)}
</FormItem>
<FormItem {...formItemLayout} label="Tenant admin url" hasFeedback>
{getFieldDecorator('tenantadminurl', {
rules: [
{
required: true,
message: 'Please input your tenant admin url!',
},
{
validator: this.handleTenantAdminUrl
}],
})(<Input name="tenantadminurl" id="tenantadminurl" onChange={this.handleChangeTenantUrl} />)}
</FormItem>
<FormItem {...formItemLayout} label="Certificate File">
<Upload onChange={this.handleupload} beforeUpload={this.beforeUpload}>
<Button >
<Icon type="upload" /> Click to Upload
</Button>
</Upload>
</FormItem>
<FormItem {...tailFormItemLayout}>
<Button type="primary" htmlType="submit" disabled={this.state.buttondisabled}>
Register tenant
</Button>
</FormItem>
</Form>
);
}
}
const WrappedRegisterTenantForm = Form.create()(RegisterTenantForm);
export default WrappedRegisterTenantForm;
TL;DR
覆盖 <Upload/>
默认上传 AJAX 实现并模拟成功上传。
完整答案
您似乎试图将 andt
的 <Upload/>
组件简单地用作文件选择器,因为您自己将文件附加到 formData
。从来没有达到status === "done"
的原因是因为文件没有真正上传到任何地方。
因此,您不需要像 OOB 那样自动上传文件。
您所需要的只是 onChange
将向您发送选定的文件,您可以将其保存在状态树中的某个位置。
<Upload/>
将另一个组件 (rc-upload) 呈现为其处理实际 AJAX 上传的子组件。
您可以通过将 customRequest
道具传递给 <Upload/>
(将传递给 rc-upload
组件)来覆盖此行为。
您可以看到here哪些选项被传递给了请求函数。
这是虚拟请求函数的实现:
const dummyRequest = ({ file, onSuccess }) => {
setTimeout(() => {
onSuccess("ok");
}, 0);
};
然后你可以传给<Upload customRequest={dummyRequest}/>
onChange
还是会被触发,不过这次是"done"
状态,因为dummy request函数模拟的是上传成功的流程。
根据官方文档Upload manually:
Upload files manually after beforeUpload
returns false
.
beforeUpload(file) {
const isJPG = file.type === 'image/jpeg';
if (!isJPG) {
message.error('You can only upload JPG file!');
}
return false;
}
扩展到@Quincy 的回答,您还可以在类似这样的组件上使用 shorthand,
<Upload beforeUpload={() => false} />
蚂蚁:
使用 beforeUpload 上传唯一的 jpeg 或 png 文件的最简单解决方案
<Upload
fileList = {this.state.selectedLogo}
customRequest = {customRequest}
onChange = {this.onChangeLogo}
showUploadList = {false}
beforeUpload = {(file) => {
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJPG) {
message.error('You can only upload JPG or PNG file!');
return false;
} else {
return true;
}
}}
>
用 Antd Dragger 做一些类似的事情来在 React Native 中上传 csv,并且能够使用 Ramy Ben Aroya 的回答让它工作:
const dummyRequest = async ({ file, onSuccess }) => {
setTimeout(() => {
onSuccess("ok");
}, 0);
}
const fileProps = {
name: 'file',
multiple: true,
customRequest: dummyRequest,
onChange(info:any) {
const { status } = info.file;
if (status !== 'uploading') {
console.log('uploading');
}
if (status === 'done') {
getBase64(info.file.originFileObj, async (file: string) => {
// handle data base or parse logic here using file
});
message.success(`${info.file.name} file uploaded successfully.`);
} else if (status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
},
onDrop(e) {
console.log('Dropped files', e.dataTransfer.files);
},
};
<Dragger {...fileProps} style={{ maxHeight: "180px" }}>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">Click or drag file to this area to upload</p>
<p className="ant-upload-hint">
Supported file types: .csv
</p>
</Dragger>
我正在使用 ant design 组件并且我有一个上传输入: https://ant.design/components/upload/
根据文档,需要对道具进行操作。
但是我不需要在上传时将文件发布到 url,我需要将整个 FORM 提交到 rest 端点(检查 handlesubmit 函数)
尝试浏览文档,我使用 handlechange 事件将文件添加到状态,但 STATUS 从未完成,因此永远不会命中该行。
我在这里错过了什么?
import React, { Component } from 'react';
import { Input, Upload , Icon, message} from 'antd';
import Form from '../../components/uielements/form';
import Checkbox from '../../components/uielements/checkbox';
import Button from '../../components/uielements/button';
import Notification from '../../components/notification';
import { adalApiFetch } from '../../adalConfig';
const FormItem = Form.Item;
class RegisterTenantForm extends Component {
constructor(props) {
super(props);
this.state = {TenantId: '', TenantUrl: '', CertificatePassword: '', confirmDirty: false, loading: false, buttondisabled: true};
this.handleChangeTenantUrl = this.handleChangeTenantUrl.bind(this);
this.handleChangeCertificatePassword = this.handleChangeCertificatePassword.bind(this);
this.handleChangeTenantId= this.handleChangeTenantId.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleupload = this.handleupload.bind(this);
this.handleTenantIdValidation = this.handleTenantIdValidation.bind(this);
this.handleTenantAdminUrl = this.handleTenantAdminUrl.bind(this);
};
handleChangeTenantUrl(event){
this.setState({TenantUrl: event.target.value});
}
handleChangeCertificatePassword(event){
this.setState({CertificatePassword: event.target.value});
}
handleChangeTenantId(event){
this.setState({TenantId: event.target.value});
}
beforeUpload(file) {
const isJPG = file.type === 'image/jpeg';
if (!isJPG) {
message.error('You can only upload JPG file!');
}
}
handleupload(info){
//let files = e.target.files;
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
this.setState({ loading: false });
this.setState({ 'selectedFile': info.file });
}
}
handleTenantIdValidation(rule, value, callback){
const form = this.props.form;
const str = form.getFieldValue('tenantid');
var re = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
if (str && !str.match(re)) {
this.setState({buttondisabled: true});
callback('Tenant id is not correctly formated id');
}
else {
this.setState({buttondisabled: false});
callback();
}
}
handleTenantAdminUrl(rule, value, callback){
const form = this.props.form;
const str = form.getFieldValue('tenantadminurl');
var re = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!$&'\(\)\*\+,;=.]+$/i;
if (str && !str.match(re)) {
this.setState({buttondisabled: true});
callback('Tenant Url is not correctly formated id');
}
else {
this.setState({buttondisabled: false});
callback();
}
}
handleSubmit(e){
e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
/*Notification(
'success',
'Received values of form',
JSON.stringify(values)
);*/
let data = new FormData();
//Append files to form data
data.append("model", JSON.stringify({ "TenantId": this.state.TenantId, "TenantUrl": this.state.TenantUrl, "CertificatePassword": this.state.CertificatePassword }));
//data.append("model", {"TenantId": this.state.TenantId, "TenantUrl": this.state.TenantUrl, "TenantPassword": this.state.TenantPassword });
let files = this.state.selectedFile;
for (let i = 0; i < files.length; i++) {
data.append("file", files[i], files[i].name);
}
const options = {
method: 'put',
body: data,
config: {
headers: {
'Content-Type': 'multipart/form-data'
}
}
};
adalApiFetch(fetch, "/Tenant", options)
.then(response => response.json())
.then(responseJson => {
if (!this.isCancelled) {
this.setState({ data: responseJson });
}
})
.catch(error => {
console.error(error);
});
}
});
}
render() {
const { getFieldDecorator } = this.props.form;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 14 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 14,
offset: 6,
},
},
};
return (
<Form onSubmit={this.handleSubmit}>
<FormItem {...formItemLayout} label="Tenant Id" hasFeedback>
{getFieldDecorator('tenantid', {
rules: [
{
required: true,
message: 'Please input your tenant id',
},
{
validator: this.handleTenantIdValidation
}],
})(<Input name="tenantid" id="tenantid" onChange={this.handleChangeTenantId}/>)}
</FormItem>
<FormItem {...formItemLayout} label="Certificate Password" hasFeedback>
{getFieldDecorator('certificatepassword', {
rules: [
{
required: true,
message: 'Please input your password!',
}
],
})(<Input type="password" name="certificatepassword" id="certificatepassword" onChange={this.handleChangeCertificatePassword}/>)}
</FormItem>
<FormItem {...formItemLayout} label="Tenant admin url" hasFeedback>
{getFieldDecorator('tenantadminurl', {
rules: [
{
required: true,
message: 'Please input your tenant admin url!',
},
{
validator: this.handleTenantAdminUrl
}],
})(<Input name="tenantadminurl" id="tenantadminurl" onChange={this.handleChangeTenantUrl} />)}
</FormItem>
<FormItem {...formItemLayout} label="Certificate File">
<Upload onChange={this.handleupload} beforeUpload={this.beforeUpload}>
<Button >
<Icon type="upload" /> Click to Upload
</Button>
</Upload>
</FormItem>
<FormItem {...tailFormItemLayout}>
<Button type="primary" htmlType="submit" disabled={this.state.buttondisabled}>
Register tenant
</Button>
</FormItem>
</Form>
);
}
}
const WrappedRegisterTenantForm = Form.create()(RegisterTenantForm);
export default WrappedRegisterTenantForm;
TL;DR
覆盖 <Upload/>
默认上传 AJAX 实现并模拟成功上传。
完整答案
您似乎试图将 andt
的 <Upload/>
组件简单地用作文件选择器,因为您自己将文件附加到 formData
。从来没有达到status === "done"
的原因是因为文件没有真正上传到任何地方。
因此,您不需要像 OOB 那样自动上传文件。
您所需要的只是 onChange
将向您发送选定的文件,您可以将其保存在状态树中的某个位置。
<Upload/>
将另一个组件 (rc-upload) 呈现为其处理实际 AJAX 上传的子组件。
您可以通过将 customRequest
道具传递给 <Upload/>
(将传递给 rc-upload
组件)来覆盖此行为。
您可以看到here哪些选项被传递给了请求函数。 这是虚拟请求函数的实现:
const dummyRequest = ({ file, onSuccess }) => {
setTimeout(() => {
onSuccess("ok");
}, 0);
};
然后你可以传给<Upload customRequest={dummyRequest}/>
onChange
还是会被触发,不过这次是"done"
状态,因为dummy request函数模拟的是上传成功的流程。
根据官方文档Upload manually:
Upload files manually after
beforeUpload
returnsfalse
.
beforeUpload(file) {
const isJPG = file.type === 'image/jpeg';
if (!isJPG) {
message.error('You can only upload JPG file!');
}
return false;
}
扩展到@Quincy 的回答,您还可以在类似这样的组件上使用 shorthand,
<Upload beforeUpload={() => false} />
蚂蚁: 使用 beforeUpload 上传唯一的 jpeg 或 png 文件的最简单解决方案
<Upload
fileList = {this.state.selectedLogo}
customRequest = {customRequest}
onChange = {this.onChangeLogo}
showUploadList = {false}
beforeUpload = {(file) => {
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJPG) {
message.error('You can only upload JPG or PNG file!');
return false;
} else {
return true;
}
}}
>
用 Antd Dragger 做一些类似的事情来在 React Native 中上传 csv,并且能够使用 Ramy Ben Aroya 的回答让它工作:
const dummyRequest = async ({ file, onSuccess }) => {
setTimeout(() => {
onSuccess("ok");
}, 0);
}
const fileProps = {
name: 'file',
multiple: true,
customRequest: dummyRequest,
onChange(info:any) {
const { status } = info.file;
if (status !== 'uploading') {
console.log('uploading');
}
if (status === 'done') {
getBase64(info.file.originFileObj, async (file: string) => {
// handle data base or parse logic here using file
});
message.success(`${info.file.name} file uploaded successfully.`);
} else if (status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
},
onDrop(e) {
console.log('Dropped files', e.dataTransfer.files);
},
};
<Dragger {...fileProps} style={{ maxHeight: "180px" }}>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">Click or drag file to this area to upload</p>
<p className="ant-upload-hint">
Supported file types: .csv
</p>
</Dragger>