I've created a service that fetches through $http.get().
My service is used in a directive that renders to several ui elements. The service is async. It's used in the directive code like: myService.getStuff(), and it returns a customized promise (from within the callbacks of the $http.get promise).
But I want my service to only perform one call to $http.get during the lifecycle of the app. And cache the http.get response.
So say I use the directive 2 times in my html. Then this is what I want: The first time it's called it (returns immediately and) fetches stuff via $http.get. The second time it's called it detects an outstanding call to $http.get. thus does not do another call to $http.get, but somehow awaits the return of the first call.
You might want to use the internal cache of $http
return $http.get(/*...*/, { cache: true }).then(/*...*/);
or just cache the promise in your service.
function MyService($http) {
var promise;
this.get = function() {
promise = promise || $http.get(/*...*/).then(/*...*/);
return promise;
}
}
If you're using uirouter, another way is to resolve the data in a parent state, and inject the resolved data to all your components.
Set cache to true like this:
$http.get(yourUrl, {cache: true})
.then(...)
Any subsequent call to this will use the cached value.
Related
I have an Angular app, which contains a menu that is defined in av MVC view. So the menu is not related to the routes in the app. In the menu controller, I would like to inject an object containing data about the current user. I will then loop this object to decide which menu items should be visible to the user.
The factory looks like this:
.factory('currentUser', ['currentUserResource', function (currentUserResource) {return currentUserResource.get()}])
The get method is a standard $resource GET method.
Problem is, the factory returns a promise which takes a short while to resolve. So in my controller, some of the menu items are processed before the promise is resolved, and aren't showing when they should show.
In my controller I inject 'currentUser', and then have tried using in these ways:
$scope.currentUser = currentUser.then(function (data){
return data
});
$scope.currentUser = currentUser;
currentUser.then(function(data){
$scope.currentUser = data;
});
Is there any way, without using routing resolve, I can make sure that currentUser is resolved before the controller is loaded, or atleast that it is resolved first thing the controller does?
Use the $promise property of the $resource object to delay the work of the controller.
resourceObject.$promise.then ( function onFulfilled(object) {
//Do work that needs fulFilled object
}).catch (function onRejected(response) {
console.log(response.status)
});;
If the the resourceObject has been resolved, the onFulfilled function will be execute immediately. If the resourceObject has not been resolved, the $q service will wait before invoking the onFulfilled function.
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data.
The Resource instances and collections have these additional properties:
$promise: the promise of the original server interaction that created this instance or collection.
On success, the promise is resolved with the same resource instance or collection object, updated with data from server. This makes it easy to use in resolve section of $routeProvider.when() to defer view rendering until the resource(s) are loaded.
On failure, the promise is rejected with the http response object, without the resource property.
-- AngularJS $resource Service API Reference
I am building a single page app using angular and .net backend.
Aside from the individual restful resources that the controllers call, I also have a System Resource which provides the app with system wide information that is used/needed by most of my controllers.
I call the system service and store the data in the rootScope when the user first logs on.
However if for some reason the user refreshes the page which will lose the rootScope then the system data is lost and no longer available to the controller.
I have managed to get the System Service to trigger if a page reload happens BUT the problem is that in many cases the services running from the controller happen BEFORE the system service call which means that the data is not always available when the controller runs.
Whats the correct way to handle this in angular? Is there a way to make my controllers dependent on a certain rootScope state which if necessary will cause my system service API to be called before the controller makes its own service calls?
Thanks
One approach could be creating a factory/service which is injected to and called by every controller needing this information. This way you don't have to mess with $rootScope.
The request to get information should be cached so that you don't have to fire off a get everytime you switch controller. If you want to persist the information even after a page refresh you could use localstorage to store your data.
Example factory:
angular.module('appName')
.factory('Settings', ['$http', function ($http) {
function getData() {
var url = 'url/to/endpoint';
return $http.get(url, {cache: true});
}
return {
promise: getData()
};
}]);
By returning a promise from the factory we ensure that the getData() call is only run once. While in this particular instance it makes almost no difference (since it is returning an inner promise already), it is a pattern to follow for best practice.
This pattern also means that getData() is called on first use regardless of if the consuming controller accesses the promise. This allows for data to be exposed easily (data binding) without the need to use the promise in some use cases.
Used in controller:
angular.module('appName')
.controller('VeryNiceCtrl', ['$scope','Settings', function ($scope, Settings) {
Settings.promise.success(function(response){
var appSettings = response;
// do stuff
})
}])
I have the following controller:
app.controller('SearchVideosController',
function SearchVideosController($scope, videoRepository) {
$scope.DoSearch(id, text) {
// Do some work...
videoRepository.getVideosForUserBasedOnSearchText(id,text)
.then(function(data){
// Do something with the data.
});
};
};
My videoRepository.getVideosForUserBasedOnSearchText() method uses $q and I want to create stub to ensure that the call is made.
I tried :
it("should have 3 searched videos", function(){
...
mockVideoRepository.getVideosForUserBasedOnSearchText.returns([]);
but get .then() is undefined.
Not sure how to handle the then() call.
You would need to get hold of $q service instance and use $q.when to create a promise wrapped value:-
mockVideoRepository.getVideosForUserBasedOnSearchText.returns($q.when([]));
Also remember you would need to manually perform a digest cycle in your test before the expecation to evaluate the result of the getVideosForUserBasedOnSearchText call. Only when a digest cycle is invoked promise will be resolved in your test. You can do it by getting hold of scope, and perform $digest or $apply. Example:- rootScope.$apply()
I have an issue where i want fire a method from a service immediately after a route change. That method has to lookup an object on a Firebase:
app.service('document',function($q,$firebase){
var databaseReference = new Firebase(firebase),
database = $firebase(databaseReference);
return{
getDocument: function(title){
return database.documents[title];
}
}
});
unfortunately, the documents property won't load directly; it takes a few ms to appear.
I know that i could wrap this up in a promise, but how can i make the service being returned only when that promise is resolved?
If you use Angular ui-router, you can put one or more promises in a resolve block of the same state your controller is in, and declare those as dependencies to be injected into your controller. The controller then does not get created until all those promises have been resolved, and within your controller you'll have access to the resolved promises. See https://github.com/angular-ui/ui-router/wiki#resolve for more info.
Can't think of a better title, sorry.
Please consider the following code -
//controller
function ProductCtrl($scope) {
getCategories(function (result) {
$scope.category = result.categories[0].name;
}); // asynchronouse method; getCategories calls uses XHR and returns the result as callback.
}
//view
{{category}}
When the view loads in the browser, getCategories gets called immediately. How do I make it load on demand, like onLoad on div or something so I can re-use this method somewhere else? Something like, $scope.getCategories returns the data I wanted, not on controller load. And how do I use this method in the view? e.g. <div onload=getCategories()></div> works?
Another question, the view does not print the category. How do I make $scope.category available outside of getCategories?
When the view loads in the browser, getCategories gets called immediately. How do I make it load on demand?
Wrap it in a function, and call that function when appropriate:
$scope.getCategories = function() {
getCategories(function (result) { ... }
}
so I can re-use this method somewhere else?
If multiple views need access to the result of getCatetories, you should create a service that stores the result. Your controllers can then inject that service to get access to it.
the view does not print the category.
Your AJAX is happening "outside" of Angular, so Angular doesn't know that a callback is being called and that $scope.category is being updated. Simply add $scope.$apply() after you update $scope.category in your callback and Angular will run a digest cycle and update your view.