I am trying to use antd to create a form with a fileupload, but I cant make work the handleupload function, I have the error:
Cannot read property 'setState' of undefined
at handleupload (registertenantform.js:43)
The code is this:
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: '' };
this.handleChangeTenantUrl = this.handleChangeTenantUrl.bind(this);
this.handleChangeCertificatePassword = this.handleChangeCertificatePassword.bind(this);
this.handleChangeTenantId= this.handleChangeTenantId.bind(this);
this.handleSubmit = this.handleSubmit.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({ 'selectedFiles': info.file });
}
}
state = {
confirmDirty: false,
loading: false,
};
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.selectedFiles;
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 uploadButton = (
<div>
<Icon type={this.state.loading ? 'loading' : 'plus'} />
<div className="ant-upload-text">Upload</div>
</div>
);
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',
},
],
})(<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="certificatepassword" onChange={this.handleChangeCertificatePassword}/>)}
</FormItem>
<FormItem {...formItemLayout} label="Tenant admin url" hasFeedback>
{getFieldDecorator('tenantadminurl', {
rules: [
{
required: true,
message: 'Please input your tenant admin url!',
}
],
})(<Input type="tenantadminurl" onChange={this.handleChangeTenantUrl} />)}
</FormItem>
<FormItem {...tailFormItemLayout}>
<Upload onChange={this.handleupload} beforeUpload={this.beforeUpload}>
<Button>
<Icon type="upload" /> Click to Upload
</Button>
</Upload>
<Button type="primary" htmlType="submit">
Register tenant
</Button>
</FormItem>
</Form>
);
}
}
const WrappedRegisterTenantForm = Form.create()(RegisterTenantForm);
export default WrappedRegisterTenantForm;
You missed to bind your handleupload function. Just add
this.handleupload = this.handleupload.bind(this)
in your RegisterTenantForm constructor
Or
you can rewrite a handleupload function using arrow function like so:
handleupload = (info) => {
//let files = e.target.files;
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
// btw you dont need two separated setState here, you can do
// it in one setState
this.setState({
'selectedFiles': info.file,
loading: false
});
}
}
Your this is treating local scope inside promise as a result setState method should be undefined so you need to assign this into another variable like below:
const that = this;
and can access that into your promise code below:
adalApiFetch(fetch, "/Tenant", options)
.then(response => response.json())
.then(responseJson => {
if (!that.isCancelled) {
that.setState({
data: responseJson
});
}
})
.catch(error => {
console.error(error);
});
Related
I have a POST API that when executed needs to render the response result in a div
I am using React.js and Redux
This is the service where the api is stored
const addMP3Request = async (payload) => {
const formData = new FormData();
formData.append("titre",payload.payload.titre);
formData.append("description",payload.payload.description);
formData.append("type",payload.payload.type);
formData.append("file1",payload.payload.fichier1);
const wsResponse = await axios.request({
method: 'post',
url: `http://localhost:3000/api/watson_tones/analyzeMP3`,
data: formData,
config: {
headers: {
'Content-Type': `multipart/form-data`,
'Accept': 'application/json',
}
}
});
return wsResponse;
}
this is my reducer
const INIT_STATE = {
responseData: ''
};
export default (state = INIT_STATE, action) => {
switch (action.type) {
case ADD_MP3: {
return {
...state,
responseData: action.responseData
}
}
default:
return state;
}
}
this is the action :
export const addMP3Action = (titre,description,type,fichier1) => {
return {
type: ADD_MP3,
payload: {
titre: titre,
description: description,
type: type,
fichier1:fichier1
},
};
};
and this is the view where I am adding the MP3 and wants to store the response of this api
import React, { Component } from "react";
import { Button, Form, Input, Select} from "antd";
import { connect } from "react-redux";
import {addMP3Action} from "../../appRedux/actions/Mp3";
import SweetAlert from "react-bootstrap-sweetalert";
import AudioPlayer from "react-h5-audio-player";
const FormItem = Form.Item;
const Option = Select.Option;
class AjoutExtrait extends Component {
constructor() {
super();
this.state = {
selectedFileUrl: '',
selectedFileName:"",
showPlayer:false,
alertMessage: "",
alertSuccess: false,
alertWarning: false,
titre:'',
description:'',
type:"",
responseData:''
};
this.onFileChange = this.onFileChange.bind(this)
this.handleChangeTitre = this.handleChangeTitre.bind(this)
this.handleChangeDescription = this.handleChangeDescription.bind(this)
this.handleChangeType = this.handleChangeType.bind(this)
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.responseData !== prevState.responseData && nextProps.responseData) {
//console.log('responseDataaa',nextProps.responseData)
return { responseData: nextProps.responseData };
} else
//console.log('responseDataaa',nextProps.responseData)
return null;
}
onFileChange(e) {
this.setState({ file: e.target.files[0] });
this.setState({ selectedFileUrl: URL.createObjectURL(e.target.files[0]) });
this.setState({ showPlayer: true });
this.setState({ selectedFileName: e.target.files[0].name });
}
handleChangeTitre(event) {
this.setState({titre: event.target.value});
}
handleChangeDescription(event) {
this.setState({description: event.target.value});
}
// handleChangeType(event) {
// this.setState({type: event.target.value});
// }
handleChangeType = (value) => {
let selectedFilter = value;
this.setState({type: selectedFilter});
}
handleFormSubmit = (e) => {
e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
this.props.addMP3Action(this.state.titre,this.state.description,this.state.type,this.state.file);
}
});
}
onConfirmAlertMessage = () => {
this.setState({
alertMessage: "",
alertSuccess: false,
alertWarning: false,
});
};
renderAlertMessage(){
var intl = this.props.intl;
const { alertSuccess, alertWarning, alertMessage } = this.state;
return(
<div>
<SweetAlert
show={alertSuccess}
success
title={alertMessage !== "" ?(intl.formatMessage({id: alertMessage})):""}
onConfirm={this.onConfirmAlertMessage}>
</SweetAlert>
<SweetAlert show={alertWarning}
warning
title={alertMessage !== "" ?(intl.formatMessage({id: alertMessage})):""}
onConfirm={this.onConfirmAlertMessage}>
</SweetAlert>
</div>
);
}
render() {
// const { getFieldDecorator } = this.props.form;
console.log("gfgfg",this.props.responseData)
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 24 },
md: { span: 12 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 24 },
md: { span: 12 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: { span: 24 },
sm: { span: 24 },
md: { span: 20 },
},
};
return (
<div ref={this.props.scrollDivAjoutExtrait} className="cm-comedien-mp3-card-ajout">
<h2>Ajouter un Extrait MP3</h2>
<p> gfdffd {this.props.responseData}</p>
<Form onSubmit={this.handleFormSubmit}>
<FormItem {...formItemLayout}>
<p>Titre</p>
<Input value={this.state.titre} onChange={this.handleChangeTitre}/>
</FormItem>
<FormItem {...formItemLayout}>
<p>Description</p>
<Input value={this.state.description} onChange={this.handleChangeDescription}/>
</FormItem>
<FormItem {...formItemLayout}>
<p>Type</p>
<Select
// name="type"
// value={this.state.type}
defaultValue=""
onChange={this.handleChangeType}
>
<Option value="1">type 1</Option>
<Option value="2">type 2</Option>
</Select>
</FormItem>
<FormItem {...formItemLayout}>
<p>Upload fichier MP3</p>
<div className="cm-comedien-mp3-ajout-file-container">
<input style={{ opacity: "0",display:"none" }}
type="file"
id="file"
name="file"
title="Choisir un fichier mp3"
accept=".mp3"
onChange={this.onFileChange}
/>
<div class="cm-comedien-mp3-ajout-file-name">
<span style={{ paddingRight: "12px" }}>
{this.state.selectedFileName}
</span>
</div>
<div class="cm-comedien-mp3-ajout-file-button">
<label for="file">
upload
</label>
</div>
</div>
</FormItem>
{this.state.showPlayer ?
<FormItem {...formItemLayout}>
<AudioPlayer
type="audio/mp3"
position="inline"
nomComedien=""
titre={this.state.selectedFileName}
fileName={this.state.selectedFileName}
url={this.state.selectedFileUrl}
/>
</FormItem>
:null}
<FormItem {...tailFormItemLayout}>
<Button type="primary" htmlType="submit">
Ajouter
</Button>
</FormItem>
</Form>
<div style={{width:"100%",height:"400px",background:"white",marginBottom:"50px"}}>
</div>
{this.renderAlertMessage()}
</div>
);
}
}
const AjoutExtraitForm = Form.create()(AjoutExtrait);
const mapStateToProps = ({ mp3 }) => {
const {
responseData
} = mp3;
return {
responseData
}
};
export default connect(mapStateToProps, { addMP3Action })(AjoutExtraitForm);
When i console.log(this.props.responseData) I get undefined Do you have any idea ?
I believe your issue is that in your reducer, action has no property responseData. Remember that your action returns an object with properties type and payload. When you pass it to your reducer to update the state, you need to look into action.payload to access the data that was sent in the action.
For example, you might want your reducer to look more like this:
const INIT_STATE = {
responseData: ''
};
export default (state = INIT_STATE, action) => {
switch (action.type) {
case ADD_MP3: {
return {
...state,
responseData: action.payload
}
}
default:
return state;
}
}
You can always refer to the documentation for more specifics:
https://redux.js.org/basics/basic-tutorial
I'm developing a React native application. I'm using the React native elements library.
I'm using the Search bar. But when I type faster than the keyboard, the search is not working properly.
Example;
I'm writing "Jack," but it's called "Ja".
I hope I can explain my problem. Because my English is not very good. Thanks in advance for your help.
handleRefresh = () => {
this.setState({
offset: 0,
maxSize: 10,
isSearch: false
}, () => {
this.loadData();
});
};
handleLoadMore = () => {
this.setState({
maxSize: this.state.maxSize + 10
}, () => {
this.loadData();
});
};
loadData = async () => {
try {
const { username, token, offset, maxSize } = this.state;
var credentials = Base64.btoa(username + ':' + token);
var URL = `https://crm.example.com/api/v1/Lead?select=name,status&sortBy=createdAt&asc=false&offset=${offset}&maxSize=${maxSize}`;
await axios.get(URL, {headers : { 'Espo-Authorization' : credentials }})
.then(this.dataSuccess.bind(this))
.catch(this.dataFail.bind(this));
}catch (error) {
Alert.alert(
'Hata',
'Bir hata meydana geldi. Lütfen yöneticiye başvurunuz.',
[
{ text: 'Tamam', onPress: () => null }
]
);
}
};
searchLead = async (text) => {
try {
if(text) {
this.setState({ searchText: text, isSearch: true, isLoading: true });
const { username, token, maxSize } = this.state;
var credentials = Base64.btoa(username + ':' + token);
var URL = "https://crm.example.com/api/v1/Lead?select=name,status&sortBy=createdAt&asc=false&where[0][type]=textFilter&where[0][value]=" + text;
await axios.get(URL, { headers : { 'Espo-Authorization' : credentials }})
.then(this.dataSearch.bind(this))
.catch(this.dataFail.bind(this));
}else {
this.setState({ searchText: '' });
this.handleRefresh();
}
}catch (error) {
Alert.alert(
'Hata',
'Arama başarısız oldu. Lütfen yöneticiniz ile görüşün.',
[
{ text: 'Tamam', onPress: () => null }
]
);
}
}
dataSuccess(response) {
this.setState({ isLoading: false, leadList: response.data.list });
}
dataSearch(response) {
this.setState({ isLoading: false, searchData: response.data.list });
}
dataFail(error) {
this.setState({ isLoading: false });
Alert.alert(
'Hata',
'Beklenmedik bir hata oluştu',
[
{ text: 'Tamam', onPress: () => null }
]
);
}
render() {
const { isLoading, isRefreshing, searchText, isSearch, leadList, searchData } = this.state;
return(
<View style={styles.container}>
<SearchBar
placeholder="Bir lead arayın..."
searchIcon={<Icon
name="search"
color="white"
size={21}
/>}
onChangeText={this.searchLead.bind(this)}
onClear={this.handleRefresh.bind(this)}
onCancel={this.handleRefresh.bind(this)}
value={searchText}
/>
{ isLoading ? <ActivityIndicator style={styles.loading} size="large" color="orange" /> :
isSearch ?
<ScrollView>
<FlatList
data={searchData}
showLoading={true}
renderItem={({item}) =>
<ListItem
title={item.name}
subtitle={item.status}
bottomDivider={true}
/>
}
keyExtractor={this.keyExtractor}
refreshing={isRefreshing}
onRefresh={this.handleRefresh}
/>
</ScrollView> :
<FlatList
data={leadList}
renderItem={({item}) =>
<ListItem
title={item.name}
subtitle={item.status}
bottomDivider={true}
/>
}
keyExtractor={this.keyExtractor}
refreshing={isRefreshing}
onRefresh={this.handleRefresh}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.2}
/>
}
</View>
)
}
}
Let me start by saying I don't know reactjs very well, but I think this may work (there may be far better solutions though)
searchLead = (() => {
let req;
const delayedRequest = callback => {
const timeout = setTimeout(callback, 500);
return {
cancel() {
clearTimeout(timeout);
}
};
};
return (text) => {
req && req.cancel();
req = delayedRequest(async () => {
try {
if(text) {
this.setState({ searchText: text, isSearch: true, isLoading: true });
const { username, token, maxSize } = this.state;
var credentials = Base64.btoa(username + ':' + token);
var URL = "https://crm.example.com/api/v1/Lead?select=name,status&sortBy=createdAt&asc=false&where[0][type]=textFilter&where[0][value]=" + text;
await axios.get(URL, { headers : { 'Espo-Authorization' : credentials }})
.then(this.dataSearch.bind(this))
.catch(this.dataFail.bind(this));
} else {
this.setState({ searchText: '' });
this.handleRefresh();
}
}catch (error) {
Alert.alert(
'Hata',
'Arama başarısız oldu. Lütfen yöneticiniz ile görüşün.',
[
{ text: 'Tamam', onPress: () => null }
]
);
}
});
};
})();
I am trying to pass values from a checkbox in a table in a child component to a parent component
As suggested here:
React: How to add an array of Ids to the state object from nested objects
My code is like this:
inner component:
import React, { Component } from 'react';
import { Table, Radio} from 'antd';
import { adalApiFetch } from '../../adalConfig';
import Notification from '../../components/notification';
class ListPageTemplatesWithSelection extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
fetchData = () => {
adalApiFetch(fetch, "/PageTemplates", {})
.then(response => response.json())
.then(responseJson => {
if (!this.isCancelled) {
const results= responseJson.map(row => ({
key: row.Id,
Name: row.Name
}))
this.setState({ data: results });
}
})
.catch(error => {
console.error(error);
});
};
componentDidMount(){
this.fetchData();
}
render(){
const columns = [
{
title: 'Id',
dataIndex: 'key',
key: 'key',
},
{
title: 'Name',
dataIndex: 'Name',
key: 'Name',
}
];
return (
<Table rowSelection={() => this.props.onRowSelect(this.columns.Id)} columns={columns} dataSource={this.state.data} />
);
}
}
export default ListPageTemplatesWithSelection;
parent component
import React, { Component } from 'react';
import { Input} from 'antd';
import Form from '../../components/uielements/form';
import Button from '../../components/uielements/button';
import Notification from '../../components/notification';
import { adalApiFetch } from '../../adalConfig';
import ListPageTemplatesWithSelection from './ListPageTemplatesWithSelection';
const FormItem = Form.Item;
class CreateModernSiteCollectionForm extends Component {
constructor(props) {
super(props);
this.state = {Alias:'',DisplayName:'', Description:''};
this.handleChangeAlias = this.handleChangeAlias.bind(this);
this.handleChangeDisplayName = this.handleChangeDisplayName.bind(this);
this.handleChangeDescription = this.handleChangeDescription.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChangeAlias(event){
this.setState({Alias: event.target.value});
}
handleChangeDisplayName(event){
this.setState({DisplayName: event.target.value});
}
handleChangeDescription(event){
this.setState({Description: event.target.value});
}
handleSubmit(e){
e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let data = new FormData();
//Append files to form data
//data.append(
const options = {
method: 'post',
body: JSON.stringify(
{
"Alias": this.state.Alias,
"DisplayName": this.state.DisplayName,
"Description": this.state.Description
}),
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
};
adalApiFetch(fetch, "/SiteCollections", options)
.then(response =>{
if(response.status === 201){
Notification(
'success',
'Site collection created',
''
);
}else{
throw "error";
}
})
.catch(error => {
Notification(
'error',
'Site collection not created',
error
);
console.error(error);
});
}
});
}
render() {
// rowSelection object indicates the need for row selection
const handleRowSelect = {
onChange: (selectedRowKeys, selectedRows) => {
console.log(selectedRowKeys);
}
};
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="Alias" hasFeedback>
{getFieldDecorator('Alias', {
rules: [
{
required: true,
message: 'Please input your alias',
}
]
})(<Input name="alias" id="alias" onChange={this.handleChangeAlias} />)}
</FormItem>
<FormItem {...formItemLayout} label="Display Name" hasFeedback>
{getFieldDecorator('displayname', {
rules: [
{
required: true,
message: 'Please input your display name',
}
]
})(<Input name="displayname" id="displayname" onChange={this.handleChangeDisplayName} />)}
</FormItem>
<FormItem {...formItemLayout} label="Description" hasFeedback>
{getFieldDecorator('description', {
rules: [
{
required: true,
message: 'Please input your description',
}
],
})(<Input name="description" id="description" onChange={this.handleChangeDescription} />)}
</FormItem>
<ListPageTemplatesWithSelection onRowSelect={this.handleRowSelect} />
<FormItem {...tailFormItemLayout}>
<Button type="primary" htmlType="submit">
Create modern site
</Button>
</FormItem>
</Form>
);
}
}
const WrappedCreateModernSiteCollectionForm = Form.create()(CreateModernSiteCollectionForm);
export default WrappedCreateModernSiteCollectionForm;
However I get this error in the console
index.js:2177 Warning: Failed prop type: Invalid prop `rowSelection` of type `function` supplied to `Table`, expected `object`.
in Table (at ListPageTemplatesWithSelection.js:53)
in ListPageTemplatesWithSelection (at CreateModernSiteCollection.js:144)
in form (created by Form)
in Form (at CreateModernSiteCollection.js:112)
in CreateModernSiteCollectionForm (created by Form(CreateModernSiteCollectionForm))
in Form(CreateModernSiteCollectionForm) (at index.js:30)
According to ANTD docs, rowSelection is an object that serves as configuration for the table's row selection behavior. It looks like in your example you are trying to pass in a selection handler to your component. So according to the signature of this object, you should try to do
const rowSelection = {
onSelect: () => this.props.onRowSelect(this.columns.Id)
};
<Table rowSelection={rowSelection} />
I have a main component and a child component where I use antd table.
The code does not throw any exception, and the selected ids are flowing correctly to the main component.
However the checkboxes are cleared after clicking on them, so I am not sure what am I missing here
Main component
import React, { Component } from 'react';
import { Input} from 'antd';
import Form from '../../components/uielements/form';
import Button from '../../components/uielements/button';
import Notification from '../../components/notification';
import { adalApiFetch } from '../../adalConfig';
import ListPageTemplatesWithSelection from './ListPageTemplatesWithSelection';
const FormItem = Form.Item;
class CreateModernSiteCollectionForm extends Component {
constructor(props) {
super(props);
this.state = {Alias:'',DisplayName:'', Description:'', PageTemplateIds : []};
this.handleChangeAlias = this.handleChangeAlias.bind(this);
this.handleChangeDisplayName = this.handleChangeDisplayName.bind(this);
this.handleChangeDescription = this.handleChangeDescription.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleRowSelect = this.handleRowSelect.bind(this);
}
handleRowSelect(ids) {
this.setState({ PageTemplateIds: ids });
}
handleChangeAlias(event){
this.setState({Alias: event.target.value});
}
handleChangeDisplayName(event){
this.setState({DisplayName: event.target.value});
}
handleChangeDescription(event){
this.setState({Description: event.target.value});
}
handleSubmit(e){
e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let data = new FormData();
//Append files to form data
//data.append(
const options = {
method: 'post',
body: JSON.stringify(
{
"Alias": this.state.Alias,
"DisplayName": this.state.DisplayName,
"Description": this.state.Description
}),
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
};
adalApiFetch(fetch, "/SiteCollections", options)
.then(response =>{
if(response.status === 201){
Notification(
'success',
'Site collection created',
''
);
}else{
throw "error";
}
})
.catch(error => {
Notification(
'error',
'Site collection not created',
error
);
console.error(error);
});
}
});
}
render() {
// rowSelection object indicates the need for row selection
const handleRowSelect = {
onChange: (selectedRowKeys, selectedRows) => {
console.log(selectedRowKeys);
}
};
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="Alias" hasFeedback>
{getFieldDecorator('Alias', {
rules: [
{
required: true,
message: 'Please input your alias',
}
]
})(<Input name="alias" id="alias" onChange={this.handleChangeAlias} />)}
</FormItem>
<FormItem {...formItemLayout} label="Display Name" hasFeedback>
{getFieldDecorator('displayname', {
rules: [
{
required: true,
message: 'Please input your display name',
}
]
})(<Input name="displayname" id="displayname" onChange={this.handleChangeDisplayName} />)}
</FormItem>
<FormItem {...formItemLayout} label="Description" hasFeedback>
{getFieldDecorator('description', {
rules: [
{
required: true,
message: 'Please input your description',
}
],
})(<Input name="description" id="description" onChange={this.handleChangeDescription} />)}
</FormItem>
<ListPageTemplatesWithSelection onRowSelect={this.handleRowSelect} />
<FormItem {...tailFormItemLayout}>
<Button type="primary" htmlType="submit">
Create modern site
</Button>
</FormItem>
</Form>
);
}
}
const WrappedCreateModernSiteCollectionForm = Form.create()(CreateModernSiteCollectionForm);
export default WrappedCreateModernSiteCollectionForm;
Child component
import React, { Component } from 'react';
import { Table, Radio} from 'antd';
import { adalApiFetch } from '../../adalConfig';
import Notification from '../../components/notification';
class ListPageTemplatesWithSelection extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
fetchData = () => {
adalApiFetch(fetch, "/PageTemplates", {})
.then(response => response.json())
.then(responseJson => {
if (!this.isCancelled) {
const results= responseJson.map(row => ({
key: row.Id,
Name: row.Name
}))
this.setState({ data: results });
}
})
.catch(error => {
console.error(error);
});
};
componentDidMount(){
this.fetchData();
}
render(){
const columns = [
{
title: 'Id',
dataIndex: 'key',
key: 'key',
},
{
title: 'Name',
dataIndex: 'Name',
key: 'Name',
}
];
const rowSelection = {
selectedRowKeys: this.props.selectedRows,
onChange: (selectedRowKeys) => {
this.props.onRowSelect(selectedRowKeys);
}
};
return (
<Table rowSelection={rowSelection} columns={columns} dataSource={this.state.data} />
);
}
}
export default ListPageTemplatesWithSelection;
This is happening because child component is expecting the selectedRows prop but the main component is not passing it to it.
const rowSelection = {
selectedRowKeys: this.props.selectedRows,
//-----------------------------^^--------
onChange: (selectedRowKeys) => {
this.props.onRowSelect(selectedRowKeys);
}
};
So when you select something, parent updates its state via onRowSelect prop. But it forgets to send the updated state back to the child via selectedRows prop. So the child doesn't know what row's have been selected and fallbacks to default unchecked behavior.
To fix it, just pass the PageTemplateIds state as selectedRows prop in your main component:
<ListPageTemplatesWithSelection
onRowSelect={this.handleRowSelect}
selectedRows={this.state.PageTemplateIds}
/>
I have a simple form with 3 inputs and one button.
The submit button is disabled by default, and each input has custom validation logic with regex.
How can I enable the button again when all validation passes?
This is my component:
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;
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 (!form.getFieldValue('tenantid').match(re)) {
callback('Tenant id is not correctly formated id');
}
else {
callback();
}
}
handleTenantAdminUrl(rule, value, callback){
const form = this.props.form;
var re = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]#!\$&'\(\)\*\+,;=.]+$/i;
if (!form.getFieldValue('tenantadminurl').match(re)) {
callback('Tenant Url is not correctly formated id');
}
else {
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 uploadButton = (
<div>
<Icon type={this.state.loading ? 'loading' : 'plus'} />
<div className="ant-upload-text">Upload</div>
</div>
);
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;
Update:
Answer should be provided using ant design API
In each validator if block, set buttonDisabledState state to true and in each else statement set it to false.
When all fields validations are passed then your btn disabled state will set to false.
Your button's disabled value is a boolean that comes from your component state.
So what you need to do is set that boolean to false when all checks have passed:
this.setState({buttonDisabled: false})
Add a boolean value to your state, i.e, isValidated that defaults to false and you change to true when all the validations pass. You're sorta already doing that with the state.buttondisabled - just setState that to false when the validations are all passing.
Have a look at this section, hopefully it will solve your problem
https://ant.design/components/form/#Form.Item