populating list item through json object reactjs - javascript

i have a json file that i fetch from a url then i put this object into a state variable, then when the user enters a city name in the search bar i fetch what the user entered and comapre it to my object's row location_city and then populate the card item accordingly, but the showResult function is not working for some reason and i can not figure it out, and when i press the button it keeps logging to the console the object fetched
update: it started magically working without me actually doing anything, there might have been a lagging problem or something, but anyway i still have the thread up because i would love if someone could tell me how i can implement the search function better and actually be able to populate more than one list item, and also if someone could help me make the slider navigation better , like when you click next on the last picture it goes to the first one and vice versa
import React from 'react';
import Card from './Card.js';
export default class Slider extends React.Component {
constructor() {
super()
this.state = {
imgArray: [ "/img/work1.jpg", "/img/work2.jpg", "/img/work3.jpg"],
imgNo: 0,
price: null,
location: null,
image: null,
name: null,
score: null,
url: "https://www.deskbookers.com/nl-nl/sajax.json?q=Amsterdam&type=-&people=any&favorite=0&pid=&sw=52.293753%2C4.634942&ne=52.455562%2C5.162286&ids=17201%2C19640%2C13692%2C13691%2C12136%2C17938%2C15292%2C14886%2C14885%2C14884%2C14883%2C15730%2C15353%2C15351%2C15330%2C15080%2C17290%2C15454%2C15451%2C15379",
current: "/img/work1.jpg",
search: null,
resultObject: null,
headlines: ["Pink Floyd Office", "Led Zeppelin Mania", "Central Perk Friends"],
headline : "Pink Floyd Office"
};
}
componentDidMount(){
this.serverRequest = $.get(this.state.url, function(result){
var info = result;
console.log(info);
this.setState({
resultObject:info
})
}.bind(this));
}
nextImg(){
if(this.state.imgNo < 2 && this.state.imgNo >=0 ){
this.setState({
imgNo : ++this.state.imgNo ,
current: this.state.imgArray[this.state.imgNo],
headline: this.state.headlines[this.state.imgNo]
})
console.log(this.state.imgNo);
}
}
prevImg(){
if(this.state.imgNo >= 1 && this.state.imgNo < 3 ){
this.setState({
imgNo : --this.state.imgNo,
current: this.state.imgArray[this.state.imgNo],
headline: this.state.headlines[this.state.imgNo]
})
console.log(this.state.imgNo);
}
}
searchQuery(){
this.setState({
search: this.refs.searchValue.value
})
}
showResult(){
var i;
for(i = 0; i<this.state.resultObject.rows.length; i++){
if(this.state.search == this.state.resultObject.rows[i].location_city){
this.setState({
price: this.state.resultObject.rows[i].day_price,
location: this.state.resultObject.rows[i].location_city,
image: this.state.resultObject.rows[i].image_urls2[0],
name: this.state.resultObject.rows[i].location_name,
score: this.state.resultObject.rows[i].location_rating
})
}
}
}
render(){
return(
<div>
<div class="slider ">
<div class="img-container">
<img src={this.state.current} class="main-img" />
<div class="headline"><span>{this.state.headline}</span></div>
</div>
<img src="/img/slider-left.png" class="slider-arrow" onClick={this.prevImg.bind(this)} />
<img src="/img/slider-right.png" class="slider-arrow slider-right" onClick={this.nextImg.bind(this)} />
<div class="search-container">
<input onChange={this.searchQuery.bind(this)} type="text" ref="searchValue" name="search" placeholder="City name..." class="search-bar" />
<button onClick={this.showResult.bind(this)} class="search-button">Sit me!</button>
</div>
</div>
<ul class="card-list">
<Card price={this.state.price} location={this.state.location} image={this.state.image}
score={this.state.score} name={this.state.name}>
</Card>
</ul>
</div>
);
}
}

Related

Broken icon is displayed for the image with alt text

I have an image called image.jpg inside the src -> images -> image.jpg. I have some problem with my image on my React app. My code is running well but the image does not show up instead on saving and loading the application, the image is not displayed but broken icon is displayed with alt text. How is it possible to solve this problem?
What I have tried is:
import React from "react";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
term: "",
names: [
{ name: "Deepak", job_profile: "Quality Analyst", description: "He is Passionate in Tester" },
{ name: "Deepinder", job_profile: "iOS Developer", description: "He is a Dedicated iOS Developer" }
],
filteredData: [{}]
};
}
render() {
let terms = "";
if (this.state.term) {
terms = this.state.term.toLowerCase();
}
return (
<div className="App">
<label>Search Employee: </label>
<input
type="text"
value={this.state.term}
id="searchEmp"
placeholder="Enter Name"
onChange={(event) => {
if (event.target.value.indexOf(" ") > -1) {
alert("Please don\'t enter space.");
this.setState({ term: "" });
return;
}
this.setState({ term: event.target.value });
}}
/>
<br />
<br />
{this.state.names &&
this.state.names
.filter((x) => x.name.toLowerCase().startsWith(terms) || (x.description.toLowerCase().includes(terms)))
.map((item) => {
return (
<div className="data-body">
<div>Name : {item.name}</div>
<div>Job Profile : {item.job_profile}</div>
<div>Description : {item.description}</div>
<div><img src={require('../src/images/image.jpg')} alt="profile_picture" /></div>
<input type="button" id="button"
value="Delete" onClick={() => {
this.setState
({ names: this.state.names.filter
(i => i.name !== item.name) });
}}/>
<div>{<br></br>}</div>
</div>
);
})}
</div>
);
}
}
export default App;
I assume that you are using create-react-app to bundle your project. If that is the case, you just need to put all your images in the public folder and just mention the name in the src attribute.
You don't need the require function while mentioning the source of an image.
So, your code should look like this:
<img src="image.jpg" alt="profile_picture"/>
If you want the image to reside in some part of your source directory, you can import the image from there and use it in your code as follows:
import Image from '../images/image.jpg'
<img src={Image} alt="profile_picture"/>
Edit
Using ES5 syntax, you could do the following:
const Image = require("../images/image.jpg")
<img src={Image} alt="profile_picture"/>
i hope this helps you
<div>
<img src='../src/images/image.jpg' alt="profile_picture" />
</div>

Can't access an array inside an Object after passed props to a modal component

i'm building this application with the help of the RestCountries Api to be able to show each country with basic details on a grid, and after a click on each box the app will show a modal with more detailed informations. That's my code so far:
class App extends React.Component{
constructor (props){
super (props);
this.state={
countries : [],
clickedCountry: {},
modalOn : false,
}
}
componentDidMount(){
axios.get(`https://restcountries.eu/rest/v2/all`)
.then(res => {
const data = res.data;
this.setState({
countries : data
})
let countries = this.state.countries
console.log(countries);
})
}
showInfo = (name) => {
this.setState({
clickedCountry : this.state.countries.find(it => it.name===name),
modalOn : true
});
}
closeModal =()=>{
this.setState({
modalOn : false
})
}
render() {
return (
<div className="container">
{this.state.countries.map(country=>
<Country name={country.name}
key={country.name}
population ={country.population}
region={country.region}
capital={country.capital}
flag={country.flag}
showInfo={this.showInfo}
languages={country.languages}
/>
)}
<div style={{display: this.state.modalOn? "block" : "none"}}>
<Modal closeModal={this.closeModal}
name={this.state.clickedCountry.name}
population={this.state.clickedCountry.population}
region={this.state.clickedCountry.region}
capital ={this.state.clickedCountry.capital}
flag={this.state.clickedCountry.flag}
nativeName ={this.state.clickedCountry.nativeName}
subregion={this.state.clickedCountry.subregion}
topLevelDomain={this.state.clickedCountry.topLevelDomain}
languages={this.state.clickedCountry.languages}
/>
</div>
</div>
)
}
}
Modal component :
const Modal = ({closeModal, name, population, region, capital, flag, languages, nativeName, subregion, topLevelDomain, currencies}) => {
return (
<div className="modal">
<div className="modal-content">
<span onClick={closeModal}>x</span>
<div className="img">
<img src={flag}/>
</div>
<p>{name}</p>
<p>Native name: {nativeName}</p>
<p>population: {population}</p>
<p>Region: {region}</p>
<p>Sub Region: {subregion}</p>
<p>Top level domain: {topLevelDomain}</p>
<p>Capital: {capital}</p>
</div>
</div>
)
}
So far for now i have mapped each country and the modal on click is showing more detailed informations. The problem now is the fact i need to access in the api an array that is nested inside an object:
area: 91
gini: null
timezones: ["UTC-04:00"]
borders: []
nativeName: "Anguilla"
numericCode: "660"
currencies: [{…}]
languages: [{…}]
translations: {de: "Anguilla", es: "Anguilla", fr: "Anguilla", ja: "アンギラ", it: "Anguilla", …}
flag: "https://restcountri
I need to access the languages array. Now if i try to map languages inside the country component, i can display the informations. But i want to show the langauges only on the modal component, and if i map the clickedCountry state responsible for the modal i will receive the error that "languages" is undefined. How it comes if is the same object filtered through the find function? Hope i was clear guys, cheers.
I know you understood whats happened!, just add this to Modal component
<ul>
{
languages && languages.map(lan=> {return <li>{lan.name}</li>} )
}
</ul>

React - one event handler to toggle one of multiple similar elements

I'm trying to practice React by rebuilding an agency website. I'm working on a section which has staff images, and clicking one of those images opens the relevant staff bio in a modal. The images and the bios are in separate containing divs.
It feels like I should be able to write one event handler that finds and opens the relevant bio depending on which image is clicked (maybe using something like the data attribute?), but I can't figure out what I'd need to add.
Currently I just have a click handler which toggles a piece of 'active' state. That state is then added as a className to toggle whether the modal is showing. Problem of course being that it doesn't differentiate between bios, so they all show regardless which bio is clicked on.
In case it's useful, here is my 'staff bio' component:
const StaffBio = (props) => {
return (
<div className={`teamMemberOverlay ${props.active}`} onClick={props.onClick}>
<div className="teamMemberExpanded">
<h6>{props.name}</h6>
<div className="seperator"></div>
<p className="title">{props.title}</p>
</div>
</div>
);
}
Which is being used like this:
<StaffBio name="NAME HERE" title="TITLE HERE" active={this.state.active} onClick={this.showBio} />
So far I've got the images set up as follows:
<img src={PaulIllustration} className="staffPhoto" onClick={this.showBio} />
And lastly, my event handler:
showBio() {
let toggle = this.state.active === 'is-active' ? '' : 'is-active';
this.setState({active: toggle});
}
class AboutUsSlider extends Component {
constructor(props) {
super(props);
this.showBio = this.showBio.bind(this)
this.next = this.next.bind(this)
this.state = { active: null }
}
next() {
this.refs.slider.slickNext()
}
showBio(id) {
this.setState({active: id});
}
hideBio(){
this.setState({active: null});
}
render() {
var settings = {...}
const people = [{name: 'Paul', title: 'some title'}, {name: 'Ben', title: 'other'}, ...];
return (
<div>
<Slider ref="slider" {...settings}>
<div className="sliderPage">
<h2>Meet our team</h2>
<div className="seperator"></div>
<div className="teamPhotos">
{ // When setting the index, you should use something unique, I'll use the name here.
people.map((p, index) =>
<img key={p.name} src={`${p.name} + 'Illustration'`} className="staffPhoto" onClick={() => this.showBio(index)}) />
}
</div>
<Button BGColor="#009ECC" text="Our process" onClick={this.next} />
</div>
</Slider>
{ this.state.active && <StaffBio name={people[this.state.active]} title={people[this.state.active].title} onClick={this.hideBio}/>
</div>
)
}
EDITED
There are a couple of things you can do.
Each person probably has an id to identify it. So you could modify your showBio to look like this:
showBio(id) {
this.setState({ active: id })
}
This way, you get which person is currently active in your state.
You also need to change your img
<img src={PaulIllustration} className="staffPhoto" onClick={() => this.showBio(PaulId)} />
Where PaulId would be different for each person.
And your StaffBio:
<StaffBio name="NAME HERE" title="TITLE HERE" active={this.state.active == personId} onClick={this.showBio} />
const StaffBio = (props) => {
return (
<div className={`teamMemberOverlay ${props.active ? 'is-active' : ''}`} onClick={props.onClick}>
<div className="teamMemberExpanded">
<h6>{props.name}</h6>
<div className="seperator"></div>
<p className="title">{props.title}</p>
</div>
</div>
);
}

reactjs render array of objects into a list gives an error

so i create an object from a fetched json file and in showResult() function i add an object based on some conditions to an the array finalArray and i pass this array to the component Card and this should render a list, but it gives me an error items.map is not a function , but also if i change
finalArray: this.state.finalArray.push(new user(price, location, image, name, score))
to
finalArray: this.state.finalArray.concat(new user(price, location, image, name, score))
then it works but it only then shows the last object only showing only 1 list item which is not what i want, can someone help with pointing out the error or how to do this correctly since i am new to react and javascript
import React from 'react';
import Card from './Card.js';
export default class Slider extends React.Component {
constructor() {
super()
this.state = {
imgArray: [ "/img/work1.jpg", "/img/work2.jpg", "/img/work3.jpg"],
imgNo: 0,
url: "https://www.deskbookers.com/nl-nl/sajax.json?q=Amsterdam&type=-&people=any&favorite=0&pid=&sw=52.293753%2C4.634942&ne=52.455562%2C5.162286&ids=17201%2C19640%2C13692%2C13691%2C12136%2C17938%2C15292%2C14886%2C14885%2C14884%2C14883%2C15730%2C15353%2C15351%2C15330%2C15080%2C17290%2C15454%2C15451%2C15379",
current: "/img/work1.jpg",
search: '',
resultObject: null,
finalArray: [],
headlines: ["Pink Floyd Office", "Led Zeppelin Mania", "Central Perk Friends"],
headline : "Pink Floyd Office"
};
}
componentDidMount(){
this.serverRequest = $.get(this.state.url, function(result){
var info = result;
console.log(info);
this.setState({
resultObject:info
})
}.bind(this));
}
nextImg(){
if(this.state.imgNo < 2 && this.state.imgNo >=0 ){
this.setState({
imgNo : ++this.state.imgNo ,
current: this.state.imgArray[this.state.imgNo],
headline: this.state.headlines[this.state.imgNo]
})
}
}
prevImg(){
if(this.state.imgNo >= 1 && this.state.imgNo < 3 ){
this.setState({
imgNo : --this.state.imgNo,
current: this.state.imgArray[this.state.imgNo],
headline: this.state.headlines[this.state.imgNo]
})
}
}
searchQuery(e){
this.setState({
search: e.target.value
})
}
showResult(){
for(var i=0 ; i<this.state.resultObject.rows.length; i++){
if(this.state.search.toLowerCase() == this.state.resultObject.rows[i].location_city.toLowerCase()){
var price = this.state.resultObject.rows[i].day_price;
var location=(this.state.resultObject.rows[i].address[0]+", "+this.state.resultObject.rows[i].address[1]+", "+this.state.resultObject.rows[i].address[2]);
var image=this.state.resultObject.rows[i].image_urls2[0];
var name=this.state.resultObject.rows[i].location_name;
var score=this.state.resultObject.rows[i].location_rating;
if( price!=null && location!=null && image!=null && name!=null && score !=null){
function user(price, location, image, name, score){
this.price = price;
this.location = location;
this.image = image;
this.name = name;
this.score = score;
}
this.setState({
finalArray: this.state.finalArray.push(new user(price, location, image, name, score))
})
}
$(".card-list").show();
$('html,body').animate({
scrollTop: $(".card-list").offset().top},
'slow');
}
else{
$(".alert-box, .cancel").animate( { "opacity": "show", bottom:"0"} , 1250 );
$(".alert-box, .cancel").animate( { "opacity": "hide", bottom:"0"} , 3750 );
this.setState({
search: ""
})
$(".card-list").hide();
break;
}
}
}
render(){
return(
<div>
<div class="slider ">
<div class="img-container">
<img src={this.state.current} class="main-img" />
<div class="headline"><span>{this.state.headline}</span></div>
</div>
<img src="/img/slider-left.png" class="slider-arrow" onClick={this.prevImg.bind(this)} />
<img src="/img/slider-right.png" class="slider-arrow slider-right" onClick={this.nextImg.bind(this)} />
<div class="search-container">
<img src="/img/cancel.png" class="cancel hide"/>
<span class="alert-box hide">No offices available in this city, please try another one!</span>
<input onChange={this.searchQuery.bind(this)} value={this.state.search} type="text" name="search" placeholder="City name..." class="search-bar" />
<button disabled={!this.state.search} onClick={this.showResult.bind(this)} class="search-button">Sit me!</button>
</div>
</div>
<Card finalArray={this.state.finalArray}></Card>
</div>
);
}
}
import React from 'react';
export default class Card extends React.Component {
render(){
var items = this.props.finalArray;
var itemslist = items.map(function(item,index){
return(
<li key={index} class="card">
<img src={item.image} class="card-img" />
<div>
<div class="card-info">
<p class="workplace-name">{item.name}</p>
<span class="score">{item.score}</span>
<p class="location">{item.location}</p>
</div>
<div class="card-footer">
<p class="price">{item.price} €</p>
</div>
</div>
</li>
);})
return(
<ul class="card-list">
{ itemslist }
</ul>
);
}
}
The .push method returns the new length of the array. So when you do
this.setState({
finalArray: this.state.finalArray.push(...)
});
you are changing the value of this.state.finalArray from an array to a number. Of course numbers don't have a .map method.
If you want to add a new element to the array and create a new array, you can use .concat instead:
this.setState({
finalArray: this.state.finalArray.concat(...)
});
Overall your code appears to be more complicated than it has to be. E.g. the user function is unnecessary, just create the object directly. The null checks might also be unnecessary.
I'm not exactly sure how you expect your code to work, but to me it looks like the showResult results method should rather look like this:
showResults() {
var search = this.state.search.toLowerCase();
var finalArray = this.state.resultObject.rows
.filter(row => search == row.location_city.toLowerCase())
.map(row => ({
price: row.day_price,
location: rows.address.slice(0,3).join(', '),
image: row.image_urls2[0],
name: row.location_name,
score: row.location_rating,
}))
.filter(user => user.price != null &&
user.image != null &&
user.name != null &&
user.score != null
);
this.setState(
{
finalArray,
search: finalArray.length > 0 ? this.state.search : '',
},
() => {
// This is executed after the component updated
if (finalArray.length > 0) {
$(".card-list").show();
$('html,body').animate({
scrollTop: $(".card-list").offset().top
}, 'slow');
} else {
$(".alert-box, .cancel").animate( { "opacity": "show", bottom:"0"} , 1250 );
$(".alert-box, .cancel").animate( { "opacity": "hide", bottom:"0"} , 3750 );
$(".card-list").hide();
}
}
);
}
That is, create your data first, an array of objects and update the components state. After the update, check whether there are results or not show or hide the list based on that result. Note that manually changing the style of components is not something you'd usually do with React.

Move method from one component to another in React and still be able to use in original

I am working to create a small contact app using React with ES6. I had some data displaying in the render function of a component - see the link to the question below for the original structure...
How to specify a key for React children when mapping over an array
However, because I was also putting a form on the same page and I needed to update my data in state, I had to move the data to a higher level component.
Now I'm having trouble traversing the components so that my original list of contacts shows up on the left. I had to remove most of what was in my render function on the contact-list component because it was completely breaking the build.
First, here is the address-book component with the form - this is working, both pulling in my initial 3 contacts from state, then concating new contacts from the form to the array. (Still need cleanup code here to make UI work right...)
import React from 'react';
import ContactList from './contact-list.jsx';
import ContactForm from './contact-form.jsx';
import ShortContact from './short-contact.jsx';
class AddressBook extends React.Component {
constructor() {
"use strict";
super();
this.state = {
showContacts: true,
contacts: [
{
id: 1,
fName: "aaa",
lName: "aaaaa",
imgUrl: "http://brainstorminonline.com/wp-content/uploads/2011/12/blah.jpg",
email: "aaa#aaaa.com",
phone: "999999999999"
},
{
id: 2,
fName: "bbbbb",
lName: "bbbbbbb",
imgUrl: "https://media.licdn.com/mpr/mpr/shrinknp_200_200/bbb.jpg",
email: "bbb#bbb-bbb.com",
phone: "888888888888"
},
{
id: 3,
fName: "Number",
lName: "Three",
imgUrl: "http://3.bp.blogspot.com/-iYgp2G1mD4o/TssPyGjJ4bI/AAAAAAAAGl0/UoweTTF1-3U/s1600/Number+3+Coloring+Pages+14.gif",
email: "three#ccccc.com",
phone: "333-333-3333"
}
];
};
}
render() {
"use strict";
return (
<div className="row address-book">
<div className="col-md-6">
<ContactList />
</div>
<div className="col-md-6">
<button className='btn' id="contact-submit-button" type="submit" value="Create Contact">Create New Contact </button>
<div>
<ContactForm addContact={this._addContact.bind(this)}/>
</div>
</div>
</div>
);
}
_addContact (fName, lName, company, email, phone, imgURL) {
"use strict";
const contact = {
id: this.state.contacts.length + 1,
fName,
lName,
company,
email,
phone,
imgURL
};
this.setState({ contacts: this.state.contacts.concat([contact]) });
}
_getContacts() {
"use strict";
return contactList.map((contact) => {
"use strict";
return (
<ShortContact contact={contact} key={contact.id}/>)
});
}
_getContactsTitle (contactCount) {
"use strict";
if(contactCount === 0) {
return 'No Contacts';
} else if (contactCount === 1) {
return '1 contact';
} else {
return `${contactCount} contacts`;
}
}
}
export default AddressBook;
However, the bottom 2 methods _getContacts and _getContactsTitle are the ones that are needed in my ContactForm component - which is this one:
import React from 'react';
import ShortContact from './short-contact.jsx';
class ContactList extends React.Component {
render() {
const contacts = this._getContacts();
return (
<div>
<h3>List of Contacts</h3>
<h4 className="contact-count">{this._getContactsTitle((contacts.length))}</h4>
<ul className="contact-list">
{contacts}
</ul>
</div>
);
}
}
export default ContactList;
The const that defines contacts as well as the <h4> through the <ul> is what breaks the app because as you can see it references the _getContactTitle method from the other component as well as {contacts} which is in the _getContacts method.
I'm guessing I need to do something like wrap them into functions and pass them - but I've gotten turned around and can't quite see how that would work here with React. Any help would be welcome. Thanks!
You need to pass the contacts down via props instead of trying to build the list of contacts in the parent to pass down. what I mean is this.
ADDRESS BOOK
render() {
"use strict";
let contacts = this._getContacts();
-----------^---------------^----------- get the list in the parent
return (
<div className="row address-book">
<div className="col-md-6">
<ContactList contacts={contacts} />
-------------------------------^-----------^---- important part here
</div>
<div className="col-md-6">
<button className='btn' id="contact-submit-button" type="submit" value="Create Contact">Create New Contact </button>
<div>
<ContactForm addContact={this._addContact.bind(this)}/>
</div>
</div>
</div>
);
}
then in your CONTACT LIST component just use the list as props.
render() {
let {contacts} = this.props;
------------^--------------^---- its in the props now
let title = contacts.length > 1 ? `${contacts.length} contacts` : contacts.length === 1 ? '1 contact' : 'No Contacts';
return (
<div>
<h3>List of Contacts</h3>
<h4 className="contact-count">{title}</h4>
<ul className="contact-list">
{contacts}
</ul>
</div>
);
}
}
from here you can remove the contactTitle function. let the child render that since it knows the length of the array (because its in props).
As a side note, in your _addContact function. instead of creating a new array and concatenating it with the state contact array just push the new one onto the current state. its better storage (i.e. dont need to make a new array to combine a new item to the array).
this.setState({ contacts: this.state.contacts.push(contact) });

Categories