8

Lets say i have several $resources and some $http around my angular application:

myApp.factory('Note', function($resource) {

  return $resource('http://', {id: '@id'}, 
    { 'index': { method: 'GET', isArray: true },
      'update': { method: 'PUT'},
    });
  });

with controller

myApp.controller('NotesController',function NotesController($scope, Note, AuthenticationService) {

$scope.notes = Note.index({}, function(data){
    console.log('success, got data: ', data);
    $scope.response = "yoy!"
  }, function(err){
    console.log('error, got data: ', err);
    $scope.response = "yay!"
  }); 
});

and some request are made by $http directly like authentication

var request = $http.post('http://', {email: email, password: password});

Where and How i can tell angular to deflate and encode/decode JSONs to base64 before the actual request is made / response is receive?

I quess i will wrap external libraries for deflate and encode/decode into factory. And then this factory will be injected somehere? Like $httpBackend ?

Jakub Kuchar
  • 1,616
  • 2
  • 22
  • 38

3 Answers3

8

You should have a look at the request / response transformers for the $http service: http://docs.angularjs.org/api/ng.$http

Request / response transformers are simply functions that can be invoked before content is sent / handed back to the caller. You can specify transforming functions globally (for all requests / responses) as well as per-request basis:

To override these transformation locally, specify transform functions as transformRequest and/or transformResponse properties of the config object. To globally override the default transforms, override the $httpProvider.defaults.transformRequest and $httpProvider.defaults.transformResponse properties of the $httpProvider.

To define global request / response transformers one would write the code along those lines (it is more like pseudo-code, won't work in all browsers, see notes about Base64 below):

angular.module('sample', [], function($httpProvider) {

    $httpProvider.defaults.transformRequest = function(data, headersGetter) {
        return btoa(JSON.stringify(data));
    };

    $httpProvider.defaults.transformResponse = function(data, headersGetter) {
        return JSON.parse(atob(data));
    };

})

Of course your transforming code could be more sophisticated and depend on request / response headers but the general idea is here. The jsFiddle with the code (check the console to see that a request gets transformed, you need to use Mozilla or a WebKit browser): http://jsfiddle.net/Ydt5j/

For the actual conversion from / to Base64 check this question: How can you encode a string to Base64 in JavaScript?

Community
  • 1
  • 1
pkozlowski.opensource
  • 116,401
  • 59
  • 321
  • 285
  • thank you, for your reply. I would love to do something like `$httpProvider.defaults.transformRequest = function(request){return Base64Factory.encode(request)};` in my myApp.config(function(){}) ,but as far as i know i cannot inject factory in config function. Would you please be more verbose and help me with this? Thank you – Jakub Kuchar Dec 30 '12 at 17:33
  • I have also tried to accomplish in myApp.run(function(){}) ,but still have no success? – Jakub Kuchar Dec 30 '12 at 17:38
  • @Mark you can't inject providers into run block so trying to inject `$httpProvider` will result in an error. Similarly, one can't inject services into configuration blocks. Updated the answer with more info, but you can check this question as well: http://stackoverflow.com/a/12903544/1418796 – pkozlowski.opensource Dec 31 '12 at 18:37
  • @all no doubt, that pkozlowski.opensource answer is correct. I don't know why i urged on my pseudo-requirement that i have to wrap base64 functionality into factory/service. But at the finish i end up with .provide() which can be injected into config function. It works, but somehow i feel that it is not ideal solution? Please leave your comments.. – Jakub Kuchar Dec 31 '12 at 19:19
  • Using a provider (.provide()) is not ideal here since providers should create new services and here you are just using it as a means of injecting something to a config block. If you really need to have it injectable maybe a constant is a way to go? But if you don't need a general purpose Base64 service (for me it sounds more like a utility function) I wouldn't bother. If the above works for you, please accept / up-vote the anwser :-) – pkozlowski.opensource Dec 31 '12 at 19:26
  • @pkozlowski.opensource as you can feel, i am using [angular-app](https://github.com/angular-app/angular-app) as my starting block. Do you mind that i will raise up one more question regarding to the AuthenticationService and poke you to there? – Jakub Kuchar Dec 31 '12 at 19:34
  • @Jakub we can discuss https://github.com/angular-app/angular-app related stuff on the AngularJS mailing list. – pkozlowski.opensource Dec 31 '12 at 20:01
2
angular.module('services.base64',[]);
angular.module('services.base64').provider('base64', function () {

  this.encode = function(str) {
    return base64_encode(str);
  }

  this.decode = function(str) {
    return base64_decode(str);
  }

  this.$get = function() {
    return {};
  }

});

var myApp = angular.module('myApp',['services.base64'])

myApp.config(['base64Provider', function (base64Provider) {
    $httpProvider.defaults.transformRequest = function(request){return base64Provider.encode(request)};

    $httpProvider.defaults.transformResponse = function(response){return base64Provider.decode(response)};
}]);
Jakub Kuchar
  • 1,616
  • 2
  • 22
  • 38
0

this is how i do in filter. item.htmlBody contains base64 encoded text with html tag

// in template
<div ng-bind-html="item.htmlBody | decodeBase64">{{item.htmlBody}}</div>

//inside controller.js

.filter('decodeBase64', function(){
   return function(text){
   return atob(text);
   }
})

well, i know this doesnt exactly answer the question, but with some minor tweak, you can get what you want (targetting future person with the same problem)

Azizi Musa
  • 969
  • 2
  • 7
  • 26