From 74fcc3120aa08f2194cea6036f4ccffc6601798e Mon Sep 17 00:00:00 2001 From: Patrick Arlt Date: Fri, 19 Sep 2014 11:51:41 -0700 Subject: [PATCH 1/2] new request features --- .../layers/clustered-feature-layer.md | 8 ++-- .../api-reference/layers/feature-layer.md | 8 ++-- .../layers/heatmap-feature-layer.md | 10 ++--- src/Layers/FeatureLayer/FeatureManager.js | 45 ++++++++++++++++--- src/Request.js | 12 ++++- 5 files changed, 61 insertions(+), 22 deletions(-) diff --git a/site/source/pages/api-reference/layers/clustered-feature-layer.md b/site/source/pages/api-reference/layers/clustered-feature-layer.md index 403784246..92605a78d 100644 --- a/site/source/pages/api-reference/layers/clustered-feature-layer.md +++ b/site/source/pages/api-reference/layers/clustered-feature-layer.md @@ -195,9 +195,9 @@ In additon to these events `L.esri.FeatureLayer` also fires the following [Mouse Returns the current `where` setting - setWhere({{{param 'String' 'where'}}}) + setWhere({{{param 'String' 'where'}}}, {{{param 'Function' 'callback'}}}, {{{param 'Object' 'context'}}}) this - Sets the new `where` option and refreshes the layer to reflect the new where filter. + Sets the new `where` option and refreshes the layer to reflect the new where filter. Accepts an optional callback and function context. getTimeRange() @@ -205,9 +205,9 @@ In additon to these events `L.esri.FeatureLayer` also fires the following [Mouse Returns the current time range as an array like [from, to] - setTimeRange({{{param 'Date' 'from'}}}, {{{param 'Date' 'to'}}}) + setTimeRange({{{param 'Date' 'from'}}}, {{{param 'Date' 'to'}}}, , {{{param 'Function' 'callback'}}}, {{{param 'Object' 'context'}}}) this - Sets the current time filter applied to features. An optional callback is run upon completion only if timeFilterMode is set to 'server'. + Sets the current time filter applied to features. An optional callback is run upon completion if timeFilterMode is set to 'server'. Also accepts function context as the last argument. authenticate({{{param 'String' 'token'}}}) diff --git a/site/source/pages/api-reference/layers/feature-layer.md b/site/source/pages/api-reference/layers/feature-layer.md index a77cdb817..b781606ff 100644 --- a/site/source/pages/api-reference/layers/feature-layer.md +++ b/site/source/pages/api-reference/layers/feature-layer.md @@ -211,9 +211,9 @@ In additon to these events `L.esri.Layer.FeatureLayer` also fires the following Returns the current `where` setting - setWhere({{{param 'String' 'where'}}}) + setWhere({{{param 'String' 'where'}}}, {{{param 'Function' 'callback'}}}, {{{param 'Object' 'context'}}}) this - Sets the new `where` option and refreshes the layer to reflect the new where filter. + Sets the new `where` option and refreshes the layer to reflect the new where filter. Accepts an optional callback and function context. getTimeRange() @@ -221,9 +221,9 @@ In additon to these events `L.esri.Layer.FeatureLayer` also fires the following Returns the current time range as an array like [from, to] - setTimeRange({{{param 'Date' 'from'}}}, {{{param 'Date' 'to'}}}) + setTimeRange({{{param 'Date' 'from'}}}, {{{param 'Date' 'to'}}}, , {{{param 'Function' 'callback'}}}, {{{param 'Object' 'context'}}}) this - Sets the current time filter applied to features. An optional callback is run upon completion only if timeFilterMode is set to 'server'. + Sets the current time filter applied to features. An optional callback is run upon completion if timeFilterMode is set to 'server'. Also accepts function context as the last argument. authenticate({{{param 'String' 'token'}}}) diff --git a/site/source/pages/api-reference/layers/heatmap-feature-layer.md b/site/source/pages/api-reference/layers/heatmap-feature-layer.md index a6310290f..41c0064c5 100644 --- a/site/source/pages/api-reference/layers/heatmap-feature-layer.md +++ b/site/source/pages/api-reference/layers/heatmap-feature-layer.md @@ -116,10 +116,10 @@ More information about Feature Layers can be found in the [`L.esri.Layers.Featur String Returns the current `where` setting - - setWhere({{{param 'String' 'where'}}}) + + setWhere({{{param 'String' 'where'}}}, {{{param 'Function' 'callback'}}}, {{{param 'Object' 'context'}}}) this - Sets the new `where` option and refreshes the layer to reflect the new where filter. + Sets the new `where` option and refreshes the layer to reflect the new where filter. Accepts an optional callback and function context. getTimeRange() @@ -127,9 +127,9 @@ More information about Feature Layers can be found in the [`L.esri.Layers.Featur Returns the current time range as an array like [from, to] - setTimeRange({{{param 'Date' 'from'}}}, {{{param 'Date' 'to'}}}) + setTimeRange({{{param 'Date' 'from'}}}, {{{param 'Date' 'to'}}}, , {{{param 'Function' 'callback'}}}, {{{param 'Object' 'context'}}}) this - Sets the current time filter applied to features. An optional callback is run upon completion only if timeFilterMode is set to 'server'. + Sets the current time filter applied to features. An optional callback is run upon completion if timeFilterMode is set to 'server'. Also accepts function context as the last argument. authenticate({{{param 'String' 'token'}}}) diff --git a/src/Layers/FeatureLayer/FeatureManager.js b/src/Layers/FeatureLayer/FeatureManager.js index 04a1236a5..a0de5660f 100644 --- a/src/Layers/FeatureLayer/FeatureManager.js +++ b/src/Layers/FeatureLayer/FeatureManager.js @@ -47,6 +47,7 @@ this._currentSnapshot = []; // cache of what layers should be active this._activeRequests = 0; + this._pendingRequests = []; }, /** @@ -83,7 +84,7 @@ }); } - this._buildQuery(bounds).run(function(error, featureCollection, response){ + return this._buildQuery(bounds).run(function(error, featureCollection, response){ if(response && response.exceededTransferLimit){ this.fire('drawlimitexceeded'); } @@ -139,12 +140,21 @@ * Where Methods */ - setWhere: function(where){ + setWhere: function(where, callback, context){ + for (var x = this._pendingRequests.length - 1; x >= 0; x--) { + this._pendingRequests[x].abort(); + } + this.options.where = (where && where.length) ? where : '1=1'; + var oldSnapshot = []; var newShapshot = []; var pendingRequests = 0; + var requestError = null; var requestCallback = L.Util.bind(function(error, featureCollection){ + if(error){ + requestError = error; + } if(featureCollection){ for (var i = featureCollection.features.length - 1; i >= 0; i--) { newShapshot.push(featureCollection.features[i].id); @@ -157,6 +167,9 @@ this._currentSnapshot = newShapshot; this.removeLayers(oldSnapshot); this.addLayers(newShapshot); + if(callback) { + callback.call(context, requestError); + } } }, this); @@ -168,9 +181,11 @@ pendingRequests++; var coords = this._keyToCellCoords(key); var bounds = this._cellCoordsToBounds(coords); - this._requestFeatures(bounds, key, requestCallback); + var request = this._requestFeatures(bounds, key, requestCallback); + this._pendingRequests.push(request); } + return this; }, @@ -186,12 +201,26 @@ return [this.options.from, this.options.to]; }, - setTimeRange: function(from, to){ + setTimeRange: function(from, to, callback, context){ + for (var x = this._pendingRequests.length - 1; x >= 0; x--) { + this._pendingRequests[x].abort(); + } + var oldFrom = this.options.from; var oldTo = this.options.to; - - var requestCallback = L.Util.bind(function(){ + var pendingRequests = 0; + var requestError = null; + var requestCallback = L.Util.bind(function(error){ + if(error){ + requestError = error; + } this._filterExistingFeatures(oldFrom, oldTo, from, to); + + pendingRequests--; + + if(pendingRequests <= 0 && callback){ + callback.call(context, requestError); + } }, this); this.options.from = from; @@ -201,9 +230,11 @@ if(this.options.timeFilterMode === 'server') { for(var key in this._activeCells){ + pendingRequests++; var coords = this._keyToCellCoords(key); var bounds = this._cellCoordsToBounds(coords); - this._requestFeatures(bounds, key, requestCallback); + var request = this._requestFeatures(bounds, key, requestCallback); + this._pendingRequests.push(request); } } }, diff --git a/src/Request.js b/src/Request.js index 9deb71dcb..a0563f1de 100644 --- a/src/Request.js +++ b/src/Request.js @@ -46,10 +46,10 @@ httpRequest.onreadystatechange = function(){ var response; var error; + if (httpRequest.readyState === 4) { try { response = JSON.parse(httpRequest.responseText); - } catch(e) { response = null; error = { @@ -146,7 +146,15 @@ callbacks++; - return L.esri._callback[callbackId]; + return { + id: callbackId, + abort: function(){ + L.esri._callback[callbackId]({ + code: 500, + message: 'Could not parse response as JSON.' + }); + } + }; } } From 2515699403a6bbb7f8b9ce877ae013638b3c9692 Mon Sep 17 00:00:00 2001 From: Patrick Arlt Date: Fri, 19 Sep 2014 14:12:03 -0700 Subject: [PATCH 2/2] abort for JSONP, callbacks for setWhere, setTimeRange --- karma.conf.js | 1 + spec/Layers/FeatureLayer/FeatureManagerSpec.js | 11 ++++++++--- spec/RequestSpec.js | 6 +++--- spec/Services/ServiceSpec.js | 2 +- src/Layers/FeatureLayer/FeatureManager.js | 17 ++++------------- 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 25a76f3be..d072ac52e 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -17,6 +17,7 @@ module.exports = function(config) { 'http://cdn.leafletjs.com/leaflet-0.7.3/leaflet-src.js', 'http://cdn-geoweb.s3.amazonaws.com/Leaflet.heat/0.1.1/leaflet-heat.js', 'http://cdn-geoweb.s3.amazonaws.com/Leaflet.markercluster/0.4.0/leaflet.markercluster-src.js', + // 'spec/Layers/FeatureLayer/FeatureManagerSpec.js', 'spec/**/*Spec.js', 'src/EsriLeaflet.js', 'src/Util.js', diff --git a/spec/Layers/FeatureLayer/FeatureManagerSpec.js b/spec/Layers/FeatureLayer/FeatureManagerSpec.js index 2dd62831f..10c219014 100644 --- a/spec/Layers/FeatureLayer/FeatureManagerSpec.js +++ b/spec/Layers/FeatureLayer/FeatureManagerSpec.js @@ -410,12 +410,14 @@ describe('L.esri.Layers.FeatureManager', function () { objectIdFieldName: 'OBJECTID' })); - layer.setTimeRange(new Date('January 13 2014 GMT-0800'), new Date('January 16 2014 GMT-0800')); + var callback = sinon.spy(); + + layer.setTimeRange(new Date('January 13 2014 GMT-0800'), new Date('January 16 2014 GMT-0800'), callback); server.respond(); + expect(callback).to.have.been.called; expect(layer.removeLayers).to.have.been.calledWith([4, 4]); - expect(layer.createLayers).to.have.been.calledWith([{ 'geometry': { 'type': 'Point', @@ -466,10 +468,13 @@ describe('L.esri.Layers.FeatureManager', function () { 'id': 1 }]); - layer.setWhere('Type="Inactive"'); + var callback = sinon.spy(); + + layer.setWhere('Type="Inactive"', callback); server.respond(); + expect(callback).to.have.been.called; expect(layer.removeLayers).to.have.been.calledWith([1]); expect(layer.createLayers).to.have.been.calledWith([{ 'type': 'Feature', diff --git a/spec/RequestSpec.js b/spec/RequestSpec.js index a96eca71b..c286b5a1b 100644 --- a/spec/RequestSpec.js +++ b/spec/RequestSpec.js @@ -55,7 +55,7 @@ describe('L.esri.Request', function () { foo: 'bar' }); - request(sampleResponse); + L.esri._callback[request.id](sampleResponse); }); it('should callback with an error on non-JSON reponses', function(done){ @@ -69,7 +69,7 @@ describe('L.esri.Request', function () { done(); }); - request('foo'); + L.esri._callback[request.id]('foo'); }); it('should callback with an error when an error is recived from the server', function(done){ @@ -78,7 +78,7 @@ describe('L.esri.Request', function () { done(); }); - request(sampleError); + L.esri._callback[request.id](sampleError); }); it('should be able to make a POST request with CORS', function(done){ diff --git a/spec/Services/ServiceSpec.js b/spec/Services/ServiceSpec.js index 26c65ee51..3ca19ab2e 100644 --- a/spec/Services/ServiceSpec.js +++ b/spec/Services/ServiceSpec.js @@ -33,7 +33,7 @@ describe('L.esri.Service', function () { done(); }); - request({foo:'bar'}); + L.esri._callback[request.id]({foo:'bar'}); }); it('should make POST requests', function(done){ diff --git a/src/Layers/FeatureLayer/FeatureManager.js b/src/Layers/FeatureLayer/FeatureManager.js index a0de5660f..b7fd6776f 100644 --- a/src/Layers/FeatureLayer/FeatureManager.js +++ b/src/Layers/FeatureLayer/FeatureManager.js @@ -141,9 +141,6 @@ */ setWhere: function(where, callback, context){ - for (var x = this._pendingRequests.length - 1; x >= 0; x--) { - this._pendingRequests[x].abort(); - } this.options.where = (where && where.length) ? where : '1=1'; @@ -155,6 +152,7 @@ if(error){ requestError = error; } + if(featureCollection){ for (var i = featureCollection.features.length - 1; i >= 0; i--) { newShapshot.push(featureCollection.features[i].id); @@ -181,11 +179,9 @@ pendingRequests++; var coords = this._keyToCellCoords(key); var bounds = this._cellCoordsToBounds(coords); - var request = this._requestFeatures(bounds, key, requestCallback); - this._pendingRequests.push(request); + this._requestFeatures(bounds, key, requestCallback); } - return this; }, @@ -202,10 +198,6 @@ }, setTimeRange: function(from, to, callback, context){ - for (var x = this._pendingRequests.length - 1; x >= 0; x--) { - this._pendingRequests[x].abort(); - } - var oldFrom = this.options.from; var oldTo = this.options.to; var pendingRequests = 0; @@ -218,7 +210,7 @@ pendingRequests--; - if(pendingRequests <= 0 && callback){ + if(callback && pendingRequests <= 0){ callback.call(context, requestError); } }, this); @@ -233,8 +225,7 @@ pendingRequests++; var coords = this._keyToCellCoords(key); var bounds = this._cellCoordsToBounds(coords); - var request = this._requestFeatures(bounds, key, requestCallback); - this._pendingRequests.push(request); + this._requestFeatures(bounds, key, requestCallback); } } },