Material-UI , Radio Button + map function ( React JS) - javascript

so i need help guys, i want to replace the old radio button (classic)
with a new one using material-ui. i couldn't make it.
please suggest a solution.
thx in advance.
in pic u will see everything
class Question extends Component {
render() {
var that = this; // Funny way to be able to access this from inside our iterator() function below.
var iterator = this.props.questionChoices.map(function(choice){
return (
<div className="radio">
<label>
<input type= {that.props.questionType} name={that.props.questionID} value={choice}/>
{choice}
</label>
<RadioButtonGroup >
<RadioButton
value="ludicrous"
label={choice}
valueSelected={choice}
style={styles.radioButton}
/>
</RadioButtonGroup>
</div>
);
});
return (
<div className="row">
<div className="form">
<Paper zDepth={1} >
<div className="h3">
<h3 >{this.props.questionID} - {this.props.questionText}</h3>
{iterator}
</div>
</Paper>
</div>
</div>
);
}
}
result of the problem image

You are rendering the old radio button as well, you also need to define the selected value in the group component and render the group only once, currently you are rendering it for every option.
var iterator = (
<RadioGroup value={this.state.value} onChange={this.handleChange}>
{ this.props.questionChoices.map(choice =>
<FormControlLabel value={choise} control={<Radio />} label={choise} />
)
}
</RadioGroup>
);
Then you need to create the handleChange function to update the state.
class Question extends Component {
state = {
value: '',
};
handleChange = (event, value) => {
this.setState({ value });
};
...
You can find a working example here: https://material-ui-next.com/demos/selection-controls/

Related

Highlight PrimeReact Datatable's Data using react-highlight-words

I have PrimeReact DataTable with search/filter functionality. My main goal here is to highlight the text or data in DataTable that matches on the Search Input of the user.
I used react-highlight-words for the highlighting of data. Using the props textToHighlight, I can highlighted the text.
The Problem:
textToHighlight only accepts string and I don't have idea how to pass the component DataTable or its data in the props.
I tried the following:
I pass the Input state in textToHighlight props but unfortunately it prints the data outside the table.
I tried to put the DataTable component inside the Highlighter component, but the table doesn't render.
Here's the image:
ThesisList.jsx
// Search Box
const renderHeader = () => {
return (
<div className="flex justify-between">
<Button
className="p-button-outlined"
icon="pi pi-filter-slash"
label="Clear"
onClick={clearFilter}
/>
<span className="p-input-icon-left">
<i className="pi pi-search" />
<InputText
placeholder="Search"
value={globalFilterValue}
onChange={onGlobalFilterChange}
/>
</span>
</div>
);
};
// The function who checks if the input matches the Filters (check initFilter()).
const onGlobalFilterChange = (e) => {
const value = e.target.value;
let _filter = { ...filters };
_filter["global"].value = value;
setFilters(_filter);
setGlobalFilterValue(value);
};
return (
<div className="p-4 w-full h-screen">
//As you can see here I used the Input state
<Highlighter
searchWords={[globalFilterValue]}
textToHighlight={globalFilterValue}
/>
<DataTable>
...
</DataTable>
</div>
);
Each column in Primereact Data table takes a prop called body through which we can format the cells, so in your case, you can pass the Highlighter as the body for each column.
Here's an example with the title column.
<Datatable>
...
<Column
field="title"
header="Title"
body={(rowData) => (
<Highlighter
searchWords={[globalFilterValue]}
textToHighlight={rowData.title}
/>
)}
/>
...
</Datatable>

onChange event fires on wrong mapped file input element

I am stuck on this issue with onChange handler being fired on a wrong element after .map function. I have a component, which I use to display mapped values, which looks like this:
const Step: React.FC<StepProps> = ({ status, title, text, onClick, onChange }) => {
return (
<button disabled={status === CompletionStatus.Completed} className={Styles.item} onClick={onClick}>
<mui.IconButton css={css.completionIcon} disabled={status === CompletionStatus.Completed}>
{status === CompletionStatus.Completed ? <muiIcons.Check /> : <Plus />}
</mui.IconButton>
<div className={Styles.content}>
<span className={status === CompletionStatus.Completed ? Styles.titleCompleted : Styles.title}>{title}</span>
<span className={status === CompletionStatus.Completed ? Styles.textCompleted : Styles.text}>{text}</span>
</div>
{onChange && (
<>
<label htmlFor="file-button" className={Styles.inputLabel} />
<input id="file-button" className={Styles.input} type={'file'} onChange={onChange} />
</>
)}
</button>
);
};
So, some of the mapped elements are being used with onClick, and two use onChange to gain photos from the user.
The issue is, that every time I trigger the onChange event on any of those inputs, only the first ones fires, e.g (I added this onChange function to test the name of the element that is being fired, and every time only the first one in the list is being console.logged)
onChange={(event: any)=> {
console.log(event, step);
event.target.value = null;
}}
So, I have figured out the issue here, maybe someone finds this helpful.
Having input with type file only having one id (file-button) was causing only the first such input to work
<label htmlFor="file-button" className={Styles.inputLabel} />
<input id="file-button" className={Styles.input} type={'file'} onChange={onChange} />
The way I fixed this, was basically having that id property unique, so I passed an index to the component and changed the id to
id={`file-button-${index}`}
Sounds like you may not have set a key for each item in your mapping function.
{yourData.map((item, index) => <Component key={`item-${index}`} item={item} />)}

React.js adding component to child

import FieldSect from "./fieldSect.js"
<div>
<FieldSect />
</div>
--FieldSect.js--
import Field from "./Field.js"
<div>
<Field />
<button onclick={addField}> addField </field>
</div>
--Field.js--
function Field (){
<div>
<label> Test </label>
<input type="text" />
</div>
}
My code works in the part where Field is loaded immediately and is shown correctly. I am really struggling on trying to figure out how to keep adding the <Field /> component under the already existing <Field /> whenever the add button is clicked. I also need to ensure to have the ability to keep adding rather than having a specific number of available
I also cannot use the DOM as I am getting an error telling me to edit the state rather than using DOM.render
End Result should look something like this:
--FieldSect.js--
<div>
<Field />
...<Field /> (Button Add Click)
...<Field /> (Button Add Click)
..
</div>
You should have Fields be part of the state of your parent component, perhaps as an array, this.state = {fields:[{id:1},{id:2},{id:3}]}.
Then, when rendering, you can iterate over the array:
<div>
{this.state.fields.map(f => <Field key={f.id}/>)}
</div>
Its super simple.
Have a default state of fields for example lets say that you want to have 1 field at the beginning
this.state = {
fields: [<Field/>]
}
Now use onClick event for AddClick and a function as follows
handleOnClick=(event)=>this.setState(prevState=>({...prevState, fields: [...prevState.fields, <Field/>]})
And in your render function iterate over fields array
PS: I am not sure what is a Field doing.
What I would do is in render
<div>
{this.state.fields.map(field => <Field {...field}/>)}
</div>
and fields would be the data of a field
If I'm understanding you correctly, you're wanting to add a every time someone clicks a button. If that's accurate, then I would simply add a constructor with a state to either the FieldSect.js or parent file to that (somewhere along the line before Field.js) that creates a trackable state. Something like:
this.state = {
NumberOfFields: 1
};
If you do this ahead of FieldSect.js, then it needs to be passed down in props.
Now you need to set up the onClick function to increment this state. Using this, you can then create a loop in FieldSect.js that creates an array of elements that will be rendered in React.
let array = []
for(let i = 0; i < this.NumberOfFields; i++){
array.push(<Field key={Math.random()}/>)
}
Then, instead of using in FieldSect.js, use {array}.
The reason I added the key attribute to the Field is that all elements that are derived from an iteration like this must have a unique key.
Here you go.
This holds the number of <Fields /> in the state of a controlled component, which can be incremented with the button.
The loop is generated by creating an Array of nbrOfFields length, destructured, and then mapped over.
const Field = () => (
<div>
<label> Test </label>
<input type="text" />
</div>
);
class App extends React.Component {
constructor() {
super();
this.state = {nbrOfFields: 1};
this.addField = this.addField.bind(this);
}
addField() {
this.setState(prevState => ({nbrOfFields: prevState.nbrOfFields + 1}));
}
render() {
return (
<div>
{[...Array(this.state.nbrOfFields)].map((item, i) => (
<Field key={i} />
))}
<button onClick={this.addField}>Add field</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

onclick props not working with Material UI?

here is my situation on React.js
I have a function on my App.js call selectNumberOfPeople,
then In my child component ( call General) I had a button as:
<button className="selectedNumberOfPeopleButton" onClick={this.selectedNumberOfPeople} value="1">
1
</button>
which was displaying the value in the console on click.
Works perfectly.
I want to use a button from Material UI instead now, so I have replace my button with:
<RaisedButton className="selectedNumberOfPeopleButton"
onClick={this.props.selectedNumberOfPeople}
value="1"
label="1"
labelPosition="before"
primary={true}
/>
But the value doesnt display anymore int he console when using this . . .
though the function being in the parent component I do pass it by:
<General selectNumberOfPeople={this.selectNumberOfPeople} selectedPanel={this.showPanelAndHideOthers} />
and I tried to updated my child component ( General.js) like:
<RaisedButton selectNumberOfPeople={this.props.selectNumberOfPeople}
className="selectedNumberOfPeopleButton"
onClick={this.props.selectedNumberOfPeople}
value="1"
label="1"
labelPosition="before"
primary={true}
/>
but it's still not working....
For your information,
the selectNumberOfPeople is in App.js as
selectNumberOfPeople(value) {
console.log('select number of people');
// update the state and provide a callback handler to be called after the state is updated.
// referencing the state before the call back function
// () => {
// console.log(this.state.numberOfPeople)
// })
// will leave the state with the same value as before the setState function is called.
this.setState(
{
numberOfPeople: value,
},
() => {
console.log(this.state.numberOfPeople);
}
);
}
and in my general.js (child component)
selectedNumberOfPeople(e) {
this.props.selectNumberOfPeople(e.target.value);
const list = document.getElementsByClassName('selectedNumberOfPeopleButton');
for (let i = 0; i < list.length; i++) {
list[i].classList.remove('hover');
}
this.toggleSelectedButtonState(e);
}
Does anyone have any guidance in what I'm doing wrong ?
It will be super !!
Many thanks !
Use this.props.selectNumberOfPeople not selectedNumberOfPeople.
<RaisedButton
className="selectedNumberOfPeopleButton"
onClick={this.props.selectNumberOfPeople}
value="1"
label="1"
labelPosition="before"
primary={true}
/>
you can also try
onClick={()=>this.props.selectedNumberOfPeople}

ReactJs Checkbox doesn't change value after first use

I have a large form with various form elements which is dynamically rendered from a get request. All other types of form (such as text and select) are working fine, however the checkbox is not.
After I check it once, it only stays on (even if I uncheck it), am I missing something or doing something wrong here?
Here is my current relevant code:
class Input extends Component{
render(){
var form;
if (this.props.componentClass=="choice") {
// select form
}
else if (this.props.componentClass=="bool")
form =(<Checkbox id={this.props.controlId} onChange={this.props.onChange}
defaultChecked={this.props.placeholder} >
</Checkbox>);
else
// text form
return (
<div>
<Form inline onSubmit={this.handleSubmit}>
<FormGroup controlId={this.props.controlId}>
<ControlLabel>{this.props.name}</ControlLabel>
{form}
<Panel>
{this.props.description}
</Panel>
<FormControl.Feedback />
</FormGroup>
</Form>
<br/>
</div>
);
}
}
// onChange code (comes from a parent component)
onChange(e){
const form = Object.assign({}, this.state.form);
form[e.target.id] = e.target.value;
this.setState({ form });
console.log('current state: ', this.state);
}
You must bind onChange function as said before, but you should use "checked" instead of "value".
Here is your example modified this way:
https://jsfiddle.net/8d3of0e7/3/
class Input extends React.Component{
constructor(props){
super(props)
this.state = {form:{}}
}
render(){
var form;
if (this.props.componentClass=="choice") {
// select form
}else if (this.props.componentClass=="bool"){
form = (
<ReactBootstrap.Checkbox
id={this.props.controlId}
onChange={this.props.onChange.bind(this)}
checked={this.state.form[this.props.controlId]}
defaultChecked={this.props.placeholder} >
</ReactBootstrap.Checkbox>);
}else{
// text form
}
return (
<div>
<ReactBootstrap.Form inline onSubmit={this.handleSubmit}>
<ReactBootstrap.FormGroup controlId={this.props.controlId}>
<ReactBootstrap.ControlLabel>
{this.props.name}
</ReactBootstrap.ControlLabel>
{form}
<ReactBootstrap.Panel>
{this.props.description}
</ReactBootstrap.Panel>
<ReactBootstrap.FormControl.Feedback />
</ReactBootstrap.FormGroup>
</ReactBootstrap.Form>
<br/>
</div>
);
}
componentDidUpdate(){
console.log('current state: ', this.state);
}
}
function onChange(e) {
const form = Object.assign({}, this.state.form);
form[e.target.id] = e.target.checked;
this.setState({ form });
}
ReactDOM.render(
<Input componentClass='bool' controlId='retired'
name='Is retired?' onChange={onChange}/>,
document.getElementById('root')
)
In this example our state will be: state:{form:{retired:true}}
The issue is that you gave the checkbox an onChange without binding it to a value. Therefor, the initial check is working since that is what the defaultChecked is doing for you, but once you actually interact with it, you have no state bound to it causing react to not rerender it in the checked or unchecked position.

Categories