How to dynamically add fields to a form on React - javascript

I make a simple react app.
I have a React form with field.
And we can add dynamically more and more same fields. And submit them together.
Maybe I can use for or while? But how?
And I must add dynamically fields name in form, and dynamically fields to object for submit.
class Form extends Component {
constructor(props) {
super(props);
this.state = {
task: '',
count: [1]
};
this.addTask = this.addTask.bind(this);
this.change = this.change.bind(this);
}
render() {
return (
<div>
<h1>Form</h1>
<form onSubmit = {this.onSubmit} method='post'>
<div>
{this.state.count.map((item, index) => {
return (
<div key={index}>
<input type='text' value={this.state.task} name='task' onChange={this.change} />
</div>
)
})}
</div>
<button onClick={this.addTask}>addTask</button>
<button>submit</button>
</form>
</div>
);
}
addTask(e) {
e.preventDefault();
this.setState({
count: [...this.state.count, '1']
})
}
onSubmit = (e) => {
e.preventDefault();
const {submit} = this.props;
submit({
task: this.state.task
});
this.setState({
task: ''
})
};
change(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
}
export default connect(null, { submit })(Form);

You can map through an array of your objects:
constructor(props) {
super(props);
this.state = {
task: '',
count: [1],
components: []
};
this.addTask = this.addTask.bind(this);
this.change = this.change.bind(this);
}
render() {
return (
<div>
<h1>Form</h1>
<form onSubmit = {this.onSubmit} method='post'>
<div>
{this.state.count.map((item, index) => {
return (
<div key={index}>
{this.state.components.map(comp => comp)}
</div>
)
})}
</div>
<button onClick={this.addTask}>addTask</button>
<button>submit</button>
</form>
</div>
);
}
addTask(e) {
e.preventDefault();
this.setState({
count: [...this.state.count, '1'],
components: this.state.components.concat(<input type='text' value={this.state.task} name='task' onChange={this.change} key={this.state.count.length + 1} />)
})
}

Related

How to automatically generate Input fields related to list of items React.JS

I'm having a problem in dynamically generating input fields in react. So I have a keys state and I need to have an input field for each key and also I have tried this link: How to implement a dynamic form with controlled components in ReactJS?
but the problem is that the my values state is empty so it will render nothing regarding keys and when I did it with this.ModifyList() it shows input fields regarding each key but it does not have onChange mehtod. the onChange method causes the error when using the this.createUI() .
Also in the end I would like to submit the values of input fields.
Is there any suggestion on how to solve this problem?
my Code Below:
export class FileUploadComponent extends Component {
constructor(props) {
super(props);
this.state = {
//Keys: [],
//values: [],
modify: { Keys: ['key1' , 'key2' , 'key3'], values: [] }
}
this.handleSubmit = this.handleSubmit.bind(this);
}
createUI() {
const { modify } = this.state;
const keys = modify.Keys
const values = modify.values
const val = keys.map(function (item, i) {
values.map(function (el, i) {
return <div key={i}>
<label>{item}</label>
<input type="text" onChange={this.handleChange.bind(this, i)} />
</div>
})
});
return val;
}
handleChange(event, i) {
const {modify} = this.state;
let values = [...modify.values];
values[i] = event.target.value;
this.setState({ values: values });
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.values.join(', '));
event.preventDefault();
}
ModifyList() {
const { modify } = this.state;
const keys = modify.Keys
const val = keys.map(function (item, i) {
return <div>
<label>{item}</label>
<input type="text" />
</div>
});
return val;
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
{/*this.ModifyList()*/}
{this.createUI()}
<input type="submit" className="btn btn-primary" value="Search !" />
<input type="submit" className="btn btn-primary" value="Edit !" />
</form>
</div>
)
}
}
export default FileUploadComponent
You have some some scope issue. One of the main difference between a fat-arrow function and a function declared with the function keyword is that the latter has its own scope, meaning that if you call this inside of it, you are referencing its scope.
In your createUI function, switch your functions to fat-arrow functions and you are all set. Just remember to bind your handle change function in your constructor.
export class FileUploadComponent extends Component {
constructor(props) {
super(props);
this.state = {
//Keys: [],
//values: [],
modify: { Keys: ["key1", "key2", "key3"], values: [""] }
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
createUI() {
const { modify } = this.state;
const keys = modify.Keys;
const values = modify.values;
const val = keys.map((item, i) => {
return values.map((el, i) => {
return (
<div key={i}>
<label>{item}</label>
<input
type="text"
onChange={(event) => this.handleChange(event, i)}
/>
</div>
);
});
});
return val;
}
handleChange(event, i) {
const { modify } = this.state;
let values = [...modify.values];
values[i] = event.target.value;
this.setState({ values: values });
}
handleSubmit(event) {
alert("A name was submitted: " + this.state.values.join(", "));
event.preventDefault();
}
ModifyList() {
const { modify } = this.state;
const keys = modify.Keys;
const val = keys.map(function (item, i) {
return (
<div>
<label>{item}</label>
<input type="text" />
</div>
);
});
return val;
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
{/*this.ModifyList()*/}
{this.createUI()}
<input type="submit" className="btn btn-primary" value="Search !" />
<input type="submit" className="btn btn-primary" value="Edit !" />
</form>
</div>
);
}
}

Toggle single element in React

I have one question. I created contact form in react and I want to collapsed only single contact, which was clicked. Toggle is method which should collapse it. And colapse is state. My problem is that when I click it affect all contact and all are collapsed. How can I improve it?
ContactBook.js
import React, { Component } from "react";
import Contact from "../Contact/Contact";
import "./ContactBook.css";
class ContactBook extends Component{
constructor(props){
super(props);
this.state = {
colapse :true,
contacts: [
{
id: 1,
name: 'Propulsion Academy',
address: 'Zurich',
avatar: 'propulsion-academy-logo.png'
},
{
id: 2,
name: 'Propulsion Academy',
address: 'Luzern',
avatar: 'propulsion-academy-logo.png'
},
{
id: 3,
name: 'Propulsion Academy',
address: 'Munich',
avatar: 'propulsion-academy-logo.png'
},
],
};
}
toggle=()=>{
const doesShow = this.state.colapse;
this.setState({colapse: !doesShow});
}
deleteContact=(contactIndex)=>{
//with slice method we create copy of an array
const contacts =this.state.contacts.slice();
contacts.splice(contactIndex, 1);
this.setState({contacts: contacts})
}
//get name from input
addName = e =>{
this.setState({
name: e.target.value,
})
}
//get address from input
addAddress = e =>{
this.setState({
address: e.target.value,
})
}
//update state on button click
handleSubmit = (e) =>{
e.preventDefault()
if(this.state.name && this.state.address) {
this.setState(state =>{
const newContact = {
id: Math.max(...state.contacts.map(c => c.id))+1,
name: this.state.name,
address: this.state.address,
}
return{
contacts:[...state.contacts, newContact]
}
})
}
}
render() {
return (
<div className="contactBook">
<form className ="addContact" >
<p>New Contact</p>
<label id="name"><p>Name</p><input type='text' id="name" onChange={this.addName}/></label>
<label id="address"><p>Address:</p><input type='text' id="address" onChange={this.addAddress} /></label>
<input type='file' name='file' />
<button type='submit' onClick= {this.handleSubmit}>SUBMIT</button>
</form>
<div className="contacts">
{this.state.contacts.map((contact, index) =>
< Contact key={contact.id} contact={contact} delete={()=>this.deleteContact(index)} colapse={this.state.colapse} toggle={this.toggle}/>)
}
</div>
</div>
);
}
};
export default ContactBook;
Contact.js
import React from "react";
import "./Contact.css";
import avatar from '../assets/user.png'
const Contact = (props) =>{
return (
<div className = "col" >
<img src={avatar} alt="avatar" onClick={props.toggle}/>
{props.colapse === true ?
<div>
<p>Name: {props.contact.name}</p>
<p>Address: {props.contact.address}</p>
<button onClick={props.delete}> Delete </button>
</div> : null
}
</div>
)
};
export default Contact;
I recommend to you to move the collapse and his method to the Contact component it self like this :
const Contact = (props) =>{
[collapse,setCollapse] = useState(true)
return (
<div className = "col" >
<img src={avatar} alt="avatar" onClick{()=>setCollape(prev=>!prev)}/>
{collapse === true ?
<div>
<p>Name: {props.contact.name}</p>
<p>Address: {props.contact.address}</p>
<button onClick={props.delete}> Delete </button>
</div> : null
}
</div>
)
};
In this component I created a state that will manage the collapse for each of the component the render in the map.
further more, the prev give you the last value you submit and it's best practice to use the prev instead of just setCollapse(!collapse)
You have one function for all contacts, and since you use .map() they will all behave the same, since toggling one toggles the state which is used to render all individual contacts. The solution would be to pass the selected contact in your state so your app actually knows which one is to be rendered! Hopefully that makes sense!
Good luck and let us know how things work out!
This is it:
ContactBook.js
import React, { Component } from "react";
import Contact from "./Contact";
// import "./ContactBook.css";
class ContactBook extends Component {
constructor(props) {
super(props);
this.state = {
contacts: [
{
id: 1,
name: "Propulsion Academy",
address: "Zurich",
avatar: "propulsion-academy-logo.png",
colapse: true
},
{
id: 2,
name: "Propulsion Academy",
address: "Luzern",
avatar: "propulsion-academy-logo.png",
colapse: true
},
{
id: 3,
name: "Propulsion Academy",
address: "Munich",
avatar: "propulsion-academy-logo.png",
colapse: true
}
]
};
}
// toggle = () => {
// const doesShow = this.state.colapse;
// this.setState({ colapse: !doesShow });
// };
deleteContact = contactIndex => {
//with slice method we create copy of an array
const contacts = this.state.contacts.slice();
contacts.splice(contactIndex, 1);
this.setState({ contacts: contacts });
};
togglecontact = contactIndex => {
let contacts = this.state.contacts.slice();
contacts[contactIndex].colapse = !contacts[contactIndex].colapse;
this.setState({ contacts: contacts });
};
//get name from input
addName = e => {
this.setState({
name: e.target.value
});
};
//get address from input
addAddress = e => {
this.setState({
address: e.target.value
});
};
//update state on button click
handleSubmit = e => {
e.preventDefault();
if (this.state.name && this.state.address) {
this.setState(state => {
const newContact = {
id: Math.max(...state.contacts.map(c => c.id)) + 1,
name: this.state.name,
address: this.state.address
};
return {
contacts: [...state.contacts, newContact]
};
});
}
};
render() {
return (
<div className="contactBook">
<form className="addContact">
<p>New Contact</p>
<label id="name">
<p>Name</p>
<input type="text" id="name" onChange={this.addName} />
</label>
<label id="address">
<p>Address:</p>
<input type="text" id="address" onChange={this.addAddress} />
</label>
<input type="file" name="file" />
<button type="submit" onClick={this.handleSubmit}>
SUBMIT
</button>
</form>
<div className="contacts">
{this.state.contacts.map((contact, index) => (
<Contact
key={contact.id}
contact={contact}
togglecontact={() => this.togglecontact(index)}
delete={() => this.deleteContact(index)}
colapse={this.state.colapse}
toggle={this.toggle}
/>
))}
</div>
</div>
);
}
}
export default ContactBook;
contactbook.js
import React from "react";
// import "./Contact.css";
const Contact = props => {
let buffer;
props.contact.colapse === true
? (buffer = (
<div>
<p>Name: {props.contact.name}</p>
<p>Address: {props.contact.address}</p>
<button onClick={props.delete}> Delete </button>
</div>
))
: null;
return (
<div className="col">
<img
onClick={props.togglecontact}
src="data:image/jpeg;base64,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"
/>
{buffer}
</div>
);
};
export default Contact;
can check it at:https://stackblitz.com/edit/react-mrvrr1?file=src%2FContact.js
only Toggling single element.

Not able to type in input field

Hi I write a small component
export default class TextInput extends React.Component {
constructor(props) {
super(props);
this.onKeyPress = this.onKeyPress.bind(this);
this.state = { tags: [], value: "" };
}
onKeyPress(e) {
if (e.key === "Enter") {
this.setState({ tags: [...this.state.tags, e.target.value], value: "" });
}
}
render() {
return (
<div>
<div styleName="fieldset width-95">
<label>Tags</label>
<div>
<div>
{this.state.tags.map(tag => (
<span>{tag}</span>
))}
</div>
<input
type="text"
onKeyPress={this.onKeyPress}
value={this.state.value}
/>
</div>
</div>
</div>
);
}
}
after writing 1st time when I enter.it creates a tag but after that, I can't type anything in the input field. can anyone please explain how to enable typing so that I will be able to create another tag.
You also need an onChange handler as well to update state on user-input. Otherwise this.state.value will never update.
class TextInput extends React.Component {
constructor(props) {
super(props);
this.onKeyPress = this.onKeyPress.bind(this);
this.state = { tags: [], value: "" };
}
onKeyPress(e) {
if (e.key === "Enter") {
this.setState({ tags: [...this.state.tags, e.target.value], value: "" });
}
}
handleOnChange = e => {
this.setState({
value: e.target.value
});
};
render() {
return (
<div>
<div styleName="fieldset width-95">
<label>Tags</label>
<div>
<div>
{this.state.tags.map(tag => (
<span>{tag}</span>
))}
</div>
<input
type="text"
onChange={this.handleOnChange}
onKeyPress={this.onKeyPress}
value={this.state.value}
/>
</div>
</div>
</div>
);
}
}
See working sandbox: https://codesandbox.io/s/serene-https-t02h2
The problem is that you forgot to add an onClick event for the input, this will handle when you type something in it (You only need to update the state every time the onChange event is fire), like this (I've also made an update to a more ES6 syntax):
import React, { Component } from 'react';
export default class TextInput extends Component {
state = { tags: [], value: "" };
onKeyPress = e => {
if (e.key === "Enter") {
this.setState({ tags: [...this.state.tags, e.target.value], value: "" });
}
};
onClick = e => {
if (e && e.target && e.target.value) {
this.setState({ value: e.target.value });
}
};
render() {
const { tags, value } = this.state;
return (
<div>
<div styleName="fieldset width-95">
<label>Tags</label>
<div>
<div>
{tags.map(tag => (
<span>{tag}</span>
))}
</div>
<input
type="text"
onChange={this.onClick}
onKeyPress={this.onKeyPress}
value={value}
/>
</div>
</div>
</div>
);
}
}
You can try below code.And you remove value which you are set on state.And use onChange method.
import React, { Component } from 'react';
export default class TextInput extends React.Component {
constructor(props) {
super(props);
this.onKeyPress = this.onKeyPress.bind(this);
this.state = { tags: [] };
}
onKeyPress(e) {
if (e.key === "Enter") {
this.setState({ tags: [...this.state.tags, e.target.value], value:
e.target.value });
console.log("tag:",this.state.tags)
}
}
handleInputChange = (event) => {
this.setState({ [event.target.name]: event.target.value });
};
render() {
return (
<div>
<div styleName="fieldset width-95">
<label>Tags</label>
<div>
<div>
{this.state.tags.map(tag => (
<span >{tag}</span>
))}
</div>
<input
type="text"
onKeyPress={this.onKeyPress}
name="demo"
value={this.state.value}
onChange={this.handleInputChange}
/>
</div>
</div>
</div>
);
}
}

Checking if input is empty on form validation, why is it always false?

I am trying to run a function on a Todo list that when a todo is added. A function will check the input box to check if its empty and if its empty, not do anything.
However, after using the function to check if the input is empty, it always returns False even when its empty. Where is the bug here?
The function name in question is "checkInput()" and it runs from the main submit button on the page
import React from "react";
import "./App.css";
import { isTemplateElement } from "#babel/types";
class TodoListt extends React.Component {
state = {};
constructor(props) {
super(props);
this.state = {
userInput: "",
list: [],
};
}
changeUserInput(input) {
this.setState({
userInput: input
})
}
addToList() {
const { list, userInput } = this.state;
this.setState({
list: [...list, {
text: userInput, key: Date.now(), done: false
}],
userInput: ''
})
}
handleChecked(e, index) {
console.log(e.target.checked);
const list = [...this.state.list];
list[index] = { ...list[index] };
list[index].done = e.target.checked;
this.setState({
list
})
}
checkInput() {
console.log(this.state.userInput);
userInput: '' ? console.log("True") : console.log("False")
}
render() {
return (
<div className="to-do-list-main">
<input
onChange={(e) => this.changeUserInput(e.target.value)}
value={this.state.userInput}
type="text"
/>
<button onClick={() => { this.checkInput(); { this.addToList(this.state.userInput) } }}>Add todo</button>
{this.state.list.map((list, index) => (
<div className="form">
<ul>
<li><input type="checkbox" onChange={(e) => this.handleChecked(e, index)} />
<span style={{ textDecoration: list.done ? 'line-through' : 'inherit' }}>
{list.text}
</span>
</li>
</ul>
</div>
))}
</div>
);
}
}
export default TodoListt;

React - render class component when checkbox is selected

I have a select dropdown that when selected renders a checkbox group using <FieldArray> from formik
<FieldArray
name="fields"
render={arrayHelpers => (
<div>
{fields.map(field => (
<div key={field.name}>
<label>
<input
name="fields"
type="checkbox"
value={field.name}
onChange={e => {
if (e.target.checked) arrayHelpers.push(field.name);
else {
const idx = fields.indexOf(field.name);
arrayHelpers.remove(idx);
}
}}
/>{" "}
{field.name}
</label>
</div>
))}
</div>
)}
/>
So in the onChange method I need for each checkbox that is selected to render a class component that has additional input fields that are tied to the field name. For example, the size and length options need to chosen for each checkbox that is selected.
class FieldInputs extends React.Component {
constructor(props) {
super(props);
this.state = {
lengthType: "",
size: [],
};
this.lengthTypeChange = this.lengthTypeChange.bind(this);
this.onSizeChange = this.onSizeChange.bind(this);
}
lengthTypeChange = lengthType => {
//handle change method for lengthType
this.setState({ lengthType });
console.log("LengthType selected: ", lengthType);
};
onSizeChange = e => {
this.setState({ [e.target.name]: e.target.value });
console.log([e.target.value]);
};
render() {
return (
<div>
<h2>
{" "}
These are the input fields for each field name checkbox selected.{" "}
</h2>
<div>
<Select
id="color"
options={lengthTypeOptions}
isMulti={false}
value={lengthType}
onChange={this.lengthTypeChange}
onBlur={this.handleBlur}
placeholder={"Select a lengthType..."}
/>
</div>
<div>
<label>Size:</label>
<input
value={this.state.size}
onChange={this.onSizeChange}
type="number"
name="size"
min="1"
placeholder="1"
required
/>
</div>
</div>
);
}
}
For instance each field.name is rendered to a checkbox and should have a select dropdown for lengthType and input for size like so:
{
field.name: {
size: 1,
lengthType: "Fixed"
}
}
So I have the component designed, just need to render it to each checkbox that is selected.
How can I pass the FieldInput component to the checkboxes based on if they are toggled or not?
Inside the <Myselect> component, you can find the <FieldArray> component, using the onChange method
onChange={e => {
if (e.target.checked) {
arrayHelpers.push(field.name);
When's it's checked it also needs to render <FieldInputs>
Here's a link to a sandbox with the classes/components described above
I made many changes this is the full code
import "./helper.css";
import { MoreResources, DisplayFormikState } from "./helper";
import React from "react";
import { render } from "react-dom";
import { Formik, FieldArray } from "formik";
import * as Yup from "yup";
import axios from "axios";
import Select from "react-select";
var MockAdapter = require("axios-mock-adapter");
var mock = new MockAdapter(axios);
mock.onGet("/dataschemas").reply(200, {
data: [
{
id: "2147483602",
selfUri: "/dataschemas/2147483602",
name: "Phone Data"
}
]
});
mock.onGet("/dataschemas/2147483602").reply(200, {
data: {
id: "2147483602",
selfUri: "/dataschemas/2147483602",
type: "DataSchema",
name: "Phone Record",
fields: [
{
name: "action"
},
{
name: "callee"
},
{
name: "caller"
},
{
name: "duration"
},
{
name: "message"
},
{
name: "time_stamp"
}
]
}
});
const lengthTypeOptions = [
{ value: "fixed", label: "Fixed" },
{ value: "variable", label: "Variable" }
];
class FieldInputs extends React.Component {
constructor(props) {
super(props);
this.state = {
lengthType: "",
size: []
};
this.lengthTypeChange = this.lengthTypeChange.bind(this);
this.onSizeChange = this.onSizeChange.bind(this);
}
lengthTypeChange = lengthType => {
//handle change method for lengthType
this.setState({ lengthType }, () => {
this.props.update(this.props.name, this.state);
});
//console.log("LengthType selected: ", lengthType);
};
onSizeChange = e => {
this.setState({ [e.target.name]: e.target.value }, () => {
this.props.update(this.props.name, this.state);
});
//console.log([e.target.value]);
};
render() {
const { lengthType } = this.state;
return (
<div>
<h2>
{" "}
These are the input fields for each field name checkbox selected.{" "}
</h2>
<div>
<Select
id="color"
options={lengthTypeOptions}
isMulti={false}
value={lengthType}
onChange={this.lengthTypeChange}
onBlur={this.handleBlur}
placeholder={"Select a lengthType..."}
/>
</div>
<div>
<label>Size:</label>
<input
value={this.state.size}
onChange={this.onSizeChange}
type="number"
name="size"
min="1"
placeholder="1"
required
/>
</div>
</div>
);
}
}
const App = () => (
<div className="app">
<h1>Formik Demo</h1>
<Formik
initialValues={{
querySchemaName: "",
schemas: [],
fields: [],
selectorField: "",
lengthType: "",
size: []
}}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 500);
}}
validationSchema={Yup.object().shape({
querySchemaName: Yup.string()
.required("QuerySchema name is required!")
.min(3, "Please enter a longer name")
.max(50, "Please ener a shorter name")
})}
>
{props => {
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
handleReset,
setFieldValue,
setTouchedValue
} = props;
return (
<form onSubmit={handleSubmit}>
<label htmlFor="querySchemaName" style={{ display: "block" }}>
QuerySchema Name:
</label>
<input
id="querySchemaName"
placeholder="Example -- QuerySchema1"
type="text"
value={values.querySchemaName}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.querySchemaName && touched.querySchemaName
? "text-input error"
: "text-input"
}
/>
{errors.querySchemaName && touched.emaquerySchemaNameil && (
<div className="input-feedback">{errors.querySchemaName}</div>
)}
<MySelect
value={values.schemas}
onChange={setFieldValue}
options={values.schemas}
onBlur={setTouchedValue}
error={errors.topics}
touched={touched.topics}
/>
<button
type="button"
className="outline"
onClick={handleReset}
disabled={!dirty || isSubmitting}
>
Reset
</button>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
<DisplayFormikState {...props} />
</form>
);
}}
</Formik>
<MoreResources />
</div>
);
class MySelect extends React.Component {
constructor(props) {
super(props);
this.state = {
schemas: [],
fields: [],
selectorField: "",
checked: []
};
this.handleChange = this.handleChange.bind(this);
this.updateSelectorField = this.updateSelectorField.bind(this);
}
componentDidMount() {
axios.get("/dataschemas").then(response => {
this.setState({
schemas: response.data.data
});
//console.log(this.state.schemas);
});
}
handleChange = value => {
// this is going to call setFieldValue and manually update values.dataSchemas
this.props.onChange("schemas", value);
const schema = this.state.schemas.find(
schema => schema.name === value.name
);
if (schema) {
axios.get("/dataschemas/2147483602").then(response => {
this.setState({
fields: response.data.data.fields
});
//console.log("fields are: " + this.state.fields);
});
}
};
updateSelectorField = value => {
this.props.onChange("selectorField", value);
};
update = (name, value) => {
this.setState(prev => {
var arr = prev.checked;
var de = null;
for (var j = 0; j < arr.length; j++) {
if (arr[j].name === name) {
de = j;
}
}
arr[de] = Object.assign(arr[de], value);
console.log(arr);
return { checked: arr };
});
};
handleBlur = () => {
// this is going to call setFieldTouched and manually update touched.dataSchemas
this.props.onBlur("schemas", true);
};
checker = field => {
var d = -1;
for (let j = 0; j < this.state.checked.length; j++) {
if (this.state.checked[j].name === field.name) {
d = j;
}
}
if (d >= 0) {
return (
<FieldInputs
key={field.name + 1}
name={field.name}
update={this.update}
/>
);
} else {
return null;
}
};
change = e =>{
var arr = this.state.checked;
var de = -1;
for (var j = 0; j < arr.length; j++) {
if (arr[j].name === e) {
de = j;
}
}
if(de >= 0){
delete arr[de];
}else{
var arr = arr.concat([
{
name: e,
size: 1,
lengthType: "Fixed"
}
])
}
var nar = [];
for(let i=0; i<arr.length; i++){
if(typeof arr[i] !== "undefined"){
nar.push(arr[i])
}
}
this.setState({checked: nar});
}
render() {
const schemas = this.state.schemas;
const fields = this.state.fields;
return (
<div style={{ margin: "1rem 0" }}>
<label htmlFor="color">
DataSchemas -- triggers the handle change api call - (select 1){" "}
</label>
<Select
id="color"
options={schemas}
isMulti={false}
value={schemas.find(({ name }) => name === this.state.name)}
getOptionLabel={({ name }) => name}
onChange={this.handleChange}
onBlur={this.handleBlur}
placeholder={"Pick a DataSchema..."}
/>
<label htmlFor="color">Selector Field - (select 1) </label>
<Select
id="color"
options={fields}
isMulti={false}
value={fields.find(({ name }) => name === this.state.name)}
getOptionLabel={({ name }) => name}
onChange={this.updateSelectorField}
placeholder={"Select a Selector Field..."}
/>
{!!this.props.error && this.props.touched && (
<div style={{ color: "red", marginTop: ".5rem" }}>
{this.props.error}
</div>
)}
<div>
<FieldArray
name="fields"
render={arrayHelpers => (
<div>
{fields.map(field => (
<React.Fragment>
<div key={field.name}>
<label>
<input
name="fields"
type="checkbox"
value={field.name}
onChange={(e) => {
this.change(field.name)
if (e.target.checked) {
arrayHelpers.push(field.name);
} else {
const idx = fields.indexOf(field.name);
arrayHelpers.remove(idx);
}
}}
/>{" "}
{field.name}
</label>
</div>
{this.checker(field)}
</React.Fragment>
))}
</div>
)}
/>
</div>
</div>
);
}
}
render(<App />, document.getElementById("root"));
here is the codesandbox

Categories

Resources