Thursday, December 18, 2014

A bottle of DB reliever

I don’t know about you, but trying to decide what technologies to use is beginning to feel like trying to choose a pain reliever: countless choices and a ton of small print. But when you think about it, it’s actually worse. Unlike pain reliever brands that tend to stick around year after year, many technologies don’t.

So in a new world liberated from the tyranny of expensive database technologies but buried in choices that will live or die depending on where the tech fashion winds blow, which do you choose?

I found this site particularly helpful for getting the big picture because it not only provides a survey of what’s out there but categorizes and ranks them based on popularity. This way I can survey the different categories of database engines, learn more about the players, and see how they’re popularity is trending.

Then I only have to dig into the small print on a few bottles before heading over to toothpaste...

Friday, October 17, 2014

Posting files with AngularJS


While AngularJS's built-in $http service posts data as JSON by default, what do you do if you need to include an image or other binary data file in the post?

Seems pretty straightforward. Just bind HTML <input type="file"> to a controller variable via ng-model and $http.post() a FormData object containing the file object as data with Content-Type set to multipart/form-data. Right?

Not so fast. It turns out that 
  •  <input type="file"> doesn't work with ng-model, and 
  • Setting Content-Type to multipart/form-data resulted in rejected posts (a bug?).

Thankfully Angular is thoughtfully extensible. The first issue can be resolved with a custom Angular directive that tells $compile to hook <input type="file"> change events and set a controller scope variable with the selected file object when fired as so:

 .directive('inputfileModel', ['$parse', function ($parse) {  
   return {  
     link: function(scope, element, attrs) {  
       var model = $parse(attrs.inputfileModel);  
       var modelAssigner = model.assign;  
       element.bind('change', function(){  
         scope.$apply(function(){  
           modelAssigner(scope, element[0].files[0]);  
         });  
       });  
     }  
   };  
 }]);  

This directive is then set on the element as a custom element attribute:

  <input type="file" inputfile-model="fileVar">  


The second issue is a little trickier to work around. After doing some digging, it turns out that setting Content-Type to undefined results in browsers both setting  Content-Type to multipart/form-data and filling in the correct boundaries. O-Kay, not sure why that is, but hey, it works. Note that $http.post()'s default behavior of transforming data to JSON also needs to be turned off, which can be done by setting it to Angular identity function if you're into functional style programming.

 .factory('Messages', ['$http', function ($http) {  
      return {  
           put: function(url, file, dataObj, success, error) {  
                var fd = new FormData();  
                fd.append('json', JSON.stringify(dataObj));  
                fd.append('file', file);  
                $http.post(url, fd, {  
                     transformRequest: angular.identity,  
                     headers: {'Content-Type': undefined}  
                })  
                .success(success)  
                .error(error);  
           }  
      }  
 }]);  


A few problems initially, but it wasn't too hard to modify and using frameworks like Angular to add some structure to JavaScript in all its expressive glory can only help ease the pain of the poor dude down the road who will have to maintain it.