I have two dropdowns, Discount Type and Offer Type, Discount type returns four elements so what I want to do is for example if I select option number 2 from that dropdown then call the URL that populates the Offer Type dropdown with the selected index, in this case '2', because now the offer type is returning all because I'm using the following URL that brings all: http://xxxxxx:8080/services/OfferType/getAll but instead of getAll I want to pass the index of the Offer Type Dropdown to have something like this http://xxxxxx:8080/services/OfferType/2
Any help on how to do this because I don't, below you'll find my current code:
import React from 'react';
import DropDownMenu from 'material-ui/DropDownMenu';
import MenuItem from 'material-ui/MenuItem';
import Divider from 'material-ui/Divider';
import cr from '../styles/general.css';
export default class ExampleDropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
DiscountTypeData: [],
OfferTypeData: [],
DiscountTypeState: '',
OfferTypeState: ''
};
this.handleChange = this.handleChange.bind(this);
this.renderDiscountTypeOptions = this.renderDiscountTypeOptions.bind(this);
this.renderOfferTypeOptions = this.renderOfferTypeOptions.bind(this);
this.handleChangeDiscountType = this.handleChangeDiscountType.bind(this);
this.handleChangeOfferType = this.handleChangeOfferType.bind(this);
}
componentDidMount() {
const offerTypeWS = 'http://xxxxxx:8080/services/OfferType/getAll';
const discountTypeWS = 'http://xxxxxx:8080/services/DiscountType/getAll';
fetch(offerTypeWS)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
OfferTypeData: findResponse.offerTypes
});
});
fetch(discountTypeWS)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
DiscountTypeData: findResponse.discountTypes
});
});
}
handleChange(event, index, value) {
this.setState({value});
}
handleChangeDiscountType(event, index, value) {
this.setState({ DiscountTypeState: (value) });
}
handleChangeOfferType(event, index, value) {
this.setState({ OfferTypeState: (value) });
}
renderDiscountTypeOptions() {
return this.state.DiscountTypeData.map((dt, i) => {
return (
<MenuItem
key={i}
value={dt.text}
primaryText={dt.text} />
);
});
}
renderOfferTypeOptions() {
return this.state.OfferTypeData.map((dt, i) => {
return (
<MenuItem
key={i}
value={dt.offerTypeDesc}
primaryText={dt.offerTypeDesc} />
);
});
}
render() {
return (
<div className={cr.container}>
<div className ={cr.boton}>
<Divider/>
<br/>
</div>
<div>
<DropDownMenu
value={this.state.DiscountTypeState}
onChange={this.handleChangeDiscountType}>
<MenuItem value={''} primaryText={'Select discount type'} />
{this.renderDiscountTypeOptions()}
</DropDownMenu>
<br/>
<DropDownMenu
value={this.state.OfferTypeState}
onChange={this.handleChangeOfferType}>
<MenuItem value={''} primaryText={'Select offer type'} />
{this.renderOfferTypeOptions()}
</DropDownMenu>
</div>
</div>
);
}
}
This is the response from the Discount Type service:
So if I select "Bulk Discount" that has the value "3" then I want to pass that 3 to the Offer Type URL..
You can call fetch from handleChangeDiscountType or handleChangeOfferType just like you called in componentDidMount. Example:
handleChangeDiscountType(event, index, value) {
fetch('http://xxxxxx:8080/services/DiscountType/' + value.id)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({ DiscountTypeState: findResponse });
});
}
Related
I have list of items that I am showing in Checkboxlist. Items are coming from get webapi and it returns xml. Some of the items are checked on page load. If I make the change in the selection, always only new selection will stay. Why pre-fill selected checkboxes are no more selected in case of change in the selection.
Xml:
<?xml version="1.0"?>
<Product>
<states is-assigned="FALSE" product-state-id="11">Alabama</states>
<states is-assigned="FALSE" product-state-id="12">Alaska</states>
<states is-assigned="FALSE" product-state-id="21">Arizona</states>
<states is-assigned="TRUE" product-state-id="22">Colorado</states> selected on page load
<states is-assigned="TRUE" product-state-id="33">Connect</states> selected on page load
</Product>
</xml>
import React from "react";
import axios from 'axios';
import XMLParser from 'react-xml-parser';
export class AssignStates extends React.Component {
constructor(props) {
super(props);
this.state = {
Template_ID: "",
templatestates: [],
checkedItems: [],
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const id = parseInt(event.target.id, 10);
const index = this.state.checkedItems.indexOf(id);
const updatedArray = [...this.state.checkedItems];
if (index !== -1) {
updatedArray.splice(index, 1);
} else {
updatedArray.push(id);
}
this.setState((prevState) => ({
checkedItems: updatedArray
}));
}
handleSubmit(event) {
event.preventDefault();
const StateID_List = this.state.checkedItems;
const Template_ID = this.props.key_id;
const data = {
Template_ID,
StateID_List
}
fetch(REQUEST_URL, {
method: 'POST',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' }
})
.then(response => response.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success', response));
}
componentDidMount() {
if (typeof this.props.key_id !== 'undefined') {
const Template_ID = this.props.key_id;
if (Template_ID > 0) {
this.getListOfStates(Template_ID);
}
}
}
componentDidUpdate(prevProps) {
const Template_ID = this.props.key_id;
if (prevProps.key_id !== this.props.key_id) {
console.log(`key_id: ${this.props.key_id}`);
this.getListOfStates(Template_ID);
}
}
getListOfStates(Template_ID) {
axios.get(REQUEST_URL, { "Content-Type": "application/xml; charset=utf-8" })
.then(response => {
const jsonDataFromXml = new XMLParser().parseFromString(response.data);
console.log(jsonDataFromXml.getElementsByTagName('states'));
})
.then((data) => {
let items = data.reduce((acc, item) => {
if (item.attributes['is-assigned'] === "TRUE") acc.push(item.attributes['product-state-id']);
return acc;
}, [])
this.setState({
templatestates: data,
checkedItems: items
});
console.log(items);
})
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<ul style={{ listStyle: 'none' }}>
{(this.state.templatestates.map((item, index) => {
return (
<li key={item.attributes['product-state-id']}>
<input
type="checkbox"
id={item.attributes['product-state-id']}
defaultChecked={item.attributes['is-assigned'] == "TRUE" ? true : false}
value={item.value}
onChange={this.handleChange}
/>
{item.value}
</li>
)
}))}
</ul>
<button type="submit">Submit</button>
<input type="button" name="selectall" value="Select All" />
<input type="button" name="unselectall" value="Clear All" />
</form>
</div>
);
}
}
export default AssignStates;
Thanks
in getListOfStates function i see you are updating the result in prodtemplatestates state. But there is no state named "prodtemplatestates"!!
The checkedItems state should be initialized to the selected XML values when the component mounts.
getListOfStates(Template_ID) {
axios
.get(REQUEST_URL, { "Content-Type": "application/xml; charset=utf-8" })
.then((response) => {
const jsonDataFromXml = new XMLParser().parseFromString(data);
const states = jsonDataFromXml.getElementsByTagName("states");
console.log(states);
this.setState({
templatestates: states,
checkedItems: states
.filter(({ attributes }) => attributes["is-assigned"] === "TRUE")
.map(({ attributes }) => Number(attributes["product-state-id"]))
});
});
}
When mapping the checkbox inputs you should use the checked prop for a controlled input (i.e. checked and onChange props). Use this.state.checkedItems array and check if the current mapped item's id is in the array.
<ul style={{ listStyle: "none" }}>
{this.state.templatestates.map((item, index) => {
return (
<li key={item.attributes["product-state-id"]}>
<label>
<input
type="checkbox"
id={item.attributes["product-state-id"]}
checked={this.state.checkedItems.includes(
Number(item.attributes["product-state-id"])
)}
value={item.value}
onChange={this.handleChange}
/>
{item.value}
</label>
</li>
);
})}
</ul>
I have a list like this:
<div className="doubleCol">
{this.state.symptoms.map(item => (
<ListItem key={item.ObjectID}>
<input type="checkbox" className="sympSelect" />
{item.name}
</ListItem>
))}
</div>
All the items rendered have checkbox and I want it to filter a different list elsewhere on the page based on which boxes are checked. To do that I need the checkboxes to change the state and pass the new state to a method which is supposed to filter and display only those items on the second list with id's associated to items on the first list.
From what I have read it shouldn't matter for this purpose if the checkboxes are controlled or uncontrolled.
class Home extends React.Component {
state = {
conditions: [],
symptoms: [],
selectedSymptom: []
}
componentDidMount() {
this.getConditionsMethod();
this.getSymptomsMethod();
}
getConditionsMethod = () => {
API.getConditions()
.then(data => {
console.log(data);
data.data.sort((a, b) => a.name.localeCompare(b.name))
this.setState({
conditions: data.data
})
})
.catch(err => console.log(err))
};
filterConditionsMethod = () => {
API.getConditions()
.then(data => {
console.log(data);
data.data.sort((a, b) => a.name.localeCompare(b.name));
this.setState({
selectedSymptom: data.data
})
})
.catch(err => console.log(err))
};
But I am kind of stuck on how to structure the onChange for when the box is checked and how to make that implement the filter.
Here is you solution you can add onChange event for checkbox and filter your records as selectedSymptoms and symptoms. Please check code is
import React, { Component } from "react";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
conditions: [],
symptoms: [
{ ObjectID: 1, name: "xyz" },
{ ObjectID: 2, name: "pqr" }
],
selectedSymptom: [],
checked: ""
};
}
updateCheckBox = (event, item) => {
if (event.target.checked) {
let selectedList = this.state.selectedSymptom;
selectedList.push(item);
this.setState({
...this.state,
checked: this.state.checked == "checked" ? "" : "checked",
selectedSymptom: selectedList
});
} else {
const symptomss = this.state.selectedSymptom.filter(element => {
if (element.ObjectID != data.ObjectID) {
return item;
}
});
this.setState({
...this.state,
checked: "",
selectedSymptom: symptomss
});
}
};
render() {
return (
<div className="doubleCol">
{this.state.symptoms.map(item => (
<ListItem key={item.ObjectID}>
<input
type="checkbox"
className="sympSelect"
onChange={this.updateCheckBox(e, item)}
id="symptoms_id"
defaultChecked={this.state.checked}
/>
{item.name}
</ListItem>
))}
</div>
);
}
}
export default Home;
I'm trying to dynamically add inputs when the user clicks the button to add a question.
Usually doing a controlled form is easy as your know what the field names are. But in this situation they are dynamic.
I've got a working solution but it mutates the state.
Is there a better way to do this?
Thanks
JSX
import React, { Component } from 'react';
import axios from 'axios';
import { saveAs } from 'file-saver';
class Form extends Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.handleForm = this.handleForm.bind(this);
this.addQuestion = this.addQuestion.bind(this);
this.removeQuestion = this.removeQuestion.bind(this);
this.state = {
questions: []
}
}
onChange(e, i) {
this.state.questions[i] = e.target.value;
this.setState({
questions: this.state.questions
})
}
handleForm(e) {
e.preventDefault();
const body = {
questions: this.state.questions
};
axios.post('/api/pdfs/create', body)
.then(() => axios.get('/api/pdfs/fetch', { responseType: 'blob' }))
.then((res) => {
const pdfBlob = new Blob([res.data], { type: 'application/pdf' });
return saveAs(pdfBlob, 'questions.pdf');
})
.catch(error => {
console.log(error.response)
});
}
addQuestion() {
this.setState({
questions: [...this.state.questions, '']
});
}
removeQuestion(index) {
this.setState({
questions: this.state.questions.filter((question, i) => i !== index)
});
}
render() {
return (
<div>
<button onClick={this.addQuestion}>Add Question</button>
<form onSubmit={this.handleForm}>
{this.state.questions.map((question, index) => (
<div key={index}>
<input type="text" name={`question-${question}`} onChange={(e) => this.onChange(e, index)} />
<button type="button" onClick={() => this.removeQuestion(index)}>x</button>
</div>
))}
<button type="submit">Submit</button>
</form>
</div>
);
}
}
export default Form;
You are mutating the state only in your onChange call, and that can be fixed easily:
onChange(e, i) {
this.setState({
questions: this.state.questions.map((v, i2) => i === i2 ? e.target.value : v),
});
}
(This won't change the functionality though, its just a "best practice improvement")
I’m using Material UI v1.0 beta.26 and I’m facing an issue with the dropdown component, in this new version you have to use the Select component combined with MenuItem.
My dropdown is populated when the app is render but when I choose any option from it I’m getting the following error:
And this is my code:
import React from 'react';
import Select from 'material-ui/Select';
import {MenuItem, MenuIcon} from 'material-ui/Menu';
//CONSTANTS
import {CREATE_LS_DISCOUNT_TYPE_DD} from './commons/constants';
import {CREATE_LS_OFFER_TYPE_DD} from './commons/constants';
import cr from '../styles/general.css';
export default class ExampleDropDown extends React.Component {
constructor(props) {
super(props);
this.state = {
DiscountTypeData: [],
OfferTypeData: [],
DiscountTypeState: '',
OfferTypeState: ''
};
this.renderDiscountTypeOptions = this.renderDiscountTypeOptions.bind(this);
this.renderOfferTypeOptions = this.renderOfferTypeOptions.bind(this);
this.handleChangeDiscountType = this.handleChangeDiscountType.bind(this);
this.handleChangeOfferType = this.handleChangeOfferType.bind(this);
}
componentDidMount() {
fetch(CREATE_LS_DISCOUNT_TYPE_DD)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
DiscountTypeData: findResponse.discountTypes,
});
});
}
handleChangeDiscountType(event, index, value) {
this.setState({ DiscountTypeState: (value)});
fetch(CREATE_LS_OFFER_TYPE_DD)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
OfferTypeData: findResponse.offerTypes
});
});
}
handleChangeOfferType(event, index, value) {
this.setState({ OfferTypeState: event.target.value });
}
renderDiscountTypeOptions() {
return this.state.DiscountTypeData.map((dt) => {
return (
<MenuItem
key={dt.id}
value={dt.text}>
{dt.text}
</MenuItem>
);
});
}
renderOfferTypeOptions() {
return this.state.OfferTypeData.map((dt) => {
return (
<MenuItem
key={dt.offerTypeCode}
value={dt.offerTypeDesc}>
{dt.offerTypeDesc}
</MenuItem>
);
});
}
render() {
return (
<div className={cr.container}>
<div>
<Select
value={this.state.DiscountTypeState}
onChange={this.handleChangeDiscountType}>
{this.renderDiscountTypeOptions()}
</Select>
</div>
<br/>
<div>
<Select
value={this.state.OfferTypeState}
onChange={this.handleChangeOfferType}>
{this.renderOfferTypeOptions()}
</Select>
</div>
</div>
);
}
}
in the following method (handleChangeDiscountType) if I leave it like this "this.setState({ DiscountTypeState: value})" I got the error in the screenshot above but if I change that line like this "this.setState({ DiscountTypeState: event.target.value}) it works so I want to understand why
handleChangeDiscountType(event, index, value) {
this.setState({ DiscountTypeState: value});
fetch(CREATE_LS_OFFER_TYPE_DD + 1)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
OfferTypeData: findResponse.offerTypes
});
});
}
also what I want to do is to get the index of my selection in order to pass it to my second web service call but I don't know how to do it, in the previous version of Material UI I just put "index" and works but in the new version ain't work so I want to know a new way to add that parameter.
fetch(CREATE_LS_OFFER_TYPE_DD + PASS INDEX HERE)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
OfferTypeData: findResponse.offerTypes
});
});
I'll appreciate any help with this..
The onChange handler provided to Select is invoked with a target that is enriched with value and name, so you need to pull value from event.target:
handleChangeDiscountType(event) {
const {
DiscountTypeData
} = this.state;
// you're using the text property as the value, but you should probably use its id
// still, here's how you'd find the item using the selected item's value
const selectedDiscount = DiscountTypeData.filter(
discount => discount.text === event.target.value,
);
// use a templated literal to specify the endpoint with the selected item's id
fetch(`${CREATE_LS_OFFER_TYPE_DD}/${selectedDiscount.id}`)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
OfferTypeData: findResponse.offerTypes,
});
});
}
The reason your code was not working is because onChange is not invoked with a third parameter, so your use of value was setting state to undefined.
For more information, see the Selects demo.
I have this piece of code that calls a web service and displays the names coming from that WS into a Dropdown component from Material UI,
What I want to do is to set the default value of the dropdown with the first element coming from the WS and also be able to select any of the options in dropdown, I read something about "State" but don't get it really good at a code level.
I'm new to React and learning by myself but some help would be nice.
import React, { Component } from 'react';
import DropDownMenu from 'material-ui/DropDownMenu';
import MenuItem from 'material-ui/MenuItem';
export default class WebserviceTest extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
this.renderOptions = this.renderOptions.bind(this);
}
componentDidMount() {
const url = 'https://randomuser.me/api/?results=4';
fetch(url)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
data: findResponse.results
});
});
}
//will set wahtever item the user selects in the dropdown
handleChange(event, index, value) {this.setState({ value });}
//we are creating the options to be displayed
renderOptions() {
return this.state.data.map((dt, i) => {
return (
<div key={i}>
<MenuItem
label="Select a description"
value={dt.name.first}
primaryText={dt.name.first} />
</div>
);
});
}
render() {
return (
<div>
<DropDownMenu value={this.state.name} onChange={this.handleChange}>
{this.renderOptions()}
</DropDownMenu>
</div>
);
}
}
Thanks in advance.
Regards.
You need to set state of selected dropdown option. And set first value of data as selected value.
componentDidMount() {
const url = 'https://randomuser.me/api/?results=4';
fetch(url)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
data: findResponse.results,
selected: findResponse.results[0].name.first // need to be sure it's exist
});
});
}
handleChange(event, index, value) {this.setState({ selected: value });}
.
.
.
render() {
return (
<div>
<DropDownMenu value={this.state.selected} onChange={this.handleChange}>
{this.renderOptions()}
</DropDownMenu>
</div>
);
}
UPDATED CODE
import React, { Component } from 'react';
import DropDownMenu from 'material-ui/DropDownMenu';
import MenuItem from 'material-ui/MenuItem';
export default class WebserviceTest extends Component {
constructor() {
super();
this.state = {
data: [],
selected: '',
};
this.renderOptions = this.renderOptions.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
const url = 'https://randomuser.me/api/?results=4';
fetch(url)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
console.log(findResponse.results[0].name.first);
this.setState({
data: findResponse.results,
selected: findResponse.results[0].name.first // need to be sure it's exist
});
});
}
handleChange(value) {this.setState({ selected: value });}
//we are creating the options to be displayed
renderOptions() {
return this.state.data.map((dt, i) => {
return (
<div key={i}>
<MenuItem
value={dt.name.first}
primaryText={dt.name.first} />
</div>
);
});
}
render() {
return (
<div>
<DropDownMenu value={this.state.selected} onChange={this.handleChange}>
{this.renderOptions()}
</DropDownMenu>
</div>
);
}
}
this.setState controls the variable this.state in a special way. Whenever you use this.setState it will run render again to check for changes accordingly. Your dynamic content that you want to be responsive should be placed in this.state and those should be shown in your render function.
There are many ways to go about solving your question, but the most important principle to use is to place what you currently want to render (or the id/index number) in this.state and use this.setState to change it as needed.
value={this.state.name} should be a single value from your data structure that you return from your fetch, assuming this is what is shown on the screen.
Also, you forgot to bind this.handleChange in your constructor.
Stating props in your constructor is perfectly fine to do. You only do that when you want to use something from this.props in your constructor. You aren't, so it's perfectly safe to leave it as constructor() and super()