Reactjs problems when ajax data from json file - json

I have a problem when I develop a react web application. Here's my code:
class TableContentRow extends React.Component {
render(){
return(
<tr>
<td>{this.props.voucher.merchantName}</td>
<td>{this.props.voucher.voucherCode}</td>
<td>{this.props.voucher.orderId}</td>
<td>{this.props.voucher.deal}</td>
<td>{this.props.voucher.dealDescription}</td>
<td>{this.props.voucher.price}</td>
<td>{this.props.voucher.redemptionStatus}</td>
<td>{this.props.voucher.redemptionTimestamp}</td>
</tr>
);
}
}
class TableContent extends React.Component {
render() {
const rows = [];
this.props.vouchers.forEach((voucher) => {
if(voucher.orderId.indexOf(this.props.filterText) === -1){return;}
rows.push(<TableContentRow voucher = {voucher} key = {voucher.orderId} />);
})
return(
<div className="panel panel-primary">
<div className="panel-heading">
<h3 className="panel-title">
All Vouchers
</h3>
</div>
<table className="table table-striped">
<thead>
<tr>
<th>Restaurant</th>
<th>Voucher Code</th>
<th>Order ID</th>
<th>Deal</th>
<th>Deal Description</th>
<th>Sale Price</th>
<th>Redemption Status</th>
<th>Redemption Timestamp</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
);
}
}
class VoucherAll extends React.Component {
constructor(props){
super(props);
this.handleFilterTextInput = this.handleFilterTextInput.bind(this);
this.loadVouchersFromServer = this.loadVouchersFromServer.bind(this);
this.state = {filterText: ''};
}
handleFilterTextInput(filterText) {
this.setState({
filterText: filterText
});
}
loadVouchersFromServer() {
$.ajax({
url: this.props.url,
success: function(data) {
this.setState({
data: data
});
},
error: function(xhr,status,err) {
console.log(this.props.url, status, err.toString());
}
})
}
componentDidMount() {
this.loadVouchersFromServer();
setInterval(this.loadVouchersFromServer, this.props.pollInterval);
}
render(){
return(
<div className="container">
<TableContent
vouchers = {this.state.data}
filterText = {this.state.filterText}
/>
</div>
);
}
}
ReactDOM.render(
<VoucherAll url = "voucher.json" pollInterval = {2000} />,
document.getElementById('voucherAll')
)
And here's my json file:
{
"merchantName":"xxxx",
"voucherCode":"xxxx",
"orderId":"xxxx",
"deal":"xxxx",
"dealDescription":"xxxx",
"price":"xxxx",
"redemptionStatus":"xxxx",
"redemptionTimestamp":"xxxx-xx-xx"
}
When I run my code, the web page shows nothing. And in the console, I cannot find any relative message. Can anyone help me to figure that out? Thanks.

You are loosing context inside ajax callbacks. Though loadVouchersFromServer is binded success and error callbacks aren't. You could use arrow functions or bind those callbacks.
loadVouchersFromServer() {
$.ajax({
url: this.props.url,
success: data => {
this.setState({
data: data
});
},
error: function(xhr,status,err) {
console.log(this.props.url, status, err.toString());
}.bind(this)
})
}

Related

How to pass json data object {items.symbol} value to another class

I am using React js. I have a class Stock.js where I am fetching an api and displaying the data on the webpage in the form of table.
When I click on the table data (table data are links) It sends the item.symbol to onhandleclick() method. For example:
|Symbol|Age|
|X | 20|
|Y |22 |
So the values in symbol table are referred as item.symbol
Here if I click on X it sends the value X to onhandleclick() and now I want to send this value X or Y whichever user clicks on to another class. By another class I mean let's say I have a class xyz.js I wanna send the value of item.symbol to class xyz.js so I can use this value and do whatever I want with that value in my xyz.js class. Is there a way to do it?
My code: (Stock.js)
import React, { Component } from "react";
import { Link } from "react-router-dom";
import Symbols from "./Symbols";
export default class Stocks extends Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
symbolsname: "",
};
}
handleClick(symbol) {
//pass the value to another class here
}
componentDidMount(symbol) {
fetch("http://131.181.190.87:3001/all")
.then((res) => res.json())
.then((json) => {
this.setState({
isLoaded: true,
items: json,
});
});
}
render() {
let filteredItems = this.state.items.filter((item) => {
return (
item.symbol.toUpperCase().indexOf(this.state.search.toUpperCase()) !==
-1 || item.industry.indexOf(this.state.search) !== -1
);
});
var { isLoaded, items } = this.state;
if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div>
<table border={2} cellPadding={1}>
<thead>
<tr>
<th>Symbol</th>
<th>Name</th>
<th>Industry</th>
</tr>
</thead>
<tbody>
{filteredItems.map((item) => (
<tr>
<Link to="/symbols">
<td
key={item.symbol}
onClick={() => this.onhandleclick(item.symbol)} //here I am passing the value of item.symbol to onhandleclick()
>
{item.symbol}
</td>
</Link>
<td key={item.name}>{item.name}</td>
<td key={item.industry}>{item.industry}</td>
</tr>
))}
}
</tbody>
</table>
</div>
);
}
}
}
After doing what maniraj-murugansaid in the answers, it says undefined, so I have uploaded the screenshot
You could redirect to symbol.js using history.push with click event handler like, (Remove Link tag here) So change,
<Link to="/symbols">
<td key={item.symbol} onClick={() => this.onhandleclick(item.symbol)} //here I am passing the value of item.symbol to onhandleclick()>
{item.symbol}
</td>
</Link>
to,
<td key={0} onClick={() => this.onhandleclick(item.symbol)}
style={{ cursor: "pointer", color: "blue" }}
>
{item.symbol}
</td>
And onHandleClick function like,
onhandleclick(data) {
const { history } = this.props;
history.push({
pathname: "/Symbol",
symbol: data
});
}
Here the second property is props that you can pass which is symbol in your case so you can give it like, symbol: data ..
Working Sandbox: https://codesandbox.io/s/react-router-v4-withrouter-demo-2luvr
Update:
-> After the update from OP , there are some changes that have been made.
=> import { BrowserRouter } from "react-router-dom"; in the main component index.js where you are initializing the parent component in the call to ReactDOM.render .
index.js:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
const rootElement = document.getElementById("root");
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
rootElement
);
stocks.js:
import React, { Component } from "react";
import { Link } from "react-router-dom";
import Symbols from "./Symbols";
const filteredItems = [
{ symbol: "X", name: "item1", industry: "industry1" },
{ symbol: "Y", name: "item2", industry: "industry2" }
];
export default class Stocks extends Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
search: "",
symbol: ""
};
}
updateSearch(event) {
this.setState({ search: event.target.value });
}
onhandleclick(data) {
const { history } = this.props;
history.push({
pathname: "/Symbols",
symbol: data
});
}
componentDidMount() {}
render() {
return (
<div>
<form className="form-for-table-search">
Search symbol or industry:  
<input
type="text"
value={this.state.search}
onChange={this.updateSearch.bind(this)}
/>
   {" "}
<button type="button" className="btn-submit">
Search
</button>
<br />
</form>
<table border={2} cellPadding={1}>
<thead>
<tr>
<th>Symbol</th>
<th>Name</th>
<th>Industry</th>
</tr>
</thead>
<tbody>
{filteredItems.map((item, index) => (
<tr key={index}>
<td
key={0}
onClick={() => this.onhandleclick(item.symbol)} //here I am passing the value of item.symbol to onhandleclick()
style={{ cursor: "pointer", color: "blue" }}
>
{item.symbol}
</td>
<td key={item.name}>{item.name}</td>
<td key={item.industry}>{item.industry}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}
Symbols.js:
import React from "react";
export default class Symbol extends React.Component {
componentDidMount() {
console.log("came here", this.props.location.symbol);
}
render() {
return <div>Symbol value: {this.props.location.symbol}</div>;
}
}
Updated Sandbox
You could export a function from Symbol.js and use that in handleClick.
// Symbol.js
export default class Symbol {
doSomething(symbol) {
// do something
}
}
// Stocks.js
import Symbol from 'Symbol.js';
handleClick(symbol) {
Symbol.doSomething(symbol);
};

Object actions in vue

I have the following structure in Vue.
The App.vue
export default {
name : "app",
router,
data() {
return {
items: {books:[], authors:[]}
};
},
created: function() {
customServiceInstance.makeAjaxCall("books.json", "get").then(res => {
this.items.books = res.books;
return res;
})
customServiceInstance.makeAjaxCall("authors.json", "get").then(res => {
this.items.authors = res.authors;
return res;
})
customServiceInstance.makeAjaxCall("genres.json", "get").then(res => {
this.items.genres = res.genres;
return res;
})
},
methods: {
removeEntry:function(index) {
this.$delete(this.items.books, index);
customServiceInstance.makeAjaxCall('books.json', 'POST', JSON.stringify(this.items.books));
}
},
computed: {
booksWithAuthor () {
let { books, authors } = this.items
return books.map(book => ({
...book,
author: authors.find(author => author.id === book.author),
}))
},
}
}
</script>
<template>
<div id="app">
<router-link to="/home" >Home 1</router-link>
<router-link to="/home/2"> Home 2</router-link>
<router-view class="view" foo="123"></router-view>
<table class="booksTable">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Genre</th>
<th>Image</th>
<th>Availability</th>
<th>Options</th>
</tr>
</thead>
<tbody>
<tr v-for="(book,index) in booksWithAuthor" v-bind:key="book.name">
<td>{{book.name}}</td>
<td>{{book.author.name}}</td>
<td>{{book.genre}}</td>
<td><img class="imageBook" :src="book.imageUrl"></td>
<td v-if="book.availability">Available</td>
<td v-else>Unavailable</td>
<td>
<button class="btn add">Add</button>
<button class="btn edit" >Edit</button>
<button class="btn delete" v-on:click="removeEntry(index)">Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import './styling.scss';
import customService from './components/customService';
const customServiceInstance= new customService();
import Vue from 'vue';
import VueRouter from 'vue-router';
import HomeR from './components/home.vue';
import Copil from './components/copil.vue';
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{path: '/home', component: HomeR},
{path: '/home/:years', component: Copil, props:true }
]
})
And this JS
export default class CustomService {
listJson(url){
var storageLocalData = localStorage.getItem(url);
var obj=JSON.parse(storageLocalData);
console.log(obj);
};
makeAjaxCall(url, methodType, data){
this.listJson(url);
var promiseObj = new Promise(function(resolve, reject){
var storageLocalData = localStorage.getItem(url);
if(!storageLocalData){
var xhr = new XMLHttpRequest();
xhr.open(methodType, url, true);
if (data) {
xhr.send(data);
} else {
xhr.send();
}
xhr.onreadystatechange = function(){
if (xhr.readyState === 4){
if (xhr.status === 200){
var response = xhr.responseText;
var respJson = JSON.parse(response);
localStorage.setItem(url, JSON.stringify(respJson));
resolve(respJson);
} else {
reject(xhr.status);
}
}
}
}
else {
resolve(JSON.parse(storageLocalData));
}
});
return promiseObj;
};
}
I want to create an object Book and have a function getBookById(id, list),
The list being the books.json that's being loaded.I want this function to return the book object, who has name, author, genre and so on.
I tried a lot of things, but with no result.
Even tried in a ts file something like this:
export default class Book {
name: String;
id: Number;
author: String;
genre: Number;
imageUrl: String;
availability: boolean;
methods: {
getBookById:(id: Number,url: String) => Book {
}
}
Please help me
I want to create an object Book and have a function getBookById(id,
list), The list being the books.json that's being loaded.I want this
function to return the book object, who has name, author, genre
this can be achieved by the es6 array function find().
all you have to do inside your function, is:
getBookById(bookId,booksList){
return booksList.find(book=>
book.id===bookId)
}
the function will return the first array item that matches the condition (book.id===bookId), or undefined if none of them did match.

React js iteration of JSON array is not working

i am unable to repeat the row dynamically. when i am using .map method it is showing .map is not a function.
Component
import React, {Component} from 'react';
const pannelWidth = {
width: '90%'
};
const pannelHeader = {
color: 'white'
};
class ProjectList extends Component {
constructor(props) {
super(props);
this.state = {
projectList : ''
}
//this.deleteProjectMessage = this.deleteProjectMessage.bind(this);
}
componentDidMount() {
let d = '';
$.get("http://localhost:8008/api/navigation/all", function (data) {
d = data;
this.setState({
projectList: d
});
}.bind(this));
console.log(this.state.projectList);
}
render() {
var listItems = this.state.projectList.map(function (item, index) {
return <tr>
<td>{item.name}</td>
<td>{item.description}</td>
<td><i className="glyphicon glyphicon-trash"></i></td>
</tr>
});
return(
<div className="container" style={pannelWidth}>
<br/>
<div className="panel panel-primary">
<div className="panel-heading">
<div className="row">
<div className="col-md-2 col-lg-2">
<h4 style={pannelHeader}>Project List</h4>
</div>
<div className="col-md-offset-8 col-lg-offset-8 col-md-2 col-lg-2">
<button className="btn btn-sm btn-success">Create New Project</button>
</div>
</div>
</div>
<div className="panel-body">
<table className="table table-striped">
<thead>
<tr>
<th>Project Name</th>
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{listItems}
</tbody>
</table>
</div>
</div>
</div>
);
}
}
export default ProjectList;
JSON
[
{
"name": "pro",
"description": "Testing of pro"
},
{
"name": "Test",
"description": "Testing of Test"
}
]
i am calling api from my local system and getting above response and updating the state. then i am trying to render in side table row using map() but it is showing map is not a function in console.
you are defaulting projectList to a string, default it to an empty array.
this.state = {
projectList : '' // should be projectList: []
}
You are making an async request so the initial render of the component is trying to call map on the initial state which is
''.map(function (item, index) {
return <tr>
<td>{item.name}</td>
<td>{item.description}</td>
<td><i className="glyphicon glyphicon-trash"></i></td>
</tr>
});
If the data comes back as a string, you can't map it. You first need to parse the JSON text into a JavaScript array.
d = JSON.parse(data);

this.state.post.map is not a function

I am receiving json data from server and i am trying to access it individually using map function in front end but i am receiving an error as this.state.post.map is not a function. below is the code how do i overcome this.
import React from 'react';
import axios from 'axios';
//require('./style.scss');
class Premontessori extends React.Component{
constructor(props){
super(props);
this.state={
post:[]
};
}
componentDidMount(){
let self = this;
axios.get('http://localhost:8080/list')
.then(function(data) {
console.log(data);
self.setState({post:data});
});
}
render(){
return (
<div>
<table>
<tbody>
{
this.state.post.map(function(item, index){
return (
<tr>
<td>{item.Id}</td>
<td>{item.Name}</td>
<td>{item.Age}</td>
</tr>
)
})
}
</tbody>
</table>
</div>
);
}
}
export default Premontessori;
From your comment,
'since this.state.post is not an array hence you get an error that map is not a function.
You need to map over the data in the post object like
<tbody>
{this.state.post.data.map(function(item, index) {
return (
<tr key={index}>
<td>{item.Id}</td>
<td>{item.Name}</td>
<td>{item.Age}</td>
</tr>
)
})
}
</tbody>
I think you have to make an adjustment in the code in the componentDidMount life-cycle method as shown below. This is because the response from the axios call is wrapping the array data, so you have to fetch that array and update your state if the status code is 200.
import React from 'react';
import axios from 'axios';
//require('./style.scss');
class Premontessori extends React.Component{
constructor(props){
super(props);
this.state={
post:[]
};
}
componentDidMount(){
let self = this;
axios.get('http://localhost:8080/list')
.then(function(res) {
console.log(res);
if(res.status === 200){
self.setState({post:res.data});
}
})
.catch(function(err){
console.log(err);
});
}
render(){
return (
<div>
<table>
<tbody>
{
this.state.post.map(function(item, index){
return (
<tr>
<td>{item.Id}</td>
<td>{item.Name}</td>
<td>{item.Age}</td>
</tr>
)
})
}
</tbody>
</table>
</div>
);
}
}
export default Premontessori;

json not render with reactjs and redux

i load a .json with axios, and the file load well, but when i rendered dont work
editprofile.js --> create the dispatch, and load de json
export const editProfile = (callback)=>{
return function(dispatch){
dispatch({type: 'EDIT_PROFILE_REQUEST'});
axios({
method: 'get',
url: 'https://gist.githubusercontent.com/anonymous/38c1444f753c70cf79ee980638a14de7/raw/34951eebfa006fea3db00fb492b491ac990c788e/vamos.json',
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
.then((response)=>{
dispatch({type:'EDIT_PROFILE_SUCCESS', payload:response.data});
if (typeof callback === 'function') {
callback(null, response.data);
}
})
.catch((error) =>{
dispatch({type:'EDIT_PROFILE_FAILURE'});
if(error.response.status == 401){
browserHistory.push('login')
toastr.error(error.response.message, 'User')
}
if(typeof callback ==='function'){
callback(error.response.data, null)
}
})
}
}
EditProfileComponent.jsx -->created the component
export default class EditProfileComponent extends Component{
render(){
return(
<table>
<thead>
<tr>
<th>SN</th>
<th>Email</th>
<th>created</th>
</tr>
</thead>
<tbody>
{this.renderEditProfile()}
</tbody>
</table>
)
}
renderEditProfile(){
let sN = 1;
return this.props.allProfile.map((user)=>{
return(
<tr key={user.sN} >
<td>{sN++}</td>
<td>{user.email ? user.email : '---'}</td>
<td>{user.created_at ? user.created_at : '---'}</td>
</tr>
);
});
}
}
join the component with the service
import {editProfile} from '../action/editProfile.js';
import EditProfileComponent from '../component/editProfileComponent.jsx';
export default class EditProfileContainer extends Component{
componentDidMount(){
this.props.editProfile();
}
render (){
return (
<EditProfileComponent allProfile={this.props.allProfile} />
);
}
}
function mapStateToProps(store) {
return {
allProfile:store.allProfile
};
}
function matchDispatchToProps(dispatch){
return bindActionCreators({
editProfile:editProfile
}, dispatch)
}
export default connect
(mapStateToProps, matchDispatchToProps)(EditProfileContainer);
editProfileReducer --> the reducer
export const editProfileReducer = (state=[], action) =>{
switch(action.type){
case 'EDIT_PROFILE_REQUEST':
return state;
case 'EDIT_PROFILE_FAILURE':
return state;
case 'EDIT_PROFILE_SUCCESS':
return [...action.payload];
default:
return state;
}
}
join all the reducer
import { editProfileReducer } from './reducer/editProfileReducer.js'
const reducers = combineReducers({
allProfile:editProfileReducer,
});
export default reducers;
There is an error in your reducer. For EDIT_PROFILE_SUCCESS, it should be
case 'EDIT_PROFILE_SUCCESS':
return [...state, action.payload];
On a side note, you can take advantage of es6's arrow function:
export const editProfile = (callback) => (dispatch) => {
dispatch({type: 'EDIT_PROFILE_REQUEST'});
// ....
};
You also should use constants for action names.
I think there is problem with :
function mapStateToProps(store) {
return {
allProfile:store.allProfile
};
}
it should be:
function mapStateToProps(state) {
return {
allProfile:state.allProfile
};
}