GIS Life Blog - Page 3

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Latest Activity

(462 Posts)
ReneRubalcava
Honored Contributor

esrijs-padding.jpg

Another neat feature that's part of the ArcGIS API for JavaScript 4.0beta1 is this concept of view padding. At first glance, you may be wondering exactly what view padding does.

Here is a sample from the docs that is a good demonstration of what view padding actually does. Looking at this sample you see Liberty Island and a DOM element with some text on the right.

view-padding-demo.png

What may not seem obvious is the center of the map was set as the location of Liberty Island, but by setting the view padding, the view will offset the center of the map by that padding amount. Remember, it's the view that controls how the map is drawn. This allows you to add sidebars, footers or headers to your map, but still be able to utilize as much of the map as possible. Think of it almost as a frame around the map where each side can be resized as needed.

Maybe you've done side panels in your application that take up a lot of space, cover up the map in certain situations when they don't need to, or you resize the map to allow the sidebar to fit. Now you have another option to just adjust the padding the of the view to offset this for you.

This also means you can adjust how the map and ui elements to interact when something is updated. Maybe you want to resize the side panel when not in use, so you probably want to adjust the view padding at the same time.

You could do that by adjust the sample above a little bit.

require([
  "esri/Map",
  "esri/views/SceneView",
  "esri/widgets/Search",
  "dojo/on",
  "dojo/domReady!"
], function(
  Map,
  SceneView,
  Search,
  on
) {
  //Create the map
  var map = new Map({
    basemap: "topo"
  });

  //Create the view set the view padding to be 320 px
  var view = new SceneView({
    container: "viewDiv",
    map: map,
    center: [-118, 34],
    zoom: 9,
    padding: {
      right: 320
    }
  });
  
  view.then(function() {
    var searchWidget = new Search({
      view: view
    }, "searchDiv");
    searchWidget.startup();
  });
  
  var resize = document.getElementById("resize");
  on(resize, "click", function() {
    if (view.padding.right === 320) {
      view.padding = { right: 18 };
    } else {
      view.padding = { right: 320 };
    }
  });

  //Using the view.padding to update the css
  var updatePadding = function(padding) {
    var right = padding.right + "px";
    var paddingRight = document.querySelector("#padding").style;
    paddingRight.width = right;
    paddingRight.visibility = "visible";
  };
  
  updatePadding(view.padding);
  
  // watch for view padding updates
  view.watch('padding', function(val) {
    updatePadding(val);
  });
});

In this case, we are watching for a button click and adjusting the view padding, which in turn will resize the side panel. That's not too difficult to accomplish the in the EsriJS beta.

You can view a demo of this application here.

View padding in the current beta release is one of those nice little touches to the API that provides a lot of flexibility to you as a developer. It allows you to get creative with your user-interface and how that impacts your users interaction with the map. So have some fun with it.

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

more
0 0 588
ReneRubalcava
Honored Contributor

esr-renderers.jpg

Recently I talked a bit about some of the visualization tools in the ArcGIS API for JavaScript. Recent releases have introduced Smart Mapping, which takes some of the guesswork around generating renderers based on your data. They've also introduced some really cool features that deal with blending of colors based on the data in your map. It's getting to a point where devs aren't going to have many excuses for ugly maps.

One thing I saw mentioned at the EsriUC last week was the idea about using Smart Mapping to generate your renderer, experiment with it a bit and use it to generate the JSON of the renderer that you can use later on without having to invoke the Smart Mapping tools. This is totally doable, but a little unclear on how you might do it. So let's take a look at a couple of techniques you might want to use.

Save it locally

So maybe your app allows the user to make some queries on the data and perform an update of the renderer based on that query. A sample straight from the docs on that could look this one. Well, you can add a couple of more functions to this sample and save the renderer to localStorage. similar to how you could with Features. Those helper functions could look something like this.

function saveLocal(data, field) {
  var key = location.pathname + '-' + field;
  localStorage.setItem(key, JSON.stringify(data));
}

function getLocal(field) {
  var key = location.pathname + '-' + field;
  var data = localStorage.getItem(key);
  if (data) {
    return JSON.parse(data);
  } else {
    return null;
  }
}

Then when you set up Smart Mapping, you could check localStorage for an existing renderer and just use that saved renderer, bypassing Smart Mapping entirely.

function createRenderer(field) {
  //smart mapping functionality begins
  var data = getLocal(field);
  if (data) {
    console.log('load renderer from localStorage');
    layer.setRenderer(jsonUtils.fromJson(data));
    layer.redraw();
    createLegend(map, layer, field);
  } else {
    smartMapping.createClassedColorRenderer({
      layer: layer,
      field: field,
      basemap: map.getBasemap(),
      classificationMethod: "quantile"
    }).then(function (response) {
      console.log('save renderer to localStorage');
      saveLocal(response.renderer.toJson(), field);
      layer.setRenderer(response.renderer);
      layer.redraw();
      createLegend(map, layer, field);
    });
  }
}

Now, that's pretty cool. The data in this sample isn't too complicated, but you can imagine if you had a large dataset, it could take a second or so for Smart Mapping to generate the renderer and this method could a dose of optimization to your app. Here is a full demo of this application in action.

Make a Test App

Another cool thing you could do is create a test app similar to this sample.  Then you can add a <pre> tag to your page and update it with the renderers JSON when it gets updated. The code may look something like this.

var data = colorRenderer.renderer.toJson();
var js = JSON.stringify(data, null, 2);
document.getElementById('results').innerHTML = js;

So now, you can copy paste this JSON directly from your web page and you could use it in a local configuration that might use the webmap spec. Now you don't have to worry about using Smart Mapping in a production app that may not need it, it just needs the cool looking renderers that it can generate. Here is a demo app showing how you might do this.

Go home

So there you go, you now have a couple of ways to generate your renderers, save them and use them in your apps to optimize performance when needed. The visualization tools in the ArcGIS API for JavaScript have come a long way over the years and I think it gives developers some pretty good resources to make cool looking maps for their apps.

You're done, you can go home now.

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

more
0 0 1,340
ReneRubalcava
Honored Contributor

geo-dev.png

So you're a developer at the EsriUC. Don't worry, sessions may not be as abundant as Developer Summit, but there are some things you may be interested in.

Go to the agenda and use the search and type in something like JavaScript, .Net or Python. You'll hit plenty of results. Just yesterday I was able to announce in a session that the ArcGIS API for JavaScript will soon have a bower package. You didn't hear that at Developer Summit, now did you. You'll learn more about some of the really cool visualizations like Smart Mapping and Blend Renderer. There's a JavaScript Road Ahead session that will talk about some of the stuff in the JavaScript 4.0beta1.

There's developer islands for JavaScript, Online, Windows, Analysis near the end of the hall downstairs by the Demo Theaters. Come by and ask any questions you have, get some insight to an issue you've been struggling with or just check out the links on some of the laptops with cool samples and documentation.

There's no need to be a lost developer at the UC. There are some sessions you can check out, there are plenty of developers you can talk to, and you get to see how other users are using technology to solve their problems in the user sessions. And how do you think they solved those problems, GIS? Well yes, probably, but I'm willing to bet there was a developer or two driving the GIS development that helped solved some of these real-world issues and that is definitely something you may want to check out.

So, if you're at the UC, stay connected, check some stuff out and if you see me around, just say hi and if you have a question, feel free to ask. I'll probably be at the JavaScript Developer island most of the next couple of days!

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

more
1 0 1,382
ReneRubalcava
Honored Contributor

esri-blend.jpg

The 3.14 release for the ArcGIS API for JavaScript was released today. You can read the announcement here and of course the documentation is updated here. There's some really cool stuff in this release and lots of bug fixes. However, one thing I think is really cool is some of the new visualization capabilities being added to the API. Hot on the heels of Smart Mapping introduced in the 3.13, which I talked about here, they have now introduced a new renderer called Blend Renderer.

What is is that makes the Blend Renderer so cool? That it doesn't work in IE8! I'm kidding, but thank goodness we could throw that crutch out the window for Blend Renderer, because it allows you to do some really cool stuff when making your maps. So what does the Blend Renderer do? As the name says, it blends your values together. But it does so based on the data. Look at the docs for examples. Let's say you have 2 values:

Beer drinkers

Wine drinkers

Now let's assume you have a parcel with 20 people and 19 out of 20 are beer drinkers. The color would be a strongly blue. Then you have another parcel with 20 people, but 10 beer drinkers and 10 wine drinkers. So the color will be more purple. Or somewhere around there, I'm a developer not a color wheel. But you should get the idea.

Ok, so you have that down. But you're like, yeah, that's like a color ramp type thing man. It's called a Blend Renderer for a reason. Take a closer look at the docs and you'll see that there's a blendMode. What does this blendMode do? It determines how these colors blend together. They aren't just making up their own blendModes, these are the standard available composition types in the Canvas API, which is linked in the docs. This gives you the ability to do some very cool visualizations of your data. Maybe you want a different blendMode based on your basemap or some other data you are going to display on your map. This gives you some creative control over how you want to display your data.

The Blend Renderer is released in beta, so if you burn your eyeballs out from some funky combo of data and blendMode, remember, it's beta.

Here's a little sample taking a demo from the docs and adding a pulldown to test out the different blendModes.

Got out and make some cool maps to go with your cool apps.

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

more
1 1 1,871
ReneRubalcava
Honored Contributor

js-hand.jpg

I'm going to assume you are currently building apps with the ArcGIS API for JavaScript or you plan to. Maybe you've been doing stuff in Flex or Silverlight and you've put off the switch until now.  wanted to address a few things I've seen pop in the forums that aren't necessarily related to the ArcGIS APIs, but maybe more in how JavaScript itself works. I also wanted to give your some resources to up your JavaScript game.

Learn the basics

Closures. I can't emphasize this enough. Learn scope, learn closures and variable declarations. So many issues that pop up on these forums have to do with errors in scope, this will be a lifesaver.

I've covered promises before, but I'm going to emphasize them again. Learn promises. Here's a cool blog post on promises. Learn how to chain your promises. Promises are used throughout the ArcGIS JS API. You may not need to use dojo/promise/all, or have nested callbacks, you may just need to chain your promises. Heck, don't even use callbacks, just use then and otherwise.

Arrays. I'm going to go ahead and say that a better understanding of arrays and their methods will make your JavaScript life so much easier. You can do so much with arrays, I can't possibly mention them all. You can filter them, you can map them, you can reduce them. You can treat them as immutables by using concat and slice. If you can master arrays, you'll find that your JavaScript prowess will grow exponentially.

The last basic thing I want to mention here is Object.prototype. Don't forget that JavaScript is a prototype language. You may think you are working with classes and classical inheritance, but part of the beauty of JavaScript is that you can treat it like that if you want, but it's simply not true. Here is good answer to this question on stackoverflow. If you ever want to up your game and play with a purely prototype language, check out io.

Resources to up your game

So if you are new to JavaScript or maybe looking for a refresher, I think Eloquent JavaScript is a good starting point. It covers a lot of ground, including canvas and Node.

Here is a great document from Esri training on resources for the ArcGIS API for JavaScript.

You Don't Know JavaScript.

This is a free book on github, but you can purchase print copies. I've see one of Kyle Simpsons online training courses for JavaScript. Even if you think you know JavaScript, he'll show you something you don't know. This is very in depth stuff and highly recommended.

MDN.

The Mozilla Developer Network. This is like having the JavaScript specs laid out for you in a nice informative fashion. This is a must-have reference when doing JavaScript development, all the samples are easy to understand and it's easy to know which features are experimental.

ES6/ES2015.

The spec is final, it's done, it's coming. I expect we'll see more features roll out in browsers over the next couple of years. Things like import may take a while, but the future is now, stop fooling around.

Functional Programming.

Ok, this one is like a teachers pet. Do I think everyone should be doing functional JavaScript? Maybe("Yes"). I'm kidding. But I do think you could learn a lot about JavaScript development by learning how to use it in some functional programming manner. You don't need to know Monads, but you already know Functors. (Arrays are Functors, which are containers that can be mapped with functions to get their values). I'm not a pro, but an avid admirer and user.

Reactive Programming

This kind of ties right into Functional Programming. Reactive Programming is dealing with streams or asynchronous data over time. The link provided is a great resource. There are libraries like RxJS and Bacon to help you out here.

I think that's enough to get you going for now. It's not totally related to the ArcGIS API for JavaScript, but trust me, master the basics, learn a little of the extras and you will definitely be rocking some higher level JavaScript.

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

more
8 1 2,413
ReneRubalcava
Honored Contributor

esri-ember-glasses.png

Not too long ago, Jeff Jackson put up a blog post on using Ember with the ArcGIS API for JavaScript. This gives a pretty good rundown on how you can use EmberJS with the ArcGIS API for JavaScript. Ember itself has grown quite a bit recently, much of this growth could be said to with the popularity of ember-cli.

What is ember-cli? I'm glad you asked.

Ember-cli is a command line tool for scaffolding, testing and running applications built with Ember, the Ember way. You see, Ember is an extremely opinionated framework. You may be thinking, I don't need a framework telling me how to build apps! Yeah, that's kind of what a framework does. Some are a little more flexible than others. I'm not saying Ember isn't flexible, I'm sure you could write horrible apps in Ember like any other framework, but where it's opinionated way of doing things seems to shine is when you are working with teams of people. It's pretty nice to know that everything is being done the same way across the board.

Ember-cli is a great tool to help speed this process along. Think of it as Yeoman, but super sleeked out and with tons more features. And with multiple developers working on the same app, using ember-cli, you have a lot more harmony. A few commands here and there, you have built in generators and tons of addons. Which is where the ArcGIS API for JavaScript comes in.

You see, ember-cli utilizes a minimal AMD loader. What does minimal mean? It's not like RequireJS or Dojo loader, it's just a synchronous loader with a dictionary of modules. You can't just drop in a legit AMD loader to replace it. Your application will explode. Believe me, I have tried, many many variations of this scenario. It all ended in tears and flames. So what is someone to do? Build an addon!

Add it all on!

I went into more detail about the addon in a recent blog post, but what it essentially does is let you use RequireJS or Dojo AMD loader in parallel with the ember-cli loader. The AMD loader can be installed via bower or can be linked from a CDN. You can view the source code in the repo the ember-cli-amd addon here. The addon itself is not specific to the ArcGIS API for JavaScript, but by utilizing the CDN option for the AMD loader, you can use it to build Ember applications with the ArcGIS API for JavaScript.

If you want to try this out, you can install ember-cli and check out this demo repo that shows how to use it. The application for this repo can be viewed here.

What the addon does is look at the configuration for your application:

var app = new EmberApp({
  srcTag: 'https://js.arcgis.com/3.13/',
  amdPackages: [ // user defined AMD packages
    'esri','dojo','dojox','dijit',
    'put-selector','xstyle','dbind','dgrid'
  ]
});

That amdPackages option is part of the key. The addon uses esprima to analyze the code in your Ember app, find all modules that have a root found in the amdPackages option and will preload those modules via AMD. There is some other black magic it does to run the AMD loader in parallel with the ember-cli loader for any modules that might need to be dynamically loaded, but that's the gist of it. You can now build ArcGIS API for JavaScript application using the ember-cli.

I had lots of fun writing this addon and I learned a lot, I mean more than I really expected about Ember and ember-cli tooling. I have a pending PR to a dependency that fixes a couple of issues when your build the ember-cli app, but as of right now, it works. You can see notes in the README for details.

I'd highly recommend you try your hand at using ember-cli to build your ArcGIS API for JavaScript apps using the ember-cli-amd addon. I'll admit, Ember in general may have an odd learning curve, but I think ember-cli helps greatly in putting everything into context.

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

more
0 0 941
ReneRubalcava
Honored Contributor

geo-storage.jpg

Every once in a while you may get an odd request from your users on features they would like added to an application. They might want to integrate some wacky API or work completely offline, neither of these things might be construed as easy, but could be managed to an extent. Maybe they want to save the last map extent when they close and open their browser back up, you can do that! One thing I've done before is save selections, so that when a user closes the browser, they can come back to the app and the last items they selected are still selected. I don't know man, office people. But hey, you can do that!

Storage

I've shown how can use the localStorage API to save map locations and even use other tools like PouchDB to nearly take your application totally offline. We'll refer to this as mostly-offline, so don't get all uppity. Even the offline-editor takes advantage of locaStorage for some bits. Normally I would recommend sing IndexedDB or storing things like graphics locally, but if I'm just concerned about storing the most recently selected features, I would totally just keep it simple with localStorage. Key-value pairs for life.

Step By Step

The steps are pretty simple.

  1. Create a unique key to store selections
  2. When a selection is complete, store the selection
  3. When selection is cleared, clear storage
  4. When application starts, check storage or saved selections and reselect

Easy-peasy!

Generating a unique key is simple. Since localStorage is partitioned out per domains, I just take my latest location and add some unique identifier to it. Something like this:

var key = location.href + '--id-selection';

Too easy.

Then you just need to store the selection in when it's complete. easy enough.

featureLayer
  .selectFeatures(selectQuery, FeatureLayer.SELECTION_NEW)
  .then(function(results) {
    var data = results.map(function(x) { return x.toJson(); });
    localStorage.setItem(key, JSON.stringify(data));
});

So what are we doing here? Well, every Graphic has a toJson method to output a vanilla object representation. As a matter of fact, this method is sprinkled all over the place in the API (hint hint, see what else you do with it).  So we can map over the results and run toJson on them to get the JSON data. They we can save it to localStorage after we stringify it. This is important, localStorage is a string-friendly environment. Plain objects will just look like "[object Object]", which is not useful.

Then make sure if there is somewhere in your application where the selection is cleared, you remove the item from localStorage too.

on(dom.byId("clearSelectionButton"), "click", function () {
  featureLayer.clearSelection();
  localStorage.removeItem(key);
});

Simple enough.

Now the cool part. When an application starts, we want to check the localStorage for saved selections. I like to have a simple helper method to do the actual parsing of graphics, so it could look something like this.

var parseToGraphics = function (str) {
  if (!str) { return undefined; }
  return JSON.parse(str).map(function(x) {
   return new Graphic(x);
  });
};

So JSON.parse does the opposite of JSON.stringify. It will attempt to turn string data into vanilla JavaScript objects. Again, another cool feature in the ArcGIS API for JavaScript is that some constructors accept JSON representations of what you are trying to create. Just like the Graphic constructor. This let's you just churn out Graphics like a Graphic maniac. Then you can wait for your FeatureLayer to load and check localStorage.

featureLayer.on('load', function() {
  var items = parseToGraphics(ls.getItem(key));
  if (items) {
   var q = new Query();
   q.objectIds = items.map(function(x) {
     return x.attributes.OBJECTID;
   });
   featureLayer
     .selectFeatures(q, FeatureLayer.SELECTION_NEW);
  }
});

And BOOM! You just saved selections and displayed the last selected items when an application loads. You can see a sample of this jsbin. I have to warn ya, jsbin seems a little wonky for this app sometimes, at least on my machine, but this works, trust me.

Why even save whole Graphic if you're just using ObjectIds? Hey, I don't tell you how to stir your oatmeal. But good question. If you know for sure, you are only going to select by ObjectIds, then sure, just save the ObjectIds. I like to have the graphics, because then I have options. You may want to do some sort of separate analysis or selection using the saved selection. Or  maybe you want to show to the InfoWindow for the first selected item when the application starts.

featureLayer.on('load', function() {
  var items = parseToGraphics(ls.getItem(key));
  if (items) {
   var q = new Query();
   q.objectIds = items.map(function(x) {
     return x.attributes.OBJECTID;
   });
   featureLayer
     .selectFeatures(q, FeatureLayer.SELECTION_NEW)
     .then(function() {
       items.map(function(x) {
         x.setInfoTemplate(infoTemplate);
       });
       map.infoWindow.setFeatures(items);
       map.infoWindow.show(items[0].geometry.getCentroid());
   });
  }
});

You can see a demo of this on jsbin. This sample needs some work, but you get the idea. I normally use this to display Popup content in some other portion of the application sync it with the map.

By saving the Graphic, you at least have some options of stuff you can play with. But if you know you don't need all this, then sure, optimize and only save the ObjectIds.

Save The World

So don't be afraid to play around with localStorage or other forms of storage to persist data in your application. Have some fun with it and make cool apps.

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

more
0 0 1,312
ReneRubalcava
Honored Contributor

esri-promises.jpg

Maybe you've heard of them, but weren't quite sure what they are. You've undoubtedly been using them in your ArcGIS API for JavaScript development this whole time and didn't even know it. Maybe they are the bane of your existence and increasing gray hairs. Love them or hate them, but learn how to use them. Promises, they flow like cracks in the wall.

You can read more about EcmaScript 2015 Promises here. Here is the spec, if you are so inclined to read it. When I say you've probably been working with them all this time and didn't even know, let's look at the Retrieve data from a web server guide in the ArcGIS JS API docs. See all the references to the stuff like doSomthing().then(function(){}). That's a Promise. The Promises used in the JS API are based on the Dojo Promise, which has been around longer than most Promise implementations. That is why it doesn't have all the methods defined above. But it works just as well. This Promise module is just the API for a Promise. In the ArcGIS API for JavaScript, we are typically dealing with Promises via dojo/Deferred. Some might say that Promises are the monads of asynchronous programming.

Hiding in plain sight

If you look throughout the ArcGIS JS API documentation, you will see that Deferred is the return type for plenty of methods. Just look at the methods on the map. But why? A Promise is used to work with some sort of asynchronous activity. It could be using the QueryTask to make some requests that could take a few milliseconds or a couple of seconds, it's a roll of the network dice sometimes. The point being is that if we didn't use a Promise implementation to handle these asynchronous requests, your application would spend most of it's time just sitting there, frozen in fear, waiting for responses. A Promise says, "look, I promise I'll be back, one way or another I'm coming back, but keep fighting the good fight. Do you what you have to do and just wait for my triumphant return!" It's a brave little worker.

While a Promise is working, once it has accomplished it's task, it will resolve with it's result or if something goes wrong, it will reject it and hopefully give you a reason. Thus, when you are the one wielding the power of a Promise and you find yourself using deffered.resolve(), you should also take care to figure out when you should use deferred.reject() and handle these errors.

You want to play a prank on your users? Zoom to the map on click, but then zoom back to where they were before they clicked. It will drive them nuts.

require(["esri/map", "dojo/domReady!"], function(Map) { 
  var map = new Map("map", {
    center: [-118, 34.5],
    zoom: 8,
    basemap: "topo"
  });
  map.on('click', function(e) {
    var pt = map.extent.getCenter();
    map.centerAndZoom(e.mapPoint, 12);
    map.centerAndZoom(pt, 8);
  });
});

Look at the sample on JSBIN.

Wait a second! That doesn't seem to work. It's not very consistent and not very funny. What's happening is that the centerAndZoom method is comprised of a smooth zoom, a gradual, almost animation like effect from one zoom level to the next. That's not an instantaneous action. Somewhere in there, you need to wait for the zoom to finish and then you can go back to where you were. That's why centerAndZoom returns a Deferred. This means we can rewrite the above like this.

require(["esri/map", "dojo/domReady!"], function(Map) { 
  var map = new Map("map", {
    center: [-118, 34.5],
    zoom: 8,
    basemap: "topo"
  });
  map.on('click', function(e) {
    var pt = map.extent.getCenter();
    map.centerAndZoom(e.mapPoint, 12).then(function() {
      map.centerAndZoom(pt, 8);
    });
  });
});

JSBIN here.

That is much better. And absolutely hilarious.

Chain it up

One thing you can o with Promises is chain the results. Let's look at this sample from the docs. It performs a query and displays information on the page. The bulk of the work is done here.

function execute () {
  query.text = dom.byId("stateName").value;
  queryTask.execute(query, showResults);
}
function showResults (results) {
  var resultItems = [];
  var resultCount = results.features.length;
  for (var i = 0; i < resultCount; i++) {
    var featureAttributes = results.features.attributes;
    for (var attr in featureAttributes) {
      resultItems.push("<b>" + attr + ":</b>  " + featureAttributes[attr] + "<br>");
    }
    resultItems.push("<br>");
  }
  dom.byId("info").innerHTML = resultItems.join("");
}

There's nothing wrong with that, but it could get a little tricky if you wanted to say, omit some attributes from being displayed or change the DOM elements being created. It just seems like an awful lot of work in little spot. Well, you can chain the Promise returned from a QueryTask like below.

function execute () {
  query.text = dom.byId("stateName").value;
  queryTask.execute(query).then(function(results) {
    // get the attributes
    return results.features.map(function(x) {
      return x.attributes;
    }).shift(); // since we know there is only one result, return first attribute
  }).then(function(attributes) {
    // Create the DOM strings
    return Object.keys(attributes).map(function(key) {
      return "<b>" + key + ":</b>  " + attributes[key] + "<br>";
    });
  }).then(function(x) {
    // Join the DOM strings
    return x.join("");
  }).then(function(elements) {
    // update the DOM
    dom.byId("info").innerHTML = elements;
  }).otherwise(function() {
    alert("Something went completely wrong");
  });
}

JSBIN here.

As you can see, as long as you keep returning a result in the functions used in the then method, you can chain them. For demonstration purposes, I chained it a little more than I normally would, but you can now easily distinguish what parts of the chain are doing what work. So you can add a new piece to the chain to do extra work or modify an existing chunk to fix it. Notice the otherwise method. This will capture any errors that occur. For example, try searching for CaliforniaFun and see what happens.

Fun fact. You can return a Promise in a Promise chain, so maybe inside a then function, you need to do some async requests, maybe merge with another data source, just return a Promise and continue the chain. Enjoy your magic functions. This is actually a more useful way of chaining Promises, to actually chain async requests. Check out this post from Sitepen for more details.

Done for now, I Promise

As you can see, Promises and their implementation in Deferred is pretty powerful. They are great tools for use with asynchronous tasks. One thing to remember is that Promises will execute right away. You just get to defer when the results get handled. If you want to try some other techniques that will defer execution until you are ready, you can try out RxJS or look at something like Folktales data.task which implements a Future monad. But I'll leave those goodies for you to explore.

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

more
3 0 4,247
ReneRubalcava
Honored Contributor

dbind2.png

So all the cool kids on the block have data binding.You can do it in Angular, React, or Ember. In each of these cases you just kind of get data binding to the DOM out of the box. Dojo doesn't quite work that way. You can do it for sure with a little work by using Stateful watch and updating the DOM when data changes. If you are using dijits, you can definitely bind to Stores and you get updates, but how about with regular old DOM elements.

That's where something like dbind comes into play. dbind falls into the category I like to refer to as Dojo friendly packages. Meaning it was written to work with Dojo, mainly as side projects of a Dojo contributor. Some of these have become full blown projects under SitePen. A recent example would be dstore.

Getting tied up

So what does dbind bring to the table? I've talked about some uses before in this post and even some advanced uses in this post. What dbind, in it's simplest form lets you do is watch for changes on an object and bind those changes to something else. For example, maybe you want to bind some text element in your application to activity on your map. You could do something like this to bind to the mouse-move event of the map.

require([
  "dojo/_base/declare",
  "dojo/dom",
  "dijit/_WidgetBase",
  "dijit/_TemplatedMixin",
  "dbind/bind",
  "esri/map",
  "esri/layers/FeatureLayer",
  "dojo/domReady!"
], function(
    declare, dom, _WidgetBase, _TemplatedMixin,
    bind,
    Map, FeatureLayer
) { 
  var map = new Map("map-div", {
    center: [-118, 34.5],
    zoom: 5,
    basemap: "topo"
  });
  var url = 'http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/5'...
  var layer = new FeatureLayer(url, { outFields: ['*'] });
  map.addLayer(layer);
  var template = '<div class="label-container"><div>X: <span data-dojo-attach-point="xNode">${x}</div><div>Y: <span data-dojo-attach-point="yNode">${y}</div></div>';
  var LabelContainer = declare([_WidgetBase, _TemplatedMixin], {
    templateString: template,
    constructor: function() {
      this.set('x', 0);
      this.set('y', 0);
    },
    postCreate: function() {
      bind(this.xNode).to(this, 'x');
      bind(this.yNode).to(this, 'y');
    }
  });
  var lblContainer = new LabelContainer(null, dom.byId('lbl-div'));
  map.on('mouse-move', function(e) {
    lblContainer.set('x', e.mapPoint.x);
    lblContainer.set('y', e.mapPoint.y);
  });
});

You could see a sample of this in action here.

As you can see, you can bind DOM elements to changes on an object, and in this case, as you move your mouse around the map, the text in the element changes. Now that's pretty cool. The coordinates however seem to be a little too accurate, your users don't need that level of detail, so you can use dbind to bind those changes to a function and then bind the DOM to the results of that function. Sound confusing? It's just one change to this code you can do like this.

  var fixed3 = function fixed3(n) {
    return n.toFixed(3);
  };
  var LabelContainer = declare([_WidgetBase, _TemplatedMixin], {
    templateString: template,
    constructor: function() {
      this.set('x', 0);
      this.set('y', 0);
    },
    postCreate: function() {
      var roundX = bind(fixed3).to(this, 'x');
      var roundY = bind(fixed3).to(this, 'y');
      bind(this.xNode).to(roundX);
      bind(this.yNode).to(roundY);
    }
  });

You can see this sample here.

More tools in your toolkit

As you can see, dbind can come in pretty handy in your application development. Maybe you have found yourself relying on watching for changes on a Stateful object and lots of boilerplate to accomplish what you think should be a simple task. dbind can help with that. Consider dbind just another tool in your toolkit. So read up on it a bit and see if it will prove useful for you. I've managed to do some pretty cool stuff using dbind with dojo/topic, so I'm sure you can definitely find some uses for it.

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

more
0 0 757
ReneRubalcava
Honored Contributor

esri-touch.jpg

Do you develop web apps that require editing? Maybe you have a feature that lets users add graphics to the map? Do you use the TemplatePicker to do your editing?

Why don't you try and spice things up a bit. We live in a world where we swipe and drag for everything on our devices. You may even swipe for your next date. But the idea is that users are becoming more sophisticated in their interactions with applications. They want something more intuitive.

A while ago, I did a writeup on this very subject, but I haven't really checked to see if the code would work in the current version of the ArcGIS JavaScript API. I was all deep into CoffeeScript at this time, so the code may not be the easiest to read.

So how could you accomplish something like this? Let's check out one solution.

require([
  "dojo/_base/declare",
  "esri/map",
  "dojo/Evented",
  "dojo/dom",
  "dojo/dom-geometry",
  "dojo/dom-attr",
  "dojo/on",
  "dojo/query",
  "esri/geometry/ScreenPoint",
  "esri/geometry/screenUtils",
  "esri/symbols/PictureMarkerSymbol",
  "esri/graphic"
], function(declare, Map, Evented, dom, domGeom, domAttr, on, query, ScreenPoint, screenUtils, PictureMarkerSymbol, Graphic) { 
  var cleanup = function(targets) {
    targets.map(function(x) {
      x.remove();
    });
  };
  var DragDropHandler = declare([Evented], {
    dragdrop: function(srcName, targetName) {
      var src = dom.byId(srcName);
      var target = dom.byId(targetName);
      var handlers = [];
      var self = this;
      handlers.push(on(target, 'dragenter', function(e) {e.preventDefault();}));
      handlers.push(on(target, 'dragover', function(e) {e.preventDefault();}));
      handlers.push(on(target, 'dragend', function(e) {cleanup(handlers);}));
      handlers.push(on(src, 'dragstart', function() {
        handlers.push(on(target, 'drop', function(e) {
          e.preventDefault();
          cleanup(handlers);
          var position = domGeom.position(e.currentTarget);
          var x = e.clientX - position.x; // in case the map does not take up whole page
          var y = e.clientY - position.y;// in case the map does not take up whole page
          self.emit('itemdrop', {
            bubbles: true,
            cancelable: true,
            dragsource: src,
            x:x,
            y:y
          });
        }));
      }));
    }
  });
  var handler = new DragDropHandler();
  var map = new Map("mapView", {
    center: [-118, 34.5],
    zoom: 8,
    basemap: "topo"
  });
  function addPoint(data) {
    var mp = screenUtils.toMapGeometry(map.extent, map.width, map.height, data.pt);
    var pms = new PictureMarkerSymbol(data.url, 24, 24);
    var graphic = new Graphic(mp, pms);
    map.graphics.add(graphic);
  }
  on(query('.drag-icon'), 'mousedown', function(e) {
    var srcName = domAttr.get(e.currentTarget, 'id');
    handler.dragdrop(srcName, 'mapView');
  });
  on(handler, 'itemdrop', function(e) {
    var data = {
      pt: new ScreenPoint(e.x, e.y),
      url: domAttr.get(e.dragsource, 'src')
    };
    addPoint(data);
  });
});

That's not really a whole lot of code. The workhorse is really the DragDropHandler. This DragDropHandler has a dragdrop method that will listen for drag events on an image, and sees when the image is dropped on the map. When this image is dropped on the map, you just need to emit an event with screen coordinates and what the dragged item was. The ArcGIS JS API provides some utilities for you to convert screen coordinates into map geometries and voila, you can quickly add graphics to the map and even copy the image src to display it on the map!

Here is an example on jsbin.

This sample just adds graphics to the map, but there is no reason it couldn't be applied to editing, so you could drag and drop features to add them to a FeatureService. So give it a shot and add some pazazz to your apps!

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

more
1 0 570
105 Subscribers