How to display an icon inside input field which got updated? - javascript

I have input boxes which are mapped with an object.
Looking for a solution if the input got updated or any user typed something in input field and then submit then that input field should display an icon inside it(for example if someone updated the first and last input field than only first and last field should contain the icon(Admin) other input boxes should be as it is.)
import React, { useState } from "react";
import Admin from 'icons'
//more imports
export default function InputCom() {
const [value, setValue] = useState("");
const data = [
{ label: "first", name: "first" },
{ label: "last", name: "last" },
{ label: "phone", name: "phone" },
];
const handleChange = (event) => {
setValue(event.target.value);
};
const handleClick = () => {
console.log(vlaue)
}
return (
<div >
<Card >
<div >
{data.map((item, key) => (
<div key={key.name} >
<input
//icon={Admin}
label={item.name}
name={item.name}
onChange={handleChange}
placeholder="text"
value={value}
/>
</div>
))}
</div>
</Card>
<button onClick={handleClick}>submit<button>
</div>
);
}

You can create a component called InputWithIcon that extends a normal <input> tag but also adds the icon. And then pass the optional icon prop so that it only renders the icon when it's provided:
<InputWithIcon
icon={!!this.state.value && Admin}
...
/>
Place the icon on the right side of the input and adjust the styles so that it looks as if the icon is inside the input.
This is the pseudo code:
const InputWithIcon = ({ icon, ...props }) => {
return (
<div>
<input {...props} />
{icon && <img src={icon} />}
</div>
);
};
Styles:
div {
position: relative;
}
img {
position: absolute;
right: 10;
top: 10;
}
input {
padding: 10;
padding-right: 20;
}
Hope this gives you the idea.

Related

How to Handle multiple radio button inputs with one onChange function handler

i have a scenario where based on a number(say numberOfFlags) i want to render numberOfFlags times an radio button group.Each group has two radio buttons approve and reject as per screenshot attached how to get values of all inputs when they change?
An lastly i have to store result of all radio buttons (approve/reject) in an array and send to API
You need to use two parameters on onChange function. One is for current index and another is for Approve/Reject.
Like below code snippet
onchange = handleOnChage(index, isApproveClicked)
You can achive this in many different ways, but I would probably simple create a state with an array of values in the parent component and pass it to each and every item to toggle its own state depending action.
App.js
export function App() {
const [list, setList] = useState([false, false, false]);
const updateItem = (value, index) => {
let copyList = [...list];
copyList[index] = !value;
setList(copyList);
};
console.log(list)
return (
<div className="App">
{list && (
<>
{list.map((value, index) => (
<Item values={[value, index]} updateItem={updateItem} key={index+"_check"} />
))}
</>
)}
</div>
);
}
Item.js
export default function Item({ values, updateItem }) {
return (
<>
<input
onChange={() => updateItem(values[0], values[1])}
type="checkbox"
checked={values[0] ? "checked" : ""}
/>
</>
);
}
Presented below is one possible way to achieve the desired objective.
Code Snippet
const {useState} = React;
const Thingy = ({...props}) => {
// num-of-flags is obtained from props
const { numOfFlags: nf} = props || {};
// if it is null or not above 0, return "invalid" message to parent
if (!(nf && nf > 0)) return "invalid num-of-flags";
// state variable to store approve/reject status
const [statusObj, setStatusObj] = useState({});
// JSX to render the UI & handle events
return (
<div>
{([...Array(nf).keys()].map(grpIdx => (
<div className="grpBox">
Group num {grpIdx+1} <br/>
<input type='radio' name={grpIdx} id={grpIdx} value={'approve'}
onChange={() => setStatusObj({
...statusObj, [grpIdx]: 'approve',
})}
/>
<label for={grpIdx}>Approve</label>{" "}
<input type='radio' name={grpIdx} id={grpIdx} value={'reject'}
onChange={() => setStatusObj({
...statusObj, [grpIdx]: 'reject',
})}
/>
<label for={grpIdx}>Reject</label>
</div>
)))}<br/>
<button
onClick={() => {
// make API call here
// for verification, displaying an alert-message showing the status
const displayMsg = [...Array(nf).keys()].map(
gi => "Group num " + (+gi+1) + " : " + (gi in statusObj ? statusObj[gi] : '__')
).join(', ');
alert(`Approve-Reject status is: ${JSON.stringify(displayMsg)}`);
}}
>
Submit
</button>
</div>
);
};
ReactDOM.render(
<div>
<div className="demoTitle">DEMO</div>
<Thingy numOfFlags={5} />
</div>,
document.getElementById("rd")
);
.demoTitle {
margin-bottom: 5px;
font-size: 20px;
text-decoration: underline;
}
.grpBox {
margin: 5px; padding: 10px;
border: 2px solid purple;
width: max-content;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="rd" />
Explanation
Inline comments added to the snippet above.
PS: If you'd like to add value to stackoverflow community,

Inserting only unique ID in form value in React

I am new to React, there are two input fields in the application, one is for ID and another for Name, There are two components I've used, in the parent component I've maintained all the state and form in separate another component. My aim is to check the id which is a input from the user, id should be unique every time, if it's same, an alert should popup and the focus turns to ID input field, and it should do the same until the ID is different from all the objects(state object)
My app.js file is,
import React, { Component } from "react";
import Form from "./Form";
export default class App extends Component {
state = {
names: [
/*
{id: 1,name: "Aashiq"}
*/
],
};
renderTable() {
return this.state.names.map((eachName) => {
const { id, name } = eachName;
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>
<input
type="button"
value="Delete"
onClick={() => this.deleteName(eachName.id)}
/>
</td>
</tr>
);
});
}
deleteName = (id) => {
console.log("ID object", id);
this.state.names &&
this.setState({
names: this.state.names.filter((name) => name.id !== id),
});
};
addName = (newName) => {
this.setState({
names: [newName, ...this.state.names],
});
};
render() {
return (
<>
<Form onSubmit={this.addName} names={this.state.names} />
{/* Table */}
<br />
<table id="details">
<tbody>
<tr>
<th>ID</th>
<th>Names</th>
<th>Operation</th>
</tr>
{/* Render dynamic rows
*/}
{this.renderTable()}
</tbody>
</table>
</>
);
}
}
You can see I try to render the data as table and we can delete the row data also
The form.js file is,
import React, { useState } from "react";
// import { uniqueId } from "lodash";
export default function Form(props) {
const [name, setName] = useState("");
const [id, setId] = useState();
const handleSubmit = (e) => {
e.preventDefault();
handleChangeandValidate();
};
const handleChangeandValidate = () => {
const { onSubmit, names } = props;
console.log("Object keys length", Object.keys(names).length);
if (Object.keys(names).length !== 0) {
names.map((name) => {
if (name.id === id) {
alert("Enter unique id");
setId("");
document.getElementById("ID").focus();
} else {
//if different id
onSubmit({ id: id, name: name });
setName("");
setId("");
}
return null;
});
} else {
onSubmit({ id: id, name: name }); // first time
setName("");
setId("");
}
};
return (
<form onSubmit={handleSubmit} id="myform">
<label style={{ fontSize: "20px", fontWeight: "bold" }}>
Name: {""}
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
</label>{" "}
<label style={{ fontSize: "20px", fontWeight: "bold" }}>
ID: {""}
<input
type="number"
onChange={(e) => setId(e.target.value)}
required
value={id}
id="ID"
/>
</label>
{""}
<input type="submit" value="Submit" />
</form>
);
}
You can see I've tried to get the state and onSubmit function from the parent component(app.js) and done some logic like comparing all the ID's, but this logic throws some error, please somebody come up with a good solution.
Thanks in advance!
I have modified your code a bit and here is a working example.
Here is what I did:
I used createRef() to create two references that refer to each input field named nameInputRef and idInputRef.
I added ref={nameInputRef} and ref={idInputRef} so that we can get their values on submit.
On submit, I get the values of the name + id using their refs.
to search for whether the ID exists or not, I used Array.find() which would return undefined if the same id doesn't exist in the list of names coming from the props.
in addName(), I used setState() but in the param I used a function to make sure I get the latest list of names as updating the state is asynchronous. Inside I also used ES6's destructuring feature to make a copy of the current list, push the new name to it and then update the state with the new list of names.

how to send id to refs(forwardrefs) to children components

I have 4 buttons and submit button, upon clicking on submit button, i need to add effects or button focus to 4 buttons based on pattern like [2,4,3,1], the buttons should animate like this pattern upon submit button click.
Here's what i have tried, I am unable to send id using refs. how do i send id to refs or how do i animate buttons based on refs or any other on how to add effects to children components.
constructor(props) {
super(props);
this.state = {
values: [
{ id: 1, color: "blue" },
{ id: 2, color: "red" },
{ id: 3, color: "green" },
{ id: 4, color: "yellow" }
]
};
this.myRef = React.createRef();
}
getvalue = (id, ref) => {
console.log(ref);
};
in render ` const { values } = this.state;`
<div className="col-md-12">
{values.map(value => (
<Card
ref={this.myRef}
key={value.id}
value={value}
id={value.id}
onbtnclick={() => this.getvalue(value.id)}
/>
))}
</div>
child component
const Card = React.forwardRef((props, ref) => {
return (
<button
ref={ref}
key={props.id}
className="btn newcard m-2 active"
aria-pressed="true"
onClick={() => this.props.onbtnclick(props.id, ref)}
style={{ background: `${props.value.color}` }}
/>
);
});
const ref = React.createRef;
i create a Sand Box for you here the Example https://codesandbox.io/s/xenodochial-flower-cl52b
u need to use useImperativeHandle of React Hooks plus Forward Refs
/// -------------
here is the refactor Sand Box you mentioned in Comments
https://codesandbox.io/s/amazing-germain-ww7yi

Identifying what item have been deleted (created and modifed) in a Formik FieldArray

Was wondering if Formik has a native solution for identifying the addition and deletion (and update) of FieldArray in the form ?
I have the code on sandbox here https://codesandbox.io/s/jn7x2m75o9 ( based on the original Formik Array example # https://github.com/jaredpalmer/formik/blob/master/examples/Arrays.js )
but also the relevant part here :
With an Initial state of 3 friend defined, how can I know in my onSubmithandler which one were modified,deleted,updated.
import React from "react";
import { Formik, Field, Form, ErrorMessage, FieldArray } from "formik";
const initialValues = {
friends: [
{
name: "Friend_A",
email: "email_A#somewhere.com"
},
{
name: "Friend_B",
email: "email_B#somewhere.com"
},
{
name: "Friend_C",
email: "email_C#somewhere.com"
}
]
};
const mySubmit = values => console.log();
const SignIn = () => (
<div>
<h1>Invite friends</h1>
<Formik
initialValues={initialValues}
onSubmit={values => {
var itemRemoved = values.GetItemRemoveFromArray; // This is what I'm looking for
console.log(itemRemoved);
// Would print Friend_A
var itemAdded = values.GetItemAddedFromArray; // This is what I'm looking for
console.log(itemAdded);
// Would print New_Friend
var itemUpdated = values.GetItemUpdatedInArray; // This is what I'm looking for
console.log(itemUpdated);
// Would print Friend_C
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
}, 500);
}}
render={({ values }) => (
<Form>
<FieldArray
name="friends"
render={({ insert, remove, push }) => (
<div>
{values.friends.length > 0 &&
values.friends.map((friend, index) => (
<div className="row" key={index}>
<div className="col">
<label htmlFor={`friends.${index}.name`}>Name</label>
<Field
name={`friends.${index}.name`}
placeholder="Jane Doe"
type="text"
/>
<ErrorMessage
name={`friends.${index}.name`}
component="div"
className="field-error"
/>
</div>
<div className="col">
<label htmlFor={`friends.${index}.email`}>Email</label>
<Field
name={`friends.${index}.email`}
placeholder="jane#acme.com"
type="email"
/>
<ErrorMessage
name={`friends.${index}.name`}
component="div"
className="field-error"
/>
</div>
<div className="col">
<button
type="button"
className="secondary"
onClick={() => remove(index)}
>
X
</button>
</div>
</div>
))}
<button
type="button"
className="secondary"
onClick={() => push({ name: "", email: "" })}
>
Add Friend
</button>
</div>
)}
/>
<button type="submit">Invite</button>
</Form>
)}
/>
</div>
);
export default SignIn;
So if with the above a user where to :
Click on the X below Friend_A
Modify Friend_C email to email_C#nothere.com
Click "Add Friend"
Enter value Name: New_Friend_X and email: XX#YY.com
Click "Add Friend"
Enter value Name: New_Friend_Z and email: Friend_Z#coolplace.com
Click "X" button below newly entered "New_Friend_X"
Click "Invite"
in my mySubmit I'm looking for a way to easily get :
Friend_A was Removed
Friend_C was Modified
New_Friend_Z was added (was not in the original initialValues to formik)
(I Don't care about New_Friend_X. No need to know it was added/removed )
Point of this is to minimize rest call to the back end to create/update entity/link and also I really dont want to write my own "secondary state" in the onClick handler of the remove button before calling the remove(index) handler provided by Formik to track what need to be deleted from the DB.
Its not built into Formik, but it is not hard to do in javascript.
First, understand that Formik clones the object you give to initialValues. So in onSubmit, you will compare the final value to your original object.
The incoming data:
const initialFriends = [
{
name: "Friend_A",
email: "email_A#somewhere.com"
},
{
name: "Friend_B",
email: "email_B#somewhere.com"
},
{
name: "Friend_C",
email: "email_C#somewhere.com"
}
];
const initialValues = { friends: initialFriends };
Modified Formik declaration:
<Formik initialValues={initialValues}
...
onSubmit={values => {
const { added, deleted, changed } = addDeleteChange(
initialFriends,
values.friends
);
setTimeout(() => {
alert(
"Added: " + JSON.stringify(Object.fromEntries(added.entries()))
);
alert(
"Deleted: " + JSON.stringify(Object.fromEntries(deleted.entries()))
);
alert(
"Changed:" + JSON.stringify(Object.fromEntries(changed.entries()))
);
alert(JSON.stringify(values, null, 2));
}, 500);
}}
...
Helper functions:
function partition(array, filter) {
let pass = [],
fail = [];
array.forEach(e => (filter(e) ? pass : fail).push(e));
return [pass, fail];
}
const addDeleteChange = (in1, out1) => {
let inMap = new Map(in1.map(f => [f.name, f]));
let outMap = new Map(out1.map(f => [f.name, f]));
let inNames = new Set(inMap.keys());
let outNames = new Set(outMap.keys());
let [kept, added] = partition(out1, f => inNames.has(f.name));
let deleted = in1.filter(f => !outNames.has(f.name));
//alert(JSON.stringify(Object.fromEntries(deleted.entries())));
let changed = kept.filter(f => f.email !== inMap.get(f.name).email);
//alert(JSON.stringify(Object.fromEntries(changed.entries())));
return { added: added, deleted: deleted, changed: changed };
};
Code in codesandbox
NOTE: If you change the name of a friend, that will appear as a delete of original friend and an add of a new friend.
A more robust solution would be to add a (hidden) "id" field to each friend. Then instead of comparing name, would compare id.
That requires generating a new id as add each friend.

Uncaught TypeError: Cannot read property 'icon' of null

i have a form for editing the tab. When a edit icon is clicked to edit that tab a form in dialog box appears where the input box has current data in it. But when i hit save without touching the icon field i get an error of Uncaught TypeError: Cannot read property 'icon' of null. If i did not touch the name field and only touch on icon field and hit save button then the tab gets edited. How can i make icon field work too like name field is working ? I mean if i want to only edit name, i can edit the name from name field and save without touching icon field which will save the tab name with edited name and current icon.
How can it be possible?
class EditForm extends Component {
render() {
const { tab } = this.props;
console.log('tab object is', this.props.tab);
const listOfIcon = _.map(this.props.fetchIcon.icons, (singleIcon) => ({
text: singleIcon.name,
id: singleIcon.id,
value: <MenuItem primaryText={singleIcon.name} />
}));
return (
<div>
<form
onSubmit={(e) => {
console.log('auto', e.target.auto);
e.preventDefault();
this.props.editTab(
tab.id,
e.target.text.value,
this.state.icon
);
this.props.closeTabIcon();
}
}
>
<div className="tab-name">
<TextField
hintText={tab.name}
name="text"
defaultValue={tab.name}
hintStyle={{ display: 'none' }}
floatingLabelStyle={{ color: '#1ab394' }}
floatingLabelFocusStyle={{ color: '#1db4c2' }}
underlineStyle={{ borderColor: '#1ab394' }}
/>
</div>
<div className="icon">
<AutoComplete
floatingLabelText={tab.icon}
name="auto"
filter={AutoComplete.noFilter}
openOnFocus
dataSource={listOfIcon}
textFieldStyle={{ borderColor: '#1ab394' }}
className="autocomplete"
onNewRequest={(e) => { this.setState({ icon: e.id }); }}
/>
</div>
<button className="btn">Save</button>
</form>
</div>
);
}
}
const mapStateToProps = state => {
console.log(state);
return {
fetchIcon: state.fetchIcon,
tabs: state.tabs.tabs.map(tab => {
const icons = state.fetchIcon.icons.find(icon => Number(icon.id) === tab.icon);
return {
...tab,
icon: icons && icons.name
};
})
};
};
function mapDispatchToProps(dispatch) {
return bindActionCreators({
editTab,
closeTabIcon
}, dispatch);
}
The state of a componnet is intitated with the null. YOu can set the intital value of state in constrocutor of the class
class EditForm extends Component {
constructor(props) {
super(props)
this.state ={}
}
render() {
const { tab } = this.props;
console.log('tab object is', this.props.tab);
const listOfIcon = _.map(this.props.fetchIcon.icons, (singleIcon) => ({
text: singleIcon.name,
id: singleIcon.id,
value: <MenuItem primaryText={singleIcon.name} />
}));..........
initialize 'input box' with empty value from code behind.

Categories

Resources