Breeze using angular $http interceptor - javascript

I use a angular $http interceptor, to check if a ajax request returns 401 (not authenticated).
If response is 401, the original request gets queued, a login form is shown and after login successfully, it retries the queued requests. This already works with $http, and the source for the angular interceptor is:
define('common.service.security.interceptor', ['angular'], function() {
'use strict';
angular.module('common.service.security.interceptor', ['common.service.security.retryQueue'])
.factory('securityInterceptor', [
'$injector',
'$location',
'securityRetryQueue',
function($injector, $location, securityRetryQueue) {
return function(promise) {
var $http = $injector.get('$http');
// catch the erroneous requests
return promise.then(null, function(originalResponse){
if(originalResponse.status === 401){
promise = securityRetryQueue.pushRetryFn('Unauthorized', function retryRequest(){
return $injector.get('$http')(originalResponse.config);
});
}
return promise;
});
};
}
])
// register the interceptor to the angular http service. method)
.config(['$httpProvider', function($httpProvider) {
$httpProvider.responseInterceptors.push('securityInterceptor');
}]);});
How can I make breeze request using this angular $http interceptor?
Breeze provides a wrapper for the angular $http service in the file "Breeze/Adapters/breeze.ajax.angular.js". So the first idea was to tell breeze to use it:
breeze.config.initializeAdapterInstance("ajax", "angular", true);
Debugging angular.js, it shows that breeze now in fact uses $http, but it does not execute the above registered interceptor. Inside $http, there is an array "reversedInterceptors", which holds the registered interceptors. I log this array to console. If i use $http, the length of this array is one (as expected), but when making request with breeze, this array is empty.
The question is, how can i use this $http interceptor with breeze requests?
Here is the code for breeze.ajax.angular.js, provided by breeze
define('breeze.ajax.angular.module', ['breeze', 'angular'], function (breeze) {
'use strict';
/* jshint ignore:start */
var core = breeze.core;
var httpService;
var rootScope;
var ctor = function () {
this.name = "angular";
this.defaultSettings = {};
};
ctor.prototype.initialize = function () {
var ng = core.requireLib("angular");
if (ng) {
var $injector = ng.injector(['ng']);
$injector.invoke(['$http', '$rootScope',
function (xHttp, xRootScope) {
httpService = xHttp;
rootScope = xRootScope;
}]);
}
};
ctor.prototype.setHttp = function (http) {
httpService = http;
rootScope = null; // to suppress rootScope.digest
};
ctor.prototype.ajax = function (config) {
if (!httpService) {
throw new Error("Unable to locate angular for ajax adapter");
}
var ngConfig = {
method: config.type,
url: config.url,
dataType: config.dataType,
contentType: config.contentType,
crossDomain: config.crossDomain
}
if (config.params) {
// Hack: because of the way that Angular handles writing parameters out to the url.
// so this approach takes over the url param writing completely.
// See: http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/
var delim = (ngConfig.url.indexOf("?") >= 0) ? "&" : "?";
ngConfig.url = ngConfig.url + delim + encodeParams(config.params);
}
if (config.data) {
ngConfig.data = config.data;
}
if (!core.isEmpty(this.defaultSettings)) {
var compositeConfig = core.extend({}, this.defaultSettings);
ngConfig = core.extend(compositeConfig, ngConfig);
}
httpService(ngConfig).success(function (data, status, headers, xconfig) {
// HACK: because $http returns a server side null as a string containing "null" - this is WRONG.
if (data === "null") data = null;
var httpResponse = {
data: data,
status: status,
getHeaders: headers,
config: config
};
config.success(httpResponse);
}).error(function (data, status, headers, xconfig) {
var httpResponse = {
data: data,
status: status,
getHeaders: headers,
config: config
};
config.error(httpResponse);
});
rootScope && rootScope.$digest();
};
function encodeParams(obj) {
var query = '';
var key, subValue, innerObj;
for (var name in obj) {
var value = obj[name];
if (value instanceof Array) {
for (var i = 0; i < value.length; ++i) {
subValue = value[i];
fullSubName = name + '[' + i + ']';
innerObj = {};
innerObj[fullSubName] = subValue;
query += encodeParams(innerObj) + '&';
}
} else if (value instanceof Object) {
for (var subName in value) {
subValue = value[subName];
fullSubName = name + '[' + subName + ']';
innerObj = {};
innerObj[fullSubName] = subValue;
query += encodeParams(innerObj) + '&';
}
} else if (value !== undefined) {
query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
}
}
return query.length ? query.substr(0, query.length - 1) : query;
}
breeze.config.registerAdapter("ajax", ctor);
breeze.config.initializeAdapterInstance("ajax", "angular", true);
/* jshint ignore:end */
});

using the setHttp method works for me to use http interceptors with the breeze angular ajax adapter. in my environment, it looks like this:
(function() {
'use strict';
var serviceId = 'entityManagerFactory';
angular.module('app').factory(serviceId, ['$http', emFactory]);
function emFactory($http) {
var instance = breeze.config.initializeAdapterInstance("ajax", "angular");
instance.setHttp($http);
...
}
})();
the only place I've really found any information about this is in the release notes for 1.4.4 on the download page. I don't really understand what this does. i'm sure one of the breeze guys will have a better explanation.

You have to call setHttp($http) as explained by #almaplayera. Please mark his as the correct answer.
Here's why that is necessary.
By default, the breeze angular ajax adapter initializes itself with whatever instance of $http is available. Unfortunately, at the time that most scripts are loading, the $http instance for YOUR APP hasn't been created. That won't happen until your module loads ... which typically happens long after breeze loads.
So rather than create an adapter that won't work at all, breeze spins up its own instance of $http and wires the angular ajax adapter to that instance.
If your code doesn't do anything special, this works fine. It's not optimal; you'll get one extra $digest cycle than necessary. But it works for most people and let's admit that there is more than enough configuration noise to deal with as it is.
But you ARE doing something special. You're configuring a very specific instance of $http, not the one that breeze created for itself.
So you have to tell breeze to use YOUR instance of $http ... and that's what happens when you call setHttp($http);
Thanks to this feedback I have updated the breeze documentation on ajax adapters to describe how to configure for an Angular app.

Related

AngularJS - $http.get request gets cancelled

I'm trying to get data from a server in my AngularJS app, using $http.get. But when I execute the request, it seems to get cancelled by something. It happens with every link I use. Local files are working.
Below the code from Controller.js:
angular
.module('schoolApp')
.controller('configController', [
'apiService', '$http', function (apiService, $http) {
var vm = this;
vm.isActive = isActive;
vm.addPortal = addPortal;
...
function addPortal() {
...
apiService.getServerData('someUrl', "1.0")
.then(function (result) {
var test = result.data;
})
.catch(function (result) {
console.log(result);
});
}
...
And the Service.js:
angular
.module('schoolApp')
.service('apiService', [ '$http', function ($http) {
var service = {
getServerData: getServerData,
};
return service;
function getServerData(portalUrl, appVersion) {
// var url = "http://../.../.svc/GetSetting?alias=...&AppVersion=1.0";
var url = "http://xml.buienradar.nl";
//var url = "test.json";
//var url = "xml.xml";
// "http://" +
// portalUrl +
// "/.../.../.svc/GetSetting?alias=" +
// portalUrl +
// "&AppVersion=" +
// appVersion;
return $http.get(url);
}
}]);
Executing this code will show the alert("ERROR: " + result) from the controller class. Data from the result:
result: Object config: Object data: null headers: (c)
status: -1 statusText: ""
__proto __: Object
Network in browser shows the call is cancelled:
http://i.stack.imgur.com/MJoZM.png
More info:
- I'm using Phonegap (tested without and doesn't work either)
- AngularJS 1.4
Ok, found the answer. I called window.location.reload() in the same function, and that conflicted. So, now I added it to the .then() function in the request.

Using Node/Angular, how do I make a call to an api (using methods from a node module) and have the data sent to the scope?

I am trying to use angular + node and am making a method call to a JS Api.
It returns data in my index.js file that I want to be made available in my angular module for attachment to the $scope.
My two approaches/attempts have been to:
1) Inject the hiw-api module as a factory parameter (won't work/how do won't pick up my module.
2) Use RequireJS to include node module hiw-api (too complex)
My goal is that once the data is in the $scope I can manipulate it, I just can't seem to get it there. Any help would be greatly appreciated! index.js Github, index.html (if you dare)
var hiw = require("hiw-api");
var apiKey = "da45e11d07eb4ec8950afe79a0d76feb";
var api = new hiw.API(apiKey);
var http = require("http");
exports.index2 = function (req, res) {
var rawresponse;
var founderror;
var indicatorDescription;
var locales;
//var allinidcatorsURL = '/Indicators/ { page }?Key= { key }';
//var filter = new hiw.Filter()
// .addEqual(hiw.Locale.Fields.ScriptFullName, "Arkansas");
hiw.Synchronizer.sync([
hiw.IndicatorDescription.getByID(279, api, function (data, response, error) {
indicatorDescription = data;
console.log(indicatorDescription.fullDescription);
console.log(indicatorDescription);
title = "Express"; //response: rawresponse, error: founderror
}),
hiw.Locale.getAll(api, function (data, response, error) {
locales = data; //Array of Locale
//console.log(locales);
})
], function (data) {
res.sendfile('./views/index.html');
//res.json(locales);
});
};
Assuming you have the HIWmethods defined as a service correctly, you have to inject it in the controller like this:
.controller('mainController', ['$scope', 'HIWmethods', function($scope, HIWmethods) {
$scope.formData = {};
$scope.loading = true;
// GET =====================================================================
// when landing on the page, get all todos and show them
// use the service to get all the todos
$scope.test = "Hello world!!!!!!!!!!"
HIWmethods.getLocales().then(function(data) {
$scope.locales = {
availableOptions: data
}
$scope.loading = false;
});
}]);

AngularJS return two http get requests only when resolved

I have an Angular app, and in the controller I need to call a function which makes two http get requests, and I need this function to return these values just when they are resolved. I cannot make them in the $routeProvider resolve because this function needs a value obtained in the same controller.
I show part of the controller:
function myController(info) {
var vm = this;
vm.name = info.data.name;
vm.extra_data = MyService.getExtras(name);
}
And here is part of the code of the service:
function myService($http, $q, API_EVENT1, API_EVENT2) {
return {
getExtras: getExtras
}
function getExtras(name) {
var request1 = API_EVENT1 + '/' + name;
var request2 = API_EVENT2 + '/' + name;
}
}
The function is not complete due to I have tried several methods, but I wasn't successful at all.
My problem is how to return (in getExtras function) both of the results of the requests. I have tried using $q.defer, $q.all and promises, but I'm not able to achieve it.
Using $q.all, for example, I got to retrieve the result, but when I call the function, the object extra_data is undefined, due to the requests have been executed asynchronously.
Thank you in advance for your responses.
I would use $q.all to return combined promise from Service, and process result in controller. e.g.:
function myService($http, $q, API_EVENT1, API_EVENT2) {
return {
getExtras: getExtras
}
function getExtras(name) {
return $q.all([
$http({method: 'GET', url: API_EVENT1 + '/' + name}),
$http({method: 'GET', url: API_EVENT2 + '/' + name})])
);
}
}
function myController(info) {
var vm = this;
vm.name = info.data.name;
MyService.getExtras(name).then(function(data) {
vm.extra_data.first = data[0];
vm.extra_data.second = data[1];
});
}

AngularJS factory function cannot access a locally stored cache variable

I have been at this for over 10 hours at this point. It does not make sense to me. I desperately need clarification on what I'm doing wrong. Below is a simple factory function that makes an AJAX call for a JSON file. There are no async. data issues and everything just works. The issue I'm having is that I'm trying to save the returned result and access it later. If the variable is populated, I don't then have to make a second AJAX call, I can simple grab the contents of a local variable. I realize there are other ways of doing this, but I'm particular to using this factory method.
storyDataAsFactory.$inject = ['$log', '$http', '$q'];
angular.module('ccsApp').factory('storyDataAsFactory', storyDataAsFactory);
function storyDataAsFactory($log, $http, $q) {
var storiesCache = [];
function getStories(url) {
url = url || '';
if (url !== '') {
var deferred = $q.defer();
alert('inside getStories, length of storiesCache = ' + storiesCache.length); // this is always zero! Why?
//determine if ajax call has already occurred;
//if so, data exists in cache as local var
if (storiesCache.length !== 0) {
$log.info('Returning stories from cache.');
deferred.resolve(storiesCache);
return deferred.promise;
}
$http({method:'GET', url:url})
.success(function (data, status, headers, config) {
deferred.resolve(data);
})
.error(function (data, status, headers, config) {
deferred.reject(status);
});
return deferred.promise;
} else {
$log.error('(within storyDataAsFactory) Failed to retrieve stories: URL was undefined.');
}
}
return {
stories: storiesCache,
getStories: function(url) {
alert('inside return factory object, length of stories = ' + this.stories.length);
//getStories returns a promise so that routeProvider
//will instantiate the controller when resolved
return getStories(url);
}
};
}
You need to use a promise. I have not ran the code below, but this is the idea:
angular.module('ccsApp').service('storyDataAsFactory', storyDataAsService);
function storyDataAsService($log, $http, $q) {
var storiesCache = [];
function getStories(url) {
url = url || '';
if (url !== '') {
alert('inside getStories, length of storiesCache = ' + storiesCache.length); // this is always zero! Why?
if (storiesCache.length == 0) {
return $http({method:'GET', url:url});
} else {
var deferred = $q.defer();
deferred.resolve(storiesCache);
return deferred.promise;
}
} else {
$log.error('(within storyDataAsFactory) Failed to retrieve stories: URL was undefined.');
}
}
return {
getStories: function(url) {
return getStories(url).then(function(stories) {
// Do whatever
return stories;
});
}
};
}
storyDataAsService.getStories().then(function(stories) {
$scope.stories = stories
})

Wrap angular $resource requests not returning POST data

I'm working on wrapping my $resource requests in a simple wrapper. The main idea
is to be able to add some logic before the request is made. I've followed the nice article written by Nils.
Here you can see a service definition to access the REST API module.
resources.factory('Device', ['RequestWrapper', '$resource', 'lelylan.config', function(RequestWrapper, $http, config) {
var resource = $resource(config.endpoint + '/devices/:id', { id: '#id' });
return RequestWrapper.wrap(resource, ['get', 'query', 'save', 'delete']);
}]);
And here you can see the request wrapper definition.
resources.factory('RequestWrapper', ['AccessToken', function(AccessToken) {
var requestWrapper = {};
var token;
requestWrapper.wrap = function(resource, actions) {
token = AccessToken.initialize();
var wrappedResource = resource;
for (var i=0; i < actions.length; i++) { request(wrappedResource, actions[i]); };
return wrappedResource;
};
var request = function(resource, action) {
resource['_' + action] = resource[action];
resource[action] = function(param, data, success, error) {
(AccessToken.get().access_token) ? setAuthorizationHeader() : deleteAuthorizationHeader()
return resource['_' + action](param, data, success, error);
};
};
var setAuthorizationHeader = function() {
$http.defaults.headers.common['Authorization'] = 'Bearer ' + token.access_token;
};
var deleteAuthorizationHeader = function() {
delete $http.defaults.headers.common['Authorization']
};
return requestWrapper;
}]);
Everything works just fine for the GET and DELETE methods (the ones that does not returns
a body seems), but I can't get $save working. What happens is that when the JSON of the
created resources returns it is not added. I have only the data I've set on the creation
phase. Let me make an example.
In this case we use the wrapped resource. If I try to get the #updated_at attribute I can't
see it. In the Chrome inspector I can see how the resource is successfully created.
$scope.device = new Device({ name: 'Angular light', type: 'http://localhost:9000/types/50bf5af4d033a95486000002' });
$scope.device.$save(function(){ console.log('Device Wrapped', $scope.device.created_at) });
# => undefined
If I use $resource everything works fine.
// Suppose authorization is already set
var Resource = $resource('http://localhost\\:9000/devices/:id');
$scope.resource = new Resource({ name: 'Angular light', type: 'http://localhost:9000/types/50bf5af4d033a95486000002' });
$scope.resource.$save(function(){ console.log('Device Base', $scope.resource.created_at); });
# => 2013-02-09T12:26:01Z
I started to check the angular-resource.js code but after few hours I couldn't really figure
it out. I can't get why the body is returned, but in the wrapper resource it is not accessible.
Any idea or help would be appreciated. Thanks.
While diving into AngularJS source code I've found the solution.
The problem was that the wrapper was returning a function instead of an object and this was giving some problems. The solution is to change the following row in the Wrapper:
return resource['_' + action](param, data, success, error);
with this one:
return resource['_' + action].call(this, params, data, success, error);
Why? The fast answer is because in the source code of angular-resource they use it. Actually #call run the function sending this to the calling object. It is often used to initialize an object. Learn more here.

Categories

Resources