I am making a recursive treeview with checkboxes using React JS. I want to make a form and submit button in order to get the checked values but I am not getting where to insert the submit button in the form. So far my code generates a submit button with each node.
toggle = () => {
this.setState(
{visible: !this.state.visible}
);
};
render() {
var childNodes;
if (this.props.node.childNodes != null) {
childNodes = this.props.node.childNodes.map(function(node, index) {
return <li key={index}><Treeview node={node} /></li>
});
}
var style;
if (!this.state.visible) {
style = {display: "none"};
}
return (
<form>
<label>
{this.props.node.title}
<input type="checkbox" onClick={this.toggle}/>
</label>
<ul style={style}>
{childNodes}
</ul>
<input type="submit" value="Submit"/>
</form>
);
}
I think there are two ways to go about doing what you want. One (Ex 1) would be having a structure with two components/methods, one for the form and another for the recursive checkbox structure.
Another (Ex 2) would to be to use props to conditionally render some portions.
Ex 1
function TreeView (props) {
var childNodes;
if (props.node.childNodes != null) {
childNodes = props.node.childNodes.map(function(node, index) {
return <li key={index}><Treeview node={node} /></li>
});
}
var style;
if (!props.visible) {
style = {display: "none"};
}
return (
<ul style={style}>
{childNodes}
</ul>
);
}
class Form extends React.Component {
constructor (props) {
super(props);
this.toggle = this.toggle.bind(this);
}
toggle () {
this.setState(
{visible: !this.state.visible}
);
};
render () {
return (
<form>
<label>
{this.props.node.title}
<input type="checkbox" onClick={this.toggle}/>
</label>
<TreeView node={props.node} visible={this.state.visible} />
<input type="submit" value="Submit"/>
</form>
);
}
}
Ex 2
class Treeview extends React.Component {
constructor (props) {
super(props);
this.toggle = this.toggle.bind(this);
}
toggle () {
this.setState(
{visible: !this.state.visible}
);
};
render () {
var childNodes;
if (this.props.node.childNodes != null) {
childNodes = this.props.node.childNodes.map(function(node, index) {
return <li key={index}><Treeview node={node} childTree /></li>
});
}
var style;
if (!this.state.visible) {
style = {display: "none"};
}
if (this.props.childTree) {
return (
<ul>
{childNodes}
</ul>
)
} else {
return (
<form>
<label>
{this.props.node.title}
<input type="checkbox" onClick={this.toggle}/>
</label>
<ul style={style}>
{childNodes}
</ul>
<input type="submit" value="Submit"/>
</form>
);
}
}
}
Related
In my app I am adding new product. But after I click second time it appears on a list. In AddProducts I console.log the array with changed list and it had been updated. But in mu ProductList it doesnt work after click Add so I apply componentDidUpdate to react on changes. Also I use useEffect in App to react on changes sending from AddProduct but it works only I click second times.
AddProduct
function AddProducts(props) {
const [newProduct, setNewProduct] = useState({
productName: ``,
category: ``,
groceries: false,
});
const [newList, setNewList] = useState(props.productsToDisplay);
function handleChange(event) {
event.preventDefault();
setNewProduct({
...newProduct,
[event.target.name]:
event.target.type === "checkbox"
? event.target.checked
: event.target.value,
});
}
function handleAddNewProduct() {
const addProduct = newList.concat([
{
nazwa: newProduct.productName,
kategoria: newProduct.category,
produktSpozywczy: newProduct.groceries,
},
]);
setNewList(addProduct);
props.sendNewProductsToParent(newList);
}
return (
<div className={styles.Wrapper}>
{console.log(newList)}
<p>Add products</p>
<input
name="productName"
value={newProduct.productName}
onChange={handleChange}
></input>
<input
name="category"
value={newProduct.category}
onChange={handleChange}
></input>
<input
name="groceries"
type="checkbox"
value={newProduct.groceries}
onChange={handleChange}
></input>
<p>Is it groceries?</p>
<button onClick={handleAddNewProduct}>Add new product</button>
</div>
);
}
ProductList
class ProductsList extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className={commonColumnsStyles.App}>
{console.log(this.props.productsToDisplay)}
<header className={commonColumnsStyles.AppHeader}>
<p>Products list</p>
<ul>
{this.props.productsToDisplay.map((currProduct, index) => (
<li key={index} onClick={() => this.addProduct(index)}>
{currProduct.nazwa}
</li>
))}
</ul>
</header>
</div>
);
}
}
function App() {
const [resultToDisplay, setResultToDisplay] = useState(``);
const [newProductsList, setNewProductsList] = useState(produkty);
return (
<div className={styles.appWrapper}>
{console.log(newProductsList)}
<AddProducts
productsToDisplay={produkty}
sendNewProductsToParent={setNewProductsList}
/>
<div className={styles.columnsWrapper}>
<ProductsList
productsToDisplay={newProductsList}
sendAddedProductsToParent={setResultToDisplay}
/>
</div>
</div>
);
}
Solution for community:
AddProduct
function AddProducts(props) {
const [newProduct, setNewProduct] = useState({
productName: ``,
category: ``,
groceries: false,
});
function handleChange(event) {
event.preventDefault();
setNewProduct({
...newProduct,
[event.target.name]:
event.target.type === "checkbox"
? event.target.checked
: event.target.value,
});
}
function handleAddNewProduct() {
const addProduct = {
nazwa: newProduct.productName,
kategoria: newProduct.category,
produktSpozywczy: newProduct.groceries,
};
props.sendNewProductToParent((listOfPrimiaryProducts) => [
...listOfPrimiaryProducts,
addProduct,
]);
}
return (
<div className={styles.Wrapper}>
<p>Add products</p>
<input
name="productName"
value={newProduct.productName}
onChange={handleChange}
></input>
<input
name="category"
value={newProduct.category}
onChange={handleChange}
></input>
<input
name="groceries"
type="checkbox"
value={newProduct.groceries}
onChange={handleChange}
></input>
<p>Is it groceries?</p>
<button onClick={handleAddNewProduct}>Add new product</button>
</div>
);
}
export default AddProducts;
ProductList
class ProductsList extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className={commonColumnsStyles.App}>
<header className={commonColumnsStyles.AppHeader}>
<p>Products list</p>
<ul>
{this.props.productsInShop.map((currProduct, index) => (
<li key={index} onClick={() => this.addProduct(index)}>
{currProduct.nazwa}
</li>
))}
</ul>
</header>
</div>
);
}
}
export default ProductsList;
App
function App() {
const [productsList, setProductsList] = useState(produkty);
return (
<div className={styles.appWrapper}>
<AddProducts sendNewProductToParent={setProductsList} />
<div className={styles.columnsWrapper}>
<ProductsList
productsInShop={productsList}
/>
</div>
</div>
);
}
export default App;
I am trying to set up some functionality on this React component so that a user can add and remove empty radio button options to a page that a user can type text into. The only issue that I am having is that I am relatively new to React and am not 100% how to do this.
import React, { Component } from 'react';
class TextRadio extends Component {
constructor() {
super();
state = {
textValue: ""
}
};
handleInputChange = event => {
const value = event.target.value;
const name = event.target.name;
this.setState({
[name]: value
});
}
addBox = () => {
}
removeBox = () => {
}
render() {
return(
<div>
<div className="form-check">
<input className="form-check-input" type="radio" id="" name="" value="" />
<label className="form-check-label" for="">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
<div className="form-check">
<input className="form-check-input" type="radio" id="option" name="option" value="option" />
<label className="form-check-label" for="option">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
<div className="form-check">
<input className="form-check-input" type="radio" id="option" name="option" value="option" />
<label className="form-check-label" for="option">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
<button type="button" className="btn btn-primary" onClick={this.addBox}>
Add Option
</button>
<button type="button" className="btn btn-danger" onClick={this.removeBox}>
Remove Option
</button>
</div>
);
}
}
export default TextRadio;
The result that I am expecting to happen is to have it so the component can add and remove radio button options from the page depending on the button that the user presses
i was completed just your addBox and RemoveBox functions, i hope that's help you
import React, { Component } from "react";
class TextRadio extends Component {
constructor() {
super();
this.state = {
radioButtons: []
};
}
handleInputChange = event => {
const value = event.target.value;
const name = event.target.name;
};
addBox = () => {
this.setState(prevstate => {
let radioButtons = prevstate.radioButtons;
if (radioButtons.length === 0) {
radioButtons.push({
id: 1,
name: "radiobutton",
value: "test"
});
return {
radioButtons: radioButtons
};
} else {
radioButtons.push({
id: radioButtons[radioButtons.length - 1].id + 1,
name: "raiodButton_" + (radioButtons[radioButtons.length - 1].id + 1),
value: radioButtons[radioButtons.length - 1].value
});
return {
radioButtons: radioButtons
};
}
});
};
removeBox = () => {
this.setState(prevstate => {
let radioButtons = prevstate.radioButtons;
if (radioButtons.length !== 0) {
radioButtons.pop(radioButtons[radioButtons.length - 1]);
return {
radioButtons: radioButtons
};
} else {
return { radioButtons: radioButtons };
}
});
};
render() {
return (
<div>
<div className="form-check">
{this.state.radioButtons.map(radiobutton => {
return (
<div>
<input
className="form-check-input"
type="radio"
id={radiobutton.id}
name={radiobutton.name}
value={radiobutton.value}
/>
<label className="form-check-label" for="">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
);
})}
</div>
<button type="button" className="btn btn-primary" onClick={this.addBox}>
Add Option
</button>
<button
type="button"
className="btn btn-danger"
onClick={this.removeBox}
>
Remove Option
</button>
</div>
);
}
}
export default TextRadio;
https://codesandbox.io/embed/confident-browser-tmojp
I was playing around with your idea and made some changes in the code, just to show you an example, how you can dynamically create new components and store them in applications state and then render out to user based on their actions.
I created new component just for form UI: option, input field and remove button. If user clicks on the Add Option, new item of the component is added to application state and then render out. Remove button is used to remove Item from state.
class TextRadio extends Component {
state = {
optionInputs: []
};
addBox = () => {
const optionInputsUpdated = [
...this.state.optionInputs,
<OptionInput id={uuid.v4()} remove={this.removeBox} />
];
this.setState({ optionInputs: optionInputsUpdated });
};
removeBox = id => {
const optionInputsUpdated = this.state.optionInputs.filter(
item => item.props.id !== id
);
this.setState({ optionInputs: optionInputsUpdated });
};
render() {
return (
<div>
{this.state.optionInputs.map((optionInput, idx) => {
return (
<div key={idx} test="123">
{optionInput}
</div>
);
})}
<button type="button" className="btn btn-primary" onClick={this.addBox}>
Add Option
</button>
</div>
);
}
}
const OptionInput = props => {
return (
<div className="form-check">
<input
className="form-check-input"
type="radio"
id=""
name="radio"
value=""
/>
<label className="form-check-label" for="">
<input className="form-control" type="text" placeholder="" />
</label>{" "}
<button
type="button"
className="btn btn-danger"
onClick={() => props.remove(props.id)}
>
Remove Option
</button>
</div>
);
};
Hope this gives you better understanding, how to achieve your goal.
If you need additional help, just post a comment under this answer, and I will update demo to help you.
Here is DEMO I created from your code: https://codesandbox.io/s/nice-ganguly-s4wls
first you have to initialize an empty array state
this.state={
radioButtons : [{input:''}]
}
then in your return statement you have to loop through the radioButtons array and show the radio button with input
{
this.state.radioButtons.map(item => (
<div className="form-check">
<input className="form-check-input" type="radio" id="option" name="option" value="option" />
<label className="form-check-label" for="option">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
))
}
then in your addBox function append an object on every click
addBox = () => {
this.setState({radioButtons:[...this.state.radioButtons, {input:''}]})
}
function to remove a radio button object
removeBox = () => {
let radioArray = this.state.radioButtons
radioArray.pop()
this.setState({radioButtons:radioArray})
}
Final code Looks like this :
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component{
constructor(props){
super(props);
this.state={
radioButtons :[{input:''}]
}
}
addBox = () => {
this.setState({radioButtons:[...this.state.radioButtons, {input:''}]})
}
removeBox = () => {
let radioArray = this.state.radioButtons
radioArray.pop()
this.setState({radioButtons:radioArray})
}
render(){
return(
<div>
{
this.state.radioButtons.map(item => (
<div className="form-check">
<input className="form-check-input" type="radio" id="option" name="option" value="option" />
<label className="form-check-label" for="option">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
))
}
<button type="button" className="btn btn-primary" onClick={this.addBox}>
Add Option
</button>
<button type="button" className="btn btn-danger" onClick={this.removeBox}>
Remove Option
</button>
</div>
)
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
codepen Example
const AnswerGrid = React.createClass({
getInitialState: function(){
return {active: null};
},
radioClick: function(e){
this.setState({active: e.target.value});
},
render : function(){
return (
<li className="answerOption">
<input type="radio" onChange={this.radioClick} id={this.props.answer.answer_id} className="radioCustomButton" name={this.props.question_id} value={this.props.answer.answer_id}/><br/>
<label className="radioCustomLabel">{this.props.answer.title}</label>
</li>
);
}
});
This is my components call AnswerGrid
const QuestionGrid = React.createClass({
render : function(){
var rows = [];
console.log(this.props.active);
//console.log(this.props.question);
var question_id = this.props.question.question_id;
this.props.question.list_answer.map(function(answer){
rows.push(<AnswerGrid key={answer.answer_id} answer={answer} question_id={question_id} />);
});
return (
<div className="row">
<div className="col-md-12">
<h2 className="question"><b>{this.props.question.question_title}</b></h2>
<input type="type" className="rs" name="rs" value={this.props.active} /><br/>
<ul className="answerOptions">
{rows}
</ul>
<hr/>
</div>
</div>
);
}
});
I am using QuestionGrid to display list li with input from AnswerGrid
When i use value={this.props.active} at QuestionGrid , it not working.
Pls help me fix it
class Form extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const { Add, noteList } = this.props;
Add('this is title's value' , 'this is content's value');
}
render() {
const { handleSubmit, noteList: { list } } = this.props;
return (
<form onSubmit={handleSubmit(this.handleClick)}>
<div>
<Field className="title" name="title" component="input" type="text" />
</div>
<div>
<Field className="content" name="content" component="textarea" />
</div>
<div>
<button type="submit" onClick={(e) => { list ? this.handleClick : e.preventDefault(); }}>add</button>
</div>
</form>
);
}
}
when i click the button, i hope to get these two values into a function Add as two arguments to do something async, what should i do , help me please
The field values in handleClick function can can be obtained onSubmit as an object.
class Form extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(values) {
const { Add, noteList } = this.props;
Add(values.title , values.content);
}
render() {
const { handleSubmit, noteList: { list } } = this.props;
return (
<form onSubmit={handleSubmit(this.handleClick)}>
<div>
<Field className="title" name="title" component="input" type="text" />
</div>
<div>
<Field className="content" name="content" component="textarea" />
</div>
<div>
<button type="submit" onClick={(e) => { list ? this.handleClick : e.preventDefault(); }}>add</button>
</div>
</form>
);
}
}
I have created react components for a form. I passed data as props from GetSteps component to PageHeader component. Then I will able to use that data from the PageHeader. Also I passed same data from the GetSteps component to YourDetails component. But the data didn't pass to the YourDetails component. I cannot find the error. any help will be appreciated. Thank You.
class GetSteps extends React.Component {
constructor(props) {
super(props);
this.state = {data: ''}
}
// componentWillMount() {
// var lang = [];
// $.each(this.props.value.Languages, function(k, v) {
// return lang[k] = v;
// }.bind(this));
// this.setState({ data: this.props.value.Languages })
//
// console.log(this.state)
// }
render()
{
return (
<div className="page-content">
<div className="container">
<PageHeader title={this.props.value.Title} subtitle={this.props.value.SubTitle} f={this.props.value.PreferredLanguage}/>
<YourDetails title={this.props.value.Title} preferredlang={this.props.value.PreferredLanguage}></YourDetails>
</div>
</div>
);
}
}
this is where I pass data as props
function PageHeader(props){
return(
<div className="page-header">
<span className="page-header-icon icon-world"></span>
<h3 className="title">{props.title}</h3>
<h4 className="sub-title special">{props.subtitle}</h4>
</div>
);
}
this is where I get data from GetSteps component
class YourDetails extends React.Component {
constructor(props) {
super(props);
this.state = {
havePartner: 'yes',
UserFirstName: "",
TravelPartnerName: "",
AmwayLocation: "",
Title: this.props.title
};
console.log(this.props);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleOptionChange = this.handleOptionChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event){
console.log(this.state);
event.preventDefault();
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'radio' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleOptionChange(event){
this.setState({
havePartner: event.target.value
});
}
render() {
return (
<div className="main-content">
<form onSubmit={this.handleSubmit}>
<div className="field dropdown requiredField">
<label className="left">
Your prefered language
</label>
<div className="middleColumn">
<select name="language"
value={this.props.preferredlang}
onChange={this.handleInputChange}
className="dropdown requiredField align--left">
<option defaultValue value={this.props.preferredlang}>{this.props.preferredlang}</option>
<option value='g'></option>
<option value='fdg'></option>
<option value='df'></option>
<option value='dfg'></option>
</select>
</div>
</div>
<div className="field requiredField align--left">
<label>
Your First Name
</label>
<input
name="UserFirstName"
type="text"
onChange={this.handleInputChange} />
</div>
<div className="field optionset requiredField travel-partner align--left">
<label className="left">
Did you have a travel partner?
</label>
<ul className="optionset requiredField travel-partner align--left">
<li>
<input type="radio" value="yes" checked={this.state.havePartner === 'yes'} onChange={this.handleOptionChange} className="radio"/>
<label>Yes</label>
</li>
<li>
<input type="radio" value="no" checked={this.state.havePartner === 'no'} onChange={this.handleOptionChange } className="radio"/>
<label>No</label>
</li>
</ul>
</div>
<div className="field text requiredField align--left">
<label className="left">
Your travel partner's first name
</label>
<input className="text requiredField align--left"
name="TravelPartnerName"
type="text"
onChange={this.handleInputChange} />
</div>
<div className="field text">
<label className="left requiredField">*Required Field</label>
</div>
<div className="Actions">
<button type="submit" value="Submit" className="action nolabel " >
<span>Proceed to Day 1</span>
<span className="progress"></span>
</button>
</div>
</form>
</div>
);
}
}
this is where I didn't get data from GetSteps component
class Step extends React.Component {
constructor(props) {
super(props);
this.state = {data: ''}
}
componentWillMount() {
var link = document.getElementById('step').getAttribute('link');
$.getJSON( link+"VideoBuildForm", function( data, status ) {
fieldValues.PreferredLanguage = data.compilation.PreferredLanguage;
fieldValues.AmwayLocation = data.location;
fieldValues.Languages =
$.each(data.languages, function(k, v) {
return data.languages[k] = v;
});
fieldValues.Title = data.title;
fieldValues.SubTitle = data.subtitle;
this.setState({ data: fieldValues });
}.bind(this));
}
render() {
return (
<div>
<GetSteps value={this.state.data}/>
</div>
);
}
}
this is where I pass data to the GetSteps component
Try using props.title in your constructor instead of this.props.title.