Editable Form ReactJS - reactjs

import React, { Component } from 'react';
let _ = require('lodash');
import {bindActionCreators} from "redux";
import {connect} from 'react-redux';
import {fetchedBeaconsEdit} from '../../actions/';
import {editBeacon} from '../../actions/index';
// TODO - come up with a decent name
class InfoRow extends Component {
render() {
return (
<tr>
<td>
{ this.props.beacon === "name"
|| this.props.beacon === "major"
|| this.props.beacon === "minor"
|| this.props.beacon === "beaconType" ?
<span>{this.props.beacon}<span className="font-css top">*</span></span>:
<span>{this.props.beacon}</span>
}
</td>
<td>
{ this.props.beacon !== "beaconType" &&
this.props.beacon !== "uuid" &&
this.props.beacon !== "status" &&
this.props.beacon !== "store"&&
this.props.beacon !== "group" ?
<div>
<input type="text"
className="form-control"
defaultValue={this.props.beaconValue}
name={this.props.beaconValue}
onChange={(e) =>this.props.handleInputChangeProp(e.target.value)}
/></div>:
this.props.beacon === "uuid" && this.props.beacon === "status" && this.props.beacon=== "store"?
<span></span>:
this.props.beacon === "beaconType"?
<select defaultValue={this.props.beaconValue} name={this.props.beaconValue} className="form-control" onChange={(e) =>this.props.handleInputChangeProp(e.target.value)}>
<option name="ibeacon">IBEACON</option>
<option name="eddystone">EDDYSTONE</option>
</select>:this.props.beaconValue
}
</td>
</tr>
)
}
}
class BeaconEdit extends Component {
constructor(props){
super(props);
this.state = {
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleInputChange(beacon, value) {
this.setState({
[beacon]: value
});
}
handleClick = () =>{
Object.keys(this.props.ebcn).map((key)=> {
if (this.state[key] !== undefined) {
this.props.editBeaconGroup[key]=this.state[key];
}
})
this.props.handleSubmitProp(this.props.editBeaconGroup);
}
render() {
const rows = [];
let a = this.props.ebcn;
Object.keys(this.props.ebcn).map((keyName, keyIndex) =>{
if (keyName === "store" || keyName === "group") {
return rows.push(<InfoRow beacon={keyName} beaconValue={a[keyName].name.toString()} name={this.state[keyName]} key={keyIndex} handleInputChangeProp={(inp) =>this.handleInputChange(keyName, inp)}/>);
}else{
return rows.push(<InfoRow beacon={keyName} beaconValue={a[keyName].toString()} name={this.state[keyName]} key={keyIndex} handleInputChangeProp={(inp) =>this.handleInputChange(keyName, inp)}/>);
}
});
return (
<div className="col-md-6">
<div className="">
<table className="table table-clear">
<tbody>
{rows}
</tbody>
</table>
</div>
<div className="px-1" >
<button className="btn btn-sm btn-info btn-size btn-block" onClick={this.handleClick}>Save</button>
</div>
</div>
)
}
}
class BeaconDetailEditComponent extends Component {
constructor(props){
super(props);
this.state = {
editbeacons: {}
};
this.handleSubmit = this.handleSubmit.bind(this);
this.validateName = this.validateName.bind(this);
this.validateMajor = this.validateMajor.bind(this);
this.validateMinor = this.validateMinor.bind(this);
}
validateMinor = (minor) => {
var re = /^[0-9]+$/;
return re.test(minor);
}
validateMajor = (major) => {
var re = /^[0-9]+$/;
return re.test(major);
}
validateName = (name) => {
var re = /^[A-Za-z ]+$/;
return re.test(name);
};
handleSubmit (beaconedited) {
console.log(beaconedited.name);
if (!this.validateName(beaconedited.name)) {
alert('Name can not be an integer')
}
else if (!this.validateMajor(beaconedited.major)) {
alert('Major number can only be an integer')
}
else if (beaconedited.major.length > 5) {
alert('Major number can not exceed 5 digits')
}
else if (!this.validateMinor(beaconedited.minor)) {
alert('Minor number can only be an integer')
}
else if (beaconedited.major > 65535) {
alert('Major number can not exceed the limit of 65535')
}
else if (beaconedited.minor > 65535) {
alert('Minor number can not exceed the limit of 65535')
}
else {
this.props.editBeacon(beaconedited, this.props.location.query.id);
}
}
componentWillMount = () => {
this.props.fetchedBeaconsEdit(this.props.location.query.id);
};
render() {
return (
<div className="container px-3 mr-3">
<div>
<div className="col-md-6 col-md-offset-3"><h1>Edit Beacon Information</h1></div>
</div>
<br/>
<br/>
{ this.props.ebcn != null?
<div>
<BeaconEdit ebcn={this.props.ebcn} handleSubmitProp={this.handleSubmit} editBeaconGroup={this.state.editbeacons}/>
</div> :
<center><img className="gif-size" src={'img/avatars/default.gif'} alt="Loading"/></center>
}
</div>
)
}
}
function mapStateToProps(state) {
return {
eBeacon: state.eBeacon,
ebcn: state.beacons
}
}
function matchDispatchToProps(dispatch){
return bindActionCreators({editBeacon: editBeacon, fetchedBeaconsEdit: fetchedBeaconsEdit}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(BeaconDetailEditComponent);
i had provided the code snippet
what i am doing is
i had fetched the values from the server and were shown in the fields and I'm making this page as editable form
what i want to do is now to take the values changed or changed by the user and to print them in console.
i had applied handleInputChange and its showing changed values while changing but i want to see those values in console on button click as well
how to do it?

First, the manner your are changing the state is not good. You are setting the new state to have only a single attribute which is the name and value of the field which is modified.
Coming to how to print the value of the field you are modifying, you should do a console log in the handle change method.
That said, your code should look like:
handleInputChange(event) {
//const {name, value} = event.target;
//const oldState = this.state;
//const newState = Object.assign({},oldState,{[name]:value});
//this.setState(newState);
this.setState({
[event.target.name]: event.target.value
});
//printing the input name and value as it is being modified.
console.log(name + ":" + value);
//if you want to see the value of the current state uncomment the line below
//console.log("State=" + JSON.stringify(newState));
}
printState = () => {
console.log("State="+JSON.stringify(this.state));
}
For more insight on how to modify objects with assign method see: MDN doc on Object.assign()
If you want to print the current state on clicking the save button then add the onClick attribute to the button as shown in the code below:
<button value="Save" onClick={this.printState}/>

Change InfoRow to
class InfoRow extends Component {
render() {
return (
<tr>
<td>
{this.props.beacon}
</td>
<td>
<input type="text"
className="form-control"
defaultValue={this.props.beaconValue}
value={this.props.name}
name={this.props.beaconValue}
onChange={(e) =>this.props.handleInputChangeProp(e.target.value)}
/>
</td>
</tr>
)
}
}
and render to
Object.keys(this.props.ebcn).map((keyName, keyIndex) =>{
return rows.push(<InfoRow beacon={keyName} beaconValue={a[keyName].toString()} name={this.state[keyName]} key={keyIndex} handleInputChangeProp={(inp) =>this.handleInputChange(keyName, inp)}/>);
});
and
handleInputChange(beacon, value) {
this.setState({
[beacon]: value
});
}
and handleClick to
handleClick = () =>{
Object.keys(this.props.ebcn).map((key)=> {
console.log(this.state[key]);
}))
}

Related

Unable to check and uncheck checkbox in reactjs

When i choose check in checkbox the value should be inserted in hobby array and when i unchecked the value should be removed from array
value is inserting and removing from array but i am not able to check and unchecked the checkbox visually. but in state it's working.
const ContactForm = () => {
const [state,setContact] = useState({
userId:"",
firstName:"",
lastName:"",
jobTitleName:"",
employeeCode:"",
emailAddress:"",
phoneNumber:"",
country:"",
status:"active",
hobbies:[]
})
const onChange = (event)=>{
setContact({...state,[event.target.name]: event.target.value});
}
e.preventDefault();
}
return (
<div className="form-group mb-2">
<label className="mb-0 font-weight-bold">Hobbies</label>
<div className="form-check">
<input type="checkbox" value="Basketball" onChange={(e)=>{
if(e.target.checked)
{
state.hobbies.push(e.target.value)
console.log(state.hobbies)
}else {
var index = state.hobbies.indexOf(e.target.value);
if (index > -1) {
state.hobbies.splice(index, 1);
console.log(state.hobbies)
}
}
}
}
/>
<label className="form-check-label">
Basketball
</label>
</div>
<div className="form-check">
<input type="checkbox" value="Vollyball" onChange={(e)=>{
if(e.target.checked)
{
state.hobbies.push(e.target.value)
console.log(state.hobbies)
}else {
var index = state.hobbies.indexOf(e.target.value);
if (index > -1) {
state.hobbies.splice(index, 1);
console.log(state.hobbies)
}
}
}
}
/>
<label className="form-check-label">
Vollyball
</label>
</div>
</div>
)
For your reference sample application
how to use multiple checkbox and adding multiple values.
const checkboxes = [
'Cricket', 'Footbal', 'Tennis'
];
class App extends Component {
state = {
checkedItems: new Map(),
}
handleChange=(e)=> {
const item = e.target.name;
const isChecked = e.target.checked;
this.setState(prevState => ({ checkedItems: prevState.checkedItems.set(item, isChecked) }));
}
render() {
console.log(this.state.checkedItems)
return (
<>
{
checkboxes.map(item => (
<label key={item}>
{item}
<input type="checkbox"
checked={this.state.checkedItems.get(item)}
onChange={this.handleChange}
name={item}/>
</label>
))
}
</>
);
}
}
You are mutating state. Your updating state value witout useState's method setContact.
Please find working code here: https://codesandbox.io/s/great-diffie-giqgr
When you mutate state or change state value without useState method or setState it won't re-render component even though your state value is changed. You onChange should be like this.
if (e.target.checked) {
const newState = {
...state,
hobbies: [...state.hobbies, e.target.value]
};
setContact(newState);
console.log("** checked", newState);
// state.hobbies.push(e.target.value);
// console.log(state.hobbies);
} else {
const newState = {
...state,
hobbies: state.hobbies.filter(prev => prev !== e.target.value)
};
console.log("** unchecked", newState);
setContact(newState);
}

Disable submit button if both radio button and text input are blank

Given a react form with multiple radio buttons and a text input that is visible only when the other option radio button is selected, I currently have the submit button disabled until a radio button is selected. However, if the other option is selected, the submit button will still work even if there is no text in the input field associated with it. How can I check the length of the input box if and only if the other option is selected?
class CancelSurvey extends React.Component {
constructor (props) {
super(props)
this.state = {
reasons: [],
reason: {},
otherReasonText: undefined,
submitting: false
}
this.processData = this.processData.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.setReason = this.setReason.bind(this)
this.setOtherReasonText = this.setOtherReasonText.bind(this)
this.otherReason = {
reason_id: 70,
client_reason: 'other'
}
}
componentDidMount () {
this.fetchSurvey()
}
/**
* Fetch reasons
*/
fetchSurvey (cb) {
superagent
.get('/api/user/survey')
.then(this.processData)
}
processData (data) {
this.setState({ reasons: data.body })
}
async handleSubmit (e) {
e.preventDefault()
await this.setState({ submitting: true })
const { reason, otherReasonText } = this.state
superagent
.post('/api/user/survey')
.send({
optionId: reason.reason_id,
optionText: reason.client_reason,
otherReasonText
})
.then(async (data) => {
await this.setState({ submitting: false })
if (data.body.success) {
this.props.setStep(OFFER)
}
})
}
setOtherReasonText (e) {
this.setState({ otherReasonText: e.target.value })
}
setReason (reason) {
this.setState({ reason })
}
/**
* render
*/
render (props) {
const content = this.props.config.contentStrings
const reasons = this.state.reasons.map((reason, i) => {
return (
<div
className='form-row'
key={i}>
<input type='radio'
id={reason.reason_id}
value={reason.client_reason}
name='reason'
checked={this.state.reason.reason_id === reason.reason_id}
onChange={() => this.setReason(reason)} />
<label htmlFor={reason.reason_id}>{reason.client_reason}</label>
</div>
)
})
return (
<div className='cancel-survey'>
<form className='cancel-survey-form'>
{ reasons }
<div className='form-row'>
<input
type='radio'
id='other-option'
name='reason'
onChange={() => this.setReason(this.otherReason)} />
<label htmlFor='other-option'>
Other reason
</label>
</div>
{ this.state.reason.reason_id === 70 &&
<div>
<input
className='valid'
type='text'
id='other-option'
name='other-text'
placeholder="placeholder"
onChange={this.setOtherReasonText} />
</div>
}
<div className='button-row'>
<button
disabled={!this.state.reason.client_reason}
className={btnClassList}
onClick={this.handleSubmit}>
<span>Submit</span>
</button>
</div>
</form>
</div>
)
}
}
export default CancelSurvey
disabled={
!this.state.reason.client_reason
||
(this.state.reason.client_reason === 'other' && !this.state.otherReasonText)
}
If you want to make sure otherReasonText is not just empty spaces, use otherReasonText: '' as initial state then check !this.state.otherReasonText.trim().
Do some conditional logic before like:
const reasonId = this.state.reason.reason_id;
const otherReasonText = this.state.otherReasonText;
const clientReason = this.state.reason.client_reason;
const shouldDisableButton = !clientReason || (reasonId === 70 && otherReasonText.length === 0)
...
<div className='button-row'>
<button
disabled={shouldDisableButton}
className={btnClassList}
onClick={this.handleSubmit}>
<span>Submit</span>
</button>
</div>

Input does not show entered value in react

class IndividualPsid extends Component {
constructor(props) {
super(props);
this.state = {
editData: false,
newSkuid: this.props.SkuId
}
this.updateState = this.updateState.bind(this);
}
updateState(e) {
const psid = e.target.value;
this.setState({ newSkuid: psid }, () => {
this.props.onPsidChange(this.props.id, this.state.newSkuid);
});
}
render() {
let member = '';
if (this.props.editingProp) {
member = (
<div>
<input value={this.state.newSkuid} key={this.props.SkuId + uuidv4()} onChange={this.updateState}
className="skuid col-xs-7" />
</div>
)
}
else {
member = (
<div key={this.props.SkuId + uuidv4()} className="skuid col-xs-7" >{this.props.SkuId}</div>
)
}
return (
<div className="row" >
<div className="skuname col-xs-5">{this.props.SkuName}</div>
{member}
</div>);
}
}
export default IndividualPsid;
Above is my child component code(Psid.js). When I click on Edit button, the input box shows , then I type something in the input box it does not show the typed number but when I click on save it shows the updated part. So basically according to my knowledge this.state.newSkuid does not update in the value of input. And below is my parent file (Category.js) that renders the IndividualPsid.
edit(skuList) {
if (this.state.editing == false) {
this.setState({
text: 'SAVE',
editing: true
});
}
else {
this.setState({
text: 'EDIT',
editing: false
});
this.props.edit_menu_items_api(this.state.changedSkus);
}
this.render();
}
render() {
return (
<button className="edit" onClick={() =>
this.edit(this.props.categoryData.productList[0].brandProductSkuList)}>
{this.state.text}</button>
)
}
It works if I put the logic of diplaying the input box on edit button click in componentWillReceiveProps function.
Here is my code of my child component.
componentWillMount() {
this.member = <div key={this.props.skuId} className="skuid col-xs-7" >{this.props.skuId}</div>
}
componentWillReceiveProps(nextProps) {
if (this.props.editingProp !== nextProps.editingProp && nextProps.editingProp) {
this.member = <div className="skuid col-xs-7">
<input defaultValue={this.state.newSkuid} key={this.props.skuId} onChange={this.updateState}
onBlur={() => { this.props.onPsidChange(this.props.id, this.state.newSkuid) }} />
</div>
} else if (this.props.editingProp !== nextProps.editingProp && !nextProps.editingProp) {
this.member = <div key={this.props.skuId} className="skuid col-xs-7" >{this.props.skuId}</div>
}
this.setState({ editData: nextProps.editingProp });
}
render() {
return (
<div className="row" >
<div className="skuname col-xs-5">{this.props.skuName}</div>
{this.member}
</div>);
}

Unable to Type and update state of input element in react

I have a text box and on typing a name i get a list of options via an api call. I then populate a list and on click of a list item i am trying to fill the text in the input box. Firstly when i add a value prop to the input element i am unable to to type anything in the text box. Also on clicking the list item the value of the text doesnt update. Can someone tell me what im doing wrong here
class AutoCompleteSearch extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: []
}
}
autoSearchInputChange(e) {
let searchValue = e.target.value;
if (!searchValue.trim()) {
this.setState({ value : '', suggestions: [] })
return ;
}
if (searchValue.length >= 3) {
setTimeout(() => {
searchDoctorByName(searchValue).then((response) => {
this.setState({ value : searchValue, suggestions: response.data })
})}, 1000);
}
}
selectItemFromList(doctorObject) {
this.setState({
value: doctorObject.name ,
suggestions: [doctorObject]
});
console.log(this.state);
}
render() {
let renderItems = () => {
let listItems = this.state.suggestions.map((suggestion, index) => {
let doctorObject = suggestion;
return (
<li onClick={() => this.selectItemFromList(doctorObject)} key={index}>
{doctorObject.name}
</li>
);
});
return (
<div>
<ul className="doctor-list">
{listItems}
</ul>
</div>
);
}
return (
<div className="form-group">
<label className="control-label form-label col-md-4" htmlFor="auto-complete-search">Doctor Name</label>
<div className="col-md-20">
<input className="custom-input"type="text" ref="test" id="auto-complete-search" required
placeholder="Enter Doctor name"
onChange={(e) => this.autoSearchInputChange(e)}
/>
{this.state.suggestions.length > 0 ? renderItems() : null}
</div>
</div>
);
}
}
export default AutoCompleteSearch;

React - toggling input in a cell of a table

I have a table where I would like it to be possible to click on a cell and for that to toggle an input that allows you to change the data in that cell. At the moment I am just rendering the input straight off the bat but ideally I don't want this. What I would really like is to show the current data in the cells and then when you click on the cell it becomes the input and allows you to edit the data.
Table Component:
import React from 'react';
import TableWithDataHeader from './TableWithDataHeader.jsx';
import Row from './Row.jsx';
import {createRowHistory} from '../../actions/DALIActions';
import TokenStore from '../../stores/TokenStore';
import TableDataStore from '../../stores/TableDataStore';
export default class Table extends React.Component {
state = {data: TableDataStore.getCells().historycells};
handleSubmitEvent = (e) => {
e.preventDefault();
console.log(this.state.data);
let data = this.state.data;
let dataEntriesArray = [];
for (let key in data) {
if (data.hasOwnProperty(key)) {
dataEntriesArray.push({contents: data[key]});
}
}
console.log(dataEntriesArray);
let access_token = TokenStore.getToken();
let row_id = TableDataStore.getRowId();
createRowHistory(access_token, row_id, dataEntriesArray);
};
handleChangeEvent = (value, cell) => {
let newState = this.state.data.slice(0);
console.log(newState);
newState[cell] = value;
console.log(newState);
this.setState({data: newState});
console.log(this.state.data);
};
render() {
let {data} = this.state;
return (
<div>
<div className="row">
<table className="table table-striped table-bordered">
<thead>
<TableWithDataHeader />
</thead>
<tbody>
<Row data={this.state.data} handleSubmitEvent={this.handleSubmitEvent} handleChangeEvent={this.handleChangeEvent} />
</tbody>
</table>
</div>
</div>
);
}
}
Row Component:
import React from 'react';
import TableDataStore from '../../stores/TableDataStore';
export default class Row extends React.Component {
render() {
let cells = this.props.data.map((el, i) => {
return (
<td key={i}>
<input type="text" className="form-control" id={el.id} defaultValue={el.contents} onChange={(e) => {
this.props.handleChangeEvent(e.target.value, i)
}} />
</td>
);
});
cells.push(
<td className="dtable-button" key={this.props.data.length}>
<button className="btn btn-primary" onClick={this.props.handleSubmitEvent}>Submit</button>
</td>
);
return (
<tr id={TableDataStore.getRowId()}>{cells}</tr>
);
}
}
Can I just simply toggle a state that will switch between a default show state (a <div>?) and the input that I already have? Or do I need to do something a little more involved?
Any help would be much appreciated!
Thanks for your time
One approach would be to create a Cell component that renders a div, but when clicked, renders an input instead.
import React from 'react';
class Cell extends React.Component {
constructor(props) {
super(props);
this.state = { editing: false };
}
render() {
const { value, onChange } = this.props;
return this.state.editing ?
<input ref='input' value={value} onChange={e => onChange(e.target.value)} onBlur={() => this.onBlur()} /> :
<div onClick={() => this.onFocus()}>{value}</div>
}
onFocus() {
this.setState({ editing: true }, () => {
this.refs.input.focus();
});
}
onBlur() {
this.setState({ editing: false });
}
}
Then you can create your table rows like this:
let cells = data.map((el, i) => (
<td key={i}>
<Cell
value={el.contents}
onChange={v => { this.props.handleChangeEvent(v, i) }}
/>
</td>
));
You can it like these.
let cells = this.props.data.map((el, i) => {
return (
<td key={i}>
{
(el.editable)?
<input type="text" className="form-control" id={el.id} defaultValue={el.contents} onChange={(e) => {
this.props.handleChangeEvent(e.target.value, i)
}} />:
<div onClick={onCellClick(i){}}>{el.contents}</div>
}
</td>
);
});
editable flag should be toggled using onCellClick(i){} & this.props.handleChangeEvent(e.target.value, i) functions. u should update the approprite state based on the index you have here

Resources