I started learning Angular JS two hours ago and saw this in the tutorial:
var app = angular.module("githubViewer", []);
var MainController = function($scope, $http){
var onUserComplete = function(response){
$scope.user = response.data;
};
var onError = function(reason){
$scope.error = "Failed to get the user info.";
};
var promise = $http.get("https://api.github.com/users/someone");
promise.then(onUserComplete, onError);
};
app.controller("MainController", MainController);
I understand $scope/$http is native to Angular but what about the response/reason? How are they passed to the function?
Those are the callback functions - onUserComplete and onError, which are called when the promise returned by $http.get() function is resolved. The callback functions are passed response returned by the completion of $http.get() invocation. The parameter in the function is that response.
#Rohit Jain is right.
This is much more a related to Javascript then Angular.
CallBack are often used in JS.
Here is a quick exemple to understand how CallBack works
function mainFunc(mycallBack){
mycallBack()
}
function onSomething(){
alert("hi");
}
mainFunc(onSomething);
http://plnkr.co/edit/Ud1E6Wbns1EnItVI8Xg7?p=preview
I hope it helps.
Regards,
Eric
Related
I have some controller and a function called to obtain some value from a REST WCF web service:
fooBar.controller('fooCtrl',
function fooCtrl($scope, $http, $resource) {
$scope.someOnClickEvent = function () {
GetSomething('a','b','c');
}
}
);
function GetSomething($scope, $resource, a, b, c) {
var ServiceHandle = $resource('some/addr');
var serviceHandle = new ServiceHandle();
serviceHandle.a = a;
serviceHandle.b = b;
serviceHandle.c = c;
serviceHandle.$save().then(function (result) {
console.log('So response should be ready');
$scope.result = result.ValueFromService;
});
}
As far as I know $save() returns promise and function inside .then should be called right after response is returned by the server. In my case it's called imediately.
If service returns true I'm going to show some popup, so I need this value to be returned before conditional instruction is executed.
Version of Angular is 1.4.9.
I've analysed code and its behavior and I'm wrong obviously. Maybe I should delete this question, but I believe it can help someone to understand similar problems.
Function inside .then is in fact called after a response comes from the server. It can be verified using for instance Chrome's developer tools (namely tab network).
However if just after calling GetSomething function we want to access a $scope.result we are making a great mistake. $scope.result will be undefined as long as function inside .then won't be executed. To use a value returned from a service we must do it inside .then function or in a function called by it.
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)
});
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();
Reading this excellent book, Mastering Web Development in AngularJS, I ran across this code:
var Restaurant = function ($q, $rootScope) {
var currentOrder;
this.takeOrder = function (orderedItems) {
currentOrder = {
deferred:$q.defer(),
items:orderedItems
};
return currentOrder.deferred.promise;
};
this.deliverOrder = function() {
currentOrder.deferred.resolve(currentOrder.items);
$rootScope.$digest();
};
this.problemWithOrder = function(reason) {
currentOrder.deferred.reject(reason);
$rootScope.$digest();
};
My understanding is that the $rootScope.$digest(); calls are made in order to alert Angular that the Promise's state has been updated.
Is my understanding correct? Also, is it necessary to make the above $rootScope.$digest(); calls?
$scope.$digest() is what processes all of the $watch events that are on the current and children scope. It essentially manually tells the scope to check if a scope variable has changed. You don't generally want to use this when you are inside of a controller or a directive, because the $scope.$apply() function calls the $digest anyway and it is called when you mutate a scope variable.
Checkout this link for an example.
You don't need a $rootScope.$digest here because resolving/rejecting the promise will fire a $rootScope.$digest internally, as $interval,$timeout,and $http (after request finished) do that for you. And this $digest can throw errors of $digest already in progress.
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.