I am building a form that lets users fill out names of an indefinite number of animals. At the end of the questions is clickable text that reveals the option to fill in another animal's name. How do I let this process occur an indefinite number of times, as right now I can click it once to reveal the extra field, but then cannot click it again.
Because each field needs to be individually checked with the backend database, each extra field cannot be overwritten when they appear and are filled out.
Below is my code snippet and a screenshot of the page after the extra field has been revealed.
Thanks in advance
const CustomModel = (props) => {
const { classes, ...rest } = props;
const [showNextAnimal, setShowNextAnimal] = React.useState(false);
const onClick = () => setShowNextAnimal(true);
const AnotherAnimal = () => (
<div>
<h4>What other animal are you looking for?</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Enter name of animal"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'description' }}
/>
</div>
<br />
</div>
);
return (
<main className={classes.content}>
<div className={classes.toolbar} />
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<Paper className={classes.paper}>
<h2>Create a new model</h2>
<Divider />
<h4>Name?</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Feel free to be creative!"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'name' }}
/>
</div>
<br />
<h4>Description</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Enter your description"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'description' }}
/>
</div>
<br />
<h4>What animal are you looking for?</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Enter name of animal"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'description' }}
/>
</div>
<br />
{showNextAnimal ? <AnotherAnimal /> : null}
<div>
<h4 onClick={onClick}>+ Add another animal</h4>
<br />
</div>
<Button
color="primary"
variant="contained"
type="submit"
className={classes.continueButton}
// onClick={(updateFormValues, handleClick)}
>
Review
</Button>
<br />
</Paper>
</GridItem>
</GridContainer>
<StickyFooter />
</main>
);
};
export default withStyles(styles)(CustomModel);
You should use a count variable and then, on click of Add new animal, increment the count variable. In the body, display <AnotherAnimal /> for count number of times.
const CustomModel = (props) => {
const { classes, ...rest } = props;
const [countOfOtherAnimals, setCountOfOtherAnimals] = React.useState(0);
const onClick = () => setCountOfOtherAnimals(countOfOtherAnimals + 1);
const AnotherAnimal = () => (
<div>
<h4>What other animal are you looking for?</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Enter name of animal"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'description' }}
/>
</div>
<br />
</div>
);
return (
<main className={classes.content}>
<div className={classes.toolbar} />
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<Paper className={classes.paper}>
<h2>Create a new model</h2>
<Divider />
<h4>Name?</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Feel free to be creative!"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'name' }}
/>
</div>
<br />
<h4>Description</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Enter your description"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'description' }}
/>
</div>
<br />
<h4>What animal are you looking for?</h4>
<div className={classes.inputbar}>
<InputBase
placeholder="Enter name of animal"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ 'aria-label': 'description' }}
/>
</div>
<br />
{
[...Array(countOfOtherAnimals)].map((e, i) => (
<AnotherAnimal key={i} />
))
}
<div>
<h4 onClick={onClick}>+ Add another animal</h4>
<br />
</div>
<Button
color="primary"
variant="contained"
type="submit"
className={classes.continueButton}
// onClick={(updateFormValues, handleClick)}
>
Review
</Button>
<br />
</Paper>
</GridItem>
</GridContainer>
<StickyFooter />
</main>
);
};
export default withStyles(styles)(CustomModel);
Related
I have a form built with Formik containing an array of objects. When I add a second element to the form with the add-button, I get the following error:
Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.
I have made sure that initial values is defined for the the form. I use the FieldArray component from formik. If I remove the time and rate fields, so it's only the name input in each object, I don't get the error, but when I add the time and rate fields, the error occurs. The component contains the following:
const TimeItemsForm = () => {
const jiraItemsStore = useJiraItemsStore();
function jiraTimeImported(timeItemIndex: number, importedHoursTotal: number, checkedItems: CheckedTimeItems) {
// Add checked items to store
jiraItemsStore.setJiraTable(timeItemIndex, checkedItems, importedHoursTotal);
}
const TimeItemSchema = Yup.object().shape({
name: Yup.string().required('Required'),
time: Yup.number().min(0).required('Required'),
// rate: Yup.number().min(0).required('Required'),
});
const TimeItemsSchema = Yup.object().shape({
timeItems: Yup.array().of(TimeItemSchema),
})
const initialValues = {
timeItems: [
{
name: '',
time: 0,
rate: 0,
},
],
};
return (
<>
<Formik
validateOnChange={false}
initialValues={initialValues}
validationSchema={TimeItemsSchema}
onSubmit={(values) => console.log(values)}>
{({ values, errors, touched }) => (
<Form onChange={() => console.log("hs")}>
<FieldArray
name="timeItems"
render={arrayHelpers => (
<div>
{values.timeItems && values.timeItems.length > 0 ? (
values.timeItems.map((timeItem, index) => (
<React.Fragment key={index}>
<Stack gap={2}>
<Flex alignItems="end" gap={4}>
<FormControl>
<FormLabel htmlFor="timeItems[${index}].name">Name</FormLabel>
<Field as={Input} placeholder="Time Item Name" variant="filled" name={`timeItems[${index}].name`} />
</FormControl>
<FormControl>
<FormLabel htmlFor="timeItems[${index}].time">Time</FormLabel>
<InputGroup>
<Field as={Input} type="number" placeholder="0 Hours" variant="filled" name={`timeItems[${index}].time`} />
<InputRightAddon children='Hours' />
</InputGroup>
</FormControl>
<FormControl>
<FormLabel htmlFor="timeItems[${index}].rate">Rate</FormLabel>
<InputGroup>
<Field as={Input} type="number" placeholder="USD 0" variant="filled" name={`timeItems[${index}].rate`} />
<InputRightAddon children='Hours' />
</InputGroup>
</FormControl>
<Flex flexShrink="0" gap={3} direction="column">
<Heading fontWeight="normal" size="sm">Apply Taxes & Discounts</Heading>
<Flex mb={0.5} gap={4}>
<Tooltip label='Tax 1' fontSize='sm'>
<IconButton variant={true ? 'solid' : 'outline'} aria-label='Tax' icon={<TbReceipt />} />
</Tooltip>
<IconButton variant='outline' aria-label='Discount' icon={<TbDiscount />} onClick={() => arrayHelpers.insert(index, { name: "", email: "" })} />
</Flex>
</Flex>
</Flex>
<Flex py={2} gap={10} justifyContent="space-between">
<TimeItemsTable timeItemIndex={index} jiraTimeImported={jiraTimeImported} />
<Flex gap={4}>
<IconButton aria-label='Create Time Item' icon={<MinusIcon />} onClick={() => arrayHelpers.remove(index)} />
<IconButton aria-label='Create Time Item' icon={<AddIcon />} onClick={() => arrayHelpers.insert(index, { name: "", email: "" })} />
</Flex>
</Flex>
</Stack>
<Divider my={4} />
</React.Fragment>
))
) : (
<button type="button" onClick={() => arrayHelpers.push('')}>
Add a Time Item
</button>
)}
<Flex mt={6} gap={10} justifyContent="space-between">
<Button colorScheme="purple" type="submit">Save</Button>
{/* <TimeItemsStats /> */}
</Flex>
</div>
)}
/>
</Form>
)}
</Formik>
</>
)
}
Solution:
The arrayHelpers.push and arrayHelpers.insert didn't insert the values with the correct type, as seen in the code.
I facing problem "TypeError: Cannot read property 'id' of undefined" when i using Grid in elements.
the error i facing like this.
Here is my sample code.
In this code only i am facing the issue when im using Grid
<ThemeProvider theme={theme}>
<Card>
<Edit {...props}>
<SimpleForm>
<Grid container spacing={1} align="center">
<Grid item sm={6}>
<ReferenceInput
source="appId"
reference="_apps"
allowEmpty
defaultValue={
props.location.data ? props.location.data.appId : ""
}
validate={required()}
>
<SelectInput optionText="label" />
</ReferenceInput>
</Grid>
<Grid item sm={6}>
<SelectInput
source="iconColor"
choices={[
{ id: "primary", name: "primary" },
{ id: "secondary", name: "secondary" },
{ id: "action", name: "action" },
{ id: "disabled", name: "disabled" },
]}
/>
</Grid>
<Grid item sm={6}>
<ReferenceManyField
label={"resources._fields.name"}
reference="_fields"
target="eid"
>
<Datagrid>
<TextInput type="text" source="label" />
<TextInput type="text" source="component" />
<BooleanField source="showInList" />
<BooleanField source="showInFilter" />
<BooleanField source="showInCreate" />
<BooleanField source="showInEdit" />
<BooleanField source="showInShow" />
<EditButton />
</Datagrid>
</ReferenceManyField>
</Grid>
<Grid item sm={6}>
<ReferenceManyField
label={"resources._triggers.name"}
reference="_triggers"
target="eid"
>
<Datagrid>
<TextInput type="text" source="name" />
<BooleanField source="beforeInsert" />
<BooleanField source="afterInsert" />
<BooleanField source="beforeUpdate" />
<BooleanField source="afterUpdate" />
<BooleanField source="beforeDelete" />
<BooleanField source="afterDelete" />
<EditButton />
</Datagrid>
</ReferenceManyField>
</Grid>
<Grid item sm={6}>
<AddTriggerButton />
<AddFieldButton />
</Grid>
</Grid>
</SimpleForm>
</Edit>
</Card>
</ThemeProvider>
This is my actual code in that screenshot:
const TriggerButton = ({ children, to, ...props }) => {
const CustomLink = React.useMemo(
() =>
React.forwardRef((linkProps, ref) => (
<StyledLink ref={ref} to={to} {...linkProps} />
)),
[to]
);
return (
<Button {...props} component={CustomLink}>
{children}
</Button>
);
};
export default class AddTriggerButton extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
props: props,
};
}
render() {
return (
<div>
<TriggerButton
component={Link}
to={{
pathname: "/_triggers/create",
data: { eid: this.state.props.record.id },
}}
startIcon={<AddIcon />}
>
New Trigger
</TriggerButton>
</div>
);
}
}
Because you don't pass any props to AddTriggerButton so props is {}. So this.state.props is {} and this.state.props.record become undefined.
This is not good logic. You need to pass props and use this.props instead this.state.props
<AddTriggerButton record={{id: value}} />
Change this.state.props.record.id to this.props.record.id
Try to console.log(this.state.props.record.id) and shate here the reault. How you defined your the props? Maybe there is an asynchronous problem, so try to put '?' before the id like this:
this.state.props.record?.id
I am building header using material UI Tab Component but I see below error:
index.js:2178 Material-UI: The value provided to the Tabs component is invalid.
None of the Tabs' children match with [object Object].
You can provide one of the following values: 0, 1.
I tried console.log the newValue to see what value it is getting and I can see 0 and 1 while navigating through tabs.
Note : Removed Some Code for better visibility
Here is my component:
const Header = (props) => {
const { classes } = props;
const [value, setValue] = useState(0);
const [modalIsOpen, setIsOpen] = React.useState(false);
const openModal = () => {
setIsOpen(true);
};
const closeModal = () => {
setIsOpen(false);
};
const handleTabChange = (event, newValue) => {
console.log(newValue);
setValue({ newValue });
};
return (
<div>
<div className="topnav">
<img src={logo} className="logo" alt="Movies App Logo" />
<div className="topnav-right">
<Button variant="contained" color="default" onClick={openModal}>
Login
</Button>
</div>
<div className="topnav-right">
<Button variant="contained" color="default">
Logout
</Button>
</div>
</div>
<Modal
ariaHideApp={false}
isOpen={modalIsOpen}
onRequestClose={closeModal}
contentLabel="Login"
aria-labelledby="Modal"
aria-describedby="Modal for Login and Registration"
style={customStyles}
>
<Paper className={classes.Paper}>
<CardContent>
<Tabs
className="tabs"
value={value}
onChange={handleTabChange}
centered
>
<Tab label="Login" />
<Tab label="Register" />
</Tabs>
{value === 0 && (
<div>
<FormControl required>
<InputLabel htmlFor="username" className={classes.inputLable}>
Username
</InputLabel>
<Input
className={classes.Input}
id="username"
type="text"
username={username}
onChange={usernameChangeHandler}
/>
<FormHelperText>
<span className="red">required</span>
</FormHelperText>
</FormControl>
<br />
<br />
<FormControl required>
<InputLabel
htmlFor="loginPassword"
className={classes.inputLable}
>
Password
</InputLabel>
<Input
className={classes.Input}
id="loginPassword"
type="password"
password={password}
onChange={passwordChangeHandler}
/>
<FormHelperText>
<span className="red">required</span>
</FormHelperText>
</FormControl>
<br />
<br />
{loggedIn === true && (
<FormControl>
<span className="success-text">Login Successful!</span>
</FormControl>
)}
<br />
<br />
<Button
variant="contained"
color="primary"
onClick={loginHandler}
>
LOGIN
</Button>
</div>
)}
{value === 1 && (
<div>
<h1>something</h2>
</div>
)}
</CardContent>
</Paper>
</Modal>
</div>
);
};
export default withStyles(styles)(Header);
For some reason you enclosed the value in an object (curly braces syntax).
Replace setValue({ newValue }) with setValue(newValue).
const AddPartQuestionModalAgain = ({
isModalVisible,
hideModal,
questionData = {
question_no: '',
question_parts: [''],
},
}) => {
const dispatch = useDispatch()
let formInput
console.log(questionData)
return (
<>
<Modal
visible={isModalVisible}
onCancel={() => {
formInput.resetFields()
hideModal()
}}
width={1200}
>
<Form
layout='vertical'
ref={(ref) => {
formInput = ref
}}
name='dynamic_form_item'
>
<Form.List name='question_parts' key='question_parts'>
{(fields, { add, remove }, { errors }) => (
<>
{fields.map((field, index) => (
<Space
key={field.key}
direction='vertical'
>
<Form.Item key={`QuestionPart${index}`}>
<Row>
<Col span={10}>
<div
style={{
overflow: 'auto',
height: 400,
width: 590,
}}
>
<Form.Item
{...field}
label={`Question Part ${index + 1} Description`}
fieldKey={[field.fieldKey, 'part_desc']}
name={[field.name, 'part_desc']}
key={`part_desc${index}`
>
<Input.TextArea
rows={8}
placeholder='Description'
/>
</Form.Item>
<Form.Item
{...field}
label={`Question Part ${index + 1} Marks`}
fieldKey={[field.fieldKey, 'part_total_marks']}
name={[field.name, 'part_total_marks']}
key={`part_total_marks${index}`}
>
<Input
placeholder='Marks'
style={{
border: '1px solid black',
width: 450,
}}
/>
</Form.Item>
</div>
</Col>
<div class='vl'></div>
<Col span={12} offset={2}>
<div
>
<Form.List
{...field}
name='part_guided_answer'
fieldKey={[field.fieldKey, 'part_guided_answer']}
name={[field.name, 'part_guided_answer']}
key={`part_guided_answer${index}`}
>
{(fields, { add, remove }, { errors }) => (
<>
{fields.map((field, index) => (
<Space
key={field.key}
direction='vertical'
>
<Form.Item key={`QuestionPart${index}`}>
<Form.Item
{...field}
label={` Question Part Guided Answer ${
index + 1
}`}
fieldKey={[
field.fieldKey,
'part_model_ans',
]}
name={[field.name, 'part_model_ans']}
key={`part_model_ans${index}`}
>
<Input.TextArea
rows={4}
placeholder='Guided Answer'
/>
</Form.Item>
<Form.Item
{...field}
label={`Question Part
Guided Answer Marks ${index + 1}`}
fieldKey={[
field.fieldKey,
'part_answer_mark',
]}
name={[
field.name,
'part_answer_mark',
]}
key={`part_answer_mark${index}`}
>
<Input
placeholder='Guided Answer Marks'
}}
/>
</Form.Item>
<MinusCircleOutlined
className='dynamic-delete-button'
onClick={() => remove(field.name)}
/>
</Form.Item>
</Space>
))}
<Form.Item style={{ fontWeight: 'bold' }}>
<Button
type='dashed'
onClick={() => add()}
icon={<PlusOutlined />}
>
Add Question Part Guided Answer
</Button>
<Form.ErrorList errors={errors} />
</Form.Item>
</>
)}
</Form.List>
</div>
</Col>
<MinusCircleOutlined
className='dynamic-delete-button'
onClick={() => remove(field.name)}
/>
</Row>
<div class='vr'></div>
</Form.Item>
</Space>
))}
<Form.Item>
<Button
type='dashed'
onClick={() => add()}
icon={<PlusOutlined /
>
Add Question Part
</Button>
<Form.ErrorList errors={errors} />
</Form.Item>
</>
)}
</Form.List>
</Form>
</Modal>
</>
)
}
The above code basically gives me this picture below
what I would like to do is hide the items that already exist which is in the blue circle and only show items without anything inside and I would also like to have it so that it resets every time I open it because right now(see picture below) even if there is nothing inside it shows me this
that's because in my database my table for these values are null
| Question_id |part_id | part_desc | part_model_ans|answer_mark|
| ------------|--------------|-----------| --------------|-----------|
|10 | null | null | null |null |
|10 | null |null | null |null |
you can hide it like this, Since you can count the string of the text, In your case the text will be coming from server right
return (<div>
{firstname.length > 0 ? null: <p>FirstName</p> }
{secondName.length > 0 ? null: <p>secondName</p> }
{thirdName.length > 0 ? null: <p>thirdName</p> }
</div>)
I have an object that contains objects and each of them has an array of items, I want to attach to each of them an edit button for viewing and deleting In React-admin, but something in my code fails and every time I press the buttons everything gets stuck, you can see in the pictures here:
Thanks!
when I press this buttons the web get stuck:
The buttons in the code:
The Edit part:
The data:
import React from "react";
import {
List,
Datagrid,
TextField,
EmailField,
EditButton,
DeleteButton,
Filter,
TextInput,
ReferenceInput,
SelectInput,
BulkDeleteWithConfirmButton,
DeleteWithConfirmButton,
ArrayField,
ImageField,
ShowButton,
RefreshButton,
CreateButton,
ExportButton,
NumberField,
} from "react-admin";
import { Fragment } from "react";
const UserBulkActionButtons = (props) => (
<Fragment>
<BulkDeleteWithConfirmButton {...props} />
</Fragment>
);
const ShopList = (props) => {
return (
<List
{...props}
filters={<ShopFilter />}
actions={<ProductActionsButtons />}
bulkActionButtons={<UserBulkActionButtons />}
style={{ width: "80%" }}
>
<Datagrid rowClick="show">
<NumberField source="id" />
<TextField source="title" />
<ArrayField source="items">
<Datagrid rowClick="edit">
{" "}
<TextField source="id" />
<TextField source="name" />
<TextField source="description" />
<ImageField source="imageUrl" />{" "}
<NumberField label="in Stock" source="amount" />
<NumberField source="price" />
<ShowButton label="" />
<EditButton />
<DeleteWithConfirmButton />
</Datagrid>
</ArrayField>
<CreateButton />
{/* <EditButton /> */}
</Datagrid>
</List>
);
};
const ShopFilter = (props) => (
<Filter {...props}>
<TextInput label="Search" source="q" alwaysOn />
{/* <ReferenceInput label="Title" source="userId" reference="users" allowEmpty>
<SelectInput optionText="name" />
</ReferenceInput> */}
</Filter>
);
const ProductActionsButtons = (props) => (
<div>
<RefreshButton {...props} />
<CreateButton {...props} />
<ExportButton {...props} />
</div>
);
export default ShopList;
import React from "react";
import {
Edit,
ImageField,
SimpleForm,
TextInput,
Datagrid,
ArrayField,
TextField,
ImageInput,
SimpleFormIterator,
ArrayInput,
Toolbar,
SaveButton,
DeleteWithConfirmButton,
NumberInput,
required,
} from "react-admin";
const ShopEdit = (props) => {
return (
<Edit {...props} title={<ShopTitle />} undoable={false}>
<SimpleForm toolbar={<ProductEditToolbar />}>
<TextInput disabled source="id" />
<TextInput label="Category" source="title" validate={[required()]} />
<ArrayInput source="items">
<SimpleFormIterator>
<TextInput
label="Product Name"
source="name"
validate={[required()]}
/>
<TextInput
label="Product Description"
source="description"
validate={[required()]}
/>
<ImageInput label="Product Image Url" source="imageUrl" />
{/* <TextInput label="Product Price" source="price" /> */}
<NumberInput min="0" step="1" label="in Stock" source="amount" />
<NumberInput
label="Product Price"
min="0.01"
step="0.01"
source="price"
validate={[required()]}
/>
</SimpleFormIterator>
</ArrayInput>
</SimpleForm>
</Edit>
);
};
const ShopTitle = ({ record }) => {
return <span>{record ? `Product "${record.name}"` : ""}</span>;
};
const ProductEditToolbar = (props) => (
<Toolbar {...props}>
<SaveButton />
<DeleteWithConfirmButton />
</Toolbar>
);
export default ShopEdit;