Syncing GET Arguments and AngularJS Models

As I was working on my D3 daylight visualization I ran into a problem. After showing the first iteration to my friends, they set out to find every single edge case that broke the tool. Obvously, I wanted to fix every case, but the bug report process was so laborious:

  1. Friend copies latitude/longitude
  2. Friend sends me that latitude/longitude
  3. I open the tool
  4. I enter the latitude/longitude and inspect the problem
  5. I fix the problem

Ok so maybe this isn't such a big deal, but it bugged me nonetheless. My idea for streamining this process was to sync the models for the lat/long with the URL, so whenever a friend runs into an issue:

  1. Friend copies the URL, which includes the latitude/longitude: http://awesome.site/#lat=xx.xx&lng=yy.yy
  2. Friend sends me the URL
  3. I navigate to that URL and inspect the problem
  4. I fix the problem

This cuts out one entire step! and we only have to copy/paste one thing (URL) rather than latitude and longitude. Incredible.

So here's how I accomplished it. I was already using angularJS for this tool, which it turns out includes everything I need within the $location service (docs) . First, I include the $location service as a dependency:

app.controller('DaylightController', function($scope, $location, ...) {
    ...
});

Then, I add watchers to the latitude and longitude models to modify the URL whenever those models change:

$scope.$watch(function() {
  return ctrl.lat;
}, function() {
  $location.search('lat', ctrl.lat).replace();
}, true);

$scope.$watch(function() {
  return ctrl.lng;
}, function() {
  $location.search('lng', ctrl.lng).replace();
}, true);

Now whenever lat or lng change, $location.search() is called in its setter configuration, and .replace() is added to make sure we overwrite the old argument.

Ok so now my friends can easily copy a URL that includes a latitude and longitude, but now I need the tool to recognize these arguments when I navigate to a URL which includes them. This just requires a few extra lines of initialization:

var keys = $location.search();

ctrl.lat = parseFloat(keys.lat) || default_lat;
ctrl.lng = parseFloat(keys.lng) || default_lng;

And there you go! If you need some proof, check out the goofy daylight hours in Alaska - why do they even bother with daylight savings time?

You can see this code in place in the project's github repo.