Last active
February 26, 2018 19:06
-
-
Save zatziky/72f0826e97a011d3f2af75df84981b61 to your computer and use it in GitHub Desktop.
Our ag-grid redux implementation. It is from our PoC and I wouldn't ever publish such a mess... I am only publishing it because of being asked. :)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import {AgGridReact} from 'ag-grid-react'; | |
import * as ColumnUtils from 'common/ag-grid/column/column-utils'; | |
import './myApp.css'; | |
import genericCellEditorFactory from './cellEditors/generic'; | |
export const ACTION_TYPE_SORT = 'sort'; | |
export const ACTION_TYPE_FILTER = 'filter'; | |
/** | |
* @class {GridComponent} | |
*/ | |
export default class GridComponent extends React.Component { | |
// constants | |
constructor(props) { | |
super(props); | |
const self = this; | |
this.gridComponent = undefined; | |
this.hasStaticRows = this.props.createDatasource === undefined; | |
this.gridOptions = { | |
// https://www.ag-grid.com/javascript-grid-context/index.php | |
context: { | |
gridInstance: this | |
}, | |
rowModelType: this.hasStaticRows ? 'normal' : 'virtual', | |
rowBuffer: 10, // no need to set this, the default is fine for almost all scenarios | |
enableServerSideFilter: true, | |
onAfterFilterChanged: () => this.gridOptions.api.refreshHeader(), | |
// Displayed rows have changed. Happens following sort, filter or tree expand / collapse events. | |
onModelUpdated: () => { | |
// console.log("onModelUpdated"); | |
}, | |
onSelectionChanged: () => { | |
const selectedNodes = this.gridOptions.api.getSelectedNodes(); | |
self.props.onSelectionChanged(selectedNodes); | |
}, | |
// Value has changed after editing. params are described under "Callback: New Value Handlers" - https://www.ag-grid.com/javascript-grid-cell-editing/index.php | |
onCellValueChanged: (params) => { | |
toggleSorting(self, self.hasStaticRows, false); // It cannot be bound to a field. => setting to false directly | |
self.gridOptions.enableServerSideFilter = false; // It cannot be bound to a field. => setting to false directly | |
self.props.onCellValueChanged(params.data, params.node, params); | |
}, | |
onCellClicked(params){ | |
console.log("onCellClicked", params); | |
}, | |
onBeforeSortChanged(){ | |
if (self.isDirty) { | |
const invokingAction = { | |
action: ACTION_TYPE_SORT, | |
data: self.api.getSortModel() | |
}; | |
self.props.showDialogDiscardChanges(invokingAction); | |
} | |
}, | |
onBeforeFilterChanged(){ | |
if (self.isDirty) { | |
const invokingAction = { | |
action: ACTION_TYPE_FILTER, | |
data: self.api.getFilterModel() | |
}; | |
self.props.showDialogDiscardChanges(invokingAction); | |
} | |
} | |
}; | |
toggleSorting(this.gridOptions, this.hasStaticRows, this.props.enableSorting); | |
} | |
componentDidUpdate(prevProps, prevState) { | |
console.log(this.props.rows); | |
console.log(prevProps.rows); | |
console.log(prevState); | |
if (this.props.rows) { | |
this.gridOptions.api.setRowData(this.props.rows); | |
this.sizeColumns(); | |
} | |
} | |
componentWillReceiveProps(nextProps) { | |
// console.log('AgGrid - Will receive props', nextProps, this.props); | |
toggleSorting(this.gridOptions, this.hasStaticRows, !nextProps.isDirty); | |
this.gridOptions.enableServerSideFilter = !nextProps.isDirty; | |
if (nextProps.invokingAction.action === ACTION_TYPE_SORT) { | |
this.props.resetReduxState(); | |
console.log("Reinvoking sort with", nextProps.invokingAction.data); | |
this.api.setSortModel(nextProps.invokingAction.data); | |
} | |
if (nextProps.invokingAction.action === ACTION_TYPE_FILTER) { | |
this.props.resetReduxState(); | |
this.api.setFilterModel(nextProps.invokingAction.data); | |
this.api.onFilterChanged(); | |
} | |
/* | |
if (nextProps.rows) { | |
// const model = this.api.getModel(); | |
this.gridOptions.api.setRowData(nextProps.rows); | |
} | |
*/ | |
if (nextProps.columnDefs !== this.props.columnDefs) { | |
let columnDefs = ColumnUtils.makeColumnsReadonly(nextProps.columnDefs, nextProps.isReadOnly); | |
if (!nextProps.isReadOnly) { | |
const genericCellEditor = genericCellEditorFactory({ | |
onEditingStart: nextProps.onEditingStart, | |
onEditingEnd: nextProps.onEditingEnd, | |
}); | |
columnDefs = ColumnUtils.setCustomCellEditor(columnDefs, genericCellEditor); | |
} | |
this.api.setColumnDefs(columnDefs); | |
} | |
// resize columns when this prop changes | |
if (nextProps.sizeColumnsProp !== this.props.sizeColumnsProp) { | |
// console.log('Grid resize columns with new value:', nextProps.sizeColumnsProp); | |
this.sizeColumns(); | |
} | |
} | |
onGridReady(params) { | |
this.api = params.api; | |
this.columnApi = params.columnApi; | |
if (this.hasStaticRows) { | |
this.api.setRowData(this.props.rows); | |
this.sizeColumns(); | |
this.api.hideOverlay(); | |
} else { | |
this.api.setDatasource(this.props.createDatasource(this)); | |
} | |
let columnDefs = ColumnUtils.makeColumnsReadonly(this.props.columnDefs, | |
this.props.isReadOnly); | |
if (!this.props.isReadOnly) { | |
const genericCellEditor = genericCellEditorFactory({ | |
onEditingStart: this.props.onEditingStart, | |
onEditingEnd: this.props.onEditingEnd, | |
}); | |
columnDefs = ColumnUtils.setCustomCellEditor(columnDefs, genericCellEditor); | |
} | |
this.api.setColumnDefs(columnDefs); | |
//todo - to remove next 3 lines | |
if (this.props.onGridReady) { | |
this.props.onGridReady(this); | |
} | |
if (this.props.selectFirstRow && (!this.api.getSelectedNodes() || !this.api.getSelectedNodes().length)) { | |
var model = this.api.getModel(); | |
if (model && model.getRowCount()) { | |
var node = model.getRow(0); | |
if (node) | |
node.setSelected(true, true); | |
} | |
} | |
} | |
onCellClicked(event) { | |
// console.log('onCellClicked: ' + event.data.name + ', col ' + event.colIndex); | |
} | |
onRowSelected(event) { | |
if (event.node.selected) { | |
const params = { | |
id: event.node.id, | |
data: event.node.data | |
}; | |
this.props.onRowSelected(params, event); | |
} | |
} | |
onRowDoubleClicked(event) { | |
this.props.onRowDoubleClicked(event); | |
} | |
getHeight() { | |
if (this.props.height) { | |
return this.props.height; | |
} | |
if (!this.props.rows || this.props.createDatasource) { | |
return 400; | |
} | |
const height = (this.props.rows.length + 1) * 22 + 24; | |
if (height < 2 * 22 + 24) return 2 * 22 + 24; | |
if (height > 400) return 400; | |
return height + 2; | |
} | |
sizeColumns() { | |
if (this.gridOptions && this.gridOptions.api) { | |
var api = this.gridOptions.api; | |
api.doLayout(); | |
setTimeout(function () { | |
api.sizeColumnsToFit(); | |
}, 200); | |
} | |
} | |
render() { | |
// console.log('onAGGridRender - props', this.props); | |
return ( | |
<div style={{height: this.getHeight()}} className="ag-blue"> | |
<AgGridReact | |
// gridOptions is optional - it's possible to provide | |
// all values as React props | |
gridOptions={this.gridOptions} | |
// listening for events | |
onGridReady={this.onGridReady.bind(this)} | |
onRowSelected={this.onRowSelected.bind(this)} | |
onCellClicked={this.onCellClicked.bind(this)} | |
onRowDoubleClicked={this.onRowDoubleClicked.bind(this)} | |
// binding to simple properties | |
showToolPanel="false" | |
quickFilterText="" | |
// binding to an object property | |
icons={this.props.icons} | |
rowSelection={this.props.rowSelection || "multiple"} | |
enableColResize="true" | |
enableFilter="true" | |
groupHeaders="true" | |
rowHeight="22" | |
debug={this.props.isInDebug} | |
ref={ref => this.gridComponent = ref} | |
/> | |
</div> | |
); | |
} | |
getApi() { | |
return this.gridOptions.api; | |
} | |
} | |
GridComponent.propTypes = { | |
isDirty: React.PropTypes.bool, | |
invokingAction: React.PropTypes.object, | |
rows: React.PropTypes.array, | |
columnDefs: React.PropTypes.array, | |
isReadOnly: React.PropTypes.bool, | |
isInDebug: React.PropTypes.bool, | |
enableSorting: React.PropTypes.bool, | |
selectFirstRow: React.PropTypes.bool, | |
onRowsChanged: React.PropTypes.func, | |
onSelectionChanged: React.PropTypes.func, | |
showDialogDiscardChanges: React.PropTypes.func, | |
onCellValueChanged: React.PropTypes.func, | |
removeRow: React.PropTypes.func, | |
addRow: React.PropTypes.func, | |
onEditingStart: React.PropTypes.func, | |
onEditingEnd: React.PropTypes.func, | |
// callbacks | |
createDatasource: React.PropTypes.func, | |
// lifecycle | |
componentWillReceiveProps: React.PropTypes.func, | |
resetReduxState: React.PropTypes.func, | |
sizeColumnsProp: React.PropTypes.number, // utility property for calling grid api | |
}; | |
function toggleSorting(gridOptions, hasStaticRows, isSortingAllowed) { | |
const enableOrDeleteKey = (object, key, isSortingAllowsd) => { | |
if (isSortingAllowsd) { | |
object[key] = true; | |
} else { | |
delete object[key]; | |
} | |
}; | |
// Set one of these to true to enable sorting. | |
// `enableSorting` will allow header clicks and show sort icons and sort within the grid. | |
// `enableServerSideSorting` will allow header clicks and show sort icons, but the sorting will be deferred to your datasource. | |
// https://www.ag-grid.com/javascript-grid-properties/index.php | |
if (hasStaticRows) { | |
enableOrDeleteKey(gridOptions, 'enableSorting', isSortingAllowed); | |
} else { | |
enableOrDeleteKey(gridOptions, 'enableServerSideSorting', isSortingAllowed); | |
} | |
} | |
GridComponent.defaultProps = { | |
enableSorting: false, | |
selectFirstRow: false, | |
icons: { | |
columnRemoveFromGroup: '<i class="fa fa-remove"/>', | |
filter: '<i class="fa fa-filter"/>', | |
sortAscending: '<i class="fa fa-long-arrow-down"/>', | |
sortDescending: '<i class="fa fa-long-arrow-up"/>', | |
groupExpanded: '<i class="fa fa-minus-square-o"/>', | |
groupContracted: '<i class="fa fa-plus-square-o"/>', | |
columnGroupOpened: '<i class="fa fa-minus-square-o"/>', | |
columnGroupClosed: '<i class="fa fa-plus-square-o"/>' | |
}, | |
columnDefs: [], | |
invokingAction: {}, | |
onRowsChanged: () => { | |
}, | |
showDialogDiscardChanges: () => { | |
}, | |
onCellValueChanged: () => { | |
}, | |
onGridReady() { | |
}, | |
onRowSelected() { | |
}, | |
onSelectionChanged() { | |
}, | |
onStartCellEditing() { | |
}, | |
onRowDoubleClicked() { | |
}, | |
onStopCellEditing() { | |
}, | |
onEditingStart() { | |
}, | |
onEditingEnd() { | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as ReactRedux from 'react-redux'; | |
import * as actions from './actions'; | |
import GridComponent from 'common/component/Grid/Grid.component'; | |
import RowsDatasource from './component/RowsDatasource'; | |
import {selectorGridSwitchPorts} from 'root-reducer.js'; | |
const mapStateToProps = (state) => { | |
const grid = selectorGridSwitchPorts(state); | |
return { | |
columnDefs: grid.columns, | |
isReadOnly: grid.isReadOnly, | |
enableSorting: grid.enableSorting, | |
} | |
}; | |
const mapDispatchToProps = (dispatch) => { | |
return { | |
onRowsChanged(ports, isModelCleared){ | |
dispatch(actions.storeRows(ports, isModelCleared)) | |
}, | |
createDatasource(gridComponent){ | |
return new RowsDatasource( | |
gridComponent, | |
data => actions.pageDataReceived(data) | |
); | |
}, | |
resetReduxState(){ | |
dispatch(actions.resetReduxState()); | |
} | |
} | |
}; | |
export default ReactRedux.connect( | |
mapStateToProps, | |
mapDispatchToProps | |
)(GridComponent) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import portService from 'service/port/ports.service'; | |
class RowsDatasource { | |
/** | |
* @param gridComponent.api | |
* @param gridComponent.gridOptions | |
*/ | |
constructor(gridComponent){ | |
this.getRows = this.getRows.bind(this, gridComponent); | |
} | |
/** https://www.ag-grid.com/angular-grid-datasource/index.php */ | |
/** | |
* @param params.startRow: The first row index to get. | |
* @param params.endRow: The first row index to NOT get. | |
* @param params.successCallback: Callback to call for the result when successful. | |
* @param params.failCallback: Callback to call for the result when failed. | |
* @param params.context: The grid context object. | |
*/ | |
getRows(gridComponent, params){ | |
console.log("GETROWS - STARTROW: ", params.startRow); | |
const query = { | |
startRow: params.startRow, | |
endRow: params.endRow, | |
sortModel: params.sortModel, | |
filterModel: params.filterModel | |
}; | |
// console.log("Getting rows for ", query); | |
portService.getPortsForAgGrid(query) | |
.then(response => response.data) | |
.then(ports => { | |
const isModelCleared = params.startRow === 0; | |
if (isModelCleared) { | |
gridComponent.props.resetReduxState(); | |
} | |
gridComponent.props.onRowsChanged(ports); | |
const lastRow = _calculateLastRow(params.startRow, params.endRow, ports); | |
params.successCallback(ports, lastRow); | |
}); | |
} | |
} | |
function _calculateLastRow(startRow, endRow, ports) { | |
if (ports.length < endRow - startRow) { | |
return startRow + ports.length; | |
} | |
return -1; | |
} | |
export default RowsDatasource; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment