odoe

Accessors for data-binding

Blog Post created by odoe on Oct 28, 2015

accessor-binding.png

So yeah, I have something of an affinity for Accessors in the ArcGIS JS API 4.0 beta.

 

I've written about them a lot.

 

Their incredibly versatile, allowing you to do things you couldn't do with regular event watching in the 3.x API. You can play with the camera, save the state of your application and much much more.

 

Lately I've been thinking about possible editing and drawing scenarios you can get out of Accessors. Lots of times, you aren't just editing geometry, you are editing attributes. I've been toying around with how you as a developer can leverage Accessors to get this done.

 

I won't go into details here, you can get the details for fields from the layer data returned from the ArcGIS REST API. This will tell you what fields are available, what's editable, what the field type is, which is really powerful information used for editing. You can leverage this information to dynamically create your edit forms for your application.

 

We'll simplify this scenario a bit.

 

Let's just assume, you want to do the following:

  • Display inputs for each attribute field
  • Bind changes in the input to the attributes
  • Display those changes

 

Accessors can let us do this.

 

Let's look at creating an input that binds changes to the input to the Accessor.

 

  function createInput(/*Accessor*/target, /*FieldName*/name) {
    var node = document.createElement('div');
    node.setAttribute('class', 'form-group');
    var lbl = document.createElement('label');
    lbl.innerText = name;
    var input = document.createElement('input');
    input.setAttribute('class', 'form-control');
    input.setAttribute('type', 'text');
    input.setAttribute('name', name);
    input.setAttribute('value', inputValue(target, name));
    node.appendChild(lbl);
    node.appendChild(input);
    on(input, 'keyup', function(e) {
      // update the accessor
      target[e.target.name] = e.target.value;
    });
    return node;
  }

 

In this sample, we listen for the keyup event of the input and update the Accessor. Pretty simple.

 

 

Now you can watch for changes done to the accessor and do something with that. You would probably want to send an update to a FeatureService and simply sync every update to the service.

 

To demonstrate this binding, I'll bind the dynamically generated form to another form that is disabled to show the updates.

 

  qTask.execute(query).then(function (results) {
    var feature = results.features[0];
    var attr = new Accessor(feature.attributes);
    Object.keys(attr).sort().map(function(key) {
      // only display certain fields
      if (key !== 'OBJECTID' && key !== '_accessorProps') {
        var node = createInput(attr, key);
        var elem = document.getElementById(key);
        elem.value = inputValue(attr, key);
        // watch for updates and update the disabled form
        attr.watch(key, function(val) {
          elem.value = val;
        });
        entryForm.appendChild(node);
      }
    });
  });

This method does the query of the service and turns attributes into an Accessor. It also creates the dynamic form, but notice that it watches for changes to the Accessor and updates the disabled form. This is where you could pass updates to a FeatureService as well if you wanted to, maybe using the edit tools here.

 

You can see this in action in this JSBIN.

 

Here is a bit of an optimized version of this sample.

  qTask.execute(query).then(function (results) {
    var feature = results.features[0];
    var attr = new Accessor(feature.attributes);
    var props = Object.keys(attr).filter(function(key) {
      return key !== 'OBJECTID' && key !== '_accessorProps';
    }).sort();
    var nodeCache = {};
    attr.watch(props, function(val, _, name) {
      var elem;
      if (nodeCache[name]) {
        elem = nodeCache[name];
      } else {
        elem = document.getElementById(name);
        nodeCache[name] = elem;
      }
      elem.value = val;
    });
    props.map(function(prop) {
      var node = createInput(attr, prop);
      var elem = document.getElementById(prop);
      elem.value = inputValue(attr, prop);
      entryForm.appendChild(node);
    });
  });

 

That really isn't too difficult when you look at. You can basically delegate the updates to the Accessor to some other methods and simply watch for when changes take place on it.

 

Play around with this, get your hands deep in the Accessors and let it sink in, you won't be disappointed.

 

For more geodev tips and tricks, check out my blog.

Outcomes