.. _geoext.wfs.wfst: Committing Feature Modifications Over WFS-T =========================================== Until GeoExt also provides writers, we have to rely on OpenLayers for writing modifications back to the WFS. This is not a big problem though, because WFS-T support in OpenLayers is solid. But it requires us to take some extra care of feature states. Managing Feature States ----------------------- For keeping track of "create", "update" and "delete" operations, OpenLayers vector features have a ``state`` property. The WFS protocol relies on this property to determine which features to commit using an "Insert", "Update" or "Delete" transaction. So we need to make sure that the ``state`` property gets set properly: * ``OpenLayers.State.INSERT`` for features that were just created. We do not need to do anything here, because the DrawFeature control handles this for us. * ``OpenLayers.State.UPDATE`` for features with modified attributes, except for features that have ``OpenLayers.State.INSERT`` set already. For modified geometries, the ModifyFeature control handles this. * ``OpenLayers.State.DELETE`` for features that the user wants to delete, except for features that have ``OpenLayers.State.INSERT`` set, which can be removed. .. rubric:: Tasks #. Open :file:`wfs-editor.html`` in your text editor. Add an "afteredit" listener to the gridPanel definition, setting the state to ``OpenLayers.State.UPDATE``, unless it is ``OpenLayers.State.INSERT``. The first few lines of yourgridPanel definition should now look like this: .. code-block:: javascript var gridPanel = new Ext.grid.EditorGridPanel({ title: "Feature Table - Manhattan (NY) landmarks", region: "center", viewConfig: {forceFit: true}, store: store, listeners: { afteredit: function(e) { var feature = e.record.get("feature"); if(feature.state !== OpenLayers.State.INSERT) { feature.state = OpenLayers.State.UPDATE; } } }, sm: new GeoExt.grid.FeatureSelectionModel({ selectControl: modifyControl.selectControl, singleSelect: true }), ... #. Modify the handler for the "Delete" button so it re-adds deleted features with a state other than ``OpenLayers.State.INSERT``, after setting their state to ``OpenLayers.State.DELETE`` (which also makes them invisible on the layer: .. code-block:: javascript { text: "Delete", handler: function() { gridPanel.getSelectionModel().each(function(rec) { var feature = rec.get("feature"); modifyControl.unselectFeature(feature); store.remove(rec); if(feature.state !== OpenLayers.State.INSERT) { feature.state = OpenLayers.State.DELETE; layer.addFeatures([feature]); } }); } } To avoid that features marked ``OpenLayers.State.DELETE`` show up in the grid, we have to configure a filter for the store, using its ``addFeatureFilter`` config property. Make the store definition look like this: .. code-block:: javascript var store = new GeoExt.data.FeatureStore({ fields: [ {name: "LANAME", type: "string"}, {name: "CFCC", type: "string"}, {name: "LAND", type: "float"}, {name: "AREA", type: "int", defaultValue: 10000} ], layer: layer, addFeatureFilter: function(feature) { return feature.state !== OpenLayers.State.DELETE; } }); Adding a Save Strategy and a Save Button ---------------------------------------- Saving feature modifications the OpenLayers way requires the vector layer to be configured with an `OpenLayers.Strategy.Save `_. We will now add such a strategy, along with a "Save" button. .. rubric:: Tasks #. Find the layer definition in your :file:`wfs-browser.html` file. Instantiate a Save strategy above, and make the layer uses it by adding it to the layer's ``strategies`` array. The whole code block should now look like this: .. code-block:: javascript var saveStrategy = new OpenLayers.Strategy.Save(); var layer = new OpenLayers.Layer.Vector("vector", { strategies: [new OpenLayers.Strategy.Fixed(), saveStrategy], protocol: new OpenLayers.Protocol.WFS({ url: "/geoserver/ows", version: "1.1.0", featureType: "parks", featureNS: "http://medford.opengeo.org", srsName: "EPSG:4326" }) }); .. warning:: If you are modifying this example to work with a layer that is stored in a shapefile or a MySQL database, modify your saveStrategy to look like this: .. code-block:: javascript var saveStrategy = new OpenLayers.Strategy.Save({ onCommit: function() { saveStrategy.layer.refresh(); } }); This is required because the mentioned data backends do not report back the ids of inserted features. What it does is that it refreshes the whole layer rather than just assigning the appropriate feature ids to the features that are already on the layer. #. Add a button on the right side of the map panel's bottom toolbar, with a handler that calls the ``save`` method of the saveStrategy. The mapPanel's bbar definition has to look like this now: .. code-block:: javascript bbar: [ new GeoExt.Action({ control: drawControl, text: "Create", enableToggle: true }), { text: "Delete", handler: function() { gridPanel.getSelectionModel().each(function(rec) { var feature = rec.get("feature"); modifyControl.unselectFeature(feature); gridPanel.store.remove(rec); if(feature.state !== OpenLayers.State.INSERT) { feature.state = OpenLayers.State.DELETE; layer.addFeatures([feature]); } }) } }, "->", { text: "Save", handler: function() { store.commitChanges(); saveStrategy.save(); } } ] #. Save your file and reload @workshop_url@/wfs-editor.html. Make some changes and hit "Save". Reload the page to see that your changes were persisted. .. figure:: wfst.png Application with "Save" button and a persisted feature after reloading. One More Look at the "Save" Button `````````````````````````````````` For the "Save" button, we have added a line of code reading ``store.commitChanges()``. .. code-block:: javascript { text: "Save", handler: function() { store.commitChanges(); saveStrategy.save(); } } In Ext JS, the ``commitChanges`` method of a store is used to save changes. We use OpenLayers to perform the WFS transaction, so we would not necessarily have to call ``commitChanges``. But doing so will make sure that the records are no longer marked "dirty", which resets the store into the same clean state that the layer will be in when the commit operation is finished. The pleasant side effect of calling ``commitChanges`` is that the tiny red triangles in the top left corner of edited grid cells disappear. Conclusion ---------- You have successfully created a WFS based feature editor. GeoExt makes working with features easy, thanks to its FeatureStore. Although there is no write support yet for the FeatureStore in GeoExt, saving changes via WFS-T is easy because of the solid WFS-T support in OpenLayers and the interoperability of GeoExt and OpenLayers.