v4.9 - Feature layer applyEdits() bug

1862
8
Jump to solution
10-25-2018 06:03 PM
NickCameron2
New Contributor III

I'm using a client side data source on a feature layer

When calling applyEdits with addFeatures specified, the object Id field on the feature layer definition and therefore the value set on the graphics attributes is not being honored when the features are added.

See this codepen for an example: https://codepen.io/anon/pen/BqvEPm?editors=1010

Two features with objectIds set in the attributes are passed to applyEdits(), but when being added the object Ids are automatically calculated by incrementing the max existing object Id and the attribute value is disregarded.

You can see it in the console or in the popup for the features in the example. The delete button won't delete anything as the object ids don't match anymore.

This feels like a bug, or is it by design?

If a bug, any known workarounds? 

I haven't tried it on a layer with a service source, as it seems specific to MemorySourceWorker though I'm guessing a service source is probably ok.

Thanks!

Tags (2)
1 Solution

Accepted Solutions
UndralBatsukh
Esri Regular Contributor

Hi there, 

It is because you are adding client side graphics via FeatureLayer.applyEdits. Anytime, you add or remove client side graphics, you must query the features to sync your client side graphics. 

So you must query features from the layer before you call applyEdits with deletes. Please see the code below:

  function deleteFeatures() {
    layer.queryFeatures({
            where: "1=1",
            returnGeometry: true,
            outFields: ["*"]
     }).then(function(results){
        var deleteFeature = results.features;
        const edits = {
            deleteFeatures: deleteFeature
        };
        layer.applyEdits(edits);
    });
  }

I updated your test app to show this behavior: An Anonymous Pen on CodePen 

This behavior is explained here.

View solution in original post

8 Replies
UndralBatsukh
Esri Regular Contributor

Hi there, 

It is because you are adding client side graphics via FeatureLayer.applyEdits. Anytime, you add or remove client side graphics, you must query the features to sync your client side graphics. 

So you must query features from the layer before you call applyEdits with deletes. Please see the code below:

  function deleteFeatures() {
    layer.queryFeatures({
            where: "1=1",
            returnGeometry: true,
            outFields: ["*"]
     }).then(function(results){
        var deleteFeature = results.features;
        const edits = {
            deleteFeatures: deleteFeature
        };
        layer.applyEdits(edits);
    });
  }

I updated your test app to show this behavior: An Anonymous Pen on CodePen 

This behavior is explained here.

View solution in original post

NickCameron2
New Contributor III

You are an absolute legend! This was driving me crazy - thank you so much!

I had read those docs earlier trying to sort this out, I don't think they quite explain what's required for deleting...solution makes sense when you see it . Thanks again!

0 Kudos
TylerKirk
New Contributor II

In the 4.12 version of the API I am still seeing the behavior noted above: "Two features with objectIds set in the attributes are passed to applyEdits(), but when being added the object Ids are automatically calculated by incrementing the max existing object Id and the attribute value is disregarded."

As Nick Cameron originally asked, is this expected behavior/by design?

I need attribute objectId values passed in to the applyEdits addFeatures array to match the objectId values from the generated graphics after the applyEdits finishes as my data source sends out updates to existing features; if the objectId value is changed by the addFeature applyEdits then how am I supposed to use the applyEdits updateFeatures capability if the graphics rendered on the client don't match the objectIds sent from the data source?

0 Kudos
UndralBatsukh
Esri Regular Contributor

Hi there, 

I am not sure if I completely understand your workflow. I apologize if I misunderstood your problem and if the following explanation does not address your concern then can you please provide me with a working sample? 

FeatureService objectIDs are system generated and we do not have control over it. If you are calling FeatureLayer.applyEdits, featurelayer features (geomery and attributes) are automatically updated for the layer. If you are copying features from a FeatureLayer then adding them to GraphicsLayer lets say and calling applyEdits for the FeatureLayer... then the behavior you are seeing is correct as we do not update client-side graphics attributes once FeatureLayer.applyEdits takes place (they are no longer connected). If you are creating a new feature then updating its attributes and expecting to see the changes in graphics in graphicslayer lets say... I'd suggest that you wait to add the new graphics to graphicsLayer until the applyEdits results are back. Once the applyEdits results are returned, you need to get new objectids, query for the features with those ids, then add the features to your graphicslayer. Do something like the following:

// ObjectID is returned in the FeatureLayer.applyEdits.

function addNewFeatureToGraphicsLayer (objectId) {
  // query feature from the server
  featureLayer.queryFeatures({
     objectIds: [objectId],
     outFields: ["*"],
     returnGeometry: true
  })
  .then(function(results) {
    if (results.features.length > 0) {
      var graphic = results.features[0];

      // if you are creating a new graphic why not wait until the edit results are back?
     graphicsLayer.add(graphic);
   }
 });

}

If the new graphics were added to the graphicslayer before applyEdits results are returned, then remove the current client side graphic version and replace the graphic with the new one. 

-Undral

0 Kudos
TylerKirk
New Contributor II

Thanks Undral for the response!

Let me better explain my workflow.

Upon startup

1. Query json REST endpoint

2. Convert json data to list of graphics. There is an existing identifier in the data that is set to the object id field in the graphic.

3. Create client side FeatureLayer from graphics.

Successive polls

1. Query json REST endpoint

2. Convert json data to list of graphics. There is an existing identifier in the data that is set to the object id field in the graphic.

2. After querying the feautreLayerView, determine which graphics need to be updated, deleted, and added by comparing object id fields between the latest graphics from the rest endpoint and the graphics from the featureLayerView query

3. Use the featureLayer.applyEdits method to update the featurelayer graphics

The issue that I am seeing is that when graphics are added to a client side featurelayer through the applyEdits method, the object ids for the graphics are changed from what was passed in to whatever the featurelayer decides their objectids should be; which appears to be "calculated by incrementing the max existing object Id".

 

When graphics are first added to the featurelayer through the source field, their object ids do not change.

Ended up creating codepen to showcase the behavior I'm seeing.

https://codepen.io/MotoTK/pen/voRLpY?editors=0010 

0 Kudos
UndralBatsukh
Esri Regular Contributor

Hi there, 

Your observations about the objectIds are correct. It is just how it works as of today. I have created an enhancement request not to override objectIds of graphics added to in memory FeatureLayer. I do not know when it will be implemented in the API. In meantime, you have to query features from your in memory FeatureLayer once applyEdits is completed as shown below. I sense that you already know how to do that. Another suggestion would be to create a new attribute field to keep to track of these unique identifiers? Hope this helps.

layer
.applyEdits({
addFeatures: addFeatureGraphics
})
.then(result => {
console.log(result);

layer.queryFeatures({
objectIds: [result.addFeatureResults[0].objectId]
}).then(function(results){
console.log("features", results.features);
});
});

TylerKirk
New Contributor II

I was already planning on moving forward with your second suggestion, "create a new attribute field to keep to track of these unique identifiers". Let me know when this is addressed though so I can remove the workarounds at that time.

Thanks Undral!

0 Kudos
AlexanderParshin
New Contributor II

I faced the same problem (in v4.14) - IDs are kept unchanged during the initial addition (using "source" property) but are automatically modified after "applyEdits()". It would be really great to fix this behavior!