Site-wide error message broadcast in Angular - javascript

I have a single way of showing the user if there is an error with a $http call, and I currently invoke it by doing something like:
$http({
//do stuff
}).
error(function(){
$scope.$broadcast('error');
});
And I have a directive that listens for the the error with $on.
However I just end up repeating the $broadcast everywhere I use $http, is it possible to extend the default error callback to broadcast the error every time?

I would suggest you use a response interceptor instead.
You can find the details on the AngularJS API Reference, but this is how it would work:
angular.module('app', []);
angular.module('app').factory('myHttpErrorInterceptor', function ($q) {
return {
'responseError': function (rejection) {
console.log('DEBUG: Status:', rejection.status);
return $q.reject(rejection);
}
};
});
angular.module('app').config(function ($httpProvider) {
$httpProvider.interceptors.push('myHttpErrorInterceptor');
});
angular.module('app').run(function ($http) {
$http.get('http://example.org/that-doesnt-exist');
});
Instead of my console.log, you can broadcast your error instead.
You can find a live example here.

Related

Angular JSONP Factory call not working, however it does work in controller

I have looked at every post on SO related to this issue but still cannot find the answer. This response appeared to be the most promising but I didn't understand the answer, and it appears the person who asked did not either.
What am I trying to do?
Make a JSONP angular GET request to the itunes API. This works perfectly when I make the GET request from my controller, however when I tried to refactor, in order to obey the principle of 'Separation of Concerns' by moving this request into a factory, and injecting into the controller I am running into difficulties. I know the call is still being made as I am getting a CORS error if I do not use a JSONP call in my factory.
Here is the factory code:
var myApp = angular.module('jDillaApp', []).factory('Music', function ($http) {
var o = {
songs: []
};
o.getNextSongs = function () {
return $http.jsonp({
method: 'GET',
url: 'https://itunes.apple.com/search?term=j+dilla&limit=25?format=jsonp&callback=JSON_CALLBACK'
}).success(function (data) {
console.log(data)
});
}
return o
})
My controller looks like this:
myApp.controller('jDillaCtrl', ['$scope', '$http', 'Music',
function ($scope, $http, Music) {
Music.getNextSongs();
$scope.songs = Music.songs;
var media = new Audio();
$scope.playSong = function () {
media.pause();
var randomSong = Math.round(Math.random() * ($scope.songs.length - 1));
media = new Audio($scope.songs[randomSong]);
media.play();
}
}]);
The error is not a lot of help as far as I can tell but worth posting anyway TypeError: h.replace is not a function
You $http.jsonp method has wrong inputs that was your first problem, $http.jsonp method accepts two parameter as such
$http.jsonp(url, [config])
url -> the url which you want make an ajax
config -> if you want to make additional changes in request then you can pass those setting from here in {} key value pair like you can set headers, type, etc.
Your service method should return promise, as you only need to return $http.jsonp object which already has promise, no need of creating extra promise object here.
Service
o.getNextSongs = function () {
return $http.jsonp('https://itunes.apple.com/search?term=j+dilla&limit=25?format=jsonp&callback=JSON_CALLBACK')
}
In controller that call will be resolved using .success & .error also you can use .then
Controller
Music.getNextSongs()
.success(function (data) {
//here you get the ajax success
console.log(data)
});

How to update $scope immediately when dealing with non angular code?

I have to deal with an non angular library and need to create a comunication between them.
<div id="MoipWidget" data-token="{{personaltoken}}" callback-method-success="successCB" callback-method-error="errorCB"></div>
Every time that the page is loaded, I have to get a token from my server.
$http.post('https://example.org', obj)
.success(function(data){
$scope.personaltoken = data.token;
//Here I call the non angular library and I get and error telling me that the token is undefined.
//If I run the code from inside a $timeout works as I need...
})
.error(function(data){
alert('error');
});
I've also tried to run inside $scope.$apply but I get an error telling that $digest already in progress
The non angularjs library that I have to call is simple is just two lines.
var settings = {}
LibraryCall(settings);
How can I update the model immediately?
I've tried to use $scope.$evalAsync as #Kjell suggested but did not work.
After reading more about $scope, I've found what I needed.
$scope.$applyAsync(function(){
var settings = {}
LibraryCall(settings);
});
$scope.$applyAsync will schedule the invocation of $apply to occur at a later time.
https://docs.angularjs.org/api/ng/type/$rootScope.Scope
I removed the error callback for brevity, don't do it in your code :)
I suppose the code you call is asynchronous, if it's not, you should not have any $scope updating problem (because all angular promises call $apply already)...
This should work:
$http.post('https://example.org', obj).success(function(data){
$scope.personaltoken = data.token;
otherLibrary.doSomething(data.token, function(error, result) {
$scope.changeSomething = 'toHey';
$scope.$apply();
});
});
This shoud also work:
$http.post('https://example.org', obj).success(function(data){
$scope.personaltoken = data.token;
otherLibrary.doSomething(data.token, function(error, result) {
$scope.$apply(function() {
$scope.changeSomething = 'toHey';
});
});
})
This shoud raise the $digest already in progress error, because $http does wrap the promise callback on a $apply call already.
$http.post('https://example.org', obj).success(function(data){
$scope.personaltoken = data.token;
$scope.$apply(function() {
otherLibrary.doSomething(data.token, function(error, result) {
$scope.changeSomething = 'toHey';
});
});
})
Try using either $scope.$evalAsync() or $scope.$applyAsync().
They are made for stuff like this. It will execute the code later in time. Not that different from $timeout, but potentially faster.
$scope.$evalAsync(function(){
var settings = {}
LibraryCall(settings);
})
Edit: Just to quote Ben Nadel on the difference between $timeout and $evalAsync, from this post:
So, in essence, $scope.$evalAsync() combines the best of both worlds:
When it can (which is most of the time), it will evaluate your
expression in the same tick; otherwise, it will evaluate your
expression in a later tick, which is exactly what $timeout() is doing.

Angularjs not storing variable to $scope after AJAX call within $http.get request

I'm using angularjs and I can't get the following controller to save to a $scope variable the data returned from an AJAX request to Flickr. The $http.get makes a call to a locally saved json file. Upon success, it uses the json returned in success() to determine the appropriate url for the AJAX call to the Flickr API. Upon success of that call, I log the data to the console. So far so good, it returns an array of three objects. However, I'm trying to set that array to a $scope variable ($scope.photos) so I can iterate over it my view template. However, when I try outputing {{photos}} in the html there is nothing. I suspect this is a promise issue, and the template is rendering before the AJAX returns the data from Flickr, but I've been pouring over the docs with no success (looked at $q a little). I'm somewhat new to Angular and would appreciate your insight. Thanks!
artistControllers.controller('PhotoController', ['$scope', '$http', '$routeParams', '$q', function ($scope, $http, $routeParams, $q){
$http.get('js/data.json').success(function(data){
$scope.artists = data;
$.ajax({
type : "GET",
dataType : "jsonp",
url : $scope.artists[$routeParams.itemId].flickr,
success: function(flickr){
$scope.photos = flickr.items;
console.log($scope.photos);
}
});
});
}]);
Don't use jQuery.ajax. Angular's $http can do JSONP too. You can read more about here.
artistControllers.controller('PhotoController', ['$scope', '$http', '$routeParams', '$q', function ($scope, $http, $routeParams, $q){
$http.get('js/data.json').success(function(data){
$scope.artists = data;
$http.jsonp($scope.artists[$routeParams.itemId].flickr).success(function(data){
$scope.photos = flickr.items;
console.log($scope.photos);
});
});
}]);
Because you are executing code outside of Angular's knowledge, you need to manually call $scope.$digest() for it to "see" your change and update the markup accordingly.
Just change your success handler to:
success: function(flickr){
$scope.photos = flickr.items;
$scope.$digest();
}
Note: $scope.$apply() would also work, because it does a $digest of every single scope in your application, starting from the $rootScope down. On a big application, this can be much slower than necessary, so in your case I recommend only digesting from the scope you are modifying down.
Thank you everyone for your help and feedback. I have found a solution using $q and $http.jsonp, in part thanks to this tutorial:
http://youtu.be/gApduktFwxw?t=17m
Here is my code, note that my API url string to flickr has &jsoncallback=JSON_CALLBACK appended to it:
$http.get('js/data.json').success(function(data){
$scope.artist = data[$routeParams.itemId];
var url = $scope.artist.flickr;
console.log(url);
$scope.init = function(){
$scope.getImages()
.then(function(res){
console.log(res);
}, function(status){
console.log(status);
});
};
$scope.getImages = function(){
var defer = $q.defer();
$http.jsonp(url)
.success(function(res){
defer.resolve(res);
console.log(res);
}).error(function(status, err){
defer.reject(status);
console.log(err);
});
return defer.promise;
};
$scope.init();

Multiple $http calls in AngularJS

So here is what I need to do. I'm using the Angular Deferred Bootstrap library in my code because I need to receive some essential data from a RESTful server before bootstrapping and trying to load the content. Anyway, I must make a second call once the first call resolves. The second call is a login that depends on some URL that is contained in the first response.
Right now, I want to try to make the login call once I receive that data (I was trying in the .success() block) but once the first call resolves, the program begins bootstrapping before the login call is finished; things break because I'm not "logged in" on the server.
window.deferredBootstrapper.bootstrap({
element: window.document.body,
module: 'app',
resolve: {
STARTUP_CONFIG: ['$http', function($http) {
return $http.get(url1 + 'blah/blah/blah/' + layout);
}],
CONTEXT_CONFIG: ['$http', function($http) {
return $http.get(url1 + 'blah/blah/blah/blah').
success(function(data) {
$http.post('https://' + data.url2 + '/10001/guestidentity?client_id=' + data.id).
success(function(result){
token = result.token;
});
});
}],
}
});
Anyone have any idea what I can do?
hi since this is a promise you can chain the next call to the then function something like
$http(url).
then(function(response){
//note that this shouldn't be the body of the response
// but the entire response stream so you need to check for the status code and the body and apply and possibly apply any transformation that is needed as it will probably be just text
// then we can start the other call
return $http(url);
})
this way the second promise will be handled to the final destination.

AngualrJS Undefined or Null Reference When Accessing $rootSope in Controller

AngularJS latest release candidate:
I am putting a javascript object - called say stuff into the $rootScope from the module's run function, which I believe is supposed to block. This is the code:
'use strict';
/* App Module */
var app = angular.module('MyApp', ['ngRoute', 'API'])
.run(function ($rootScope, API) {
$rootScope.stuff = null;
// call the API
API.getStuff()
.success(function(data){
$rootScope.stuff = data;
})
.error(function(data){
$rootScope.stuff = null;
});
});
Now, when I attempt to access the stuff property of $rootScope from my controller, I am getting an 'undefined or null reference' error on stuff. Code looks like this:
'use strict';
app.controller('indexController',
function ($scope, $rootScope, otherAPI) {
var
stuff = $rootScope.stuff;
// call the other API
otherAPI.getDifferentStuff(stuff.property)
.success(function(data){
$scope.differentStuff = data;
})
.error(function(data){
// do some error handling stuff here
});
});
I know the api call in the run function is succeeding, and it is assigning a value to stuff in the $rootScope. Can anyone see anything obvious wrong with my code here?
Thanks for any help!
Rich
Is API.getStuff an asynchronous api call (it looks like it). In that case most likely your controller is getting initialized before the asynchronous call has returned, so $rootScope.stuff is still equal to null. If you wait until the call succeeds, then you will have your data.

Categories