How to insert a subdocument into a mongo collection? - mongodb

MERN stack.
There are a main storyModel and an eventModel which is an embedded subdocument. I create a story and then add events to it. I've tried several ways, searched SO for the way to insert a subdocument to an existing mongo collection, looked at the mongo and mongoose documentation(very little) and I can't seem to insert. I was able to do it before but after refactoring, the code doesn't work anymore. I tried isolating where the problem is, and it seems to be pointing to route although I'm not 100% on this.
Here is my model/schema(using mongoose):
const mongoose = require('mongoose')
const GeoSchema = mongoose.Schema({
type: {
type: String,
enum: ['Point', 'LineString', 'Polygon'],
default: "Point"
},
coordinates: {
type: [Number],
index: "2dsphere"
}
})
const EventSchema = mongoose.Schema({
eventDate: {
type: Date
},
eventTitle: {
type: String,
required: true,
minlength: 3
},
eventDescription: {
type: String,
minlength: 3
},
eventImageUrl: {
type: String
},
eventLink: {
type: String
},
eventAudio: {
type: String
},
eventLocation: GeoSchema,
})
const StorySchema = mongoose.Schema({
storyTitle: {
type: String,
minlength: 5,
required: true
},
storySummary: {
type: String
},
storyImageUrl: {
type: String,
required: true
},
storyStatus: {
type: String,
default: 'public',
enum: ['public', 'private']
},
storyCreator: {
type: mongoose.Types.ObjectId,
// required: true,
ref: 'User'
},
storyReferences: [String],
storyTags: [String],
storyMapStyle: {
type: String,
default: 'mapbox://styles/mapbox/light-v9',
},
likes: [{
type: mongoose.Types.ObjectId,
ref: 'User'
}],
event: [ EventSchema ]
}, {timestamps: true})
module.exports = mongoose.model('Story', StorySchema)
Here is my express Route:
// Create a new event for a specific story, used by AddEvent.js
router.put('/:story_id/update', (req, res) => {
const {
eventDate,
eventTitle,
eventDescription,
eventImageUrl,
eventLink,
eventAudio } = req.body
console.log('eventDate',eventDate)
Story.findByIdAndUpdate(
{ _id: req.params.story_id},
{
$push:
{ event:
{
eventDate,
eventTitle,
eventDescription,
eventImageUrl,
eventLink,
eventAudio
}}
})
})
Here is the React code just in case:
import React, {useEffect, useState} from 'react';
import { useForm } from 'react-hook-form'
import { useHistory, useParams } from 'react-router-dom'
import * as yup from 'yup'
import { yupResolver } from "#hookform/resolvers/yup"
import axios from 'axios'
import clsx from 'clsx';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField';
import Typography from '#material-ui/core/Typography';
import { makeStyles } from '#material-ui/core/styles';
import Container from '#material-ui/core/Container';
import Collapse from '#material-ui/core/Collapse';
import InfoIcon from '#material-ui/icons/Info';
import InputAdornment from '#material-ui/core/InputAdornment';
import InputLabel from '#material-ui/core/InputLabel';
import MenuItem from '#material-ui/core/MenuItem';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select';
const schema = yup.object().shape({
eventDate: yup
.date(),
eventTitle: yup
.string()
.required('Title is a required field.')
.min(3),
eventDescription: yup
.string(),
eventImageUrl: yup
.string(),
eventLink: yup
.string(),
eventAudio: yup
.string(),
// eventType: yup
// .string(),
// eventLatitude: yup
// .number()
// .transform(cv => isNaN(cv) ? undefined : cv).positive()
// .nullable()
// .lessThan(90)
// .moreThan(-90)
// .notRequired() ,
// eventLongitude: yup
// .number()
// .transform(cv => isNaN(cv) ? undefined : cv).positive()
// .nullable()
// .lessThan(180)
// .moreThan(-180)
// .notRequired() ,
})
const useStyles = makeStyles((theme) => ({
paper: {
marginTop: theme.spacing(12),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(1),
},
submit: {
margin: theme.spacing(2, 0, 0),
},
formControl: {
marginTop: '1rem',
},
}));
export default function AddEvent(props) {
const classes = useStyles();
const history = useHistory()
const { register, handleSubmit, errors } = useForm({
resolver: yupResolver(schema)
})
const { story_id } = useParams()
const [data, setData] = useState('')
const [expanded, setExpanded] = useState(false);
const {
eventDate,
eventTitle,
eventDescription,
eventImageUrl,
eventLink,
eventAudio,
eventLocation
} = data
useEffect(() => {
axios.put(`http://localhost:5000/story/${story_id}/update`, {
eventDate,
eventTitle,
eventDescription,
eventImageUrl,
eventLink,
eventAudio,
eventLocation
})
.then(() => history.goBack() )
.catch(err => console.log(err))
}, [data])
const handleExpandClick = () => {
setExpanded(!expanded);
};
const handleCancel = () => {
history.goBack()
};
const onSubmit = (data) => {
console.log(data);
setData(data)
}
return (
<Container component="main" maxWidth="xs">
<div className={classes.paper}>
<Typography>
Add New Event
</Typography>
<form className={classes.form} noValidate onSubmit={handleSubmit(onSubmit)}>
<TextField
variant="outlined"
margin="normal"
fullWidth
id="eventDate"
label="eventDate"
name="eventDate"
autoComplete="eventDate"
type="text"
autoFocus
inputRef={register}
error={!!errors.eventDate}
helperText={errors?.eventDate?.message}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
required
id="eventTitle"
label="eventTitle"
name="eventTitle"
autoComplete="eventTitle"
type="text"
inputRef={register}
error={!!errors.eventTitle}
helperText={errors?.eventTitle?.message}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
multiline
rows={4}
name="eventDescription"
label="eventDescription"
type="text"
id="eventDescription"
inputRef={register}
error={!!errors.eventDescription}
helperText={errors?.eventDescription?.message}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
name="eventImageUrl"
label="eventImageUrl"
type="text"
id="eventImageUrl"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<Button
className={clsx(classes.expand, {
[classes.expandOpen]: expanded,
})}
onClick={handleExpandClick}
aria-expanded={expanded}
aria-label="show more"
>
<InfoIcon size='sm' />
</Button>
</InputAdornment>
),
}}
inputRef={register}
error={!!errors.eventImageUrl}
helperText={errors?.eventImageUrl?.message}
/>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<p>Pls paste either an image or video url link here.</p>
<p>If you are using a YouTube link: Navigate to the video you wish to embed. Click the Share link below the video, then click the Embed link. The embed link will be highlighted in blue. Copy and paste this link here.
</p>
</Collapse>
<TextField
variant="outlined"
margin="normal"
fullWidth
name="eventLink"
label="eventLink"
type="text"
id="eventLink"
inputRef={register}
error={!!errors.eventLink}
helperText={errors?.eventLink?.message}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
name="eventAudio"
label="eventAudio"
type="text"
id="eventAudio"
inputRef={register}
error={!!errors.eventAudio}
helperText={errors?.eventAudio?.message}
/>
{/* <FormControl fullWidth variant="outlined" className={classes.formControl}>
<InputLabel id="demo-simple-select-outlined-label">eventType</InputLabel>
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
value={eventType}
onChange={handleChange}
defaultValue={'Point'}
label="eventType"
className={classes.selectEmpty}
>
<MenuItem value={'Point'}>Point</MenuItem>
<MenuItem value={'LineString'}>LineString</MenuItem>
<MenuItem value={'Polygon'}>Polygon</MenuItem>
</Select>
</FormControl> */}
<TextField
variant="outlined"
margin="normal"
fullWidth
name="eventLatitude"
label="eventLatitude"
type="number"
id="eventLatitude"
inputRef={register}
error={!!errors.eventLatitude}
helperText={errors?.eventLatitude?.message}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
name="eventLongitude"
label="eventLongitude"
type="number"
id="eventLongitude"
inputRef={register}
error={!!errors.eventLongitude}
helperText={errors?.eventLongitude?.message}
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Submit
</Button>
<Button
fullWidth
color="default"
onClick={handleCancel}
className={classes.submit}
>
Cancel
</Button>
</form>
</div>
</Container>
);
}

in a simple answer, you should call Story.updateOne and not Story.findByIdAndUpdate, but I would suggest a bit more (look at this as a code review thingy) ...
make use of pluralism, so the property should be called events and not event as it will have more than one
remove property prefixes, so StoryTitle would simply be title (we already know it's a Story, as it's in a Story collection), and EventTitle would be simply `event) (we already know it's an event as it's in an "events" array)
why set storyReferences as a string, should be easier manipulated if an array of string, same for storyTags
I took the liberty, just to help in your MongoDB API development, to create a very simple NodeJs project in GitHub with very simple REST calls, where you can go through and make calls to create documents and query them...
GitHub project https://github.com/balexandre/so65351733

Related

values.map is not a function Sequelize

I'm trying to send a form to my db but I get this error :
The problem XD
I have this problem only when I try to do it from the front, because when I try with Insomnia it works. So I'm not sure where the problem is coming from.
I'm using multer for the image.
The model:
const { DataTypes } = require('sequelize');
// Exportamos una funcion que define el modelo
// Luego le injectamos la conexion a sequelize.
module.exports = (sequelize) => {
// defino el modelo
sequelize.define('Recipe', {
id:{
type: DataTypes.UUID(5),
primaryKey:true,
defaultValue:DataTypes.UUIDV4(5)
},
title: {
type: DataTypes.STRING,
allowNull: false,
},
summary:{
type:DataTypes.STRING,
allowNull: false,
},
healthScore:{
type: DataTypes.INTEGER,
},
steps:{
type:DataTypes.ARRAY(DataTypes.STRING)
},
dishTypes:{
type:DataTypes.ARRAY(DataTypes.STRING)
},
readyInMinutes:{
type: DataTypes.INTEGER,
get(){
return "Ready in " + this.getDataValue("readyInMinutes") + " minutes"
}
},
ingredients:{
type: DataTypes.ARRAY(DataTypes.STRING)
},
servings:{
type:DataTypes.STRING
},
image:{
type:DataTypes.STRING,
defaultValue: `https://post.healthline.com/wp-content/uploads/2020/08/food-still-life-salmon-keto-healthy-eating-732x549-thumbnail-732x549.jpg`
}
},{
timestamps: false
});
};
The post route (actually the helper):
const createNewRecipe = require("../helpers/RecipeCont/CreateRecipe/CreateRecipe")
const createNewRecipeRoute = async (req, res) => {
try {
const data = {
title,
summary,
healthScore,
steps,
dishTypes,
readyInMinutes,
ingredients,
servings,
Diet_type,
} = req.body;
const image = req.file.path
let newRecipe = await createNewRecipe({
title,
summary,
healthScore,
steps,
dishTypes,
readyInMinutes,
ingredients,
servings,
image,
});
await newRecipe.addDiet_type(Diet_type)
console.log(req.file)
res.status(200).json("Receta creada con éxito");
} catch (error) {
console.log(error)
res.status(400).json(error.message);
}
}
module.exports = createNewRecipeRoute;
The form
import React, {useState} from "react";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { createRecipe, getDietTypes } from "../../actions/actions";
import styles from "./form.module.css"
export default function Form(){
const [form,setForm] = useState({
title:"",
summary:"",
healthScore:0,
steps:[],
dishTypes:[],
readyInMinutes:0,
ingredients:[],
servings:0,
image:"",
Diet_type:[1]
})
const [steps, setSteps] = useState("")
const [dishTypes, setDishType]=useState("")
const [ingredients, setIngredients]= useState("")
const dispatch=useDispatch()
useEffect(()=>{
dispatch(getDietTypes())
},[])
const diets = useSelector(state=> state.diet)
const changeHandler=(e)=>{
if(e.target.name==="image"){
setForm({...form,[e.target.name]: e.target.file })
}
setForm({...form, [e.target.name]:e.target.value})
}
const stepHandler = (e)=>{
let aux = e.target.name
let auxV = e.target.value
if(e.key==="Enter"){
e.preventDefault()
setForm({...form, [e.target.name]: [...form[aux] , auxV ]})
aux==="steps"? setSteps("") : aux==="ingredients"? setIngredients("") : setDishType("")
}
console.log(form)
}
const deleteHandler = (e)=>{
let help = e.target.name
e.preventDefault()
let aux = form[help].filter(s=> s!==e.target.value)
setForm({...form, [help]: [...aux]})
}
const imageHandler = (e)=>{
setForm({...form, [e.target.name]:e.target.files[0]})
}
const sendHandler = (e)=>{
e.preventDefault();
if(form.image!==""){
const formData = new FormData()
formData.append("image",form.image)
formData.append("title",form.title)
formData.append("summary",form.summary)
formData.append("healthScore",form.healthScore)
formData.append("steps",form.steps)
formData.append("dishTypes",form.dishTypes)
formData.append("readyInMinutes",form.readyInMinutes)
formData.append("Ingredients",form.ingredients)
formData.append("servings",form.servings)
formData.append("Diet_type",form.Diet_type)
for (var key of formData.entries()) {
console.log(key[0] + ', ' + key[1]);
}
dispatch(createRecipe(formData))
} else {
dispatch(createRecipe(form))
}
console.log(form)
}
return(
<>
<div className={styles.div} >
<h2>Create your own recipe!</h2>
<form encType="multipart/form-data" method="POST" onSubmit={sendHandler}>
<div className={styles.divTitle}>
<h2>Title:</h2>
<input type="text" placeholder="Title" name="title" value={form.title} onChange={changeHandler}></input>
</div>
<input type="text" placeholder="summary" name="summary" value={form.summary} onChange={changeHandler}></input>
{form.healthScore}<input type="range" placeholder="healthScore" name="healthScore" min="1" max="100" step="1" value={form.healthScore} onChange={changeHandler} ></input>
<div>
<input type="text" name="steps" value={steps} onChange={(e)=>{setSteps(e.target.value)}} onKeyDown={stepHandler} placeholder="Steps"></input>
<div>
{form.steps.length>0? form.steps.map(e=><li>{e}<button value={e} name="steps" onClick={deleteHandler}>x</button></li>) : "Add the steps of your recipe!"}
</div>
</div>
<div>
<input type="text" name="ingredients" value={ingredients} onChange={(e)=>{setIngredients(e.target.value)}} onKeyDown={stepHandler} placeholder="Ingredients"></input>
<div>
{form.ingredients.length>0? form.ingredients.map(e=><li>{e}<button value={e} name="ingredients" onClick={deleteHandler}>x</button></li>) : "Add the ingredients of your recipe!"}
</div>
</div>
<div>
<input type="text" name="dishTypes" value={dishTypes} onChange={(e)=>{setDishType(e.target.value)}} onKeyDown={stepHandler} placeholder="Dish types"></input>
<div>
{form.dishTypes.length>0? form.dishTypes.map(e=><li>{e}<button value={e} onClick={deleteHandler}>x</button></li>) : "Add the dish types of your recipe!"}
</div>
</div>
<select>
{console.log(diets)}
{diets?.map(d=><option key={d.id} id={d.id}>{d.name}</option>)}
</select>
<input className={styles.number} name="readyInMinutes" value={form.readyInMinutes} onChange={changeHandler} type="number"></input>
<input className={styles.number} name="servings" value={form.servings} onChange={changeHandler} type="number"></input>
<input type="file" name="image" onChange={imageHandler}></input>
<input type="submit"></input>
</form>
</div>
</>
)
}
I'm still working on the form, in Diet_type for example, but even trying to hardcode the state to make the post it doesn't work.
The "for" is because the console.log doesn't work with formData and at the beginning I thought it was that I wasn't sending anything, but actually I do.
I save the image of all the request even for those which can fulfill so the middleware seems its working too.
I hope you can help me to understand what's going on and try to find a solution, c: Thanks for your time!!
The object values does not have the map methode.
You expect that there should be a map methode, but this is not a javascipt array with protype map. So you get the error.
You can use the loadash library.
const {map} require(`lodash`)
map(values, value => {
console.log(value)
// your code coes here
}

the autocomplete option labels are not getting cleared when reset() onSubmit(), is something wrong with this code or mui autocomplete issue?

Hi here I am building a form with material ui autocomplete component and react-hook-form useController and useForm, as mui select component's option labels are getting clear on form reset but with autocomplete it is not happening, though its value gets cleared on reset as wanted, so is something wrong with this code or autocomplete issue,
Registered with useController (react-hook-form)
import React from "react";
import Autocomplete from "#mui/material/Autocomplete";
import TextField from "#mui/material/TextField";
import { useController, UseControllerProps } from "react-hook-form";
//type Multi = string;
interface MultiOptionInterface {
label: string;
value: string;
}
interface Props extends UseControllerProps {
label: string;
type?: React.HTMLInputTypeAttribute | undefined;
id: string;
placeholder?: string | undefined;
multiSelectOptions: MultiOptionInterface[];
}
export function RegisteredMultiAutocomplete({
name,
control,
label,
type,
id,
placeholder,
multiSelectOptions
}: Props) {
const {
field: { ref, onChange, ...inputProps },
fieldState: { error },
formState: { isSubmitSuccessful }
} = useController({
name,
control,
defaultValue: ""
});
return (
<Autocomplete
fullWidth
id={id}
multiple
clearOnEscape
options={multiSelectOptions}
getOptionLabel={(option: MultiOptionInterface) => option.label}
renderInput={(params) => (
<TextField
{...inputProps}
{...params}
inputRef={ref}
error={!!error}
helperText={error ? error.message : null}
variant="outlined"
margin="normal"
label={label}
type={type}
placeholder={placeholder}
/>
)}
onChange={(_, data) => {
onChange(data);
return data;
}}
/>
);
}
export default RegisteredMultiAutocomplete;
onSubmit() code
import React from "react";
import { useForm } from "react-hook-form";
import * as Yup from "yup";
import { yupResolver } from "#hookform/resolvers/yup";
import { Box, Button } from "#mui/material";
import { RegisteredMultiAutocomplete } from "./RegisteredMultiAutocomplete";
interface FormInputs {
productCategories: string[];
}
const indcat = [
{ label: "Car", value: "Car" },
{ label: "Bike", value: "Bike" },
{ label: "Bus", value: "Bus" },
{ label: "Truck", value: "Truck" },
{ label: "Van", value: "Van" },
{ label: "Jeep", value: "Jeep" },
{ label: "Tractor", value: "Tractor" }
];
export function App() {
const validateSchema = Yup.object().shape({
productCategories: Yup.array().required("Product category is required")
});
const { handleSubmit, control, reset } = useForm({
mode: "onChange",
resolver: yupResolver(validateSchema)
});
const onSubmit = (data: unknown) => {
console.log(data);
reset();
};
return (
<Box component="form" onSubmit={handleSubmit(onSubmit)}>
<RegisteredMultiAutocomplete
id="product-categories"
name="productCategories"
control={control}
label="Select Product Categories"
type="text"
placeholder="Category"
multiSelectOptions={indcat}
/>
<Button type="submit" color="primary" variant="contained">
{" "}
Submit
</Button>
</Box>
);
}
export default App;
here is the app on codesandbox
on submit the value of autocomplete gets cleared but the option label not
expecting the autocomplete option label should get cleared on reset()

Text field with multiple value(image included for reference)

I'm looking for a text field with multiple inputs as:
Here as you can see I can add new text and on press of enter it saves that keyword.
Can someone guide which package to look for.... I found something similar in material ui autocomplete's costomized hook: https://material-ui.com/components/autocomplete/,
but I don't want it to be drop down I want it to be simply be a text field with option as shown in image.
I cannot find with such functionality or I might be looking work as I don't know proper term for it.
Any guidance will be of great help.
I've included material-ui , reactstrap as this is something I have worked with so if there is any other package also let me know
I was also building something like this few days back. This is what I've built till now.
import {
Chip,
FormControl,
Input,
makeStyles,
} from "#material-ui/core";
import React, { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const classes = useStyles();
const [values, setValues] = useState(["test"]);
const [currValue, setCurrValue] = useState("");
const handleKeyUp = (e) => {
console.log(e.keyCode);
if (e.keyCode == 32) {
setValues((oldState) => [...oldState, e.target.value]);
setCurrValue("");
}
};
useEffect(() => {
console.log(values);
}, [values]);
const handleChange = (e) => {
setCurrValue(e.target.value);
};
const handleDelete = ( item, index) =>{
let arr = [...values]
arr.splice(index,1)
console.log(item)
setValues(arr)
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<FormControl classes={{ root: classes.formControlRoot }}>
<div className={"container"}>
{values.map((item,index) => (
<Chip size="small" onDelete={()=>handleDelete(item,index)} label={item}/>
))}
</div>
<Input
value={currValue}
onChange={handleChange}
onKeyDown={handleKeyUp}
/>
</FormControl>
</div>
);
}
const useStyles = makeStyles((theme) => ({
formControlRoot: {
display: "flex",
alignItems: "center",
gap: "8px",
width: "300px",
flexWrap: "wrap",
flexDirection: "row",
border:'2px solid lightgray',
padding:4,
borderRadius:'4px',
"&> div.container": {
gap: "6px",
display: "flex",
flexDirection: "row",
flexWrap: "wrap"
},
"& > div.container > span": {
backgroundColor: "gray",
padding: "1px 3px",
borderRadius: "4px"
}
}
}));
Here is the working demo:
One possible way to do this using react-hook-form and Autocomplete using the Chip with renderTags function here is an example:
import {
Box,
TextField,
Autocomplete,
Chip,
} from '#mui/material'
...
const {
handleSubmit,
control,
formState: { errors },
} = useForm()
...
<Box mt={2}>
<Controller
control={control}
name="tags"
rules={{
required: "Veuillez choisir une réponse",
}}
render={({ field: { onChange } }) => (
<Autocomplete
defaultValue={
useCasesData?.tags ? JSON.parse(useCasesData?.tags) : []
}
multiple
id="tags-filled"
options={[]}
freeSolo
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
variant="outlined"
label={option}
{...getTagProps({ index })}
/>
))
}
onChange={(event, values) => {
onChange(values);
}}
renderInput={(params) => (
<TextField
{...params}
label="Métadonnées"
placeholder="Ecriver les métadonnées"
helperText={errors.tags?.message}
error={!!errors.tags}
/>
)}
/>
)}
/>
</Box>

DataGrid does not show inside Tabs with filterValues

I am trying to reproduce a component with Tabs and according filters like the OrderList in the react-admin demo (credentials are admin/password). The source can be found here.
What I have done is the following:
import React, { Fragment } from "react";
import {
List,
TextField,
Datagrid
} from "react-admin";
import Divider from "#material-ui/core/Divider";
import Tabs from "#material-ui/core/Tabs";
import Tab from "#material-ui/core/Tab";
class TabbedDatagrid extends React.Component {
tabs = [
{ id: 1, name: "in service" },
{ id: 2, name: "scrapped" },
{ id: 3, name: "locked" }
];
state = { service: [], scrapped: [], locked: [] };
static getDerivedStateFromProps(props, state) {
if (props.ids !== state[props.filterValues.conditionId]) {
return { ...state, [props.filterValues.conditionId]: props.ids };
}
return null;
}
handleChange = (event, value) => {
const { filterValues, setFilters } = this.props;
setFilters({ ...filterValues, conditionId: value });
};
render() {
const { classes, filterValues, ...props } = this.props;
return (
<Fragment>
<Tabs
variant="fullWidth"
centered
value={filterValues.conditionId}
indicatorColor="primary"
onChange={this.handleChange}
>
{this.tabs.map(choice => (
<Tab key={choice.id} label={choice.name} value={choice.id} />
))}
</Tabs>
<Divider />
<div>
{filterValues.conditionId === 1 && (
<Datagrid {...props} ids={this.state.service}>
<TextField source="id" />
</Datagrid>
)}
{filterValues.conditionId === 2 && (
<Datagrid {...props} ids={this.state.scrapped}>
<TextField source="id" />
</Datagrid>
)}
{filterValues.conditionId === 3 && (
<Datagrid {...props} ids={this.state.locked}>
<TextField source="id" />
</Datagrid>
)}
</div>
</Fragment>
);
}
}
const RollList = ({ classes, ...props }) => (
<List
title="Roll Inventory"
{...props}
filterDefaultValues={{ conditionId: 1 }}
sort={{ field: "name", order: "ASC" }}
perPage={25}
>
<TabbedDatagrid />
</List>
);
export default RollList;
When I run this code:
I see this
Data is not shown, although when I look at the props and the number of records in the right bottom corner I can see that data is there.
The console doesn't show any error (besides missing translations).
I am using the following (relevant) dependencies:
"#material-ui/core": "^3.9.2",
"#material-ui/icons": "^3.0.2",
"ra-data-json-server": "^2.8.0",
"react": "^16.8.4",
"react-admin": "^2.8.1",
"react-dom": "^16.8.4",
"react-scripts": "^2.1.8"
Has anyone an idea what I can do about it?
I found a solution:
I saw that the only difference to the react-admin demo OrderList is that the id of the Tabs is a string whereas in my code it is an integer (like in my data provider).
I changed my code and also my data to be a string as an id instead of an integer.
tabs = [
{ id: 'service', name: 'in service' },
{ id: 'scrapped', name: 'scrapped' },
{ id: 'locked', name: 'locked' }
];
and also in the condition part further down
{filterValues.conditionId === 'service' && (
<Datagrid {...props} ids={this.state.service}>
{//...}
</DataGrid>
)}
... not to forget the default value in the RollList wrapper at the end
filterDefaultValues={{ conditionId: 'service' }}
now it is working.
Thanks to all who thought about it.
Cheers.

Reactjs how to get value from selected element

so I have this code for posting to my backend API. Normal form perfectly fine; I managed to post to my database. So I add a Cascader from Ant Design CSS Framework, and every time I selected the value, it produced an error
TypeError: Cannot read property 'value' of undefined
Here is the code:
import React from 'react';
import axios from 'axios';
import { Button, Cascader, Form, Input, Modal } from 'antd';
const FormProduct = Form.Item;
const computerType = [
{
value: 'computer',
label: 'Computer',
},
{
value: 'laptop',
label: 'Laptop',
}
]
export default class FormInventory extends React.Component {
state = {
category: '',
productname: '',
};
handleCategoryChange = event => { this.setState({ category: event.target.value }) }
handleProductNameChange = event => { this.setState({ productname: event.target.value }) }
handleSubmit = event => {
event.preventDefault();
axios.post('myapi',
{
category: this.state.category,
productname: this.state.productname,
})
.then(
function success() {
const modal = Modal.success({
title: 'Success',
content: 'Data successfully add',
});
setTimeout(() => modal.destroy(), 2000);
}
)
}
render() {
return (
<Form onSubmit={this.handleSubmit}>
<FormProduct {...formProductLayout} label="Computer Category">
<Cascader options={computerType} category={this.state.value} onChange={this.handleCategoryChange} />
</FormProduct>
<FormProduct {...formProductLayout} label="Product Name">
<Input type="text" productname={this.state.productname} onChange={this.handleProductNameChange} />
</FormProduct>
<FormProduct wrapperCol={{ span: 12, offset: 2 }}>
<Button type="primary" htmlType="submit">
Add Item
</Button>
</FormProduct>
</Form>
)
}
}
You need to either bind your event handlers in the constructor or use arrow function.
Option 1: Bind
constructor(props) {
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
Option 2: Arrow function
<Input onChange={(e) => this.handleChange(e)} />
According to antd docs you don't need event.target.
https://ant.design/components/cascader/
handleCategoryChange = category => { this.setState({ category }) }
The code above will work fine.