How to access attributes on xml feed - javascript

I am trying to parse data from an xml file in my React JS app, but it seems to return a full xml object comprising of 25 or so 'cube' elements. I'm interested in accessing the 'currency' and 'rate' attribute of each cube, and output each of these inside of a dropdown. Is there a way of looping over all the cubes and somehow targeting these? I'm trying to build a currency converter that automatically converts a price entered by the user.
My code:
import React, { Component } from 'react';
import "../../App.css"
class Countries extends Component {
constructor() {
super();
this.state = {
countrycodes: [],
exchangerates: []
};
}
componentDidMount(){
fetch('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml')
.then(response => response.text())
.then(str => (new window.DOMParser()).parseFromString(str, "text/xml"))
.then(data => {
const cubes = data.getElementsByTagName("Cube")
for( const element of cubes) {
if (!element.getAttribute('currency')) {
continue;
}
let countrycodes = element.getAttribute('currency')
let exchangerates = element.getAttribute('rate')
this.setState({
countrycodes: countrycodes,
exchangerates: exchangerates
})
}
});
}
render() {
return (
<div className="container2">
<div className="container1">
<select>{this.state.countrycodes.map((country) => {
<option>{country}</option>})
}
</select>
</div>
</div>
)
}
}
export default Countries;
Thanks,
Robert

Use getAttribute:
class Countries extends React.Component {
constructor() {
super();
this.state = {
countryCodes: []
};
}
componentDidMount(){
fetch({url: 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'})
.then(response => response.text())
.then(str => (new window.DOMParser()).parseFromString(str, "text/xml"))
.then(data => {
const countryCodes = [];
const cubes = data.getElementsByTagName("Cube");
for (const element of cubes) {
if (!element.getAttribute('currency')) {
// skip cube with no currency
continue;
}
countryCodes.push({
currency:element.getAttribute('currency'),
rate: element.getAttribute('rate')
});
}
this.setState({countryCodes});
});
}
render() {
const options = this.state.countryCodes.map(
({currency, rate}) => (<option value={rate}>{currency} - {rate}</option>));
return (
<div className="container2">
<div className="container1">
<select>
{options}
</select>
</div>
</div>
)
}
}
To check you can open http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml and run fetch(...) directly in browser's console:

Related

what's the reason to get this error in React js - TypeError: this.state.data.map is not a function

I am new to React js and I am trying to fetch data from API and I am getting all data in this.state but while displaying in dropdown I am getting an above error. Please go through the code and let me know where I went wrong.
Json data:
{
"status":true,
"message":"Success",
"data":{
"id":37,
"pan_no":"12345",
"pan_name":"abhishek",
"pan_front_image":"C:\\fakepath\\download.jpg",
"customer_image":"C:\\fakepath\\download.jpg",
"document_type":"Driving License",
"document_front_image":"C:\\fakepath\\download.jpg",
"document_back_image":"C:\\fakepath\\download.jpg",
"bank_name":"ada",
"account_no":"12345",
"confirmed_acc_no":"12345",
"ifsc_code":"MITM2250451",
"account_holder_name":"fasfdas",
"phone_no":"1234567890",
"nick_name":"213123"
}
}
Code:
import React, { Component } from 'react';
class Home extends Component {
constructor() {
super();
this.state = {
data: [],
};
}
componentDidMount() {
fetch("http://127.0.0.1:8003/api/kyc/")
.then(results => results.json())
.then(data => this.setState({ data: data }));
// .catch(()=>this.setState({hasErrors:true}));
}
render() {
console.log(this.state.data);
return ( <div>
<div class = "ab-info-con" >
<h4> Menu </h4>
<select> {
this.state.data.map((obj) => { <
option value = { obj.id } > { obj.pan_no } </option>
})
} </select>
</div>
</div>
)
}
}
export default Home;
The error is being thrown because .map is an array method and you are calling it on an object. From the structure of your response and your code, I can guess that what you should do is this.
import React, {Component} from 'react';
class Home extends Component {
constructor(){
super();
this.state = {
id: '',
panNo: ''
};
}
componentDidMount() {
fetch("http://127.0.0.1:8003/api/kyc/")
.then(results=>results.json())
.then(data=>this.setState({ id: data.data.id, panNo: data.data.pan_no }));
// .catch(()=>this.setState({hasErrors:true}));
}
render(){
return (
<div>
<div class="ab-info-con">
<h4>Menu</h4>
<select>
{
<option value={this.state.id}>{this.state.panNo}</option>
}</select>
</div>
</div>
)
}
}
export default Home;
import React, { Component } from 'react';
class Home extends Component {
constructor() {
super();
this.state = {
data: [],
};
}
componentDidMount() {
fetch("http://127.0.0.1:8003/api/kyc/")
.then(results => results.json())
.then(data => this.setState({ data: data }));
// .catch(()=>this.setState({hasErrors:true}));
}
renderOptions = () => {
const { data } = this.state;
let options = [];
if (typeof data !== "undefined") {
if (Object.keys(data).length === 0) {
for (const property in data) {
options.push(<option value={property.id}>{property.pan_no}</option>)
}
}
}
return options;
}
render() {
console.log(this.state.data);
return (
<div>
<div class="ab-info-con">
<h4>Menu</h4>
<select>
{this.renderOptions()}
</select>
</div>
</div>
)
}
}
export default Home;
try this, i have added the empty check before looping the data.
Your example doesn't need mapping at all, as you are having only one record. To get the id and panNo you should use this.state.data.data.id and this.state.data.data.panNo.
If you would need to use map() on an object, I would suggest using Object.keys:
Instead of this:
<select> {
this.state.data.map((obj) => { <
option value = { obj.id } > { obj.pan_no } </option>
})
} </select>
Use this (doesn't make sense with this data structure, only to show the idea):
<select> {
Object.keys(this.state.data).map((obj) => { if(obj == 'data'){ <
option value = { this.state.data[obj].id } > { this.state.data[obj].pan_no } </option>
}})
} </select>

How to pass argument from functional component to class component

EDIT - I fixed this and posted the working code.
I'm working on a project and I am having a specific issue I can't figure out how to fix. I am displaying a list of champions images and when the user clicks on one of them (s) then it will change the page to display that champions name. Currently I can console.log any of the names without any issues which means my functional component Newchamp() is working! However I am having trouble passing an argument from NewChamp to the class component SpecificChamp. When I add the last line in Newchamp return and try to display it in SpecificChamp using {s} its undefined!
Is it possible to pass an argument from my functional class to my component class? if not how can I get the page to change to the specific image that is clicked? I am new to react and appreciate any help!
Can anyone please help me out with this
import React, { Component } from 'react';
import './Champions.css';
class AllChamps extends Component {
render() {
let champion = this.props.champion;
return(
<div className='champions'>
<h1> all champions</h1>
{Object.keys(this.props.champions).map((s) => (
<div className='champs' onClick={() => this.props.NewChamp({s, champion})}>
<img
alt='Champion Images'
src={`http://ddragon.leagueoflegends.com/cdn/10.16.1/img/champion/${s}.png`}
onClick={this.props.onClick}
></img>
{s}
</div>
))}
</div>
)}}
class SpecificChamp extends Component {
render() {
let champion = this.props.champion
let Spec = champion[champion.length - 1];
return (
<div className='champions'>
<h1> 1 champions</h1>
<div className='champs'>
<button onClick={this.props.onClick}></button>
{Spec}
</div>
</div>
)}
}
class Champions extends Component {
constructor(props) {
super(props);
this.handleAllChamps = this.handleAllChamps.bind(this);
this.handleSpecificChamp = this.handleSpecificChamp.bind(this);
this.NewChamp = this.NewChamp.bind(this);
this.state = {
champions: [],
champion: [],
clickedChamp: false,
thisChamp: 'ahri'
}}
NewChamp = (props) =>
{
let s = props.s;
props.champion.push(s);
fetch(`http://ddragon.leagueoflegends.com/cdn/10.16.1/data/en_US/champion/${s}.json`)
.then(response => { return response.json() })
.then((response) => {
Object.keys(response.data).map((a) => (s = a
))})
fetch(`http://ddragon.leagueoflegends.com/cdn/10.16.1/data/en_US/champion/${s}.json`)
.then(response => { return response.json() })
.then((response) => {
console.log(s)
console.log(response.data)
console.log(props.champion)
})
console.log(`http://ddragon.leagueoflegends.com/cdn/10.16.1/data/en_US/champion/${s}.json`);
}
handleAllChamps = (props) => {
this.setState({ clickedChamp: true,
})};
handleSpecificChamp = () => {
this.setState({ clickedChamp: false,
})};
componentDidMount(props) {
const apiUrl = `http://ddragon.leagueoflegends.com/cdn/10.16.1/data/en_US/champion.json`;
fetch(apiUrl)
.then(response => { return response.json() })
.then((response) => {
this.setState({
champions: response.data
}, () => (this.state.champions))
return
})
}
render() {
const clickedChamp = this.state.clickedChamp;
let display;
if (clickedChamp ) {
display = <SpecificChamp champion={this.state.champion} onClick={this.handleSpecificChamp} s={this.state.thisChamp}/>;
} else {
display = <AllChamps champions={this.state.champions} onClick={this.handleAllChamps} NewChamp={this.NewChamp} thisChamp={this.state.thisChamp} champion={this.state.champion} />;
}
return (
<div>
<div className='champions'></div>
{display}
</div>
);
}
}
export default Champions;
The render function in class component does not has any props. You should use props from this like what you have done with handle click.
class SpecificChamp extends Component {
render() {
return (
<div className='champions'>
<h1> 1 champions</h1>
<div className='champs'>
<button onClick={this.props.onClick}></button>
{this.props.s}
</div>
</div>
)}
}

In React, How to update state between multiple select dropdowns , each with same options?

I have a problem where I have to render 4 dropdowns, each with similar options(i just rendered 1 here). If I select an option in any one of the dropdowns, that option should not be available in the other three.
How should I update the selected_planets in the state? The below code updates the selected_planets from 1 select dropdown. But still that same option is available everywhere and I could not able to get 4 different options inside selected_planets array? How should I proceed?
Also, the response from API fetch is an array of objects, which I mapped through and update in planets Array. For demo purpose, let's consider, planets: [Neptune, Saturn, Mars, Earth, Venus, Jupiter]
import React, { Component } from 'react';
export default class Dashboard extends Component {
state = {
planets: [],
selected_planets: []
};
componentDidMount() {
fetch('url')
.then(response => {
return response.json();
})
.then(data => {
this.setState({ planets: data });
});
}
handleSelect = event => {
this.setState({ selected_planets: [event.target.value] });
};
render() {
let select_Planets = this.state.planets.map(planet => {
return planet.name;
});
let options = select_Planets.map(planet => (
<option key={planet} value={planet}>
{planet}
</option>
));
return (
<select onChange={this.handleSelect}>
<option defaultChecked></option>
{options}
</select>
);
}
}
This can be achieved by producing a new set of options for each dropdown on render based off of what are the currently selected options, and what is the selected option for that dropdown.
First make sure each dropdown is binding to a property in your component's state and updating on change:
constructor() {
super();
this.state = {
planets: ["a", "b", "c", "d"],
inputs: {
d1: "",
d2: ""
}
};
this.handleChange = this.handleChange.bind(this);
}
handleChange({ target }) {
this.setState({
...this.state,
inputs: {
...this.state.inputs,
[target.name]: target.value
}
});
}
<select
name="d1"
value={this.state.inputs.d1}
onChange={this.handleChange}>
Then you can obtain a list of the selected planets within the render method by converting the input object into an array using Object.values():
const selectedPlanets = Object.values(this.state.inputs);
Then create a new array for each of the dropdowns which will omit any planets which have already been selected unless it is selected by that particular dropdown itself:
const d1Options = this.state.planets.filter(
p => !selectedPlanets.find(sP => sP === p) || p === this.state.inputs.d1
);
const d2Options = this.state.planets.filter(
p => !selectedPlanets.find(sP => sP === p) || p === this.state.inputs.d2
);
<select
name="d1"
value={this.state.inputs.d1}
onChange={this.handleChange}>
<option></option>
{d1Options.map(o => (
<option key={o}>{o}</option>
))}
</select>
I've put together a working example here:
https://codepen.io/curtis__/pen/pozmOmx
You can solve this is multiple ways. Here is pseudo-code.
Create an object or array to hold the values of selected index. I will suggest to use useState API. Instead of setState.
class extends Component {
static state = {
dropdown1: "",
dropdown2: "",
dropdown3: "",
dropdown4: ""
}
constructor(props) {
super(props)
}
handleClick = (id, {target: {value}}) => {
this.setState({
[id]: value
})
}
render() {
<div>
<select onChange={this.handleClick.bind(null, "dropdown1")}>
</select>
<select onChange={this.handleClick.bind(null, "dropdown2")}>
</select>
<select onChange={this.handleClick.bind(null, "dropdown3")}>
</select>
<select onChange={this.handleClick.bind(null, "dropdown4")}>
</select>
</div>
}
}
You should replace this line
let select_Planets = this.state.planets.map(planet => {
return planet.name;
});
with
let select_Planets = this.state.planets.filter(planet => !this.state.selected_planets.includes(planet))
This will make sure that the only available options are those that have not been selected already. you can do it for all the other dropdowns.
you also replace the following lines
handleSelect = event => {
this.setState({ selected_planets: [event.target.value] });
};
with
handleSelect = event => {
this.setState({ selected_planets: [...this.state.selected_planets, event.target.value] });
};
You can manage all value in a Mother component, passing the selected option to all child components.
function DropdownBoxes({ url }) {
const [options, setOptions] = useState([]);
const [selected, setSelected] = useState({ a: 'ABC', b: 'CDE' });
useEffect(() => { // componentDidMount
fetch(url).then(setOptions);
}, [url]);
const onChange = (key, value) => {
setSelected(prev => ({ // this.setState
...prev,
[key]: value,
}));
}
const filterOptions = Object.values(selected); // ['ABC', 'CDE']
return (
<div>
<DropdownBox
options={options}
filterOptions={filterOptions}
value={selected['a']}
onChange={val => onChange('a', val)}
/>
<DropdownBox
options={options}
filterOptions={selected}
value={selected['b']}
onChange={val => onChange('b', val)}
/>
</div>
)
}
When you render the options, add a filter to show the option only if it is equal to the value, or it is not a subset of filterOptions. If you cannot/ do not want to chnage any code of dropdown box, you can add the filter to mother component when to passing options.
function DropdownBox({options, filterOptions, value, onChange}) {
...
const filter = (opt) => {
return opt.value === value || !filterOptions.includes(opt.value);
}
return (
<select>
{options.filter(filter).map(opt => (
<option value={opt.value} ...>{opt.label}</option>
))}
</select>
)
}
You can create a SelectDropDown view component and render it in the parent component. And the selected value for all dropdown is maintained by its parent component eg: selectedPlanets state.
// structure for selectedPlanets state.
type SelectedPlanets = {
[dropDownId: string]: string
};
Rusable SelectDropDown.js
import * as React from 'react';
type Props = {
valueField: string,
primaryKeyField: string, // field like id or value.
selectedValues: Array<string>, // values of the primaryField attribute that uniquely define the objects like id, or value
options: Array<Object>,
handleSelect: (event: SystheticEvent<>) => void
}
export default class SelectDropDown extends React.Component<Props> {
render() {
const {
options,
selectedValues,
primaryKeyField,
valueField,
handleSelect
} = this.props;
const optionsDom = options.map(option => {
if(!selectedValues.includes(option[primaryKeyField])){
return (
<option key={option[primaryKeyField]} value={option[valueField]}>
{planet}
</option>
);
}
});
return (
<select onChange={handleSelect}>
<option defaultChecked></option>
{optionsDom}
</select>
);
}
}
Sudo code Dashboard.js
import * as React from 'react';
import SelectDropDown from "./SelectDropDown"
export default class Dashboard extends React.Component {
state = {
planets: [],
selectedPlanets: {}
};
/*
Assumimg planets struct
[{
planetId: 1,
planet: "Earth"
}]
*/
componentDidMount() {
fetch('url')
.then(response => {
return response.json();
})
.then(data => {
this.setState({ planets: data });
});
}
handleSelect = (dropDownId, event) => {
const { selectedPlanets } = this.state;
const selectedPlanetsCopy = {...selectedPlanets};
selectedPlanetsCopy[dropDownId] = event.target.value;
this.setState({ selectedPlanets: selectedPlanetsCopy });
};
getSelectedValues = (dropDownId) => {
const {selectedPlanets} = this.state;
const selectedValues = [];
Object.keys(selectedPlanets).forEach((selectedPlanetDropDownId) => {
if(dropDownId !== selectedPlanetDropDownId) {
selectedValues.push(selectedPlanets[selectedPlanetDropDownId]);
}
});
return selectedValues;
}
render() {
const { planets } = this.state;
return (
<SelectDropDown
valueField={"planet"}
primaryKeyField={"planetId"}
selectedValues={this.getSelectedValues("dropDown1")}
options={planets}
handleSelect={this.handleSelect.bind(this, "dropDown1")}
/>
<SelectDropDown
valueField={"planet"}
primaryKeyField={"planetId"}
selectedValues={this.getSelectedValues("dropDown2")}
options={planets}
handleSelect={this.handleSelect.bind(this, "dropDown2")}
/>
);
}
}

How to show information from API when using search box in ReactJS?

I'm using the Star Wars API to build a React JS project. The aim of my app is to be able to search for characters.
Here is my code for the search component in the my app.
At the moment I'm able to retrieve data the API and show the information on the page but I can't work out how to show this information when it's searched for.
Any ideas?
import React, { Component } from 'react';
class Search extends Component {
constructor(props){
super(props)
this.state = {
query:'',
peoples: [],
}
}
onChange (e){
this.setState({
query: e.target.value
})
if(this.state.query && this.state.query.length > 1) {
if(this.state.query.length % 2 === 0){
this.componentDidMount()
}
}
}
componentDidMount(){
const url = "https://swapi.co/api/people/";
fetch (url,{
method:'GET'
}).then(results => {
return results.json();
}).then(data => {
let peoples = data.results.map((people) => {
return(
<ul key={people.name}>
<li>{people.name}</li>
</ul>
)
})
this.setState({peoples: peoples});
console.log("state", peoples)
})
}
render() {
return (
<form>
<input
type="text"
className="search-box"
placeholder="Search for..."
onChange={this.onChange.bind(this)}
/>
{this.state.peoples}
</form>
)
}
}
export default Search
You could put your fetch in a separate function instead of in componentDidMount and call that when the component mounts and when your query changes.
Since you might be creating multiple requests if the user types quickly, you could use a debounce to only send one request, or use something that verifies that you always use the result of the latest request, like e.g. a token.
Example
class Search extends Component {
token = null;
state = {
query: "",
people: []
};
onChange = e => {
const { value } = e.target;
this.setState({
query: value
});
this.search(value);
};
search = query => {
const url = `https://swapi.co/api/people?search=${query}`;
const token = {};
this.token = token;
fetch(url)
.then(results => results.json())
.then(data => {
if (this.token === token) {
this.setState({ people: data.results });
}
});
};
componentDidMount() {
this.search("");
}
render() {
return (
<form>
<input
type="text"
className="search-box"
placeholder="Search for..."
onChange={this.onChange}
/>
{this.state.people.map(person => (
<ul key={person.name}>
<li>{person.name}</li>
</ul>
))}
</form>
);
}
}
You have to define it in diff function to manage easy.
import React, { Component } from 'react';
class Search extends Component {
constructor(props) {
super(props)
this.state = {
query: null,
peoples: [],
}
}
componentDidMount() {
this.serachPeople(this.state.query);
}
onChange(e) {
this.setState({ query: e.target.value }, () => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
this.serachPeople(this.state.query);
}
} else {
this.serachPeople(this.state.query);
}
})
}
serachPeople(query) {
const url = "https://swapi.co/api/people/";
if (query) {
// if get value ion query so filter the data based on the query.
fetch(url, {
method: 'GET'
}).then(results => {
return results.json();
}).then(data => {
let peoples = data.results.filter(people => people.name === query).map((people) => {
return (
<ul key={people.name}>
<li>{people.name}</li>
</ul>
)
})
this.setState({ peoples: peoples });
console.log("state", peoples)
})
} else {
fetch(url, {
method: 'GET'
}).then(results => {
return results.json();
}).then(data => {
let peoples = data.results.map((people) => {
return (
<ul key={people.name}>
<li>{people.name}</li>
</ul>
)
})
this.setState({ peoples: peoples });
console.log("state", peoples)
})
}
}
render() {
return (
<form>
<input
type="text"
className="search-box"
placeholder="Search for..."
onChange={this.onChange.bind(this)}
/>
{this.state.peoples}
</form>
)
}
}
export default Search;
I hope this will help for u. Let me know if u have any query.

javascript/ReactJS: Show results from backend in a list

I am sending a GET request on a Node API with a MongoDB server. I am getting the response as JSON in an array of object format. I want to show all those results in a list. Right now i am making a function like this
class VendorDashboard extends React.Component {
constructor() {
super();
this.state = {
paginationValue: '86',
title: ""
}
this.handleLogout = this.handleLogout.bind(this);
this.gotoCourse = this.gotoCourse.bind(this);
}
componentDidMount() {
axios.get('/vendor/showcourses') //the api to hit request
.then((response) => {
console.log(response);
let course = [];
course = response.data.map((courseres) => {
this.setState({
title: courseres.title
});
})
});
Right now what is happening is it is showing just one result. I want to show all results on that api. How can i do it?
This segment here is overriding the title per course.
course = response.data.map((courseres) => {
this.setState({
title: courseres.title
});
})
You can keep the state as an array of titles and do;
course = response.data.map((courseres) => {
return courseres.title;
})
this.setState({titles: course});
And then you can repeat on the array of titles in your component.
Like so in the render method;
const { titles } = this.state;
return <div>{titles.map((title, index) => <div key={index}>{title}</div>)}</div>
You need to collect all the server response and set that as an array of data to the state and use this state data to render:
class VendorDashboard extends React.Component {
constructor() {
super();
this.state = {
paginationValue: '86',
course: []
}
this.handleLogout = this.handleLogout.bind(this);
this.gotoCourse = this.gotoCourse.bind(this);
}
componentDidMount() {
axios.get('/vendor/showcourses') //the api to hit request
.then((response) => {
const course = response.data.map((courseres) => ({
id: courseres.id,
title: courseres.title
}));
this.setState({
course
});
});
}
render() {
return (
<ul>
{
this.state.course.map((eachCourse) => {
return <li key={eachCourse.id}>{eachCourse.title}</li>
})
}
</ul>
)
}
}
In each map iteration you rewrite your piece of state, it is wrong.
Just put courses in your state:
console.log(response);
this.setState({ courses: response.data });
In render method go through your state.courses:
render(){
return(
<div>
{this.state.courses.map(course => <h2>{course.title}</h2>)}
</div>
);
}

Categories

Resources