Prevent an antd form from switching to vertical layout - javascript

I want to prevent an antd form from automatically switching to the vertical layout when the window width is shrunk down.
I tried forcing layout={'horizontal'} in the form, but it still switches.
Here's the behavior I'm referring to:
https://d.pr/v/J6qddP
Here's a link to the codepen example:
https://codepen.io/opike99/pen/xxawJge
This is with antd 5.2.0 and react 18.
Codepen code:
const { Form, Input, Select, Button, Checkbox } = antd;
const FormItem = Form.Item;
const Option = Select.Option;
class AntdClassForm extends React.Component {
formRef = null;
render() {
console.log('here');
return (
<Form
name="basic"
// labelCol={{ span: 8 }}
// wrapperCol={{ span: 16 }}
style={{ maxWidth: 300 }}
layout={'horizontal'}
initialValues={{ remember: true }}
// onFinish={onFinish}
// onFinishFailed={onFinishFailed}
autoComplete="off"
>
<Form.Item
label="Username"
name="username"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="Password"
name="password"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
<Form.Item name="remember" valuePropName="checked" wrapperCol={{ offset: 8, span: 16 }}>
<Checkbox>Remember me</Checkbox>
</Form.Item>
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>);
}
}
ReactDOM.createRoot(mountNode).render(<AntdClassForm />);

Related

Use a button send a form and href at the same time in react.js

I have build a Form that users can fill out and send via a send-button (see code below, button at the very end). It works perfectly fine but when a user clicks send, the input that this user sends just dissapeared (so the user could think sending didnt work, altough it did).
My very simple solution for that was to add an href="/xxx" to the button. That href works but if its in the button, the form is not sent when the user clicks send.
Does anyone know how I can have these two functions (sending the form and href) in one button or another way to solve that?
import React, { Component } from 'react'
import * as emailjs from 'emailjs-com'
import { Form } from 'reactstrap'
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import { FormGroup } from '#material-ui/core';
import TextField from "#material-ui/core/TextField";
import FormHelperText from "#material-ui/core/FormHelperText";
import {indigo} from "#material-ui/core/colors";
const ColorButton = withStyles((theme) => ({
root: {
color: "#fff",
backgroundColor: "#434A7E",
'&:hover': {
backgroundColor: indigo[200],
},
fontWeight: "bold",
fontFamily: '"Segoe UI Emoji"',
},
}))(Button);
const styles = {
root: {
'& .MuiTextField-root': {
margin: "10px",
width: '100ch',
},
},
container: {
display: 'flex',
flexWrap: 'wrap',
},
textField: {
marginLeft: "5%",
marginRight: "5%",
marginTop: "10px",
marginBottom: "10px",
width: "90%",
},
messageField: {
marginLeft: "5%",
marginRight: "5%",
marginTop: "10px",
marginBottom: "10px",
width: "90%",
},
heroButtons: {
marginLeft: "25%",
marginRight: "25%",
marginTop: "10px",
marginBottom: "10px",
width: "50%",
},
};
class Buchungsformular extends Component {
state = {
email: '',
subject: '',
message: '',
anfahrt: '',
abfahrt: '',
kinder: '',
erwachsene: '',
vorname: '',
nachname: '',
}
handleSubmit(e) {
e.preventDefault()
const { email, subject, message, anfahrt, abfahrt, kinder, erwachsene, vorname, nachname } = this.state
let templateParams = {
email: email,
to_name: 'Altes Waschhaus',
subject: subject,
message_html: message,
anfahrt: anfahrt,
abfahrt: abfahrt,
kinder: kinder,
erwachsene: erwachsene,
vorname: vorname,
nachname: nachname,
}
emailjs.send(
'gmail',
'template_4CvV5FV9',
templateParams,
'user_wAhmfmSiEivROjdwXA3Ls'
)
this.resetForm()
}
resetForm() {
this.setState({
email: '',
message: '',
anfahrt: '',
abfahrt: '',
kinder: '',
erwachsene: '',
vorname: '',
nachname: '',
})
}
handleChange = (param, e) => {
this.setState({ [param]: e.target.value })
}
render() {
const { classes } = this.props; //<----- grab classes here in the props
return (
<div>
<Form onSubmit={this.handleSubmit.bind(this)}>
<div>
<FormGroup controlId="formBasicName">
<TextField
required
id="outlined-required"
label="Vorname"
defaultValue=" "
variant="outlined"
name="vorname"
className={classes.textField}
value={this.state.vorname}
onChange={this.handleChange.bind(this, 'vorname')}
/>
</FormGroup>
<FormGroup controlId="formBasicName">
<TextField
required
id="outlined-required"
label="Nachname"
defaultValue=" "
variant="outlined"
name="nachname"
className={classes.textField}
value={this.state.nachname}
onChange={this.handleChange.bind(this, 'nachname')}
/>
</FormGroup>
</div>
<FormGroup controlId="formBasicEmail">
<TextField
required
id="outlined-required"
label="E-Mail"
defaultValue=" "
variant="outlined"
name="email"
className={classes.textField}
value={this.state.email}
onChange={this.handleChange.bind(this, 'email')}
/>
</FormGroup>
<div>
<FormGroup controlId="formBasicErwachsene">
<TextField
required
id="outlined-number"
label="Erwachsene"
type="number"
InputLabelProps={{
shrink: true,
}}
className={classes.textField}
variant="outlined"
value={this.state.erwachsene}
onChange={this.handleChange.bind(this, 'erwachsene')}
/>
</FormGroup>
<FormGroup controlId="formBasicKinder">
<TextField
id="outlined-number"
label="Kinder (0-14 Jahre)"
type="number"
InputLabelProps={{
shrink: true,
}}
className={classes.textField}
variant="outlined"
value={this.state.kinder}
onChange={this.handleChange.bind(this, 'kinder')}
/>
</FormGroup>
<FormGroup controlId="formBasicEmail">
<TextField
required
id="date"
label="Anreise"
type="date"
defaultValue="2020-06-01"
className={classes.textField}
InputLabelProps={{
shrink: true,
}}
variant="outlined"
value={this.state.anfahrt}
onChange={this.handleChange.bind(this, 'anfahrt')}
/>
</FormGroup>
<FormGroup controlId="formBasicEmail">
<TextField
required
id="date"
label="Abfahrt"
type="date"
defaultValue="2020-06-01"
className={classes.textField}
InputLabelProps={{
shrink: true,
}}
variant="outlined"
value={this.state.abfahrt}
onChange={this.handleChange.bind(this, 'abfahrt')}
/>
</FormGroup>
<FormGroup className={classes.textField} controlId="formBasicMessage">
<TextField
id="outlined-multiline-static"
label="Ihre Nachricht"
multiline
rows={10}
defaultValue=" "
variant="outlined"
name="message"
value={this.state.message}
onChange={this.handleChange.bind(this, 'message')}
/>
<FormHelperText>Bitte geben sie hier an ob sie sich für die Ferienwohnung "Vilm" und/oder "Altes Waschhaus" interessieren.</FormHelperText>
</FormGroup>
</div>
<ColorButton className={classes.heroButtons} type="submit" value="Send" variant="contained" color="primary" size="large" >
Send
</ColorButton>
</Form>
</div>
)
}
}
export default withStyles(styles)(Buchungsformular)
You could add one property to your state and then conditionally render a "thank you" message depending on the state of that property. By default the message wouldn't be rendered since the property value is set to false initially. Then you would update the value of that property inside handleSubmit and set it to true.
I partially updated your code to give you an example:
class Buchungsformular extends Component {
state = {
// ... your state props: email, subject, etc.
sent: false, // this is the new property
}
handleSubmit(e) {
// ... your code, send email, reset form, etc.
this.setState({ sent: true });
}
// your other methods ...
render() {
// ...
return (
<div>
{this.state.send && <p>
Vielen Dank für Ihre Anfrage...
</p>}
<Form onSubmit={this.handleSubmit.bind(this)}>
{* ... no changes here ... *}
</Form>
</div>
)
}
}
However in case emailjs.send(...) fails you would also need to display an error message. I don't exactly know the api of that emailjs object but assuming it uses promises you would need something similar to this inside your handleSubmit method:
emailjs.send(...)
.then(() => this.setState({ sent: true }))
.catch(() => this.setState({ error: true}))
As you see there is another property error that you would need to create in order to show a error feedback (similar to the thank you message) so that the user gets informed and can try again.

In react, I`m using ant design library and inside "map" function using two <Row> tag, the key for the <Row> element is same and hence error

Warning message => react-dom.development.js:88 Warning: Encountered two children with the same key, 0. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off" >
<Form.List name="users" initialValue={[{ccn: "",cn:"",title:"" ,major: "",year:""}]}>
{(fields, { add, remove }) => (
<>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="baseline">
<div>
<Row>
<Form.Item
{...field}
name={[field.name, 'ccn']}
fieldKey={[field.fieldKey, 'ccn']}
rules={[{ required: true, message: 'Missing ccn' }]}
>
<AutoComplete
options={cocOptions}
style={{
width: 200,
}}
onSelect={oncocSelect}
onSearch={oncocSearch}
placeholder="Country of College/University"
/>
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'cn']}
fieldKey={[field.fieldKey, 'cn']}
rules={[{ required: true, message: 'Missing cn name' }]}
>
<AutoComplete
options={cnOptions}
style={{
width: 200,
}}
onSelect={oncnSelect}
onSearch={oncnSearch}
placeholder="College/University Name"
/>
</Form.Item>
</Row>
<Row>
<Form.Item
{...field}
name={[field.name, 'title']}
fieldKey={[field.fieldKey, 'title']}
rules={[{ required: true, message: 'Missing title name' }]}
>
<Input placeholder="title Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'major']}
fieldKey={[field.fieldKey, 'major']}
rules={[{ required: true, message: 'Missing major name' }]}
>
<AutoComplete
options={mOptions}
style={{
width: 200,
}}
onSelect={onmSelect}
onSearch={onmSearch}
placeholder="Major"
/>
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'year']}
fieldKey={[field.fieldKey, 'year']}
rules={[{ required: true, message: 'Missing year name' }]}
>
<Select placeholder="Year" style={{ width: 120 }} onChange={handleChange}>
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
<Option value="Yiminghe">yiminghe</Option>
</Select>
</Form.Item>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Row>
</div>
</Space>
))}
<Form.Item>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
Add field
</Button>
</Form.Item>
</>
)}
</Form.List>
<Form.Item hidden>
<Button ref={myButton} type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>

How to reduce the default margin/padding applied by ant design between elements in <Form>?

I am using ant design <Form layout="vertical"> and I want to set the space between Price Map elements inside <Form> to be less. I tried setting style={{margin:"0px"}} on elements but it has no change on UI.
Here's the code for the Price Map.
<Form.Item
label="Price Map"
rules={[{ required: true, message: "Please input Prices!" }]}
>
{selectedProducts.flatMap(productId =>
products
.filter(product => product.id === productId)
.map(product => (
<Input.Group compact layout="horizontal">
<Form.Item label={product.productName + " : "} />
{product.priceList.map(priceObj => (
<Input
defaultValue={priceObj.price}
addonBefore={priceObj.type}
rules={[
{
required: true,
message: "Please input price!"
}
]}
style={{ width: "34%" }}
/>
))}
</Input.Group>
))
)}
</Form.Item>
<Form.Item style={{ marginBottom: 0 }} />
I was able to achieve this by setting style={{ marginBottom: 5 }} for the <Input>
Here's my updated code.
<Form.Item
label="Price Map"
rules={[{ required: true, message: "Please input Prices!" }]}
>
{selectedProducts.flatMap(productId =>
products
.filter(product => product.id === productId)
.map(product => (
<Input.Group compact layout="horizontal">
<div style={{ marginRight: 5 }}> { product.productName + " : " }</div>
{product.priceList.map(priceObj => (
<Input
defaultValue={priceObj.price}
addonBefore={priceObj.type}
rules={[
{
required: true,
message: "Please input price!"
}
]}
onChange={(changedValue) => {
setPriceMap({
...priceMap,
[priceObj.priceId]: priceObj.price
});
}}
style={{ width: "34%" , marginBottom: 5 }}
/>
))}
</Input.Group>
))
)}
</Form.Item>

Set first fields open in a dynamic form

I have a dynamic form where users can add data. By default i want to set the form in this way:
How you can see, the first pair of input is open when user opens the application.
I made this by changing this:
const Demo = () => {
const onFinish = values => {
console.log("Received values of form:", values);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
console.log(fields);
return (
<div>
{fields.map(field => (
<Space
key={field.key}
style={{ display: "flex", marginBottom: 8 }}
align="start"
>
<Form.Item
{...field}
name={[field.name, "first"]}
fieldKey={[field.fieldKey, "first"]}
rules={[{ required: true, message: "Missing first name" }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, "last"]}
fieldKey={[field.fieldKey, "last"]}
rules={[{ required: true, message: "Missing last name" }]}
>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
to this:
const Demo = () => {
const onFinish = values => {
console.log("Received values of form:", values);
};
const firstDefaultOpen = {
name: 0,
key: 0,
isListField: true,
fieldKey: 0
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
console.log(fields);
return (
<div>
{fields.concat(firstDefaultOpen).map(field => (
<Space
key={field.key}
style={{ display: "flex", marginBottom: 8 }}
align="start"
>
<Form.Item
{...field}
name={[field.name, "first"]}
fieldKey={[field.fieldKey, "first"]}
rules={[{ required: true, message: "Missing first name" }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, "last"]}
fieldKey={[field.fieldKey, "last"]}
rules={[{ required: true, message: "Missing last name" }]}
>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
Issue: But appears a problem when i try to add another input, because writing on first pair of inputs i can see the same text on the next inputs. Probably this is hapenening because of the same ids.
Question: How to set default open first pair of input as in the image using the code above?
demo: https://codesandbox.io/s/hungry-star-nu5ld?file=/index.js:235-2322
ps: i know that exists the next solution:
<Form initialValues={{ users: [""] }} name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
console.log(fields);
return (
<div>
{fields.map(field => (
...
...
But i don't want this solution because i can't interact with <Form/> tag in my real application, so even this could be a solution, but i need another, like mine with concat() but of course to be workable.

Add unique keys for items in reactjs

I have the next application:
https://codesandbox.io/s/suspicious-murdock-87j13?file=/SubForm.js
<Form.List name="names">
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => {
console.log("m", field);
return (
<Form.Item
{...(index === 0
? formItemLayout
: formItemLayoutWithOutLabel)}
label={index === 0 ? "Passengers" : ""}
required={false}
key={index}
>
<Form.Item
{...field}
name={[field.name, "outer"]}
fieldKey={[field.fieldKey, "outer"]}
validateTrigger={["onChange", "onBlur"]}
rules={[
{
required: true,
whitespace: true,
message:
"Please input passenger's name or delete this field."
}
]}
noStyle
>
<Input
placeholder="passenger name"
style={{ width: "60%" }}
/>
</Form.Item>
<Form.Item
{...field}
name={[field.name, "outer1"]}
fieldKey={[field.fieldKey, "outer1"]}
validateTrigger={["onChange", "onBlur"]}
rules={[
{
required: true,
whitespace: true,
message:
"Please input passenger's name or delete this field."
}
]}
noStyle
>
<Input
placeholder="passenger name"
style={{ width: "60%" }}
/>
</Form.Item>
<Form.Item>
<InnerForm fieldKey={field.key} />
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: "0 8px" }}
onClick={() => {
remove(field.name);
}}
/>
) : null}
</Form.Item>
);
})}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "60%" }}
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
When user click on Add field button, in console appears the warning:
Warning: Encountered two children with the same key, `0`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
Also this warning appears when i click on Add sub-field. I can't figure out why this warning appears.
Question: Why the above warning appears and how to remove it?
You have two Form.Item Component in your code, and they use same key index, try to use different key like 'item1' + index and 'item2' + index.
And, in your index.js file also have same problem.
Here is Demo forked from you: https://codesandbox.io/s/dark-darkness-xz0wf?file=/SubForm.js
As reactJs primary works on re rendering items every time you make a change the state of that component. It requires an unique key to track each and every item so that re-rendering can be optimized. Not all items needs to be re-render every time.
This warning essentially means that you have multiple items with same key names. In order to fix this you have to provide unique "key" attribute to each child component which is getting updated, components which can potentially trigger an re-render.
you don't need to use multiple times key={index} or key={"g"}
use once only.
Please try following
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Form, Input, Button, Space } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '#ant-design/icons';
const Demo = () => {
const onFinish = values => {
console.log('Received values of form:', values);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
return (
<div>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name, 'innerName']}
fieldKey={[field.fieldKey, 'innerName']}
>
<Input placeholder="passenger 1" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'innerName2']}
fieldKey={[field.fieldKey, 'innerName2']}
>
<Input placeholder="passenger 2" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, document.getElementById('container'));

Categories