jQuery UI Widgets › Forums › Grid › initrowdetails does not work first time with React typescript JQXGrid
Tagged: jqxGrid ;, reactjs, typescript
This topic contains 6 replies, has 2 voices, and was last updated by LaxGulawani 5 years, 9 months ago.
-
Author
-
February 5, 2019 at 7:39 am initrowdetails does not work first time with React typescript JQXGrid #103857
we are working to implement nested grid which loads data in initrowdetails function and then we try to render jqxgrid using ReactDOM.render, like below
ReactDOM.render(<JqxGrid source={subGridAdapter} columns={subGridColumns} />, document.getElementById(gridId));
On the first time get an error as ‘Target container is not a DOM element’, however on the second click, it shows nested table correctly.
I suspect it is because the div in which grid is going to be rendered is not present on DOM, so what could be an alternate solution for this?
Thank you
February 5, 2019 at 10:40 am initrowdetails does not work first time with React typescript JQXGrid #103862Hello LaxGulawani,
The following example for nested grids of jqxGrid with React with typescript is working fine.
It is using exactlyReactDOM.render
:import * as React from 'react'; import * as ReactDOM from 'react-dom'; import JqxGrid, { IGridProps, jqx } from 'jqwidgets-scripts/jqwidgets-react-tsx/jqxgrid'; class App extends React.PureComponent<{}, IGridProps> { private myGrid = React.createRef<JqxGrid>(); constructor(props: {}) { super(props); const source: any = { datafields: [ { name: 'FirstName' }, { name: 'LastName' }, { name: 'Title' }, { name: 'Address' }, { name: 'City' } ], datatype: 'xml', id: 'EmployeeID', record: 'Employee', root: 'Employees', url: './assets/sampledata/employees.xml' }; const ordersDetailsSource: any = { datafields: [ { name: 'EmployeeID', type: 'string' }, { name: 'ShipName', type: 'string' }, { name: 'ShipAddress', type: 'string' }, { name: 'ShipCity', type: 'string' }, { name: 'ShipCountry', type: 'string' }, { name: 'ShippedDate', type: 'date' } ], datatype: 'xml', record: 'Order', root: 'Orders', url: './assets/sampledata/orderdetails.xml' }; const ordersDataAdapter = new jqx.dataAdapter(ordersDetailsSource, { autoBind: true }); const photoRenderer = (row: number, column: any, value: string): string => { const name = this.myGrid.current!.getrowdata(row).FirstName; const imgurl = './assets/images/' + name.toLowerCase() + '.png'; const img = '<div style="background: white;"><img style="margin: 2px; margin-left: 10px;" width="32" height="32" src="' + imgurl + '"></div>'; return img; } const renderer = (row: number, column: any, value: string): string => { return '<span style="margin-left: 4px; margin-top: 9px; float: left;">' + value + '</span>'; } const nestedGrids: any[] = []; const initrowdetails = (index: number, parentElement: any, gridElement: any, record: any): void => { const id = record.uid.toString(); const nestedGridContainer = parentElement.children[0]; nestedGrids[index] = nestedGridContainer; const filtergroup = new jqx.filter(); const filtervalue = id; const filtercondition = 'equal'; const filter = filtergroup.createfilter('stringfilter', filtervalue, filtercondition); // fill the orders depending on the id. const orders = ordersDataAdapter.records; const ordersbyid = []; for (const order of orders) { const result = filter.evaluate(order.EmployeeID); if (result) { ordersbyid.push(order); } } const ordersSource = { datafields: [ { name: 'EmployeeID', type: 'string' }, { name: 'ShipName', type: 'string' }, { name: 'ShipAddress', type: 'string' }, { name: 'ShipCity', type: 'string' }, { name: 'ShipCountry', type: 'string' }, { name: 'ShippedDate', type: 'date' } ], id: 'OrderID', localdata: ordersbyid } const nestedGridAdapter = new jqx.dataAdapter(ordersSource); if (nestedGridContainer != null) { const columns: IGridProps['columns'] = [ { text: 'Ship Name', datafield: 'ShipName', width: 200 }, { text: 'Ship Address', datafield: 'ShipAddress', width: 200 }, { text: 'Ship City', datafield: 'ShipCity', width: 150 }, { text: 'Ship Country', datafield: 'ShipCountry', width: 150 }, { text: 'Shipped Date', datafield: 'ShippedDate', width: 200 } ]; ReactDOM.render( <JqxGrid width={780} height={200} source={nestedGridAdapter} columns={columns} />, document.getElementById(nestedGridContainer.id) ); } }; this.state = { columns: [ { text: 'Photo', width: 50, cellsrenderer: photoRenderer }, { text: 'First Name', datafield: 'FirstName', width: 100, cellsrenderer: renderer }, { text: 'Last Name', datafield: 'LastName', width: 100, cellsrenderer: renderer }, { text: 'Title', datafield: 'Title', width: 180, cellsrenderer: renderer }, { text: 'Address', datafield: 'Address', width: 300, cellsrenderer: renderer }, { text: 'City', datafield: 'City', width: 170, cellsrenderer: renderer } ], initrowdetails, ready: (): void => { this.myGrid.current!.showrowdetails(1); }, rowdetailstemplate: { rowdetails: '<div id="nestedGrid" style="margin: 10px;"></div>', rowdetailsheight: 220, rowdetailshidden: true }, source: new jqx.dataAdapter(source) } } public render() { return ( <JqxGrid ref={this.myGrid} // @ts-ignore width={getWidth('grid')} height={365} source={this.state.source} columns={this.state.columns} rowdetails={true} rowsheight={35} initrowdetails={this.state.initrowdetails} ready={this.state.ready} rowdetailstemplate={this.state.rowdetailstemplate} /> ); } } export default App;
I hope this would help you!
Best Regards,
MartinjQWidgets Team
http://www.jqwidgets.com/February 6, 2019 at 7:22 am initrowdetails does not work first time with React typescript JQXGrid #103874Hi Martin,
Thanks for your inputs, I guess the issue is related to fetching data dynamically for the nested grid.
We just have minor modification in example you have provided as below:
const initrowdetails = (index: number, parentElement: any, gridElement: any, record: any): void => { const id = record.uid.toString(); const nestedGridContainer = parentElement.children[0]; nestedGrids[index] = nestedGridContainer; const orderId = record.id; //till below call we can see nestedGridContainer is present on actual DOM this.props.requestDetailsByOrderId(orderId); // after above line execution nestedGridContainer is removed from actual DOM, which causes exeception during ReactDOM.render at the end of function. const ordersbyid = this.props.orders const ordersSource = { datafields: [ { name: 'EmployeeID', type: 'string' }, { name: 'ShipName', type: 'string' }, { name: 'ShipAddress', type: 'string' }, { name: 'ShipCity', type: 'string' }, { name: 'ShipCountry', type: 'string' }, { name: 'ShippedDate', type: 'date' } ], id: 'OrderID', localdata: ordersbyid } const nestedGridAdapter = new jqx.dataAdapter(ordersSource); if (nestedGridContainer != null) { const columns: IGridProps['columns'] = [ { text: 'Ship Name', datafield: 'ShipName', width: 200 }, { text: 'Ship Address', datafield: 'ShipAddress', width: 200 }, { text: 'Ship City', datafield: 'ShipCity', width: 150 }, { text: 'Ship Country', datafield: 'ShipCountry', width: 150 }, { text: 'Shipped Date', datafield: 'ShippedDate', width: 200 } ]; ReactDOM.render( <JqxGrid width={780} height={200} source={nestedGridAdapter} columns={columns} />, document.getElementById(nestedGridContainer.id) ); } };
Please can you suggest why this happens or any alternate way to ensure element is not removed from actual DOM.
Thanks again for your support.
February 6, 2019 at 9:11 am initrowdetails does not work first time with React typescript JQXGrid #103878Hello LaxGulawani,
What is happening inside the
requestDetailsByOrderId
method? Fetching the data should not affect the DOM at all.
Also, there is a check:if (nestedGridContainer != null)
before callingReactDOM.render
.
Below is a sample of loading the subdata dynamically, similarly to your example.Please, note that in this case there is
async: false
set to the ordersDataAdapter, so the execution of the code does not continue
before the data is loaded.import * as React from 'react'; import * as ReactDOM from 'react-dom'; import JqxGrid, { IGridProps, jqx } from 'jqwidgets-scripts/jqwidgets-react-tsx/jqxgrid'; class App extends React.PureComponent<{}, IGridProps> { private myGrid = React.createRef<JqxGrid>(); private subGridData: any[] = []; constructor(props: {}) { super(props); this.loadSubDataById = this.loadSubDataById.bind(this); const source: any = { datafields: [ { name: 'FirstName' }, { name: 'LastName' }, { name: 'Title' }, { name: 'Address' }, { name: 'City' } ], datatype: 'xml', id: 'EmployeeID', record: 'Employee', root: 'Employees', url: './assets/sampledata/employees.xml' }; const photoRenderer = (row: number, column: any, value: string): string => { const name = this.myGrid.current!.getrowdata(row).FirstName; const imgurl = './assets/images/' + name.toLowerCase() + '.png'; const img = '<div style="background: white;"><img style="margin: 2px; margin-left: 10px;" width="32" height="32" src="' + imgurl + '"></div>'; return img; } const renderer = (row: number, column: any, value: string): string => { return '<span style="margin-left: 4px; margin-top: 9px; float: left;">' + value + '</span>'; } const nestedGrids: any[] = []; const initrowdetails = (index: number, parentElement: any, gridElement: any, record: any): void => { const id = record.uid.toString(); const nestedGridContainer = parentElement.children[0]; nestedGrids[index] = nestedGridContainer; this.loadSubDataById(id); const ordersbyid = this.subGridData; const ordersSource = { datafields: [ { name: 'EmployeeID', type: 'string' }, { name: 'ShipName', type: 'string' }, { name: 'ShipAddress', type: 'string' }, { name: 'ShipCity', type: 'string' }, { name: 'ShipCountry', type: 'string' }, { name: 'ShippedDate', type: 'date' } ], id: 'OrderID', localdata: ordersbyid } const nestedGridAdapter = new jqx.dataAdapter(ordersSource); if (nestedGridContainer != null) { const columns: IGridProps['columns'] = [ { text: 'Ship Name', datafield: 'ShipName', width: 200 }, { text: 'Ship Address', datafield: 'ShipAddress', width: 200 }, { text: 'Ship City', datafield: 'ShipCity', width: 150 }, { text: 'Ship Country', datafield: 'ShipCountry', width: 150 }, { text: 'Shipped Date', datafield: 'ShippedDate', width: 200 } ]; ReactDOM.render( <JqxGrid width={780} height={200} source={nestedGridAdapter} columns={columns} />, document.getElementById(nestedGridContainer.id) ); } }; this.state = { columns: [ { text: 'Photo', width: 50, cellsrenderer: photoRenderer }, { text: 'First Name', datafield: 'FirstName', width: 100, cellsrenderer: renderer }, { text: 'Last Name', datafield: 'LastName', width: 100, cellsrenderer: renderer }, { text: 'Title', datafield: 'Title', width: 180, cellsrenderer: renderer }, { text: 'Address', datafield: 'Address', width: 300, cellsrenderer: renderer }, { text: 'City', datafield: 'City', width: 170, cellsrenderer: renderer } ], initrowdetails, ready: (): void => { this.myGrid.current!.showrowdetails(1); }, rowdetailstemplate: { rowdetails: '<div id="nestedGrid" style="margin: 10px;"></div>', rowdetailsheight: 220, rowdetailshidden: true }, source: new jqx.dataAdapter(source) } } public render() { return ( <JqxGrid ref={this.myGrid} // @ts-ignore width={getWidth('grid')} height={365} source={this.state.source} columns={this.state.columns} rowdetails={true} rowsheight={35} initrowdetails={this.state.initrowdetails} ready={this.state.ready} rowdetailstemplate={this.state.rowdetailstemplate} /> ); } private loadSubDataById(id: any) { const ordersDetailsSource: any = { datafields: [ { name: 'EmployeeID', type: 'string' }, { name: 'ShipName', type: 'string' }, { name: 'ShipAddress', type: 'string' }, { name: 'ShipCity', type: 'string' }, { name: 'ShipCountry', type: 'string' }, { name: 'ShippedDate', type: 'date' } ], datatype: 'xml', record: 'Order', root: 'Orders', url: './assets/sampledata/orderdetails.xml' }; const ordersDataAdapter = new jqx.dataAdapter(ordersDetailsSource, { autoBind: true, async: false }); const filtergroup = new jqx.filter(); const filtervalue = id; const filtercondition = 'equal'; const filter = filtergroup.createfilter('stringfilter', filtervalue, filtercondition); const orders = ordersDataAdapter.records; for (const order of orders) { const result = filter.evaluate(order.EmployeeID); if (result) { this.subGridData.push(order); } } } } export default App;
Best Regards,
MartinjQWidgets Team
http://www.jqwidgets.com/February 6, 2019 at 9:22 am initrowdetails does not work first time with React typescript JQXGrid #103879Hi Martin,
Below is the method for actionCreator
requestDetailsByOrderId: (id: number): AppThunkAction<KnownAction> => (dispatch, getState) => { if (orderNumber !== getState().orders.orderNumber) { const fetchTask = fetch(<code>api/orders/GetOrderDetailsForOrderId/${id}</code>) .then((response) => response.json() as Promise<Order[]>) .then((data) => { dispatch({ type: 'RECEIVE_ORDERBYORDERID', id, orders: data }, ); }); addTask(fetchTask); dispatch({ type: 'REQUEST_ORDERBYORDERID', id}); } }
As you have mentioned calling this method should not impact DOM in any way. however, currently it is causing a problem.
Thanks for your prompt response.
February 7, 2019 at 9:58 am initrowdetails does not work first time with React typescript JQXGrid #103887Hello LaxGulawani,
What I suppose that may be the reason is that when you are fetching the data, the state gets updated,
so the component is re-rendered which will result in the rowdetails not being open.
And as you mentioned you are getting an error on the first click only, because after that you have the data already.Best Regards,
MartinjQWidgets Team
http://www.jqwidgets.com/February 18, 2019 at 4:55 am initrowdetails does not work first time with React typescript JQXGrid #104008Hi Martin,
We have relooked at the problem and decided not to update state while displaying nested grid. That solved problem.
Now we are binding ajax result directly to nested grid and it works well.
Thanks for your help.
-
AuthorPosts
You must be logged in to reply to this topic.