SketchViewModel breaking changes at 4.10

SketchViewModel has significant breaking changes at 4.10 that will allow you to accomplish same functionality with less code. For example, we condensed over 20 events into 4 events without losing functionality. This change should make it easier and more straightforward to maintain your code to respond to these events. The following list covers all the breaking changes.

Property changes

SketchViewModel.layer is now a required property. Additionally, only graphics added to this layer are recognized as candidates for updates by SketchViewModel. When SketchViewModel is initialized, you must set the layer property as shown below:


const sketchViewModel = new SketchViewModel({
  layer: graphicsLayer, // was not required at 4.9
  view: view
});

const sketchViewModel = new SketchViewModel({
  view: view
});

The SketchViewModel.graphic property was removed. Use the SketchViewModel.createGraphic or SketchViewModel.updateGraphics properties to get references to graphics being created or updated.

Method changes

The parameters for the SketchViewModel.update() method were changed. The update method now accepts an array of graphics for the first parameter, instead of a single graphic. This also means users can update multiple graphics simultaneously. An optional second parameter, updateOptions, was added to allow users to modify the default behavior of SketchViewModel during an update operation.

At 4.9 and previous versions of the API, developers were required to add custom application logic to identify a specific graphic for an update operation. This was typically done by utilizing MapView's click event, along with MapView.hittest() to identify the candidate graphic to be updated. At 4.10, this logic was integrated into the default behavior of SketchViewModel, so less custom logic is needed in your application. If you have a specific workflow that conflicts with this default behavior, you can still opt out by setting the SketchViewModel.updateOnGraphicClick property to false.



// At 4.10, SketchViewModel provides out-of-the-box logic for updating graphics
// The update method now gives more options to control how selected graphics can be
// updated. For example, graphic can only be reshaped or moved with the following.
sketchVM.update([graphic], {
    tool: "reshape",
    toggleToolOnClick: false
});

// at 4.9, you had to listen to MapView.click event and check
// the MapView.hittest() to select a graphic and pass the graphic
// to SketchViewMode.update() as shown below
view.on("click", function(event) {
  view.hitTest(event).then(function(response) {
    var results = response.results;
    if (results.length > 0) {
      for (var i = 0; i < results.length; i++) {
        // Check if we're already editing a graphic
        if (!editGraphic && results[i].graphic.layer.id === "tempGraphics") {
          // Save a reference to the graphic we intend to update
          editGraphic = results[i].graphic;

          // Remove the graphic from the GraphicsLayer
          // Sketch will handle displaying the graphic while being updated
          graphicsLayer.remove(editGraphic);
          sketchViewModel.update(editGraphic);
          break;
        }
      }
    }
  });
});

Events changes

At 4.9, SketchViewModel emitted more than 20 events depending on whether a user was creating or updating a graphic. At 4.10, we condensed these into four distinct events: create, update, undo and redo. See Sketch update validation sample and Query statistics by geometry sample.


// Listen to update, undo and redo events
sketchViewModel.on(["update", "undo", "redo"], onGraphicUpdate);

function onGraphicUpdate(event) {
  // get the graphic as it is being updated
  const graphic = event.graphics[0];
  // check if the graphic is intersecting school buffers or is
  // still contained by the boundary polygon as the graphic is being updated
  intersects = geometryEngine.intersects(buffers, graphic.geometry);
  contains = geometryEngine.contains(boundaryPolygon, graphic.geometry);

  // change the graphic symbol to valid or invalid symbol
  // depending the graphic location
  graphic.symbol = (intersects) || (!contains) ? invalidSymbol : validSymbol

  // check if the update event's the toolEventInfo.type is move-stop or reshape-stop
  // then it means user finished moving or reshaping the graphic, call complete method.
  // this will change update event state to complete and we will check the validity of the graphic location.
  if (event.toolEventInfo && (event.toolEventInfo.type ===
      "move-stop" || event.toolEventInfo.type === "reshape-stop")) {
    if (contains && !intersects) {
      sketchViewModel.complete();
    }
  } else if ((event.state === "cancel" || event.state === "complete")) {
    // graphic moving or reshaping has been completed or cancelled
    // if the graphic is in an illegal spot, call sketchviewmodel's update method again
    // giving user a chance to correct the location of the graphic
    if ((!contains) || (intersects)) {
      sketchViewModel.update({
        tool: "reshape",
        graphics: [graphic]
      });
    }
  }
}

// Listen to sketchViewModel events to make sure that the graphic does not
// intersect school buffers or is contained by the boundary polygon. If it does,
// change the graphic symbol to an invalid symbol and give the user a chance
// to correct the invalid update.
sketchViewModel.on([
  "update-init",
  "move-start",
  "move",
  "move-complete",
  "scale-start",
  "scale",
  "scale-complete",
  "rotate-start",
  "rotate",
  "rotate-complete",
  "reshape-start",
  "reshape",
  "reshape-complete",
  "update",
  "undo",
  "redo"
], checkGraphicUpdate);

// Event handler for update-cancel and update-complete events
sketchViewModel.on(["update-cancel", "update-complete"], graphicUpdateEnded);

// Called from various sketchViewModel update events
function checkGraphicUpdate(event) {
  // update the new development graphic geometry to match the update geometry
  newDevelopmentGraphic.geometry = event.geometry;

  // Check if the new development graphic geometry intersects school buffers
  // or the boundary polygon. If it does, change the graphic symbol to indicate
  // the update is invalid.
  intersects = geometryEngine.intersects(buffers,
    newDevelopmentGraphic.geometry);
  contains = geometryEngine.contains(boundaryPolygon,
    newDevelopmentGraphic.geometry);
  newDevelopmentGraphic.symbol = (intersects) || (!contains) ?
    invalidSymbol : validSymbol
}

// Called from sketchViewModel's update-cancel and update-complete events.
function graphicUpdateEnded(event) {
  // If the updated graphic intersects school buffers or is not contained
  // by the boundary polygon, change the graphic symbol and Pass the invalid
  // graphic to sketchViewModel.update method.
  if ((!contains) || (intersects)) {
    newDevelopmentGraphic.symbol = invalidSymbol;
    sketchViewModel.update(newDevelopmentGraphic);
  }
}

Sample updates

The following samples were updated to reflect these changes.