I would like to include the Facebook javascript libraries in an Angular project such that all the facebook API calls (login, logout, etc) are encapsulated inside a service. But because of the async nature of the FB library my code seems overly verbose and I have several calls to $rootScope.apply() which I'm not sure is best practice.
Right now I have something like this:
app.factory('Facebook', function($rootScope, $window, $q){
var FBdefer = $q.defer();
var FBpromise = FBdefer.promise;
$window.fbAsyncInit = function(){
$rootScope.$apply(function(){
FB.init(/* FB init code here*/);
FBdefer.resolve(FB);
}
}
var fb_service_api = {
login: function(){
var deferred = $q.defer();
FBPromise.then(function(FB){
FB.login(function(response){
$rootScope.$apply(
deferred.resolve(response)
);
});
}
return deferred.promise.
}
}
return fb_service_api;
})
Looking for a good design pattern here that fits well with the angular framework.
I think that you may be able to solve this using Misko Hevery's approach here to delay loading of your controller(s) until the Facebook XHR calls have 'resolved'.
This seems to have solved all of my asynchronous data loading issues and will probably mean you can just delete your $apply() calls too.
Related
I want to use the library MIDI.js in my AngularJS app. To initialise it, you call MIDI.loadPlugin which takes a callback that fires when some files are loaded.
I want to make a controller in my app aware of when the files are loaded.
My current approach is a dedicated service which calls MIDI.loadPlugin when it is created, and sends a $rootScope.$broadcast when the callback is fired.
Is this the best approach, particularly in regards to testability?
I would use a promise in your service to get access to the async resource. Then for all functions in the service that need it they just call the get method that returns a promise.
I'll try to post some example code later when on the computer.
Here's the solution I came up with based on Gordon's suggestion.
.factory('midiLoader', ['MIDI', '$q', function (MIDI, $q) {
var service = {};
service.loadFiles = function() {
return $q(function(resolve, reject) {
MIDI.loadPlugin({
soundfontUrl: "bower_components/midi/soundfont/",
instrument: "acoustic_grand_piano",
callback: function () {
resolve(null);
}
});
});
}
return service;
});
I am just learning Angular and I have some questions regarding the architecture of my app.
The project I will be working on will be using allot of external libraries: jQuery, jQuery.ui, jsPlumb and so on, with different loading times.
I know that each code related to these libraries will have to be handled inside directives.
I worked with Backbone which uses Require JS effectively - on each view, I could set what libraries I need and the view would be loaded as soon as those libraries are available.
Now, on angular side - what would be the correct way of handling this issue?
From the top of my head, I am thinking of:
Place checks inside the router - checking if the desired libraries for a certain route are loaded.
Place checks inside each directive - for example if one directive uses jsPlumb, place a check inside and return the directives content when that library is loaded - I believe this could generate problems when interacting with other directives on the same view, which require multiple libraries with different loading times.
Load angular only after every other library is loaded - that would lead to long loading times.
What's the best way to handle all those issues?
You can create a factory to load the external library you need. Return a deferred object for the library's script after it loads. Here is one I used for d3 library:
var d3js = angular.module('d3', []);
d3js.factory('d3Service', ['$document', '$q', '$rootScope', '$window',
function($document, $q, $rootScope, $window) {
var d = $q.defer();
function onScriptLoad() {
// Load client in the browser
$rootScope.$apply(function() { d.resolve($window.d3); });
}
// Create a script tag with d3 as the source
// and call our onScriptLoad callback when it
// has been loaded
var scriptTag = $document[0].createElement('script');
scriptTag.type = 'text/javascript';
scriptTag.async = true;
scriptTag.src = 'lib/d3.v3.js';
scriptTag.onreadystatechange = function () {
if (this.readyState == 'complete') onScriptLoad();
}
scriptTag.onload = onScriptLoad;
var s = $document[0].getElementsByTagName('body')[0];
s.appendChild(scriptTag);
return {
d3: function() { return d.promise; }
};
}]);
then in your directive, use then function of the deferred to wait until it's ready
d3Service.d3().then(function(d3) {
// place code using d3 library here
}
If your directive is needing access to multiple libraries, you can chain the deferreds.
d3Service.d3().then(function(d3) {
someOtherLibSvc.getLib().then(function(lib){
// place code using d3 library and someOtherLibrary here
}
}
To avoid this chaining check out bluebird and use Promise.join, Angular comes with $q automatically so I just used that here.
Note: if you just load JQuery before angular, then angular.element will already reference JQuery. So you don't need to do this for JQuery, just load it in your main html page before Angular
In short go
http://slides.com/thomasburleson/using-requirejs-with-angularjs#/
route.
However now suffer having to put the defines and requires everywhere. For a large application this may get messy if not planned carefully. Use grunt requirejs plugin for builds because without this things would be wash.
I'm not sure this is the "correct" way to do this, but here is perhaps a simpler way to handle external libraries (in this case d3.js) within Angular 1.x code.
This is the same basic idea as #aarosil's answer (use a factory), but using fewer dependencies on other services - for what that's worth.
var app = angular.module('SomeApp', []);
app.factory('LibraryFactory', function () {
var factory = {};
factory.getD3 = function(callback) {
if(!window.d3) {
var script = document.createElement("script");
script.src = "https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.9/d3.min.js";
document.head.appendChild(script);
script.onload = function() {
callback(window.d3);
};
} else {
callback(window.d3);
}
};
//factory.getSomeOtherLibrary = ...ditto
return factory;
});
And to use the factory (eg, in a controller):
app.controller('SomeTrialWithD3Ctrl', function ($scope, LibraryFactory) {
LibraryFactory.getD3(function main(d3) {
// place code using d3 library here
});
});
And of course, the callbacks can be chained if you need multiple libraries at once.
My question is that given the power of interceptors, does it make sense to wrap $http in a service so that all my other code just calls that wrapper. Now basic tasks like header/exception handling can easily be done by interceptors. So though I cant think of a valid usecase now, but lets say just to shield any future api changes etc to $http? Or maybe later migrate to $resource?
Also please note that here I am talking of a basic wrapper service around $http’s methods, not a client service like DataService with methods sendData, receiveData that wraps $http calls.
Please find below sample code -
angular.module(‘myapp’).factory(‘myhttpwrapper’, ['$http', function (http) {
return {
myGet: function (getUrl) {
return http.get(getUrl);
},
myPost: function (postUrl, data) {
return http.post(postUrl, data);
},
// other $http wrappers
};
}]);
Now all other code will use myhttpwrapper’s myGet, myPost methods instead of $http’s get, post methods. Hope it makes sense!
[EDIT] What use-cases we'll definitely have is to intercept requests for adding headers, logging, and responses for logging, exception handling, etc. But I am sure these can be handled by interceptors. Later moving from $http to $resource is not known at this point.
Thanks.
For the specific case you describe, I'd advise against wrapping $http. There is no real gain in doing it.
A case where this could come into play would be where you'd want a more 'speaking' API. Lets say you have User(s) in a system, and Address(es). You described it as a data based service DataService:
var app = angular.module("users", []);
app.service("User", ['$http', '$q', function(http, q) {
return {
getAddress: function(user) {
var address = q.defer();
http.get("/user/" + user.id + "/address").then(function(data) {
address.resolve(data);
}, function(err) {
address.reject(err);
});
return address.promise;
},
getUser: function() {
var user = = q.defer();
http.get("/user/address").then(function(data) {
user.resolve(data);
}, function(err) {
user.reject(err);
});
return user.promise;
}
}
}]);
This allows you to use parameters for your call. Whenever you'd have to change routes, you would have only one place to change them (this would be really awful if, say, you had a dozen controllers making $http requests).
You can also make use of $resource here if you which (and you actually have compatible resources). The decision making factor(s) here should be readability, reusability and ease of change later.
So, if you only ever have on place where you make these calls and the routes never change, go ahead and use the $http directly, without a wrapper. This I'd consider unlikely in a growing application.
I needed my 'get' generalized so did it like this:
app.factory('myHttp', ['$http', '$q', function($http, $q) {
return {
get: function(url) {
var deferred = $q.defer();
$http.get(url).then(
function(response) {
//console.log("response good");
deferred.resolve(response)
},
function(response) {
//console.log("response bad");
deferred.reject(response)
})
return deferred.promise;
}
};
}]);
You can now chain an additional "then" to the result of the get. You can have core logic for handling error or success (my use case being to log out the user if credentials expired) but still allow for additional context specific logic for the get response (like setting $scope etc...).
I was reading this article: http://eviltrout.com/2013/06/15/ember-vs-angular.html
And it said,
Due to it’s lack of conventions, I wonder how many Angular projects
rely on bad practices such as AJAX calls directly within controllers?
Due to dependency injection, are developers injecting router
parameters into directives? Are novice AngularJS developers going to
structure their code in a way that an experienced AngularJS developer
believes is idiomatic?
I am actually making $http calls from my Angular.js controller. Why is it a bad practice? What is the best practice for making $http calls then? and why?
EDIT: This answer was primarily focus on version 1.0.X. To prevent confusion it's being changed to reflect the best answer for ALL current versions of Angular as of today, 2013-12-05.
The idea is to create a service that returns a promise to the returned data, then call that in your controller and handle the promise there to populate your $scope property.
The Service
module.factory('myService', function($http) {
return {
getFoos: function() {
//return the promise directly.
return $http.get('/foos')
.then(function(result) {
//resolve the promise as the data
return result.data;
});
}
}
});
The Controller:
Handle the promise's then() method and get the data out of it. Set the $scope property, and do whatever else you might need to do.
module.controller('MyCtrl', function($scope, myService) {
myService.getFoos().then(function(foos) {
$scope.foos = foos;
});
});
In-View Promise Resolution (1.0.X only):
In Angular 1.0.X, the target of the original answer here, promises will get special treatment by the View. When they resolve, their resolved value will be bound to the view. This has been deprecated in 1.2.X
module.controller('MyCtrl', function($scope, myService) {
// now you can just call it and stick it in a $scope property.
// it will update the view when it resolves.
$scope.foos = myService.getFoos();
});
The best practise would be to abstract the $http call out into a 'service' that provides data to your controller:
module.factory('WidgetData', function($http){
return {
get : function(params){
return $http.get('url/to/widget/data', {
params : params
});
}
}
});
module.controller('WidgetController', function(WidgetData){
WidgetData.get({
id : '0'
}).then(function(response){
//Do what you will with the data.
})
});
Abstracting the $http call like this will allow you to reuse this code across multiple controllers. This becomes necessary when the code that interacts with this data becomes more complex, perhaps you wish to process the data before using it in your controller, and cache the result of that process so you won't have to spend time re-processing it.
You should think of the 'service' as a representation (or Model) of data your application can use.
The accepted answer was giving me the $http is not defined error so I had to do this:
var policyService = angular.module("PolicyService", []);
policyService.service('PolicyService', ['$http', function ($http) {
return {
foo: "bar",
bar: function (params) {
return $http.get('../Home/Policy_Read', {
params: params
});
}
};
}]);
The main difference being this line:
policyService.service('PolicyService', ['$http', function ($http) {
I put an answer for someone who wanted a totally generic web service in Angular. I'd recommend just plugging it in and it will take care of all your web service calls without needing to code them all yourself. The answer is here:
https://stackoverflow.com/a/38958644/5349719
I'm writing a service that will retrieve data asynchronously ($http or $resource). I can hide the fact that it is asynchronous by returning an array that will initially be empty, but that will eventually get populated:
.factory('NewsfeedService1', ['$http', function($http) {
var posts = [];
var server_queried = false;
return {
posts: function() {
if(!server_queried) {
$http.get('json1.txt').success(
function(data) {
server_queried = true;
angular.copy(data, posts);
});
}
return posts;
}
};
}])
.controller('Ctrl1', ['$scope','NewsfeedService1',
function($scope, NewsfeedService1) {
$scope.posts = NewsfeedService1.posts();
}])
Or I can expose the asynchronicity by returning a promise:
.factory('NewsfeedService2', ['$http', function($http) {
var posts = [];
var server_queried = false;
var promise;
return {
posts_async: function() {
if(!promise || !server_queried) {
promise = $http.get('json2.txt').then(
function(response) {
server_queried = true;
posts = response.data;
return posts;
});
}
return promise;
}
};
}])
.controller('Ctrl2', ['$scope','NewsfeedService2',
function($scope, NewsfeedService2) {
NewsfeedService2.posts_async().then(
function(posts) {
$scope.posts = posts;
});
// or take advantage of the fact that $q promises are
// recognized by Angular's templating engine:
// (note that Peter and Pawel's AngularJS book recommends against this, p. 100)
$scope.posts2 = NewsfeedService2.posts_async();
}]);
(Plunker - if someone wants to play around with the above two implementations.)
One potential advantage of exposing the asychronicity would be that I can deal with errors in the controller by adding an error handler to the then() method. However, I'll likely be catching and dealing with $http errors in an application-wide interceptor.
So, when should a service's asynchronicity be exposed?
My guess is that you'll find people on both sides of this fence. Personally, I feel that you should always expose the asynchronicity of a library or function (or more correctly: I feel that you should never hide the asynchronicity of a library or function). The main reason is transparency; for example, will this work?
app.controller('MyController', function(NewsfeedService) {
$scope.posts = NewsfeedService.posts();
doSomethingWithPosts($scope.posts); // <-- will this work?
});
If you're using the first method (e.g. $resource), it won't, even though $scope.posts is technically an array. If doSomethingWithPosts has its own asynchronous operations, you could end up with a race condition. Instead, you have to use asynchronous code anyway:
app.controller('MyController', function(NewsfeedService) {
$scope.posts = NewsfeedService.posts(function() {
doSomethingWithPosts($scope.posts);
});
});
(Of course, you can make the callback accept the posts as an argument, but I still think it's confusing and non-standard.)
Luckily, we have promises, and the very purpose of a promise is to represent the future value of an operation. Furthermore, since promises created with Angular's $q libraries can be bound to views, there's nothing wrong with this:
app.controller('MyController', function(NewsfeedService) {
$scope.posts = NewsfeedService.posts();
// $scope.posts is a promise, but when it resolves
// the AngularJS view will work as intended.
});
[Update: you can no longer bind promises directly to the view; you must wait for the promise to be resolved and assign a scope property manually.]
As an aside, Restangular, a popular alternative to $resource, uses promises, and AngularJS' own $resource will be supporting them in 1.2 (they may already support them in the latest 1.1.x's).
I would always go with async option since i don't like hiding the async nature of the underlying framework.
The sync version may look more clean while consuming it, but it inadvertently leads to bug where the developer does not realize that the call is async in nature and tries to access data after making a call.
SO is filled with questions where people make this mistake with $resource considering it sync in nature, and expecting a response. $resource also takes similar approach to option 1, where results are filled after the call is complete, but still $resource exposes a success and failure function.
AngularJS tries to hide the complexities of async calls if promises are returned, so binding directly to a promise feels like one is doing a sync call.
I say no, because it makes it harder to work with multiple services built this way. With promises, you can use $q.all() to make multiple request and respond when all of them complete, or you can chain operations together by passing the promise around.
There would be no intuitive way to do this for the synchronous style service.