Permalink
Browse files

Merge pull request #104 from elemoine/searchaction

Add form search action
  • Loading branch information...
sbrunner committed May 11, 2012
2 parents a3caba4 + 65f542d commit 5b0b398d385117dc8ead3b73dfefd68208426aa3
Showing with 378 additions and 0 deletions.
  1. +171 −0 src/GeoExt/form/action/Search.js
  2. +206 −0 tests/form/action/Search.html
  3. +1 −0 tests/list-tests.html
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2008-2012 The Open Source Geospatial Foundation
+ *
+ * Published under the BSD license.
+ * See http://svn.geoext.org/core/trunk/geoext/license.txt for the full text
+ * of the license.
+ */
+
+/*
+ * @include GeoExt/widgets/form.js
+ */
+
+/**
+ * A specific `Ext.form.action.Action` to be used when using a form to
+ * trigger search requests using an `OpenLayers.Protocol`.
+ *
+ * When run this action builds an `OpenLayers.Filter` from the form
+ * and passes this filter to its protocol's read method. The form fields
+ * must be named after a specific convention, so that an appropriate
+ * `OpenLayers.Filter.Comparison` filter is created for each
+ * field.
+ *
+ * For example a field with the name `foo__like` would result in an
+ * `OpenLayers.Filter.Comparison` of type
+ * `OpenLayers.Filter.Comparison.LIKE` being created.
+ *
+ * Here is the convention:
+ *
+ * * `<name>__eq: OpenLayers.Filter.Comparison.EQUAL_TO`
+ * * `<name>__ne: OpenLayers.Filter.Comparison.NOT_EQUAL_TO`
+ * * `<name>__lt: OpenLayers.Filter.Comparison.LESS_THAN`
+ * * `<name>__le: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO`
+ * * `<name>__gt: OpenLayers.Filter.Comparison.GREATER_THAN`
+ * * `<name>__ge: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO`
+ * * `<name>__like: OpenLayers.Filter.Comparison.LIKE`
+ *
+ * In most cases your would not directly create `GeoExt.form.action.Search`
+ * objects, but use `GeoExt.form.FormPanel` instead.
+ *
+ * Sample code showing how to use a GeoExt Search Action with an Ext
+ * form panel:
+ *
+<pre><code>
+var formPanel = Ext.create('Ext.form.Panel', {
+ renderTo: "formpanel",
+ items: [{
+ xtype: "textfield",
+ name: "name__like",
+ value: "mont"
+ }, {
+ xtype: "textfield",
+ name: "elevation__ge",
+ value: "2000"
+ }]
+});
+
+var searchAction = Ext.create('GeoExt.form.action.Search', {
+ form: formPanel.getForm(),
+ protocol: new OpenLayers.Protocol.WFS({
+ url: "http://publicus.opengeo.org/geoserver/wfs",
+ featureType: "tasmania_roads",
+ featureNS: "http://www.openplans.org/topp"
+ }),
+ abortPrevious: true
+});
+
+formPanel.getForm().doAction(searchAction, {
+ callback: function(response) {
+ // response.features includes the features read
+ // from the server through the protocol
+ }
+});
+</code></pre>
+ */
+Ext.define('GeoExt.form.action.Search', {
+ extend: 'Ext.form.Action',
+ alternateClassName: 'GeoExt.form.SearchAction',
+ alias: 'formaction.search',
+ requires: ['GeoExt.Form'],
+
+ /**
+ * @property {String}
+ * The action type string.
+ * @private
+ */
+ type: "search",
+
+ /**
+ * @property {OpenLayers.Protocol.Response} response
+ * `OpenLayers.Protocol.Response` A reference to the response
+ * resulting from the search request. Read-only.
+ */
+
+ /**
+ * @cfg {OpenLayers.Protocol} protocol
+ * The protocol to use for search requests.
+ */
+ /**
+ * @property {OpenLayers.Protocol} protocol
+ * The protocol.
+ */
+
+ /**
+ * @cfg {Object} readOptions
+ * (optional) Extra options passed to the protocol's read method.
+ */
+
+ /**
+ * @cfg {Function} callback
+ * (optional) Callback function called when the response is
+ * received.
+ */
+
+ /**
+ * @cfg {Object} scope
+ * (optional) Scope {@link #callback}.
+ */
+
+ /**
+ * @cfg {Boolean} abortPrevious
+ * If set to true, the abort method will be called on the protocol
+ * if there's a pending request. Default is `false`.
+ */
+
+ /**
+ * Run the action.
+ * @private
+ */
+ run: function() {
+ var form = this.form,
+ f = GeoExt.Form.toFilter(form, this.logicalOp, this.wildcard);
+ if(this.clientValidation === false || form.isValid()){
+
+ if (this.abortPrevious && form.prevResponse) {
+ this.protocol.abort(form.prevResponse);
+ }
+
+ this.form.prevResponse = this.protocol.read(
+ Ext.applyIf({
+ filter: f,
+ callback: this.handleResponse,
+ scope: this
+ }, this.readOptions)
+ );
+
+ } else if(this.clientValidation !== false){
+ // client validation failed
+ this.failureType = Ext.form.action.Action.CLIENT_INVALID;
+ form.afterAction(this, false);
+ }
+ },
+
+ /**
+ * Handle the response to the search query.
+ * @param {OpenLayers.Protocol.Response} response The response object.
+ * @private
+ */
+ handleResponse: function(response) {
+ var form = this.form;
+ form.prevResponse = null;
+ this.response = response;
+ if(response.success()) {
+ form.afterAction(this, true);
+ } else {
+ form.afterAction(this, false);
+ }
+ if(this.callback) {
+ this.callback.call(this.scope, response);
+ }
+ }
+});
@@ -0,0 +1,206 @@
+<!DOCTYPE html>
+<html debug="true">
+ <head>
+ <link rel="stylesheet" type="text/css" href="http://cdn.sencha.io/ext-4.1.0-gpl/resources/css/ext-all.css" />
+ <script type="text/javascript" charset="utf-8" src="http://cdn.sencha.io/ext-4.1.0-gpl/ext-all-debug.js" ></script>
+ <script src="http://openlayers.org/api/2.12-rc3/OpenLayers.js"></script>
+
+ <script type="text/javascript">
+
+ Ext.Loader.setConfig({
+ disableCaching: false,
+ enabled: true,
+ paths: {
+ //Ext: "<PATH_TO_EXT_JS>/src",
+ GeoExt: '../../../src/GeoExt'
+ }
+ });
+
+ Ext.require([
+ 'Ext.form.Basic',
+ 'Ext.form.TextField',
+ 'GeoExt.form.action.Search',
+ ]);
+
+ function test_constructor(t) {
+ t.plan(1);
+
+ /*
+ * Set up
+ */
+
+ var form, action, protocol;
+
+ form = Ext.create('Ext.form.Basic', Ext.get("form"));
+ protocol = new OpenLayers.Protocol();
+ action = Ext.create('GeoExt.form.action.Search',
+ {form: form, protocol: protocol});
+
+ /*
+ * Test
+ */
+
+ t.ok(action.protocol == protocol,
+ "Search Action constructor properly sets protocol in instance");
+ }
+
+ function test_run(t) {
+ t.plan(2);
+
+ /*
+ * Set up
+ */
+
+ var field, form, action, protocol;
+
+ field = Ext.create('Ext.form.TextField', {
+ name: "foo__eq",
+ value: "bar"
+ });
+
+ form = Ext.create('Ext.form.Panel', {
+ renderTo: "form",
+ items: [field]
+ });
+
+ protocol = new OpenLayers.Protocol({
+ read: function(options) {
+ t.ok(options.filter instanceof OpenLayers.Filter.Comparison,
+ "run calls protocol.read with a comparison filter");
+ t.eq(options.some, 'option',
+ "run calls protocol.read with extra options");
+ }
+ });
+
+ action = Ext.create('GeoExt.form.action.Search', {
+ form: form.getForm(),
+ protocol: protocol,
+ clientValidation: false,
+ readOptions: {'some': 'option'}
+ });
+
+ /*
+ * Test
+ */
+
+ action.run();
+
+ /*
+ * Tear down
+ */
+
+ form.destroy();
+ }
+
+ function test_abort(t) {
+ t.plan(2);
+
+ /*
+ * Set up
+ */
+
+ var field, form, action, protocol, log = {};
+
+ field = Ext.create('Ext.form.TextField', {
+ name: "foo__eq",
+ value: "bar"
+ });
+
+ form = Ext.create('Ext.form.FormPanel', {
+ renderTo: "form",
+ items: [field]
+ });
+
+ protocol = new OpenLayers.Protocol({
+ read: function(options) {
+ return "something";
+ },
+ abort: function() {
+ log.abort++;
+ }
+ });
+
+ action = Ext.create('GeoExt.form.SearchAction', {
+ form: form.getForm(),
+ protocol: protocol,
+ clientValidation: false,
+ abortPrevious: true
+ });
+
+ /*
+ * Test
+ */
+
+ log.abort = 0;
+ action.run();
+ action.run();
+ t.ok(log.abort == 1, "protocol abort called once");
+
+ log.abort = 0;
+ action.abortPrevious = false;
+ action.run();
+ t.ok(log.abort == 0, "protocol abort not called");
+
+ /*
+ * Tear down
+ */
+ form.destroy();
+ }
+
+ function test_handleResponse(t) {
+ t.plan(3);
+
+ /*
+ * Set up
+ */
+
+ var log, field, form, action, response;
+
+ field = Ext.create('Ext.form.TextField', {
+ name: "foo__eq",
+ value: "bar"
+ });
+
+ form = Ext.create('Ext.form.FormPanel', {
+ renderTo: "form",
+ items: [field]
+ });
+
+ action = Ext.create('GeoExt.form.SearchAction', {
+ form: form.getForm(),
+ callback: function(response) {
+ log.response = response;
+ log.scope = this;
+ },
+ scope: {'some': 'scope'}
+ });
+
+ response = new OpenLayers.Protocol.Response({
+ code: OpenLayers.Protocol.Response.SUCCESS
+ });
+
+ /*
+ * Test
+ */
+
+ log = {};
+ action.handleResponse(response);
+
+ t.ok(action.response === response,
+ 'handleResponse sets the response in the instance');
+ t.ok(log.response === response,
+ 'handleResponse calls callback with expected response');
+ t.eq(log.scope, {'some': 'scope'},
+ 'handleResponse calls callback with expected scope');
+
+ /*
+ * Tear down
+ */
+ form.destroy();
+ }
+
+ </script>
+ <body>
+ <div id="form"></div>
+ </body>
+</html>
View
@@ -8,6 +8,7 @@
<li>container/VectorLegend.html</li>
<li>selection/FeatureModel.html</li>
<li>grid/column/Symbolizer.html</li>
+ <li>form/action/Search.html</li>
<li>slider/Zoom.html</li>
<li>slider/LayerOpacity.html</li>
<li>state/PermalinkProvider.html</li>

0 comments on commit 5b0b398

Please sign in to comment.