How to change context in Ag Grid React - javascript

I'm trying to use the documentation for adding context to my React Ag Grid app. My issue is that their code example doesn't use the way that I create the grid. This is how they add the context
var gridOptions = {
columnDefs: columnDefs,
defaultColDef: {
flex: 1,
resizable: true
},
rowData: rowData,
context: {
reportingCurrency: 'EUR'
},
};
// setup the grid after the page has finished loading
document.addEventListener('DOMContentLoaded', function() {
var gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);
});
And they change the context value like this
function currencyChanged() {
var value = document.getElementById('currency').value;
gridOptions.context = {reportingCurrency: value};
gridOptions.api.refreshCells();
gridOptions.api.refreshHeader();
}
I'm using React though, so I'm creating my component like this
const [gridParams, setGridParams] = useState(null);
const onFirstDataRendered = (params) => {
setGridParams(params)
}
<AgGridReact
columnDefs={columnDefs}
defaultColDef={{
flex: 1,
resizable: true
}}
rowData={rowData}
context={{ reportingCurrency: 'EUR' }}
onFirstDataRendered={onFirstDataRendered}
>
This is how I'm changing context in my file
const currencyChange = (value) => {
gridParams.context = { reportingCurrency: value };
gridParams.api.refreshCells();
};
When I update the context like this, my cells don't see that the context has changed for them. Since the documentation doesn't show how to implement this for React, I figured I'm just missing something. Does anyone have any idea what that could be?

It looks like the way to change the context is actually different. After poking around, it looks like it needs to be changed like this
const currencyChange = (value) => {
gridParams.api.gridOptionsWrapper.gridOptions.context = { reportingCurrency: value };
gridParams.api.refreshCells();
};

Related

AG Grid React: How to get the state of rows after changing the order?

After implementing the drag and drop feature on AG Grid table, I'm looking for a way to get the current state with the updated order/index of rows. My goal is to persist the table data after changing the order, but can't find the respective state of the current order.
I'd appreciate any help or any idea.
Sandbox demo and example code below
import React from "react";
import { AgGridReact } from "ag-grid-react";
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";
function App() {
const [gridApi, setGridApi] = React.useState(null);
const [gridColumnApi, setGridColumnApi] = React.useState(null);
const onGridReady = (params) => {
setGridApi(params.api);
setGridColumnApi(params.columnApi);
};
const defaultColDef = {
flex: 1,
editable: true
};
const columnDefs = [
{
headerName: "Name",
field: "name",
rowDrag: true
},
{ headerName: "stop", field: "stop" },
{
headerName: "duration",
field: "duration"
}
];
const rowData = React.useMemo(
() => [
{
name: "John",
stop: 10,
duration: 5
},
{
name: "David",
stop: 15,
duration: 8
},
{
name: "Dan",
stop: 20,
duration: 6
}
],
[]
);
return (
<div>
<h1 align="center">React-App</h1>
<div>
<div className="ag-theme-alpine" style={{ height: "700px" }}>
<AgGridReact
columnDefs={columnDefs}
rowData={rowData}
defaultColDef={defaultColDef}
onGridReady={onGridReady}
rowDragManaged={true}
></AgGridReact>
</div>
</div>
</div>
);
}
export default App;
You can get the order of the rows inside the grid by iterating over them using the Grid API method forEachNode:
API for Row Nodes
const rows = [];
gridApi.forEachNodeAfterFilterAndSort((node) => rows.push(node.data));
console.log(rows);
See this implemented in the following sample.
You're currently using managed dragging by passing rowManagedDragging={true}, which means the AgGridReact component is managing the row order state.
If you want to maintain row order state outside the component, you need to use Unmanaged Dragging.
Add a handler for onRowDragMove, and use the node and overIndex or overNode properties of the event to update your local event order state, and pass it to the AgGridReact component to re-render.
Take a look at this example from the docs

Change Status Bar Component when data change in React AgGrid

I need to change the value I show in my custom status bar when a prop changes.
So let's say I got:
const CustomGrid = ({
data, lastUpdate, framweworkComponents, columns
}) => {
const [gridApi, setGridApi] = useState(null);
const [gridColumnApi, setGridColumnApi] = useState(null);
const [gridParams, setGridParams] = useState(null);
const onGridReady = (params) => {
setGridApi(params.api);
setGridColumnApi(params.columnApi);
setGridParams(params);
}
};
const renderLastUpdateStatusBar = () => (
<div>
{lastUpdate}
</div>
);
return(
<div className={theme || 'ag-theme-balham'} style={style}>
<AgGridReact
modules={AllModules}
onGridReady={onGridReady}
columnDefs={columns}
rowData={data}
defaultColDef={defaultColDef || {
resizable: true,
menuTabs: [],
}}
frameworkComponents={{
...frameworkComponents,
lastUpdateStatusBarComponent: renderLastUpdateStatusBar,
}}
statusBar={{
statusPanels: [
{
statusPanel: 'lastUpdateStatusBarComponent',
key: 'lastUpdateStatusBarKey',
align: 'left',
},
],
}
/>
</div>
)
}
When 'lastUpdate' changes, it is correctly passed to CustomGrid component but the status bar doesn't get re-rendered, so I see the first value forever.
My goal is to make the status bar updating every time 'lastUpdate' changes. I tried making a state containing my framework components and set it in a useEffect when lastUpdate changes, then put this state in frameworkComponents prop in agGrid, but it does not work:
const [frameworkC, setFrameworkC] = useState(null);
useEffect(() => {
setFrameworkC({
...frameworkComponents,
lastUpdateStatusBarComponent: renderLastUpdateStatusBar,
})
}, [lastUpdate]);
...
<AgGridReact
...
frameworkComponents={frameworkC}
...
>
I also tried setting this state in onGridReady function, same result.
Also I tried calling api.redrawRows(gridParams) in the useState, no way.
Is there some API I can use to update the status bar component? Or some any approach?
You need to add key for the component to rerender
<AgGridReact
key={lastUpdate}
modules={AllModules}
onGridReady={onGridReady}
columnDefs={columns}
rowData={data}
defaultColDef={defaultColDef || {
resizable: true,
menuTabs: [],
}}
frameworkComponents={{
...frameworkComponents,
lastUpdateStatusBarComponent: renderLastUpdateStatusBar,
}}
statusBar={{
statusPanels: [
{
statusPanel: 'lastUpdateStatusBarComponent',
key: 'lastUpdateStatusBarKey',
align: 'left',
},
],
}
/>

React component in a Tabulator custom formatter

I am trying to insert a Link component inside a Tabulator table cell via a custom formatter.
Nothing is shown in the cell, as seen in the codesandbox.
Why can't the JSX be returned from a function? How can I achieve this?
const invoiceLinkFormatter = (cell, formatterParams) => { // <------ Custom formatter definition
let key = cell.getValue();
let link = `/invoices/${key}`;
return (<Link to={link}>{key}</Link>);
};
invoicesTable.current = new Tabulator(refInvoicesTable.current, {
columns: [
{
title: "Invoices",
field: "invoiceKey",
formatter: invoiceLinkFormatter // <------ Custom formatter use
},
{ title: "Dates", field: "invoiceDate" }
]
});
This approach works, but it defeats the purpose as the link leaves the react app and reloads everything.
const columns = [
{
title: "Invoice",
field: "invoiceKey",
formatter: "link",
formatterParams: { url: cell => { return "/invoices/" + cell.getValue() } }
},
{ title: "Date", field: "invoiceDate" },
];
Forgive me if I'm misunderstanding, but I believe you can use a combination of ReactDOM.render() and the onRendered parameter of Tabulator's custom formatters to help here.
import ReactDOM from "react-dom";
...
const journalLinkFormatter = (cell, formatterParams, onRendered) => {
onRendered(() => {
let key = cell.getValue();
let link = `/journals/${key}`;
ReactDOM.render(
<Link
style={{ color: "blue", fontWeight: "bold", background: "red" }}
to={link}
>
{key}
</Link>,
cell.getElement()
);
});
};
This is because the layout framework you are using parses the DOM on page load, so any elements added to the page after this point wont be parsed correctly unless they are added through the frameworks functions.
There is probably a function you can call on your layout framework to re-parse the DOM after Tabulator has formatted the cell.

Dynamically update Highcharts chart in react

I am using highcharts-react-official in react-redux to create a drilldown chart.
However when I click on a bar to drilldown, I also update some props which causes the component to re-render - which seems to prevent the drilldown event.
I kind of gathered from Change series data dynamically in react-highcharts without re-render of the chart that I should use shouldComponentUpdate and getChart() to prevent re-render and instead dynamically update the data.
My issue is that getChart() doesn't seem to work for the official highcharts react package. I'm getting Uncaught TypeError: this.refs.chart.getChart is not a function
Is there an alternative I'm meant to be using to get and dynamically update the chart? Or some examples that I could look at?
Just including render and shouldComponentUpdate parts here:
shouldComponentUpdate(nextProps, nextState) {
let chart = this.refs.chart.getChart();
//dynamically update data using nextProps
return false;
}
render () {
const options = {
chart: {
type: 'column',
height: 300,
events: {
drillup: (e) => {this.drilledUp(e)}
}
},
plotOptions: {
series: {
events:{
click: (e) => {this.categoryClicked(e)}
}
}
},
xAxis: {type: "category"},
yAxis: {title: {text: 'Amount Spent ($)'}},
series: [{
name: 'weekly spending',
showInLegend: false,
data: this.props.transactionChartData.series_data,
cursor: 'pointer',
events: {
click: (e)=> {this.weekSelected(e)}
}
}],
drilldown: {
series: this.props.transactionChartData.drilldown_data
}
};
return (
<HighchartsReact
highcharts={Highcharts}
options={options}
ref="chart"
/>
)
}
In highcharts-react-official v2.0.0 has been added allowChartUpdate option, which should work great in your case. By using this option you can block updating the chart with updating the component:
categoryClicked() {
this.allowChartUpdate = false;
this.setState({
...
});
}
...
<HighchartsReact
ref={"chartComponent"}
allowChartUpdate={this.allowChartUpdate}
highcharts={Highcharts}
options={...}
/>
Moreover, to get the chart instance use refs:
componentDidMount(){
const chart = this.refs.chartComponent.chart;
}
Live demo: https://codesandbox.io/s/98nl4pp5r4
// react native functional
import React, {useState, useRef,useLayoutEffect} from "react"
import HighchartsReactNative from '#highcharts/highcharts-react-native'
function chartcomponent(props){
const [options, setoptions] = useState({});
const chartRef = useRef(null);
useEffect(() => {
// create the options for your chart.
setoptions({chart:{}, yaxis:{}, xAxis:{},})// etc.
}, []);
useLayoutEffect(() => {
// new point to add, you can get new data via props, fetch, socket, etc.
var x = new Date(hr.timestamp).getTime();
var y = 10
// these are the important parts here:
var series = chartRef.current.props.options.series;
if (!Array.isArray(series)){return;}
var seriesdata = series[0].data;
seriesdata.push([x,y]);
// next line limits points in chart to 10, so new poitn replaces first point
if (seriesdata.length>10){seriesdata.splice(0,1);}
return () => {};
}, [props]);
return(
<View style={styles.charting}><HighchartsReactNative styles={{height:300, width:600}} options={options} ref={chartRef} ></HighchartsReactNative></View>
)
}

Vis.js/React/JavaScript: Render Vis.timeline

I am in the middle of trying to solve a problem with vis.js timeline I hope to get some guidance from you folks. console.log is showing data but the browser shows a blank screen. Unfortunately I am all out of ideas on what else to try it to make it work.
I have the following code. I have tried different ways to make it work but so far no luck. Any help will be greatly appreciated.
// Config for the Timeline as JSON
const options = {
width: '100%',
height: '60px',
stack: false,
showMajorLabels: true,
showCurrentTime: true,
zoomMin: 1000000,
type: 'background',
format: {
minorLabels: {
minute: 'h:mma',
hour: 'ha'
}
}
}
class ScaleTime extends Component{
constructor({data=[]}) {
super({data})
this.state = {data, id:''}
//console.log('ScaleTime Data:', data)
}
render(){
const { data } = this.state
const newAction = data.action.map((actionItem, index) => ({
...actionItem,
id: index + 1
}));
const items = {
...data,
action: newAction
};
const timeLineData = new vis.DataSet([{items}])
console.log('timeLineData:', timeLineData)
var container = document.getElementById('timeline');
return(
<div className="timeline">
<Timeline
items={items.action}
options={options}
container={container}
/>;
</div>
)
}
}//component
Update:
After adding id now I need to change the 'timestamp' property to start. The error message I am now getting is: Property "start" missing in item 1.
you need to make sure that items has content before calling Timeline. You can do:
if (!items) return <SpinLoader />; return <Timeline items={items.action} options={options} container={container} />;

Categories