Still new to React. As the subject states, I want to listen for data that comes through my service, which is the following:
import axios from "axios"
const base_url = 'https://localhost:5001/OrderItem';
export let orderItemByDivision = {
data: []
}
export const getOrderItemByDiv = (div, yr) => {
orderItemByDivision.data = axios.get(base_url + "/" + div + "/" + yr);
return axios.get(base_url + "/" + div + "/" + yr)
}
The aforementioned service is triggered by selecting a year from a drop down menu, followed by selecting a division, which sends a get to an API responding with JSON
import React from 'react';
import { FormGroup, FormControl, Button, IconButton, Menu, MenuItem } from '@material-ui/core';
import { getDivision } from '../services/division-service';
import { getOrderItemByDiv } from '../services/order-item-service';
import { MuiThemeProvider } from '@material-ui/core/styles';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import theme from '../theme';
export default class DivisionDropDownComponent extends React.Component {
state = {
divData: [],
divValue: 'Select Division',
divOpen: false,
divAnchorEl: null,
yrValue: '2020',
yrOpen: false,
yrAnchorEl: null,
yrs: ['2020','2019','2018','2017','2016','2015','2014','2013','2012']
}
constructor(props) {
super(props);
this.handleDivChange = this.handleDivChange.bind(this);
}
componentDidMount() {
getDivision()
.then((res) => {
this.setState({ divData: res.data });
})
.catch(error => {
console.error(error);
});
}
handleDivChange = (divData) => {
// this.setState({value: event.target.value ? event.target.value : ''});
console.log("divData selected: " + divData.code_division);
getOrderItemByDiv(divData.code_division, this.state.yrValue)
.then((res) => {
console.log(res.data);
})
.catch(error => {
console.error(error);
});
this.onCloseDiv();
}
handleYrChange = (event) => {
// this.setState({value: event.target.value ? event.target.value : ''});
this.setState({ yrValue: event.target.value });
console.log("divData selected: " + event.target.value);
this.onCloseYr();
}
handleDivClick = (event) => {
this.setState({ divAnchorEl: event.target })
this.setState({ divOpen: true });
}
handleYrClick = (event) => {
this.setState({ yrAnchorEl: event.target })
this.setState({ yrOpen: true });
}
onCloseDiv = () => {
this.setState({ divOpen: false });
}
onCloseYr = () => {
this.setState({ yrOpen: false });
}
render(){
let arrayOfData = this.state.divData;
let dropdowns = arrayOfData.map((divData) =>
<MenuItem onClick={(event) => this.handleDivChange(divData)} key={divData.code_division}
value={divData.code_division} text={divData.code_division}>
{divData.code_division}
</MenuItem>
);
let arrayOfYrs = this.state.yrs;
let yrDropDown = arrayOfYrs.map((yrs) =>
<MenuItem onClick={(event) => this.handleYrChange(event)} value={yrs} key={yrs}>
{yrs}
</MenuItem>
);
return (
<MuiThemeProvider theme={theme}>
<FormGroup column='true'>
<FormControl>
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={this.handleDivClick}
>
<MoreVertIcon />
</IconButton>
<Menu id="div-menu" anchorEl={this.state.divAnchorEl} open={this.state.divOpen} onClose={this.onCloseDiv}
className='dropDownDiv' defaultValue={this.state.divValue ? this.state.divValue: ''} >
<MenuItem value="Select Division">
Select Division
</MenuItem>
{dropdowns}
</Menu>
<Button aria-controls="simple-menu" aria-haspopup="true" onClick={this.handleYrClick}>
Select Year
</Button>
<Menu id="yrs-menu" open={this.state.yrOpen}
anchorEl={this.state.yrAnchorEl} onClose={this.onCloseYr}
defaultValue={this.state.yrValue ? this.state.yrValue: ''} >
{yrDropDown}
</Menu>
</FormControl>
</FormGroup>
</MuiThemeProvider>
)
}
}
As you can see by the screen shot, the data is coming through in the console. How do I have the Admin dataProvider listen to any changes coming from the order-item-service.jsx file?
Can I place an observable on the orderItemByDivision.data?
As usual, thanks in advance
Editing my post to give more detail to my question. In Angular, I used HttpClient and RxJS to subscribe and observe. I have tried the following in React, but it is having issues with setState:
import { ajax } from "rxjs/ajax";
import { Observable } from "rxjs";
const base_url = "https://localhost:5001/OrderItem";
export let orderItemByDivision = {
data: []
};
export const getOrderItemByDiv = (div, yr) => {
return new Observable(observe => {
orderItemByDivision.data = ajax
.get(base_url + "/" + div + "/" + yr)
.subscribe(resu => {
setState({ orderItemByDivision.data: resu });
observe.next(resu);
});
});
};
See the "orderItemByDivision.data" it is having issues with the dot notation. Since this is a service and not within a component class, I cannot call this.setState. How can I set the state, so that I can observe and subscribe to the ajax request?
Thanks in advance
OK, I was able emulate what I have done in Angular 6, which is to create an observable, which can then be subscribed (listen for changes)
First, the order-item-service.jsx:
import { Subject } from 'rxjs';
import { ajax } from "rxjs/ajax";
import { Observable } from "rxjs";
const subject = new Subject();
const base_url = 'https://localhost:5001/OrderItem';
export let orderItemByDivision = {
data: []
}
export const getOrderItemByDiv = (div, yr) => {
return new Observable(observe => {
orderItemByDivision.data = ajax
.get(base_url + "/" + div + "/" + yr)
.subscribe(resu => {
orderItemByDivision.data = resu.response ;
observe.next(resu.response);
});
});
};
export const messageService = {
sendMessage: message => subject.next({ message }),
clearMessage: () => subject.next(),
getMessage: () => subject.asObservable()
}
As you can see, I tried to use the Subject from rxjs for sending data between components, but using ajax from rxjs/ajax was what ultimately lead me to be able to listen to changes
I proceeded to the same with the division-service.jsx service:
import { ajax } from "rxjs/ajax";
import { Observable } from "rxjs";
const base_url = 'https://localhost:5001/Division';
let state = {
data: []
}
export const getDivision = () => {
return new Observable(observe => {
state.data = ajax
.get(base_url)
.subscribe(resu => {
state.data = resu.response ;
observe.next(resu.response);
});
});
}
As you can see in both cases, it creates on observable after it subscribes to the get method - in other words, every time that the API URL is used, it will alert anyone who is subscribed to the two aforementioned methods.
The following division-dropdown-component.jsx component, receives the division JSON, which fills the drop down menu of divisions. Once the end user clicks on a division, the getOrderItemByDiv is subscribed, which returns the JSON that we are needing to populate the @material-ui Table - listed down below:
import React from "react";
import {
FormGroup,
FormControl,
Button,
Menu,
MenuItem
} from "@material-ui/core";
import { getDivision } from "../services/division-service";
import { getOrderItemByDiv } from "../services/order-item-service";
import { MuiThemeProvider } from "@material-ui/core/styles";
import theme from "../theme";
import { messageService } from "../services/order-item-service";
export default class DivisionDropDownComponent extends React.Component {
state = {
divData: [],
divValue: "Select Division",
divOpen: false,
divAnchorEl: null,
yrValue: "2020",
yrOpen: false,
yrAnchorEl: null,
yrs: [
"2020",
"2019",
"2018",
"2017",
"2016",
"2015",
"2014",
"2013",
"2012"
]
};
constructor(props) {
super(props);
this.handleDivChange = this.handleDivChange.bind(this);
}
componentDidMount() {
getDivision().subscribe(res => {
this.setState({ divData: res });
});
}
handleDivChange = divData => {
getOrderItemByDiv(divData.code_division, this.state.yrValue).subscribe(
res => {
this.setState({ divData: res });
messageService.sendMessage(res);
}
);
this.onCloseDiv();
};
handleYrChange = event => {
this.setState({ yrValue: event.target.value });
this.onCloseYr();
};
handleDivClick = event => {
this.setState({ divAnchorEl: event.target });
this.setState({ divOpen: true });
};
handleYrClick = event => {
this.setState({ yrAnchorEl: event.target });
this.setState({ yrOpen: true });
};
onCloseDiv = () => {
this.setState({ divOpen: false });
};
onCloseYr = () => {
this.setState({ yrOpen: false });
};
render() {
let arrayOfData = this.state.divData;
let dropdowns = arrayOfData.map((divData, index) => (
<MenuItem
onClick={event => this.handleDivChange(divData)}
key={index}
value={divData.code_division}
text={divData.code_division}
>
{divData.code_division}
</MenuItem>
));
let arrayOfYrs = this.state.yrs;
let yrDropDown = arrayOfYrs.map(yrs => (
<MenuItem
onClick={event => this.handleYrChange(event)}
value={yrs}
key={yrs}
>
{yrs}
</MenuItem>
));
return (
<MuiThemeProvider theme={theme}>
<FormGroup column="true">
<FormControl>
<Button
aria-controls="simple-menu"
aria-haspopup="true"
onClick={this.handleYrClick}
>
Select Year
</Button>
<Menu
id="yrs-menu"
open={this.state.yrOpen}
anchorEl={this.state.yrAnchorEl}
onClose={this.onCloseYr}
defaultValue={this.state.yrValue ? this.state.yrValue : ""}
>
{yrDropDown}
</Menu>
<Button
aria-controls="simple-menu"
aria-haspopup="true"
onClick={this.handleDivClick}
>
Select Division
</Button>
<Menu
id="div-menu"
anchorEl={this.state.divAnchorEl}
open={this.state.divOpen}
onClose={this.onCloseDiv}
className="dropDownDiv"
defaultValue={this.state.divValue ? this.state.divValue : ""}
>
<MenuItem value="Select Division">Select Division</MenuItem>
{dropdowns}
</Menu>
</FormControl>
</FormGroup>
</MuiThemeProvider>
);
}
}
The following is the order-item-component.jsx component, which fills the material-ui Table:
import React from "react";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import { TablePagination } from "@material-ui/core";
import TableFooter from "@material-ui/core/TableFooter";
import { messageService } from "../services/order-item-service";
export default class OrderItemComponent extends React.Component {
state = {
data: [],
_columns: [],
Header: [],
totalCount: 10,
pageSize: 16,
page: 0
};
componentDidMount() {
componentDidMount() {
this.subscription = messageService.getMessage().subscribe(message => {
if (message) {
this.setState({ data: message.message });
this.setState({ totalCount: Math.ceil(this.state.data.length / this.state.pageSize) });
this.setState({ Header: ['order_id', 'order_item_id', 'product_id', 'code_division', 'code_product',
'quantity_due', 'quantity_shipped', 'price', 'date_shipped', 'date_due',
'customer_id','ship_via','value_due','value_shipped','date_order','date_modified'] });
} else {
// do nothing
this.setState({ data: [] });
}
})
}
componentWillUnmount() {
// unsubscribe to ensure no memory leaks
this.subscription.unsubscribe();
}
getOrderItem(){
this.setState({ data: messageService.getMessage() });
}
handleChangePage = (event, newPage) => {
this.setState({ page: newPage });
if (this.state.totalCount !== this.state.data.length) {
}
}
render() {
return (
<div>
<TableContainer component={Paper}>
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Order ID</TableCell>
<TableCell align="right">Qty Due</TableCell>
<TableCell align="right">Prod ID</TableCell>
<TableCell align="right">Div</TableCell>
<TableCell align="right">Prod Code</TableCell>
<TableCell align="right">Qty Sh</TableCell>
<TableCell align="right">Price</TableCell>
<TableCell align="right">Dt SH</TableCell>
<TableCell align="right">Dt Due</TableCell>
<TableCell align="right">Cust ID</TableCell>
<TableCell align="right">Ship Via</TableCell>
<TableCell align="right">Val Dt</TableCell>
<TableCell align="right">Val Sh</TableCell>
<TableCell align="right">Dt Or</TableCell>
<TableCell align="right">Dt Mod</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.data.map((row, index) => (
<TableRow key={ index }>
<TableCell component="td" scope="row">
{ row.order_id }
</TableCell>
<TableCell align="right">{ row.quantity_due }</TableCell>
<TableCell align="right">{ row.product_id}</TableCell>
<TableCell align="right">{ row.code_division }</TableCell>
<TableCell align="right">{ row.code_product }</TableCell>
<TableCell align="right">{ row.quantity_shipped }</TableCell>
<TableCell align="right">{ row.price }</TableCell>
<TableCell align="right">{ row.date_shipped}</TableCell>
<TableCell align="right">{ row.date_due }</TableCell>
<TableCell align="right">{ row.customer_id }</TableCell>
<TableCell align="right">{ row.ship_via }</TableCell>
<TableCell align="right">{ row.value_due }</TableCell>
<TableCell align="right">{ row.value_shipped }</TableCell>
<TableCell align="right">{ row.date_order }</TableCell>
<TableCell align="right">{ row.date_modified }</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={ [5, 10, 25, { label: 'All', value: -1 }] }
colSpan={ 3 }
count={ this.state.data.length }
rowsPerPage={ this.state.pageSize }
page={ this.state.page }
SelectProps={ {
inputProps: { 'aria-label': 'rows per page' },
native: true,
} }
onChangePage={ this.handleChangePage }
/>
</TableRow>
</TableFooter>
</Table>
</TableContainer>
</div>
)
}
}
Now, you can see how I use the getMessage method to set the data
Almost all of it works, except the pagination - when I click on the "more" arrow, it will calculate correctly what the next set of data is, but it will not display the "new" data within the Table. I will have to figure out how to do a refresh on a @material-ui Table when pagination is involved, but nevertheless, I thought I would share in case someone else wanted to know how to create an observable.
I tried to use the example on @material-ui documentation but setPage = React.useState(5) kept causing an error having to do with invalid hook or something like that - regarding the following function:
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
I will post another question on that topic, but for now, the question that I asked has been answered
Thanks once more