edgee:slingshot error [Invalid directive] - javascript

So I have been trying to add edgee:slingshot to my Meteor + React project but for some reason I keep getting the same error:
"Error: The directive myFileUploads does not seem to exist [Invalid directive]"
I have followed the tutorial to implement all the information but can't seem to figure out what's wrong with my code.
Any help is highly appreciated.
My upload-rules file:
Slingshot.fileRestrictions("myFileUploads", {
allowedFileTypes: ["image/png", "image/jpeg", "image/gif"],
maxSize: 10 * 1024 * 1024 // 10 MB (use null for unlimited)
});
Slingshot.createDirective("myFileUploads", Slingshot.S3Storage, {
bucket: "ec2016",
region: "eu-central-1",
acl: "public-read",
authorize: function () {
//Deny uploads if user is not logged in.
if (!this.userId) {
var message = "Please login before posting files";
throw new Meteor.Error("Login Required", message);
}
return true;
},
key: function (file) {
//Store file into a directory by the user's username.
var user = Meteor.users.findOne(this.userId);
return user.username + "/" + file.name;
}
});
My form:
export default class AddSpark extends Component {
constructor(props) {
super(props);
this.upload = this.upload.bind(this)
}
createSpark(event){
event.preventDefault();
const spark = {
city: this.city.value,
person: this.person.value,
location: this.location.value,
title: this.title.value,
content: this.content.value,
url: this.url.value,
}
console.log(spark);
}
componentWillMount(){
// we create this rule both on client and server
Slingshot.fileRestrictions("myFileUploads", {
allowedFileTypes: ["image/png", "image/jpeg", "image/gif"],
maxSize: 10 * 1024 * 1024 // 10 MB (use null for unlimited)
});
}
upload(){
var uploader = new Slingshot.Upload("myFileUploads");
uploader.send(document.getElementById('input').files[0], function (error, downloadUrl) {
if (error) {
// Log service detailed response
alert (error);
}
else {
Meteor.users.update(Meteor.userId(), {$push: {"profile.files": downloadUrl}});
}
});
}
render() {
return (
<div>
<form ref={(input) => this.sparkForm = input} onSubmit={(e) => this.createSpark(e)}>
<ControlLabel>Select your city</ControlLabel>
<select id="formControlsCity" placeholder="Choose your city" className="form-control" onClick={ moreOptions } ref={(input) => this.city = input}>
<option value="select">Choose your city</option>
<option value="Beijing">Beijing</option>
<option value="Shanghai">Shanghai</option>
<option value="Chengdu & Chongqing">Chengdu & Chongqing</option>
</select>
<ControlLabel>Select your person</ControlLabel>
<select id="formControlsPerson" placeholder="Choose your person" className="form-control" ref={(input) => this.person = input}>
<option value="select">First select your city</option>
</select>
<ControlLabel>Select your location</ControlLabel>
<select id="formControlsLocation" placeholder="Choose your location" className="form-control" ref={(input) => this.location = input}>
<option value="select">First select your city</option>
</select>
<ControlLabel>Title</ControlLabel>
<input type="text" label="Title" placeholder="Enter your title" className="form-control" ref={(input) => this.title = input}/>
<ControlLabel>Content</ControlLabel>
<textarea placeholder="Enter your comment here" className="form-control" ref={(input) => this.content = input}/>
<div className="upload-area">
<p className="alert alert-success text-center">
<span>Click or Drag an Image Here to Upload</span>
<input type="file" id="input" ref={(input) => this.url = input} onChange={this.upload} />
</p>
</div>
<Button type="submit">Submit</Button>
</form>
</div>
)}
}
Beside that I ofcourse also have a setting.json file where I store my S3 keys.
Have a great day and don't forget to smile.

I already managed to fix this. Eventhough I thought my upload-rules.js was in a server folder. It turned out to not be in the right one. After moving it to the right folder it all works.
So for anyone who has the same problem, double check if your file are in the right place.

Related

File input always shows No file chosen

I'm building a form using React & Nodejs & MongoDB, where you can fill the form and upload a file with some other text inputs.
However, after i submit, i receive only text inputs in my database. The chosen file (desired to upload) doesn't appear at all in the database. I am expecting to get all the data form (the uploaded file and text inputed) in my database.
I tested my backend and it works correctly (it uploads all the inputs).
Ps: In browser, when i select a file (.pdf) to upload, the file input always shows no file chosen !
Console.dev : Error
{title: 'Miss', fname: 'zaezae', lname: 'zaeazee', email: 'zaea#mail.com', phoneNumber: '12345678', …}
coverLetter: "zaez e az ezae e zae aeae "
cv: ""
email: "zaea#mail.com"
fname: "zaezae"
lname: "zaeazee"
myFile: "C:\\fakepath\\test.pdf"
phoneNumber: "12345678"
title: "Miss"
Formulaire.jsx:29
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'then')
at submit (Formulaire.jsx:29:1)
Backend:
server.js:
const express = require('express')
const mongoose = require('mongoose')
const bodyparser = require('body-parser')
const FormRoute = require('./routes/FormRoute')
//database
mongoose.connect('mongodb://localhost:27017/form', { useNewUrlParser: true, useUnifiedTopology: true })
const db = mongoose.connection
db.on('error', (err) => {
console.log(err)
})
db.once('open', () => {
console.log("Database connection established!")
})
//app
const app = express()
app.use(bodyparser.urlencoded({ extended: true }))
app.use(bodyparser.json())
//cors
const cors = require('cors')
app.use(cors())
//server run
const PORT = process.env.PORT || 5000
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
})
app.use('/api/form', FormRoute);
Axios.js:
import axios from 'axios'
export const Axios = axios.create({
baseURL: 'http://localhost:5000',
})
Apiroutes.js:
const form = "/api/form"
export const requests = {
formApi: {
store: form + '/store'
}
}
formservices.js:
import { Axios } from "../config/axios";
import { requests } from "../config/apiroutes";
export const FormService = {
store: (data) => {
Axios.post(requests.formApi.store, data)
.then(res => {
return res
})
.catch(err => {
return err
})
}
}
formController.js :
const form = require('../models/FormModel')
const store = (req, res, next) => {
let candidate = new form({
title: req.body.title,
fname: req.body.fname,
lname: req.body.lname,
email: req.body.email,
phoneNumber: req.body.phoneNumber,
coverLetter: req.body.coverLetter
})
if (req.file) {
candidate.cv = req.file.path
}
candidate.save()
.then(response => {
res.json({
success: true,
message: 'Candidate added successfully!',
data: candidate,
})
})
.catch(error => {
res.json({
success: false,
message: 'An error occured!',
error: error,
})
})
}
module.exports = {
store
}
Frontend:
Formulaire.jsx :
import React, { useState } from 'react'
import Divider from '#mui/material/Divider';
import './Formulaire.css'
import { titles } from '../../mock/titles'
import { FormService } from '../../services/formServices';
const Form = () => {
const [storedata, setstoredata] = useState({
title: '',
fname: '',
lname: '',
email: '',
phoneNumber: '',
cv: '',
coverLetter: ''
})
const handleChange = e => {
const { name, value } = e.target;
setstoredata(prevState => ({
...prevState,
[name]: value
}));
};
const submit = async () => {
console.log(storedata);
await FormService.store(storedata)
.then(res => {
console.log(res);
})
.catch(err => {
return err
})
}
return (
<div className='container'>
<div className='header'>
<div className='title'>
<a className='quizbutton' href="/quiz">Take a Test (Quiz)</a>
<h1>Apply for a Position :</h1>
</div>
</div>
<Divider style={{ maxWidth: '1000px', marginLeft: '250px' }} />
<div id="content">
<div id="formWrapper">
<form id="msform" method='post' action='/uploadFile' enctype="multipart/form-data">
<fieldset id="fieldset3">
<h2 class="fs-title">Please complete the form below for a position with us.</h2>
<h3 class="fs-subtitle">Reference 0001</h3>
{/* <div class="fs-error"></div> */}
<div class="wrapper">
<label for="title">Title :</label>
<select name="title" value={storedata.title} onChange={handleChange}>
<option hidden></option>
{
titles.map((c, i) => {
return (
<option key={i} value={c}>{c}</option>
)
})
}
</select>
<label for="fname">First Name<span>*</span> :</label>
<input type="text" name="fname" value={storedata.fname} onChange={handleChange} id="fname" placeholder="Please enter your first name" required />
<label for="lname">Last Name<span>*</span> :</label>
<input type="text" name="lname" value={storedata.lname} onChange={handleChange} id="lname" placeholder="Please enter your last name" required />
<label for="email">Email<span>*</span> :</label>
<input type="email" name="email" value={storedata.email} onChange={handleChange} id="email" placeholder="Please enter your email" required />
<label for="phoneNumber">Phone N° :</label>
<input type="number" name="phoneNumber" value={storedata.phoneNumber} onChange={handleChange} id="phoneNumber" placeholder="Phone number" />
<label for="CV">Upload CV <span>*</span>:</label>
<input type="file" name="myFile" id="cv" value={storedata.cv} onChange={handleChange} accept="application/msword, application/pdf" placeholder="Cover Letter" required />
<label for="coverLetter">Cover Letter :</label>
<textarea type="text" name="coverLetter" value={storedata.coverLetter} onChange={handleChange} id="coverLetter" placeholder="cover Letter" />
</div>
<br />
<input type="submit" name="submit" class="submit action-button" value="Submit" onClick={submit} />
</fieldset>
</form>
</div>
</div>
</div>
)
}
export default Form
I fixed my problem by changing name="myFile" id="cv" to name="cv" id="cv"

How do I add user inputs on click?

Self taught coder. Hopefully I can explain this adequately. Users create recipe cooking guides by inputing values like dish name, food category, cover img, etc. The steps array only has 1 step at the moment. I want a button that users can click that would add another "step" and "gif" to the steps array. Can anyone suggest a method to have another set of "step" and "img" render on screen when the user clicks the Add Step button? I have a handleAddSteps function with nothing inside. Somebody recommended using DOM? Just want to know if this is a good way to go before investing time down the wrong rabbit hole. Thank you kindly for any advice.
import React from "react";
import { useState, useEffect } from "react";
// Styles
import "./UploadGuide.scss";
// Firebase
import { doc, setDoc } from "firebase/firestore";
import { db, storage } from "../../firebase";
import { v4 as uuidv4 } from "uuid";
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
function UploadGuide() {
const [file, setFile] = useState("");
const [data, setData] = useState({});
const [percentage, setPercentage] = useState(null);
// const [stepsCounter, setStepsCounter] = useState(0);
// Upload IMG
useEffect(() => {
const uploadFile = () => {
const name = new Date().getTime() + file.name;
const storageRef = ref(storage, file.name);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
setPercentage(progress);
switch (snapshot.state) {
case "paused":
console.log("Upload is paused");
break;
case "running":
console.log("Upload is running");
break;
default:
break;
}
},
(error) => {
console.log(error);
},
() => {
// Handle successful uploads on complete
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
setData((prev) => ({ ...prev, img: downloadURL }));
});
}
);
};
file && uploadFile();
}, [file]);
console.log(file);
console.log(data);
// When user clicks Add Steps button, page renders another steps card
const handleAddSteps = () => {};
// When user submits guide, upload data to firecloud db and imgs to firebase storage.
const handleAddGuide = async (e) => {
e.preventDefault();
const newId = uuidv4();
// const stepsArray = []
await setDoc(doc(db, "guides", newId), {
author: e.target.author.value,
categoryId: "100",
categoryName: e.target.categoryName.value,
coverGif: data.img,
id: newId,
name: e.target.name.value,
tags: e.target.tags.value,
// steps: stepsArray
steps: [
{
step: e.target.step.value,
gif: data.img,
},
],
});
};
return (
<div className="upload">
<form onSubmit={handleAddGuide} className="upload__form">
<div className="upload__card">
<div className="upload__inputs">
<label className="upload__label">Guide Name</label>
<input
className="upload__input"
placeholder="Name of your recipe"
type="name"
name="name"
// value=""
></input>
</div>
<div className="upload__inputs">
<label className="upload__label">Author</label>
<input
className="upload__input"
type="author"
name="author"
></input>
</div>
<div className="upload__inputs">
<label className="upload__label">Category</label>
<select
name="categoryName"
className="upload__input"
defaultValue={"Cooking"}
>
<option value="Cooking">Cooking</option>
<option></option>
</select>
</div>
<div className="upload__inputs">
<label className="upload__label">Tags</label>
<input
name="tags"
className="upload__input upload__input--large"
placeholder="Add relevant tags to help people find your guide"
></input>
</div>
</div>
<div className="upload__card upload__card--addstep">
<div className="upload__inputs">
<label className="upload__label">Step 1</label>
<input
className="upload__input upload__input--large"
name="step"
></input>
</div>
<div className="upload__inputs">
<div>
<img src={file ? URL.createObjectURL(file) : ""}></img>
</div>
<label
htmlFor="file"
style={{ cursor: "pointer" }}
className="upload__add"
>
+ IMG
</label>
<input
id="file"
onChange={(e) => setFile(e.target.files[0])}
style={{ display: "none" }}
type="file"
></input>
</div>
</div>
<div className="upload__box">
<button
className="upload__add"
onClick={handleAddSteps}
style={{ cursor: "pointer" }}
>
Add Step
</button>
</div>
<div className="upload__cta">
<button
disabled={percentage !== null && percentage < 100}
type="submit"
className="upload__submit"
style={{ cursor: "pointer" }}
>
Upload
</button>
</div>
</form>
</div>
);
}
export default UploadGuide;
Here is an image of what the page looks like:
enter image description here
Please let me know if I'm missing any key information. Any advice is appreciated. Thank you.
Since each Guide can have multiple steps, I would rather create a state that handles an array of multiple step objects, than just a counter.
With this, you can render as much steps as you have in the array.
Then, adding steps just will be a matter of adding objects (with an empty state) to that array.
I made this functional example, that should be clear enough to show what I mean:
import React, { useState } from 'react';
const EMPTY_STEP = { text: '', file: null };
export default function App() {
const [guideSteps, setGuideSteps] = useState([{ ...EMPTY_STEP }]);
const handleStepChange = (stepIndex, property, value) => {
setGuideSteps((prevState) => {
const newGuideSteps = [...prevState];
newGuideSteps[stepIndex] = { ...newGuideSteps[stepIndex], [property]: value };
return newGuideSteps;
});
};
const handleAddStep = () => {
setGuideSteps((prevState) => [...prevState, { ...EMPTY_STEP }]);
};
return (
<div className="Steps">
{guideSteps.map((step, index) => {
return (
<div key={`step-${index + 1}`}>
<h2>{`Step ${index + 1}`}</h2>
<input type="text" value={step.text} onChange={(e) => handleStepChange(index, 'text', e.target.files ? e.target.files[0] : null, index)} />
<input type="file" value={step.file} onChange={(e) => handleStepChange(index, 'file', e.target.value, index)} />
</div>
);
})}
<button onClick={handleAddStep}>Add Step</button>
</div>
);
}

PUT request from redux actions not working

I am trying to update my MongoDB database containing data for a project information form through a PUT request on my action, but I am unable to get the handleSumbit function on my component to call my project action. I am receiving a 404 error. I have no clue what I am missing or have done wrong. Any insight would be appreciated.
My Component:
import React, { useState } from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import '../styles/ViewProject.css'
import { updateProjects } from '../actions/project.actions'
const EditProject = ({ project, match }) => {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [projectName, setProjectName] = useState('')
const [projectType, setProjectType] = useState('')
const [multiple, setMultiple] = useState(false)
const [dueDate, setDueDate] = useState(Date)
const [url, setUrl] = useState('')
const [description, setDescription] = useState('')
const handleSubmit = e => {
e.preventDefault()
updateProjects(name, email, projectName, projectType, dueDate, url, description)
console.log('Data ', name, email, projectName)
}
return(
<form className="viewProjectContainer" onSubmit={handleSubmit}>
{project.map((task, i) => (
<div key={i++} >
{match.params.id === task._id ?
<div className="vp">
<div className="vpLeft">
<div className="vpTitle">
<input className='vpProjectName' defaultValue={task.projectName} onChange={e => setProjectName(e.target.value)} />
</div>
<div className="vpSub">
<p>Submitted by: </p>
<label>Name: </label>
<input className='vpName' defaultValue={task.name} onChange={e => setName(e.target.value)}/>
<label>Email: </label>
<input className='vpEmail' defaultValue={email} onChange={e => setEmail(e.target.value)}/>
</div>
<div className="vpDescription">
<label>{task.multiple === true ? "(Multiple)" : "Single"}</label>
<p>Project Type: </p>
<select className='formInput' onChange={e => setProjectType(e.target.value)} defaultValue={projectType}>
<optgroup label='Digital'>
<option value='banner'>Banner</option>
<option value='emailBlast'>Email Blast</option>
<option value='productImage'>Product Image</option>
<option value='socialMediaAd'>Social Media Ad</option>
<option value='video'>Video</option>
<option value='websiteMockup'>Website Mockup</option>
<option value='other'>Other (Please be specific in Description)</option>
</optgroup>
<optgroup label='Physical Product'>
<option value='bottleOpener'>Bottle Opener</option>
<option value='clothing'>Clothing</option>
<option value='keyChain'>Key Chain</option>
<option value='popSocket'>Pop Socket</option>
<option value='usb'>USB</option>
<option value='other'>Other (Please be specific in Description)</option>
</optgroup>
<optgroup label='Print'>
<option value='businessCard'>Business Card</option>
<option value='brochureBiFold'>Brochure - Bi-fold</option>
<option value='brochureTriSquare'>Brochure - Trifold Square</option>
<option value='brochureTriStandard'>Brochure - Trifold Standard</option>
<option value='catalogMore'>Catalog - Less than 50 pages</option>
<option value='catalogLess'>Catalog - More than 50 pages</option>
<option value='documentFull'>Document - Full Sheet</option>
<option value='documentHalf'>Document - Half Sheet</option>
<option value='flyer4x6'>Flyer - 4x6</option>
<option value='flyer5x7'>Flyer - 5x7</option>
<option value='flyer6x9'>Flyer - 6x9</option>
<option value='postCard4x6'>Post Card 4x6</option>
<option value='postCard5x7'>Post Card 5x7</option>
<option value='postCard6x9'>Post Card 6x9</option>
<option value='poster11x17'>Poster 11x17</option>
<option value='poster24x36'>Poster 24x36</option>
<option value='other'>Other (Please be specific in Description)</option>
</optgroup>
<optgroup label='Other'>
<option value='consultation'>Consultation</option>
<option value='labeling'>Labeling</option>
<option value='other'>Other (Please be specific in Description)</option>
</optgroup>
</select>
<div className="vpDescBox">
<textarea className='updateDescription' defaultValue={description} onChange={e => setDescription(e.target.value)}></textarea>
</div>
</div>
</div>
<div className="vpRight">
<div className="vpTicket">
<p>Ticket Number: {task.ticketNumber}</p>
<label>Submitted: {task.date} </label>
<label>Preferred due date: </label>
<input type='date' defaultValue={task.dueDate} onChange={e => setDueDate(e.target.value)}/>
</div>
<div>
<label>Reference links: </label>
<input type='text' defaultValue={task.url} onChange={e => setUrl(e.target.value)}/>
<label></label>
</div>
<div>
<p>Project Status:</p>
<p>Task Recieved: {task.recieved === true ? "Recieved" : "Not Revieved"}</p>
<p>Task in progress: {task.inProgress === true ? "In Progress" : "Not in Progress"}</p>
<p>Completed: {task.completed === true ? "Complete" : "Incomplete"}</p>
</div>
</div>
</div>: null}
</div>
))}
<div className='buttonsContainer'>
<button type='submit' className='projectViewButton1'><Link to={'/' + match.params.id}>Save</Link></button>
</div>
</form>
)
}
const mapStateToProps = state => ({
project: state.projects.project,
id: state.projects.id
})
export default connect(
mapStateToProps,
{ updateProjects }
)(EditProject)
My action:
export function updateProjects(name, email, projectName, projectType, dueDate, url, description) {
console.log('reducer ', name)
axios.put('api/projects/update/:id', {
name: name,
email: email,
projectName: projectName,
projectType: projectType,
// multiple: multiple,
dueDate: dueDate,
url: url,
description: description
}).then(resp => {
console.log("Data updated: ", resp.data)
})
}
My Backend:
// #route PUT api/projects/update/:id
// #desc Update projects
// #access Private
router.put('/update/:id', async (req, res) => {
try {
const {
name,
email,
projectName,
projectType,
multiple,
dueDate,
reference,
attachment,
description,
ticketNumber,
received,
inProgress,
completed
} = req.body
const project = await Project.findByIdAndUpdate(req.params.id, {
name,
email,
projectName,
projectType,
multiple,
dueDate,
reference,
attachment,
description,
ticketNumber,
received,
inProgress,
completed
})
res.json(project);
} catch (err) {
console.error(err.message)
res.status(500).send('Server Error')
}
})
Thank you for helping!
The endpoint you are hitting with the axios.put is api/projects/update/:id, which is incomplete. You should put your host as well. For example: http://localhost:8000/api/projects/update/:id
And also, you need to specify a proper value for :id in the URL. Not sure if you left that out in purpose for example's sake but just wanna point that one out too haha.
in your react project folder's package.json file :
"proxy":"http://localhost:8000"
your http url should be below :
axios.put('/api/projects/update/', {
params: {
id: 'value of id'
}
});
or :
axios.put(`/api/projects/update/${value of id}`)

Updating React Json Value Conditionally

I didn't figure out how to edit JSON data from a Put request.(it must be PUT request)
I created some inputs as you see and I need to find a way for updating/adding new credit-debit datas on JSON data differs by "to" and "from".
Also, if a "to" value added, it must decreased from total balance and if a "from" value added, it must be added to total balance.
I created a select box and an input for this (didin't connect between json and component)
My Updater component is as follows:
Component itself:
import React, { Component } from 'react';
import './Updater.scss';
import Axios from 'axios';
export default class Updater extends Component {
constructor(){
super();
this.state = {
amount: '',
description: '',
from: '',
to: '',
date: new Date()
}
}
onSubmitEdit = () => {
Axios.put('http://localhost:8080/api/balance/add',
{});
}
render() {
return (
<div className="updatercontainer">
<div className="updaterinputs">
<input className="amount" name="amount"
type="text" placeholder="Enter Amount"/>
<input className="description" name="description"
type="text" placeholder="Enter Description"/>
</div>
<div className="selectbox">
<select>
<option value="From">From</option>
<option value="To">To</option>
</select>
<input className="fromto" type="text"
name="fromto" placeholder="Enter From or To Name"/>
</div>
<div className="selectboxcontainer">
<div className="button-container">
<a href="#" onClick={this.onSubmitEdit}
className="button amount-submit">
<span></span>Update</a>
</div>
</div>
</div>
)
}
}
class Updater extends React.Component {
constructor() {
super();
this.state = {
amount: 0,
description: "",
_from: true,
_to: false,
date: new Date()
};
}
onAmountChange = e => {
this.setState({
amount: e.target.value
});
};
onDescriptionChange = e => {
this.setState({
description: e.target.value
});
};
onSelectTypeChange = e => {
console.log(e.target.value);
this.setState({
[e.target.value === "from" ? "_from" : "_to"]: true,
[e.target.value !== "from" ? "_from" : "_to"]: false
});
if(e.target.value !== "from" && (this.state.from != null || this.state.from !== "")) {
this.setState({
to: this.state.from,
from: null
});
} else if(e.target.value === "from" && (this.state.to != null || this.state.to !== "")){
this.setState({
from: this.state.to,
to: null
});
}
};
onFromToChange = (e) => {
this.setState({
[this.state._from ? "from" : "to"]: e.target.value
});
}
onSubmitEdit = () => {
Axios.put(
"https://httpbin.org/put",
{
...this.state,
},
{ headers: { "Content-Type": "application/json" } }
)
.then(response => {
// handle Response
})
.catch(err => {
// handle Error
});
};
render() {
return (
<div className="updatercontainer">
<div className="updaterinputs">
<input
onChange={this.onAmountChange}
className="amount"
name="amount"
type="text"
placeholder="Enter Amount"
/>
<input
onChange={this.onDescriptionChange}
className="description"
name="description"
type="text"
placeholder="Enter Description"
/>
</div>
<div className="selectbox">
<select onChange={this.onSelectTypeChange}>
<option value="from">From</option>
<option value="to">To</option>
</select>
<input onChange={this.onFromToChange} className="fromto" type="text"
name="fromto" placeholder="Enter From or To Name"/>
</div>
<div className="selectboxcontainer">
<div onClick={this.onSubmitEdit} className="button-container">
<a href="#" className="button amount-submit">
<span>Update</span>
</a>
</div>
</div>
</div>
);
}
}
Consider Reading More About Handling Inputs, Forms, Events
Working Sandbox!
you just need an onChange event to update the state based on the input values name
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value })
}
//On your inputs
<input
className="amount"
name="amount"
type="text"
placeholder="Enter Amount"
value={this.state.amount}
onChange={() => this.handleChange(e)}
/>

How can I get this dynamic form to work?

I have been trying to get my dynamic form to work in my Meteor React application.
It was all working as requested before I started add this but now I can't get it to work. I'm getting a "Uncaught TypeError: Cannot set property '0' of undefined" error. this points to this line:
{this.state.inputs.map((input, idx) => <input
This my whole code, I know it's a bit messy so any feedback is highly appreciated:
import React, { Component } from 'react';
import { Row, Col, Checkbox, Radio, ControlLabel, HelpBlock, FormGroup, FormControl, Button, Tabs, Tab } from 'react-bootstrap';
import { Bert } from 'meteor/themeteorchef:bert';
import { insertComment } from '../../../api/comments/methods';
import ReactQuill from 'react-quill';
var s3Url = null;
export default class AddSparkShanghai extends Component {
constructor(props) {
super(props);
this.createSpark = this.createSpark.bind(this);
this.onChange = this.onChange.bind(this);
this.state ={
inputs: ['input-0'],
city: '',
person: '',
location: '',
title: '',
content: [],
mediaUrls: [],
};
}
componentWillMount(){
// we create this rule both on client and server
Slingshot.fileRestrictions("myFileUploads", {
allowedFileTypes: ["image/png", "image/jpeg", "image/gif"],
maxSize: 10 * 1024 * 1024 // 10 MB (use null for unlimited)
});
}
upload(file){
var uploader = new Slingshot.Upload("myFileUploads");
uploader.send(document.getElementById('input').files[0], function (error, downloadUrl) {
if (error) {
// Log service detailed response
alert (error);
}
else {
s3Url = encodeURI(downloadUrl);
Bert.alert('File uploaded!', 'success');
Meteor.users.update(Meteor.userId(), {$push: {"profile.files": downloadUrl}});
}
});
}
createSpark(event) {
event.preventDefault();
var formData = $('#form').serializeArray()
console.log(formData);
var mediaArray = [];
if (this.mediaUrls.value == 0) {
mediaArray = [];
} else {
mediaArray.push(encodeURI(this.mediaUrls.value));
console.log(this.mediaUrl.value);
console.log(mediaArray);
}
const city = 'Shanghai';
const person = this.person.value;
const location = this.location.value;
const title = this.title.value;
const content = this.state.content;
const fileLink = s3Url;
const timestamp = parseInt(this.props.timestamp);
const mediaUrls = mediaArray;
const approved = true;
const adminSpark = true;
const createdBy = Meteor.userId();
insertComment.call({
city, person, location, title, content, fileLink, timestamp, approved, adminSpark, createdBy, mediaUrl,
}, (error) => {
if (error) {
Bert.alert(error.reason, 'danger');
} else {
Bert.alert('Spark added!', 'success');
}
});
}
onChange(html) {
this.setState ({ content: html });
}
appendInput() {
var newInput = `input-${this.state.inputs.length}`;
console.log (newInput);
this.setState({ inputs: this.state.inputs.concat([newInput]) });
}
render() {
const events = {
'text-change': delta => {
}
}
return (
<div className="background-container">
<form ref={(input) => this.sparkForm = input} onSubmit={(e) => this.createSpark(e)}>
<ControlLabel>Select your person (optional)</ControlLabel>
<select id="formControlsPerson" placeholder="Choose your person" className="form-control" ref={(input) => this.person = input}>
<option value='select'>Select your person</option>
<option value='jane'>Jane Siesta</option>
<option value='ben'>Ben Huang</option>
<option value='han'>Han Han</option>
<option value='mau'>Mau Mau</option>
<option value='void'>VOID</option>
<option value='tommy'>Tommy Hendriks</option>
<option value='gareth'>Gareth Williams</option>
<option value='gigi'>Gigi Lee</option>
</select>
<ControlLabel>Select your location (optional)</ControlLabel>
<select id="formControlsLocation" placeholder="Choose your location" className="form-control" ref={(input) => this.location = input}>
<option value='select'>Select your location</option>
<option value='shelter'>Shelter</option>
<option value='mansion'>The Mansion</option>
</select>
<ControlLabel>Title</ControlLabel>
<input type="text" label="Title" placeholder="Enter your title" className="form-control" ref={(input) => this.title = input}/>
<ControlLabel>Add Image</ControlLabel>
<div className="upload-area">
<p className="alert alert-success text-center">
<span>Click or Drag an Image Here to Upload</span>
<input type="file" id="input" className="file_bag" onChange={this.upload} />
</p>
</div>
<ControlLabel>Content</ControlLabel>
<div className='_quill'>
<ReactQuill
toolbar={false}
theme="snow"
ref='editor'
onChange={this.onChange}
events={events} />
</div>
<br />
<ControlLabel>Media (optional)</ControlLabel>
<div id="dynamicInput">
{this.state.inputs.map((input, idx) => <input
key={ input }
type="text"
label="Media"
placeholder="Add your media url"
className="form-control"
ref={(input) => this.mediaUrls[idx] = input}/> )}
</div>
<Button onClick={ () => this.appendInput() }>
Add media field
</Button>
<ControlLabel>Media (optional)</ControlLabel>
<div id="dynamicInput">
{this.state.inputs.map(input => <input key={input} type="text" label="Media" placeholder="Add your media url" className="form-control" ref={(input) => this.mediaUrl = input}/> )}
</div>
<Button onClick={ () => this.appendInput() }>
Add media field
</Button>
<Button type="submit" data-dismiss="modal">Submit</Button>
</form>
</div>
)}
}
I guess the problem is this line: ref={(input) => this.mediaUrls[idx] = input}/> )}, it seems like the value of this.mediaUrls is undefined

Categories