I need to pass value for same component
I need to pass my value here
<tr>
<td><b>Past</b></td>
<td className="hrs_applicant_data"><b>tes</b></td>
</tr>
Here Is my code
import React, { Component } from 'react';
import axios from 'axios';
import {UIPageSubNav} from '../../helpers/UI';
import {SpaceDiv} from '../../helpers/Generic';
//import ViewJobs from './ViewJobs';
//import PostJob from './PostJob';
import ViewApplicantDetails from './ViewApplicantDetails';
import Dropdown from 'react-dropdown';
import 'react-dropdown/style.css';
import {Tabs, Tab} from 'react-bootstrap-tabs';
import Popup from 'react-popup';
export default class ViewApplicants extends Component {
constructor(props) {
super(props);
this.state = {
//applicantResults:[],
//searchExperiencesResults: [],
//searchMessagesResults: [],
jobTitleList: null
};
}
// onReloadPage(callbackRef) {
// console.dir(callbackRef);
// this.onGetApplicants(callbackRef);
// }
onClickFitStaus(){
}
onClickApplicantDetails(personId){
console.log(personId.props.applicantData.PersonId);
var personId = personId.props.applicantData.PersonId;
var self = this;
axios.get(window.mg.api.baseURL + 'applicantdetails' + '/'+ personId, { // use id because personId != id
})
.then(function (response) {
var items = [];
var Element = [];
var index = [];
var experience: []
//console.log(response.data.results.experience);
var experience = response.data.results.experience;
console.log(experience);
//return false;
var applicantResult = response.data.results.main[0];
var id = personId;
//console.log(experience);
// self.setState({applicantResults: response.data.results.main[0]});
// self.setState({searchMessagesResults: response.data.results.message});
// self.setState({searchExperiencesResults: response.data.results.experience});
// let mySpecialPopup = Popup.register({
// title: 'I am special',
// content: 'Since I am special you might need me again later. Save me!',
// buttons: {
// left: ['cancel'],
// right: ['ok']
// }
// });
// Popup.alert('Hello');
// Popup.create({
// title: 'Immediate popup',
// content: 'This popup will be displayed straight away',
// className: 'alert',
// buttons: {
// right: ['ok']
// }
// }, true);
//Popup.queue(mySpecialPopup);
window.mg.popupRef.customPopup(
<ViewApplicantDetails applicantData={applicantResult} pid={id} customPopup={true} />
);
//check if we need to tell the callee to update too (React's smart update needed or not doesn't alway work the way I want)
})
.catch(function (error) {
console.log('error');
console.dir(error);
window.mg.popupRef.alert('Error getting data, please try again in a minute.');
});
}
// onClick(e) {
// // passes our data back up to the Employees component
// this.props.onClickRef(this.props.applicantData);
// }
onClickUpdateFit(fitStatus, personId){
// if (this.props.hasOwnProperty('userData') === false) {
// return (<div></div>);
// }
console.log(personId);
console.log(this.props.screenData);
var userId = 2;
axios.post(window.mg.api.baseURL + 'fitstatusupdate', {
headers: {
'X-CSRF-TOKEN': window.mg.api.token,
'Content-Type':'application/x-www-form-urlencoded'
},
params: {
personId: personId,
fitStatus: fitStatus,
message: null,
userId: userId
}
}).then(response => {
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
console.log('Error', thrown.message);
}
});
}
render() {
var self = this;
var applicant = this.props.applicantData;
const jobtitle = this.state.jobTitleList;
console.log(applicant);
return (
<div className="hrs_applicant_main_container">
<div className="sod_applicant_container">
<div className="sod_job_company_logo">
<img src={applicant.imgUrl} />
</div>
<div className="sod_job_details">
<div className="sod_job_header">
<div className="sod_job_info">
<h3 onClick={this.onClickApplicantDetails.bind(applicant.personId,this)}>{applicant.Name}</h3>
<span>{applicant.location}, {applicant.country}</span>
</div>
<div className="sod_job_salary">
<div>
<span><b>Date Uploaded</b></span>
<span>{applicant.created_at}</span>
</div>
</div>
<div className="sod_job_location">
<img src="../../images/icon_upload.png" />
<div>
<span><b>CV</b></span>
</div>
</div>
</div>
<div className="sod_job_content">
<div className="hrs_applicant_details">
<table className="hrs_applicant_datas">
<tr>
<td><b>Current</b></td>
<td className="hrs_applicant_data"><b>{applicant.created_at}</b></td>
</tr>
<tr>
<td><b>Past</b></td>
<td className="hrs_applicant_data"><b>tes</b></td>
</tr>
<tr>
<td><b>Education</b></td>
<td className="hrs_applicant_data"><b>CIPD, MCIPD | 2013</b></td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div className="hrs_applicant_action">
<div className="BTNFilled BTNOutlineFilled" onClick={() => this.onClickUpdateFit(0, applicant.PersonId)}>
<i class="fa fa-times"></i> <img src="/images/img_close.png" alt="" className="notFitIconImg" />
NOT A FIT
</div>
<div className="BTNFilled BTNOutlineFilled" onClick={() => this.onClickUpdateFit(1, applicant.PersonId)}>
<img src="/images/img_tick.png" alt="" className="fitIconImg" />
GOOD FIT
</div>
<div className="BTNFilled BTNOutlineFilled" onClick={this.onClickApplicantDetails.bind(applicant.personId,this)}>
<img src="/images/message-icon.png" alt="" className="fitIconImg" />
MESSAGE
</div>
<div className="BTNFilled BTNOutlineFilled DropdownButton">
<Dropdown options={jobtitle} onChange={this._onSelect} placeholder="ASSOCIATE TO JOB POSTING" />
</div>
</div>
</div>
)
var BTNStyle = {};
BTNStyle.marginRight = "10px";
BTNStyle.marginLeft = "0px";
BTNStyle.marginBottom = "30px";
}
}
Experience is a list of values right so you can display those values through map function. I will give one example below
this.state.experience = [{name:"anu",age:"21",email:"anu#gmail.com"},{name:"sai",age:"21",email:"sai#gmail.com"}]
render(){
return(
this.state.experience.map((user,index)=>{
return <p key={index}>{user.name} </p>
})
)
}
Related
I am trying to render multiple images, stored on firebase, on a single page.
This is the function to get the image url:
async getTokenImage(fileName) {
const ref = firebase.storage().ref('token/' + fileName);
const url = await ref.getDownloadURL();
console.log('in here')
return url
}
This is the function call:
<div
class="card-img-top"
style={{
backgroundColor: '#'+token.color,
backgroundImage: 'url('+this.getTokenImage(token.fileName).then((value) => {return value})+')'
}}></div>
console logging the "value" gives the needed image url
but returning the "value" gives nothing.
for the sake of testing, I have done this:
let image = this.getTokenImage(token.fileName).then((result) => {return result})
console.log(image)
which results in:
So how do I get the image url within the initial function call?
Part of grandfather component:
<MintedTokens
account={this.state.account}
contract={this.state.contract}
tokens={this.state.tokens} />
Father component:
import React from 'react';
import "./minted-tokens.scoped.css";
import MultipleMintedTokens from './multiple-minted-tokens.js';
import SingleMintedTokens from './single-minted-token.js';
class MintedTokens extends React.Component {
render() {
const color = window.location.pathname.split('/')[2]
let display
if(color == undefined) {
display = <MultipleMintedTokens
account={this.props.account}
tokens={this.props.tokens}></MultipleMintedTokens>
} else {
display = <SingleMintedTokens
account={this.props.account}
color={color}
tokens={this.props.tokens}></SingleMintedTokens>
}
return (
<div class="Minted-Token">
{display}
</div>
);
}
}
export default MintedTokens;
Child component (where the multiple images need to be rendered):
import React from 'react';
import ether from '../../assets/ether.svg'
import firebase from "../../firebase.js"
import { Link } from 'react-router-dom';
class MultipleMintedTokens extends React.Component {
async getTokenImage(fileName) {
const ref = firebase.storage().ref('token/' + fileName);
const url = await ref.getDownloadURL();
console.log('in here')
return url
}
render() {
return (
<div class="welcome minted-tokens">
<h1 class="">Minted Tokens</h1>
<div class="row">
{this.props.tokens.map((token, key) => {
let name = '',
description = ''
if(token.to == this.props.account) {
token.name.length >= 17 ? name = token.name.slice(0,17) + '...' : name = token.name
token.description.length >= 28 ? description = token.description.slice(0,29) + '...' : description = token.description
let image = this.getTokenImage(token.fileName).then((result) => {return result})
console.log(image)
return (
<Link to={token.tokenURI} key={key}>
<div class='token-id'>{token.id}</div>
<div class="card card-width">
<div
class="card-img-top"
style={{
backgroundColor: '#'+token.color,
backgroundImage: 'url('+this.getTokenImage(token.fileName).then((value) => {return value})+')'
}}></div>
<h5 class="card-header" style={{
backgroundColor: '#'+token.color,
color: '#'+token.fontColor}}>{name}</h5>
<div class="card-body">
<p class="card-text">{description}</p>
<div class="foot-of-card">
<span class="row price-row">
<img src={ether} alt="ether" class="icon" />
<p class="card-text">{token.price}</p>
</span>
<p class="card-text datetime">{token.dateTime}</p>
</div>
</div>
</div>
</Link>
)
}
})}
</div>
</div>
);
}
}
export default MultipleMintedTokens;
I could not test it well but you can try the following:
Add:
constructor(props) {
super(props);
this.state = {
urlImgs: [],
};
}
...
componentDidMount() {
const { tokens } = this.props;
const promiseArray = tokens.map((token) => getTokenImage(token.fileName));
Promise.all(promiseArray)
.then(valueArray => {
this.setState(prevState => ({
...prevState,
urlImgs: valueArray
}))
})
.catch(err => console.log(err));
}
...
backgroundImage: `url(${urlImgs[key] ?? null})`
All Code:
class MultipleMintedTokens extends React.Component {
constructor(props) {
super(props);
this.state = {
urlImgs: [],
};
}
async getTokenImage(fileName) {
const ref = firebase.storage().ref('token/' + fileName);
const url = await ref.getDownloadURL();
console.log('in here')
return url
}
componentDidMount() {
const { tokens } = this.props;
const promiseArray = tokens.map((token) => getTokenImage(token.fileName));
Promise.all(promiseArray)
.then(valueArray => {
this.setState(prevState => ({
...prevState,
urlImgs: valueArray
}))
})
.catch(err => console.log(err));
}
render() {
const { urlImgs } = this.state;
return (
<div class="welcome minted-tokens">
<h1 class="">Minted Tokens</h1>
<div class="row">
{this.props.tokens.map((token, key) => {
let name = '',
description = ''
if(token.to == this.props.account) {
token.name.length >= 17 ? name = token.name.slice(0,17) + '...' : name = token.name
token.description.length >= 28 ? description = token.description.slice(0,29) + '...' : description = token.description
let image = this.getTokenImage(token.fileName).then((result) => {return result})
console.log(image)
return (
<Link to={token.tokenURI} key={key}>
<div class='token-id'>{token.id}</div>
<div class="card card-width">
<div
class="card-img-top"
style={{
backgroundColor: '#'+token.color,
backgroundImage: `url(${urlImgs[key] ?? null})`
}}></div>
<h5 class="card-header" style={{
backgroundColor: '#'+token.color,
color: '#'+token.fontColor}}>{name}</h5>
<div class="card-body">
<p class="card-text">{description}</p>
<div class="foot-of-card">
<span class="row price-row">
<img src={ether} alt="ether" class="icon" />
<p class="card-text">{token.price}</p>
</span>
<p class="card-text datetime">{token.dateTime}</p>
</div>
</div>
</div>
</Link>
)
}
})}
</div>
</div>
);
}
}
export default MultipleMintedTokens;
if you have React with version > 16.8, I advice you to start using Stateless Components and Hooks. And read about the lifecycle of React and the render method here:
I built a shopping cart in my app. This is the flow:
- The customer sees a list of items and clicks on one he wants;
- The next page is where he chooses the quantity of products and then I save in localStorage;
- By clicking Confirm, he goes to the shopping cart with the same products he has chosen. On this page (shopping cart) he can change the quantity and in this moment the total and quantity must change (see the image).
I was able to do this reloading the page, but when in production the page is not working properly. I need to do this without reload the page.
How to do this without reload the page?
I got 3 components: Header (with the icon cart), ChooseQuantity and Cart.
See below the code:
//My Choose Quantity Component
import React from 'react';
import '../../components/ChooseQuantity/ChooseQuantity.css';
class ChooseQuantity extends React.Component {
constructor(props) {
super(props);
const { lotQuantity, totalQuantity, maxTotalItems, maxPurchase, lot, totalTickets, events, onChange } = this.props;
this.state = {
counter: 0,
lotQuantity: lotQuantity,
totalQuantity: totalQuantity,
maxTotalItems: maxTotalItems,
maxPurchase: maxPurchase,
totalTickets: totalTickets,
onChange: onChange,
events: events,
lot: lot,
tickets: events.tickets,
cart: []
}
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
}
componentDidMount() {
// console.log(this.state.lot);
// localStorage.setItem('teste', JSON.stringify(this.state.lotQuantity));
}
// static getDerivedStateFromProps(props, state) {
// console.log(props.lot);
// // console.log(state);
// if (props.selected !== state.selected) {
// return {
// selected: props.selected,
// };
// }
// }
// componentDidUpdate(prevProps, prevState) {
// console.log(this.props.lot);
// localStorage.setItem('teste', JSON.stringify(this.props.lot.quantity));
// if (this.props.lot !== prevProps.lot) {
// // this.selectNew();
// }
// }
async increment() {
await this.setState({
lotQuantity: this.state.lotQuantity + 1,
totalQuantity: + 1,
});
let lotUniqueNumber = this.state.lot.lotUniqueNumber;
let lotQuantity = this.state.lotQuantity;
var ar_lot = [];
this.state.tickets.lot.forEach(function (item) {
if (lotUniqueNumber === item.lotUniqueNumber) {
item.quantity = lotQuantity;
item.total = item.totalLotPrice * item.quantity
}
ar_lot.push(item);
})
// console.log(ar_lot);
//CALCULATING A QUANTITY
var ob_qtd = ar_lot.reduce(function (prevVal, elem) {
const ob_qtd = prevVal + elem.quantity;
return ob_qtd;
}, 0);
await this.setState({ totalTickets: ob_qtd })
//CALCULATING A QUANTITY
//CALCULATING THE TOTAL
var ob_total = ar_lot.reduce(function (prevVal, elem) {
const ob_total = prevVal + elem.total;
return ob_total;
}, 0);
// CALCULATING THE TOTAL
//RIDING THE SHOPPING CART
let total = {
price: ob_total,
totalQuantity: ob_qtd,
};
let tickets = {
name: this.state.tickets.name,
prevenda: this.state.tickets.prevenda,
unique_number: this.state.tickets.unique_number,
lot: ar_lot
}
let events = {
banner_app: this.state.events.banner_app,
installments: this.state.events.installments,
max_purchase: this.state.events.max_purchase,
name: this.state.events.name,
tickets: tickets
}
var cart = { events: events, total: total };
this.setState({
cart: cart
})
// console.log(cart);
localStorage.setItem('cart', JSON.stringify(cart));//RECORDING CART IN LOCALSTORAGE
localStorage.setItem('qtd', JSON.stringify(ob_qtd));
window.location.reload();//UPDATE PAGE FOR CHANGES TO BE UPDATED
}
async decrement() {
await this.setState({
lotQuantity: this.state.lotQuantity - 1,
totalQuantity: - 1,
totalTickets: this.state.totalTickets - 1,
});
let lotUniqueNumber = this.state.lot.lotUniqueNumber;
let lotQuantity = this.state.lotQuantity;
var ar_lot = [];
this.state.tickets.lot.forEach(function (item) {
if (lotUniqueNumber === item.lotUniqueNumber) {
item.quantity = lotQuantity;
item.total = item.totalLotPrice * item.quantity
}
ar_lot.push(item);
})
//CALCULANDO A QUANTIDADE
var ob_qtd = ar_lot.reduce(function (prevVal, elem) {
const ob_qtd = prevVal + elem.quantity;
return ob_qtd;
}, 0);
//CALCULANDO A QUANTIDADE
//CALCULANDO O TOTAL
var ob_total = ar_lot.reduce(function (prevVal, elem) {
const ob_total = prevVal + elem.total;
return ob_total;
}, 0);
//CALCULANDO O TOTAL
let total = {
price: ob_total,
totalQuantity: ob_qtd,
};
let tickets = {
name: this.state.tickets.name,
prevenda: this.state.tickets.prevenda,
unique_number: this.state.tickets.unique_number,
lot: ar_lot
}
let events = {
banner_app: this.state.events.banner_app,
installments: this.state.events.installments,
max_purchase: this.state.events.max_purchase,
name: this.state.events.name,
tickets: tickets
}
var cart = { events: events, total: total };
localStorage.setItem('cart', JSON.stringify(cart));
localStorage.setItem('qtd', JSON.stringify(ob_qtd));
window.location.reload();
}
render() {
return (
<div className="choose-quantity">
{
this.state.lotQuantity <= 0 ?
<div className="space-button"></div> :
<button className='minus' onClick={this.decrement}><i className="fas fa-minus"></i></button>
}
<div id='counter' className="qtd" value={this.state.lotQuantity} onChange={this.onChange}>{this.state.lotQuantity}</div>
{
this.state.totalTickets >= this.state.maxPurchase ?
<div className="space-button"></div> :
<button className="plus" onClick={() => this.increment(this.state.lotQuantity)}><i className="fas fa-plus"></i></button>
}
</div>
)
}
}
export default ChooseQuantity;
//My Shopping Cart Component
import React, { Component } from 'react';
import Swal from "sweetalert2";
import { Link } from 'react-router-dom';
import './Cart.css';
import '../../components/Css/App.css';
import Lot from './Lot';
import ChooseQuantity from './ChooseQuantity';
import Header from '../../components/Header/Header';
import Tabbar from '../../components/Tabbar/Tabbar';
const separator = '/';
class Cart extends Component {
constructor(props) {
super(props);
this.state = {}
this.choosePayment = this.choosePayment.bind(this);
}
async componentDidMount() {
const company_info = JSON.parse(localStorage.getItem('company_info'));
await this.setState({
company_image: company_info.imagem,
company_hash: company_info.numeroUnico,
})
const cart = JSON.parse(localStorage.getItem('cart'));
const total = cart.total;
if(cart){
const {
events,
events: { tickets },
total
} = cart;
await this.setState({
cart,
events,
tickets: tickets,
banner_app: events.banner_app,
eventName: cart.events.name,
priceTotal: total.price,
quantity: total.totalQuantity,
lots: tickets.lot,
maxTotalItems: cart.events.max_purchase,
selectedLots: tickets.lot,
total: total.totalQuantity
});
}
const teste = JSON.parse(localStorage.getItem('teste'))
this.setState({teste: teste})
}
choosePayment() {
Swal.fire({
title: 'Método de Pagamento',
text: 'Qual o médtodo de pagamento que você deseja usar?',
confirmButtonText: 'Cartão de Crédito',
confirmButtonColor: '#007bff',
showCancelButton: true,
cancelButtonText: 'Boleto Bancário',
cancelButtonColor: '#007bff',
}).then((result) => {
if (result.value) {
this.props.history.push('/checkout');
} else{
this.props.history.push('/checkout-bank-slip');
}
})
}
render() {
return (
<div>
<Header Title="Carrinho" ToPage="/" />
{
this.state.total <= 0 ?
<Tabbar />
:
null
}
<div className="cart">
<div className="container-fluid">
{
this.state.total > 0 ?
<div>
<div className="box-price">
<div className="row box-default ">
<div className="col col-price">
<h6>{this.state.quantity} INGRESSO{this.state.quantity > 1 ? 'S' : ''}</h6>
<h5>R$ {parseFloat(this.state.priceTotal).toFixed(2).replace('.', ',')}</h5>
</div>
</div>
</div>
<div className="row">
<div className="col-12 col-image no-padding">
<img src={this.state.banner_app} alt="" />
</div>
</div>
<div className="row">
<div className="col">
<h1 className="event-title text-center">{this.state.eventName}</h1>
</div>
</div>
<div className="padding-15">
{
this.state.lots.map((lot, l) =>
<div key={l}>
{
lot.quantity > 0 ?
<div>
<div className="row">
<div className="col">
<h5 className="ticket-name">{lot.ticketName}</h5>
</div>
</div>
<div className="row">
<div className="col-8">
<h5 className="lot-name">
{ lot.lotName } - ({lot.lotNumber}º Lote)
</h5>
<h6 className="lot-price">
R$ {lot.lotPrice.replace('.', ',')} ({lot.lotPrice.replace('.', ',')} + {lot.lotPriceTax.replace('.', ',')})
</h6>
</div>
<div className="col-4">
<h3 className='lot-big-price'>
{lot.lotPrice.replace('.', ',')}
</h3>
</div>
</div>
<div className="row">
<div className="col align-items">
<ChooseQuantity
lotQuantity={lot.quantity}
maxPurchase={this.state.events.max_purchase}
totalTickets={this.state.total}
lot={lot}
events={this.state.events}
maxTotalItems={this.state.maxTotalItems}
onCLick={this.onClick}
/>
</div>
</div>
</div>
:
null
}
</div>
)
}
<div className="row cart-footer" style={{ marginRight: '-15px', marginLeft: '-15px', backgroundColor: '#f4f7fa' }}>
<button className="col col-purchase" style={{ justifyContent: 'center', alignItems: 'center' }} onClick={this.choosePayment}>
Confirmar Comprar
</button>
</div>
</div>
</div>
:
<div className='padding-15'>
<div className="mt-5 no-margin box-default row">
<div className="col">
<h3 className="text-center">
Não há nenhum item em seu carrinho.
</h3>
<p className="text-center">
Toque no botão <strong>Buscar Eventos</strong> para iniciar uma nova pesquisa.
</p>
<Link className="btn btn-primary btn-block" to="/">
Buscar Eventos
</Link>
</div>
</div>
<div className="row no-margin box-default mt-3">
<div className="col">
<img src={`//www.yeapps.com.br/admin/files/empresa/${this.state.company_hash}/${this.state.company_image}`} alt={`${this.state.company_name}`} />
</div>
</div>
</div>
}
</div>
</div>
</div>
);
}
}
export default Cart;
//My Header Component
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
// import { withRouter } from 'react-router';
import './Header.css';
import BackButton from '../BackButton/BackButton';
class Header extends Component {
constructor(props){
super(props);
this.state = {
qtd: 0
}
}
componentDidMount() {
const qtd = JSON.parse(localStorage.getItem('qtd'));
this.setState({qtd: qtd});
}
render() {
const { Title } = this.props;
return (
<div>
<nav className="navbar">
{ this.props.Title === 'Home' ? null : <BackButton />}
<Link to="/cart" className="icon-cart">
<i className="fas fa-shopping-cart"></i>
<span className="badge badge-danger">
{this.state.qtd}
</span>
</Link>
<div className="navbar-brand">
{Title}
</div>
</nav>
</div>
);
}
}
export default Header;
You need to update your presentational components(Card,Header) state in componentDidUpdate the same way as you did it in componentDidMount. componentDidMount works only one time with first render and there you set your state variables which you use in render. Do the same setState in componentDidUpdate
P.S. but you need to be sure that with counter increasing your presentational components get new props which will trigger componentDidUpdate.In your case you use localStorage but it will be better to pass data through common parent container(the one which holds those 3 components) as props to Card and Header.
I am writing a simple react page that renders 2 different html tables based off of which button is clicked on the screen. The issue I am having is that the table that is rendered for each button click is associated with the previous button click. (E.G. if I click button 1 one time then click button 2 the table associated with button 1 will be displayed.)
I am new to react so in order to get the tables to update I refactored my code to hold as much of the state as possible in the App.js class, I created the toggleState callback to associate the button clicks with state change of the parent, and I then pass that to DataProvider via the endpoint property. I realize this is probably where the state / UI disconnect is occurring, but I'm uncertain of the cause since I'm adhering to react principles to the best of my capability.
my class structure is as follows:
App
/ \
/ \
/ \
DataProvider ButtonToggle
|
Table
If it is relevant the table class is building the table based off of an API call, I will add the code for this, but it is not causing me problems so I do not believe it to be the source of the issue.
App.js
import React, { Component } from "react";
import PropTypes from "prop-types";
import DataProvider from "./DataProvider";
import Table from "./Table";
import ButtonToggle from "./ButtonToggle";
class App extends Component {
constructor(props){
super(props);
this.state = {
input : 'employees',
endpoint : "api/employees/"
};
console.log("constructor app: " + this.state.input + "\n" + this.state.endpoint);
}
toggleState(input) {
if(input == "employees") {
this.setState({input : input, endpoint: "api/employees/"});
}
else {
this.setState({input : input, endpoint: "api/categories/"});
}
console.log("toggleState " + this.state.input + "\n" + this.state.endpoint);
}
render() {
return (
<div className="col-lg-12 grid-margin">
<div className="card">
<div className="card-title">
<div className="row align-items-center justify-content-center">
<div className="col-3"></div>
<div className="col-6">
<h1> Striped Table</h1>
</div>
<div className="col-3"></div>
</div>
<ButtonToggle toggleInput={ (input) => this.toggleState(input)}/>
</div>
<div className="card">
<div className="card-title"></div>
<div className="card-body">
<DataProvider endpoint={this.state.endpoint}
render={data => <Table data={data} />} />
</div>
</div>
</div>
</div>
);
}
}
export default App;
DataProvider.js
class DataProvider extends Component {
static propTypes = {
endpoint: PropTypes.string.isRequired,
render: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
data: [],
loaded: false,
placeholder: "Loading..."
};
}
componentWillReceiveProps(props) {
console.log("dataprov: " + this.props.endpoint);
this.componentDidMount();
}
componentDidMount() {
fetch(this.props.endpoint)
.then(response => {
if (response.status !== 200) {
return this.setState({ placeholder: "Something went wrong" });
}
return response.json();
})
.then(data => this.setState({ data: data, loaded: true }));
}
render() {
const { data, loaded, placeholder } = this.state;
return loaded ? this.props.render(data) : <p>{placeholder}</p>;
}
}
export default DataProvider;
ButtonToggle.js
class ButtonToggle extends Component {
constructor (props) {
super(props);
}
render() {
return (
<div className="row align-items-center justify-content-center">
<div className="col-3 center-in-div">
<button type="button" className="btn btn-info btn-fw" onClick={this.props.toggleInput.bind(this, 'categories')}> Categories </button>
</div>
<div className="col-3 center-in-div">
<button type="button" className="btn btn-info btn-fw" onClick={this.props.toggleInput.bind(this, 'employees')}>
Employees
</button>
</div>
<div className="col-6"></div>
</div>
);
}
}
export default ButtonToggle;
Table.js : I don't think this is a problem, but I may stand corrected.
import React from "react";
import PropTypes from "prop-types";
import key from "weak-key";
const Table = ({ data }) =>
!data.length ? (
<p>Nothing to show. Records: {data.length} </p>
) : (
<div className="table-responsive">
<h2 className="subtitle">
Showing <strong>{data.length} items</strong>
</h2>
<table className="table table-hover">
<thead>
<tr>
{Object.entries(data[0]).map(el => <th key={key(el)}>{el[0]}</th>)}
</tr>
</thead>
<tbody>
{data.map(el => (
<tr key={el.id}>
{Object.entries(el).map(el => <td key={key(el)}>{el[1]}</td>)}
</tr>
))}
</tbody>
</table>
</div>
);
Table.propTypes = {
data: PropTypes.array.isRequired
};
export default Table;
Below is the minimum working code I could come up with. Your Button and Table components can be dumb components which will get data from parent component and will present it.
Your Parent or container component will have logic to set the properties for Button and Table component.
As Table and Button components are dumb you can go with functional components.
I have added the code for calling api (I have tried to mimic the api call) and getting data in same parent component, you can separate it out.
You can work on style and validations as per your needs.
Let me know if you need any further help.
class ParentComponent extends Component {
constructor() {
super();
this.state = {
name: "Category"
}
this.onBtnClick = this.onBtnClick.bind(this);
}
componentDidMount() {
this.getData(this.state.name)
}
getData(name) {
if (name === "Category") {
this.apiCall("/Category").then((data) => {
this.setState({ data: data })
})
} else {
this.apiCall("/Employee").then((data) => {
this.setState({ data: data })
})
}
}
apiCall(url) {
return new Promise((res, rej) => {
setTimeout(() => {
if (url === "/Employee") {
res([{ "Emp Name": "AAA", "Emp Age": "20" }, { "Emp Name": "BBB", "Emp Age": "40" }])
} else {
res([{ "Cat Id": "XXX", "Cat Name": "YYY" }, { "Cat Id": "MMM", "Cat Name": "NNN" }])
}
}, 1000)
});
}
onBtnClick(name) {
let newName = "Category"
if (name === newName) {
newName = "Employee"
}
this.setState({ name: newName, data: [] }, () => {
this.getData(newName);
})
}
render() {
return (<>
<ButtonComponent name={this.state.name} onBtnClick={this.onBtnClick}></ButtonComponent>
<TableComponent data={this.state.data} />
</>)
}
}
const ButtonComponent = ({ name, onBtnClick }) => {
return <Button onClick={() => { onBtnClick(name) }}>{name}</Button>
}
const TableComponent = ({ data }) => {
function getTable(data) {
return < table >
<thead>
<tr>
{getHeading(data)}
</tr>
</thead>
<tbody>
{getRows(data)}
</tbody>
</table >
}
function getHeading(data) {
return Object.entries(data[0]).map((key) => {
return <th key={key}>{key[0]}</th>
});
}
function getRows(data) {
return data.map((row, index) => {
return <tr key={"tr" + index}>
{Object.entries(data[0]).map((key, index) => {
console.log(row[key[0]]);
return <td key={"td" + index}>{row[key[0]]}</td>
})}
</tr>
})
}
return (
data && data.length > 0 ?
getTable(data)
: <div>Loading....</div>
)
}
I'm trying to make simple CRUD example using react.js as frontend.
I already have add/edit functionality done in a component,
but I want to call this component dynamically on click and show it as a popup or modal window on the same page without redirecting to another route.
Does anyone have experience with doing this using react.js?
This is my parent component code where I show a grid of items displaying cities:
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { Link, NavLink } from 'react-router-dom';
interface FetchNaseljeDataState {
nasList: NaseljeData[];
loading: boolean;
}
export class FetchNaselje extends React.Component<RouteComponentProps<{}>, FetchNaseljeDataState> {
constructor() {
super();
this.state = { nasList: [], loading: true };
fetch('api/Naselje/Index')
.then(response => response.json() as Promise<NaseljeData[]>)
.then(data => {
this.setState({ nasList: data, loading: false });
});
// This binding is necessary to make "this" work in the callback
this.handleDelete = this.handleDelete.bind(this);
this.handleEdit = this.handleEdit.bind(this);
}
public render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: this.renderNaseljeTable(this.state.nasList);
return <div>
<h1>Naselje Data</h1>
<p>This component demonstrates fetching Naselje data from the server.</p>
<p>
<Link to="/addnaselje">Create New</Link>
</p>
{contents}
</div>;
}
// Handle Delete request for an naselje
private handleDelete(id: number) {
if (!confirm("Do you want to delete naselje with Id: " + id))
return;
else {
fetch('api/Naselje/Delete/' + id, {
method: 'delete'
}).then(data => {
this.setState(
{
nasList: this.state.nasList.filter((rec) => {
return (rec.idnaselje != id);
})
});
});
}
}
private handleEdit(id: number) {
this.props.history.push("/naselje/edit/" + id);
}
// Returns the HTML table to the render() method.
private renderNaseljeTable(naseljeList: NaseljeData[]) {
return <table className='table'>
<thead>
<tr>
<th></th>
<th>ID Naselje</th>
<th>Naziv</th>
<th>Postanski Broj</th>
<th>Drzava</th>
</tr>
</thead>
<tbody>
{naseljeList.map(nas =>
<tr key={nas.idnaselje}>
<td></td>
<td>{nas.idnaselje}</td>
<td>{nas.naziv}</td>
<td>{nas.postanskiBroj}</td>
<td>{nas.drzava && nas.drzava.naziv}</td>
<td>
<a className="action" onClick={(id) => this.handleEdit(nas.idnaselje)}>Edit</a> |
<a className="action" onClick={(id) => this.handleDelete(nas.idnaselje)}>Delete</a>
</td>
</tr>
)}
</tbody>
</table>;
}
}
export class NaseljeData {
idnaselje: number = 0;
naziv: string = "";
postanskiBroj: string = "";
drzava: DrzavaData = { iddrzava: 0, naziv: ""};
drzavaid: number = 0;
}
export class DrzavaData {
iddrzava: number = 0;
naziv: string = "";
}
This is my child component that I want to dynamically show on create new link click:
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { Link, NavLink } from 'react-router-dom';
import { NaseljeData } from './FetchNaselje';
import { DrzavaData } from './FetchNaselje';
interface AddNaseljeDataState {
title: string;
loading: boolean;
drzavaList: Array<any>;
nasData: NaseljeData;
drzavaId: number;
}
export class AddNaselje extends React.Component<RouteComponentProps<{}>, AddNaseljeDataState> {
constructor(props) {
super(props);
this.state = { title: "", loading: true, drzavaList: [], nasData: new NaseljeData, drzavaId: -1 };
fetch('api/Naselje/GetDrzavaList')
.then(response => response.json() as Promise<Array<any>>)
.then(data => {
this.setState({ drzavaList: data });
});
var nasid = this.props.match.params["nasid"];
// This will set state for Edit naselje
if (nasid > 0) {
fetch('api/Naselje/Details/' + nasid)
.then(response => response.json() as Promise<NaseljeData>)
.then(data => {
this.setState({ title: "Edit", loading: false, nasData: data });
});
}
// This will set state for Add naselje
else {
this.state = { title: "Create", loading: false, drzavaList: [], nasData: new NaseljeData, drzavaId: -1 };
}
// This binding is necessary to make "this" work in the callback
this.handleSave = this.handleSave.bind(this);
this.handleCancel = this.handleCancel.bind(this);
}
public render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: this.renderCreateForm(this.state.drzavaList);
return <div>
<h1>{this.state.title}</h1>
<h3>Naselje</h3>
<hr />
{contents}
</div>;
}
// This will handle the submit form event.
private handleSave(event) {
event.preventDefault();
const data = new FormData(event.target);
// PUT request for Edit naselje.
if (this.state.nasData.idnaselje) {
fetch('api/Naselje/Edit', {
method: 'PUT',
body: data,
}).then((response) => response.json())
.then((responseJson) => {
this.props.history.push("/fetchnaselje");
})
}
// POST request for Add naselje.
else {
fetch('api/Naselje/Create', {
method: 'POST',
body: data,
}).then((response) => response.json())
.then((responseJson) => {
this.props.history.push("/fetchnaselje");
})
}
}
// This will handle Cancel button click event.
private handleCancel(e) {
e.preventDefault();
this.props.history.push("/fetchnaselje");
}
// Returns the HTML Form to the render() method.
private renderCreateForm(drzavaList: Array<any>) {
return (
<form onSubmit={this.handleSave} >
<div className="form-group row" >
<input type="hidden" name="idnaselje" value={this.state.nasData.idnaselje} />
</div>
< div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Naziv">Naziv</label>
<div className="col-md-4">
<input className="form-control" type="text" name="naziv" defaultValue={this.state.nasData.naziv} required />
</div>
</div >
<div className="form-group row">
<label className="control-label col-md-12" htmlFor="PostanskiBroj" >Postanski broj</label>
<div className="col-md-4">
<input className="form-control" name="PostanskiBroj" defaultValue={this.state.nasData.postanskiBroj} required />
</div>
</div>
<div className="form-group row">
<label className="control-label col-md-12" htmlFor="Drzava">Država</label>
<div className="col-md-4">
<select className="form-control" data-val="true" name="drzavaid" defaultValue={this.state.nasData.drzava ? this.state.nasData.drzava.naziv : ""} required>
<option value="">-- Odaberite Državu --</option>
{drzavaList.map(drzava =>
<option key={drzava.iddrzava} value={drzava.iddrzava}>{drzava.naziv}</option>
)}
</select>
</div>
</div >
<div className="form-group">
<button type="submit" className="btn btn-default">Save</button>
<button className="btn" onClick={this.handleCancel}>Cancel</button>
</div >
</form >
)
}
}
I'm assuming I'll have to make css for the create/edit component to make it look like a popup...
EDIT: I would appreciate if someone could make code example using my classes, thanks...
In the parent component set a state on click functionality, say for eg:
this.setState({display: true})
In the parent component render based on condition display child component, say for eg:
<div>{(this.state.display) ? <div><childComponent /></div> : ''}</div>
To display the child component in a modal/popup, put the component inside say a bootstrap or react-responsive-modal. For that, you have to install and import react-responsive-modal and then
In the render method,
return (
<div>
{this.state.toggleModal ? <div className="container">
<Modal open={this.state.toggleModal} onClose={this.onCloseModal} center>
<div className="header">
<h4>{Title}</h4>
</div>
<div className="body">
<div>
{this.state.toggleModal ? <someComponent /> : ''}
</div>
</div>
</Modal>
</div>
: null}
</div>
)
Have your popup component receive a prop from the parent that will tell it if it should be displayed or not, a simple boolean will do the trick. Then, when you want something to show the popup, just change that state in the parent.
i am using react-js-pagination.
i am able to fetch the data and can show the list of data. but i am trying to impelment paggination using react-js-pagination. i am able to show paggination bar in button but not able to get functionality.
here i am trying show 3 records per page.
UI
<div style={pannelFooter}>
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={3}
totalItemsCount={this.state.projectList.length}
pageRangeDisplayed={5}
onChange={this.handlePageChange}
/>
</div>
Method
handlePageChange(pageNumber) {
this.setState({ activePage: pageNumber });
}
Constructor
constructor(props) {
super(props);
this.state = {
activePage: 1,
projectList: [],
originalProjectList: []
};
this.handlePageChange = this.handlePageChange.bind(this);
}
FUll Component
import React, { Component } from 'react';
import ReactDOM from "react-dom";
import Pagination from "react-js-pagination";
import {
BrowserRouter as Router,
Route,
IndexRoute,
Link,
} from 'react-router-dom';
import ProjectDetails from './ProjectDetails';
import DashboardContainer from '../UIcomponent/DashboardContainer';
const pannelWidth = {
width: '90%'
};
const pannelHeader = {
color: 'white'
};
const pannelFooter = {
float: 'right'
};
class ProjectList extends Component {
constructor(props) {
super(props);
this.state = {
activePage: 1,
searchText: '',
isdiagram:true,
isMax:false,
projectList: [],
originalProjectList: []
};
//this.handlePageChange = this.handlePageChange.bind(this);
this.projectDetails = this.projectDetails.bind(this);
this.deleteMessage = this.deleteMessage.bind(this);
this.updateInputValue = this.updateInputValue.bind(this);
this.setSize=this.setSize.bind(this);
}
componentDidMount() {
let d = '';
$.get("http://localhost:8008/api/navigation/all", function (data) {
d = data;
this.setState({
projectList: d,
originalProjectList: d
});
}.bind(this));
}
handlePageChange(pageNumber) {
this.setState({ activePage: pageNumber });
console.log(this.state.projectList);
}
projectDetails(item, index) {
console.log(index);
}
deleteMessage(item, index) {
showconfrim("Do you want to delete this Project?", this.deleteProject(item, index));
console.log('delete');
}
deleteProject(item, index) {
$("#confirmwindow").modal('hide');
console.log('delete');
}
setSize(){
this.setState({
isMax:!this.state.isMax
});
if(!this.state.isMax){
//clear style for jquery animate;
$(this.refs.selfdiv).attr("style",null);
setTimeout(()=>{
$(this.refs.selfdiv).animate({
top:'0px',
right: '0px',
bottom: '0px',
left: '0px'
},500);
},100);
}
console.log(this.props.children);
if(this.props.children[1].props['data-event']){
var self=this;
setTimeout(()=>{
self.props.children[1].props['data-event'].call();
},700);
}
}
updateInputValue(event) {
this.setState({
searchText: event.target.value
}, function () {
let textToSearch = this.state.searchText;
let originalData = this.state.projectList;
if (textToSearch != undefined || textToSearch != '') {
let searchData = [];
for (var i = 0; i < this.state.projectList.length; i++) {
if (this.state.projectList[i].name.indexOf(textToSearch) != -1 || this.state.projectList[i].description.indexOf(textToSearch) != -1) {
searchData.push(this.state.projectList[i]);
}
}
this.setState({
projectList: searchData
});
}
if(textToSearch == '') {
this.setState({
projectList: this.state.originalProjectList,
});
}
});
}
render() {
var listItems = this.state.projectList.map((item, index) => {
return <tr key={index}>
<td onClick={e => this.projectDetails(item, index)}><a><u>{item.name}</u></a></td>
<td>{item.description}</td>
<td><i className="glyphicon glyphicon-trash" onClick={e => this.deleteMessage(item, index)}></i></td>
</tr>
});
return (
<div className="container" style={pannelWidth} ref="selfdiv">
<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-6 col-lg-6">
<input type="text" className="form-control" placeholder="Search" value={this.state.searchText} onChange={this.updateInputValue}/>
</div>
<div className="col-md-2 col-lg-2">
<button className="btn btn-sm btn-success">Create New Project</button>
</div>
<div className="col-md-2 col-lg-2">
<div className="captiontoolbar buttoncontainer">
<span onClick={this.setSize} style={pannelFooter} className={
this.state.isMax ? ("boxMaxsize glyphicon glyphicon-resize-small") : ("boxMaxsize glyphicon glyphicon-fullscreen")
}></span>
</div>
</div>
</div>
</div>
<div className="panel-body">
<table className="table table-striped">
<thead>
<tr>
<th><b>Project Name</b></th>
<th><b>Description</b></th>
<th><b>Action</b></th>
</tr>
</thead>
<tbody>
{listItems}
</tbody>
</table>
</div>
<div style={pannelFooter}>
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={3}
totalItemsCount={this.state.projectList.length}
pageRangeDisplayed={5}
onChange={this.handlePageChange.bind(this)}
/>
</div>
</div>
</div>
);
}
}
export default ProjectList;
var indexOfLastTodo = this.state.activePage * this.state.itemPerPage;
var indexOfFirstTodo = indexOfLastTodo - this.state.itemPerPage;
var renderedProjects = this.state.projectList.slice(indexOfFirstTodo, indexOfLastTodo);
var listItems = renderedProjects.map((item, index) => {
return <tr key={index}>
<td onClick={e => this.projectDetails(item, index, e)}><a><u>{item.projectName}</u></a></td>
<td>{item.description}</td>
<td><i className="glyphicon glyphicon-trash" onClick={(e) => { if (window.confirm('All its related data will be deleted. Are you sure you want to delete?')) this.deleteMessage(item, index) } } > </i></td>
<td><i className="glyphicon glyphicon-edit" id="edit" onClick={e => this.editProject(item, index, e)}></i></td>
</tr>
});
Here need to update the projectList array using slice(firstIndex, lastIndex). then subarray should be use for rander purpose.
and paggination tag should be like below
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={this.state.itemPerPage}
totalItemsCount={this.state.originalProjectList.length}
pageRangeDisplayed={5}
onChange={this.handlePageChange.bind(this)}
/>
Take advantage of ES6.
So, insted of doing something like this:
this.handlePageChange = this.handlePageChange.bind(this);
You can do this:
handlePageChange = (pageNumber) => {
console.log(`active page is ${pageNumber}`);
this.setState({activePage: pageNumber});
}
Hope this helps.