From c95bab9a0382d4a8de245dbb6d0acff97690d0a7 Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Mon, 9 Jan 2017 11:36:15 +0100 Subject: [PATCH] Add pagination to feature Grid (#1367) First version of the pagination for the FeatureGrid plugin (#1320) - Renamed state variables in consistend way - Fixed pagination (no virtual pagination for now) - Add pagination disabled mode --- web/client/actions/wfsquery.js | 32 +++++++-- .../data/featuregrid/DockedFeatureGrid.jsx | 71 ++++++++----------- .../data/featuregrid/FeatureGrid.jsx | 4 +- .../components/data/query/QueryToolbar.jsx | 15 ++-- web/client/plugins/FeatureGrid.jsx | 7 +- web/client/plugins/QueryPanel.jsx | 4 +- web/client/plugins/TOC.jsx | 4 +- web/client/reducers/query.js | 14 ++++ 8 files changed, 89 insertions(+), 62 deletions(-) diff --git a/web/client/actions/wfsquery.js b/web/client/actions/wfsquery.js index c046f1ad40..43dbc01ca8 100644 --- a/web/client/actions/wfsquery.js +++ b/web/client/actions/wfsquery.js @@ -10,14 +10,14 @@ const FEATURE_TYPE_LOADED = 'FEATURE_TYPE_LOADED'; const FEATURE_LOADED = 'FEATURE_LOADED'; const FEATURE_TYPE_ERROR = 'FEATURE_TYPE_ERROR'; const FEATURE_ERROR = 'FEATURE_ERROR'; +const QUERY_CREATE = 'QUERY_CREATE'; const QUERY_RESULT = 'QUERY_RESULT'; const QUERY_ERROR = 'QUERY_ERROR'; const RESET_QUERY = 'RESET_QUERY'; const axios = require('../libs/ajax'); - const {toggleControl, setControlProperty} = require('./controls'); - +const FilterUtils = require('../utils/FilterUtils'); function featureTypeSelected(url, typeName) { return { type: FEATURE_TYPE_SELECTED, @@ -57,9 +57,11 @@ function featureError(typeName, error) { }; } -function querySearchResponse(result) { +function querySearchResponse(result, searchUrl, filterObj) { return { type: QUERY_RESULT, + searchUrl, + filterObj, result }; } @@ -110,14 +112,30 @@ function loadFeature(baseUrl, typeName) { }); }; } +function createQuery(searchUrl, filterObj) { + return { + type: QUERY_CREATE, + searchUrl, + filterObj + }; +} -function query(seachURL, data) { +function query(searchUrl, filterObj) { + createQuery(searchUrl, filterObj); + let data; + if (typeof filterObj === 'string') { + data = filterObj; + } else { + data = filterObj.filterType === "OGC" ? + FilterUtils.toOGCFilter(filterObj.featureTypeName, filterObj, filterObj.ogcVersion, filterObj.sortOptions, filterObj.hits) : + FilterUtils.toCQLFilter(filterObj); + } return (dispatch) => { - return axios.post(seachURL + '?service=WFS&&outputFormat=json', data, { + return axios.post(searchUrl + '?service=WFS&&outputFormat=json', data, { timeout: 60000, headers: {'Accept': 'application/json', 'Content-Type': 'application/json'} }).then((response) => { - dispatch(querySearchResponse(response.data)); + dispatch(querySearchResponse(response.data, searchUrl, filterObj)); }).catch((e) => { dispatch(queryError(e)); }); @@ -145,12 +163,14 @@ module.exports = { FEATURE_LOADED, FEATURE_TYPE_ERROR, FEATURE_ERROR, + QUERY_CREATE, QUERY_RESULT, QUERY_ERROR, RESET_QUERY, featureTypeSelected, describeFeatureType, loadFeature, + createQuery, query, resetQuery, toggleQueryPanel diff --git a/web/client/components/data/featuregrid/DockedFeatureGrid.jsx b/web/client/components/data/featuregrid/DockedFeatureGrid.jsx index 684afae256..f4c6170f49 100644 --- a/web/client/components/data/featuregrid/DockedFeatureGrid.jsx +++ b/web/client/components/data/featuregrid/DockedFeatureGrid.jsx @@ -19,6 +19,7 @@ const {head} = require('lodash'); const I18N = require('../../../components/I18N/I18N'); const Spinner = require('react-spinkit'); const assign = require('object-assign'); +const {isEqual} = require('lodash'); require("./featuregrid.css"); @@ -48,13 +49,11 @@ const DockedFeatureGrid = React.createClass({ selectFeatures: React.PropTypes.func, totalFeatures: React.PropTypes.number, pagination: React.PropTypes.bool, - filterFields: React.PropTypes.array, - groupFields: React.PropTypes.array, - spatialField: React.PropTypes.object, featureTypeName: React.PropTypes.string, ogcVersion: React.PropTypes.string, onQuery: React.PropTypes.func, searchUrl: React.PropTypes.string, + filterObj: React.PropTypes.object, dataSourceOptions: React.PropTypes.object, withMap: React.PropTypes.bool.isRequired, onConfigureQuery: React.PropTypes.func, @@ -85,7 +84,7 @@ const DockedFeatureGrid = React.createClass({ featureTypeName: null, ogcVersion: "2.0", columnsDef: [], - pagination: false, + pagination: true, params: {}, groupFields: [], filterFields: [], @@ -93,7 +92,7 @@ const DockedFeatureGrid = React.createClass({ searchUrl: null, dataSourceOptions: { rowCount: -1, - pageSize: 10 + pageSize: 20 }, initWidth: 600, withMap: true, @@ -113,15 +112,6 @@ const DockedFeatureGrid = React.createClass({ componentWillMount() { let height = getWindowSize().maxHeight - 108; this.setState({width: `calc( ${this.props.initWidth} - 30px)`, height}); - if (this.props.pagination && this.props.dataSourceOptions.pageSize) { - this.dataSource = this.getDataSource(this.props.dataSourceOptions); - }else if ( this.props.pagination && !this.props.dataSourceOptions.pageSize) { - let newFilter = FilterUtils.getOgcAllPropertyValue(this.props.featureTypeName, this.props.attributes[0].attribute); - this.props.onConfigureQuery(this.props.searchUrl, newFilter, this.props.params, { - "maxFeatures": 15, - "startIndex": 0 - }); - } }, shouldComponentUpdate(nextProps) { return Object.keys(this.props).reduce((prev, prop) => { @@ -135,10 +125,18 @@ const DockedFeatureGrid = React.createClass({ if (!nextProps.loadingGrid && nextProps.pagination && (nextProps.dataSourceOptions !== this.props.dataSourceOptions)) { this.dataSource = this.getDataSource(nextProps.dataSourceOptions); } - if (!nextProps.loadingGrid && this.featureLoaded && nextProps.features !== this.props.features && Object.keys(nextProps.features).length > 0) { - let rowsThisPage = nextProps.features[this.getRequestId(this.featureLoaded)] || []; - this.featureLoaded.successCallback(rowsThisPage, nextProps.totalFeatures); - this.featureLoaded = null; + if (!nextProps.loadingGrid && this.featureLoaded && nextProps.features !== this.props.features) { + let rowsThisPage = nextProps.features || []; + if (rowsThisPage) { + this.featureLoaded.successCallback(rowsThisPage, nextProps.totalFeatures); + } + } + }, + componentDidUpdate(prevProps) { + if (!this.props.loadingGrid && !this.featureLoaded && !this.props.pagination && this.props.searchUrl) { + if (this.props.filterObj && !isEqual(prevProps.filterObj, this.props.filterObj)) { + this.getFeatures(); + } } }, onGridClose(filter) { @@ -164,31 +162,24 @@ const DockedFeatureGrid = React.createClass({ return params.sortModel.reduce((o, m) => ({sortBy: this.getSortAttribute(m.colId), sortOrder: m.sort}), {}); }, getFeatures(params) { - if (!this.props.loadingGrid) { - let reqId = this.getRequestId(params); - let rowsThisPage = this.props.features[reqId]; - if (rowsThisPage) { - params.successCallback(rowsThisPage, this.props.totalFeatures); - }else { - let pagination = {startIndex: params.startRow, maxFeatures: params.endRow - params.startRow}; - let filterObj = { - groupFields: this.props.groupFields, - filterFields: this.props.filterFields.filter((field) => field.value), - spatialField: this.props.spatialField, + if (!this.props.loadingGrid && this.props.searchUrl) { + let pagination = this.props.pagination ? {startIndex: params.startRow, maxFeatures: params.endRow - params.startRow} : null; + let filterObj = { + ...this.props.filterObj, + sortOptions: params && params.sortModel && this.getSortOptions(params) || null, pagination - }; - let filter = FilterUtils.toOGCFilter(this.props.featureTypeName, filterObj, this.props.ogcVersion, this.getSortOptions(params)); - this.featureLoaded = params; - this.sortModel = params.sortModel; - this.props.onQuery(this.props.searchUrl, filter, this.props.params, reqId); - } + }; + this.featureLoaded = params; + this.sortModel = params && params.sortModel; + this.props.onQuery(this.props.searchUrl, filterObj, this.props.params); + } }, getDataSource(dataSourceOptions) { return { rowCount: dataSourceOptions.rowCount, getRows: this.getFeatures, - pageSize: dataSourceOptions.pageSize, + pageSize: this.props.pagination ? dataSourceOptions.pageSize : 10000000, overflowSize: 20 }; }, @@ -242,9 +233,9 @@ const DockedFeatureGrid = React.createClass({ }); } - let gridConf = this.props.pagination ? {dataSource: this.dataSource, features: []} : {features: this.props.features}; + let gridConf = this.props.pagination ? {dataSource: this.getDataSource(this.props.dataSourceOptions), features: []} : {features: this.props.features}; - if (this.props.features && this.props.features.length > 0) { + if (this.props.filterObj) { return ( field.value), - spatialField: this.props.spatialField + ...this.props.filterObj }; let SLD_BODY = FilterUtils.getSLD(this.props.featureTypeName, filterObj, '1.0'); this.props.selectAllToggle(this.props.featureTypeName, SLD_BODY); diff --git a/web/client/components/data/featuregrid/FeatureGrid.jsx b/web/client/components/data/featuregrid/FeatureGrid.jsx index 72b355e363..aeaab4537c 100644 --- a/web/client/components/data/featuregrid/FeatureGrid.jsx +++ b/web/client/components/data/featuregrid/FeatureGrid.jsx @@ -70,7 +70,7 @@ const FeatureGrid = React.createClass({ virtualPaging: false, paging: false, overflowSize: 10, - pageSize: 15, + pageSize: 10, agGridOptions: {}, columnDefaultOptions: { width: 125 @@ -150,7 +150,7 @@ const FeatureGrid = React.createClass({ } if (this.props.toolbar.selectAll) { - let nOfFeatures = this.props.features.length; + let nOfFeatures = this.props.features && this.props.features.length; if (this.props.paging && this.api) { nOfFeatures = 0; this.api.forEachNode(() => {nOfFeatures++; }); diff --git a/web/client/components/data/query/QueryToolbar.jsx b/web/client/components/data/query/QueryToolbar.jsx index 49f0f519ee..3fcc3d31ea 100644 --- a/web/client/components/data/query/QueryToolbar.jsx +++ b/web/client/components/data/query/QueryToolbar.jsx @@ -10,7 +10,6 @@ const React = require('react'); const {Button, Glyphicon, ButtonToolbar, Modal} = require('react-bootstrap'); const I18N = require('../../I18N/I18N'); -const FilterUtils = require('../../../utils/FilterUtils'); const QueryToolbar = React.createClass({ propTypes: { @@ -94,17 +93,17 @@ const QueryToolbar = React.createClass({ }, search() { let filterObj = { + featureTypeName: this.props.featureTypeName, groupFields: this.props.groupFields, filterFields: this.props.filterFields.filter((field) => field.value), spatialField: this.props.spatialField, - pagination: this.props.pagination + pagination: this.props.pagination, + filterType: this.props.filterType, + ogcVersion: this.props.ogcVersion, + sortOptions: this.props.sortOptions, + hits: this.props.hits }; - - let filter = this.props.filterType === "OGC" ? - FilterUtils.toOGCFilter(this.props.featureTypeName, filterObj, this.props.ogcVersion, this.props.sortOptions, this.props.hits) : - FilterUtils.toCQLFilter(filterObj); - - this.props.actions.onQuery(this.props.searchUrl, filter, this.props.params); + this.props.actions.onQuery(this.props.searchUrl, filterObj, this.props.params); }, reset() { this.props.actions.onChangeDrawingStatus('clean', null, "queryform", []); diff --git a/web/client/plugins/FeatureGrid.jsx b/web/client/plugins/FeatureGrid.jsx index ff4f5a235e..9dfedcf52a 100644 --- a/web/client/plugins/FeatureGrid.jsx +++ b/web/client/plugins/FeatureGrid.jsx @@ -7,10 +7,13 @@ */ const {connect} = require('react-redux'); const {updateHighlighted} = require('../actions/highlight'); +const {query} = require('../actions/wfsquery'); module.exports = { FeatureGridPlugin: connect((state) => ({ features: state.query && state.query.result && state.query.result.features, + filterObj: state.query && state.query.filterObj, + searchUrl: state.query && state.query.searchUrl, initWidth: "100%", columnsDef: state.query && state.query.typeName && state.query.featureTypes && state.query.featureTypes[state.query.typeName] @@ -21,9 +24,11 @@ module.exports = { headerName: attr.label, field: attr.attribute })), + query: state.query && state.query.queryObj, totalFeatures: state.query && state.query.result && state.query.result.totalFeatures }), { - selectFeatures: updateHighlighted + selectFeatures: updateHighlighted, + onQuery: query })(require('../components/data/featuregrid/DockedFeatureGrid')) }; diff --git a/web/client/plugins/QueryPanel.jsx b/web/client/plugins/QueryPanel.jsx index 82c797fc26..36958b4f3f 100644 --- a/web/client/plugins/QueryPanel.jsx +++ b/web/client/plugins/QueryPanel.jsx @@ -48,7 +48,7 @@ const { zoneChange } = require('../actions/queryform'); -const {query} = require('../actions/wfsquery'); +const {createQuery} = require('../actions/wfsquery'); const { changeDrawingStatus, @@ -104,7 +104,7 @@ const SmartQueryForm = connect((state) => { zoneChange }, dispatch), queryToolbarActions: bindActionCreators({ - onQuery: query, + onQuery: createQuery, onReset: reset, onChangeDrawingStatus: changeDrawingStatus }, dispatch) diff --git a/web/client/plugins/TOC.jsx b/web/client/plugins/TOC.jsx index 2acd841bc2..86a25a48cc 100644 --- a/web/client/plugins/TOC.jsx +++ b/web/client/plugins/TOC.jsx @@ -51,7 +51,7 @@ const { zoneChange } = require('../actions/queryform'); -const {query, toggleQueryPanel, describeFeatureType} = require('../actions/wfsquery'); +const {createQuery, toggleQueryPanel, describeFeatureType} = require('../actions/wfsquery'); const { changeDrawingStatus, @@ -114,7 +114,7 @@ const SmartQueryForm = connect((state) => { zoneChange }, dispatch), queryToolbarActions: bindActionCreators({ - onQuery: query, + onQuery: createQuery, onReset: reset, onChangeDrawingStatus: changeDrawingStatus }, dispatch) diff --git a/web/client/reducers/query.js b/web/client/reducers/query.js index 0b5528b439..94f3dd9fb2 100644 --- a/web/client/reducers/query.js +++ b/web/client/reducers/query.js @@ -12,6 +12,7 @@ const { FEATURE_TYPE_ERROR, FEATURE_LOADED, FEATURE_ERROR, + QUERY_CREATE, QUERY_RESULT, QUERY_ERROR, RESET_QUERY @@ -100,9 +101,17 @@ function query(state = initialState, action) { featureTypes: assign({}, state.data, {[action.typeName]: {error: action.error}}) }); } + case QUERY_CREATE: { + return assign({}, state, { + searchUrl: action.searchUrl, + filterObj: action.filterObj + }); + } case QUERY_RESULT: { return assign({}, state, { result: action.result, + searchUrl: action.searchUrl, + filterObj: action.filterObj, resultError: null }); } @@ -113,6 +122,11 @@ function query(state = initialState, action) { }); } case QUERY_FORM_RESET: + return assign({}, state, { + result: null, + filterObj: null, + searchUrl: null + }); case RESET_QUERY: { return assign({}, state, { result: null,