Wait for Firebase to completely load in Angular Service - angularjs

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.

Related

Avoid multiple $http.get in async service

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.

Returning result of a resolved promise from factory

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

Call an API in every angular controller if data not yet loaded

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
})
}])

$viewContentLoaded is executed before initializing root scope

Method on $viewcontentloaded is firing asynchronously. To detail my problem, I have a variable in root scope i.e. my Main controller, which need to be initialized before my view controller loads. In module.run I am calling a sync function to initialize $rootScope.session. And In my view controller of a route, I am checking the status of session in afunction that is called like
$scope.$on('$viewContentLoaded', function() {
$scope.initialize();
});
But some times on page refreash, I am getting an undefined value for $rootScope.session, as It may have initialized later. So, Is there any way to make this synchronous like rootscope will be initialized before view loads. And for curiosity, how it will affect, if I call the $scope.initialize(); normally in my controller, in $viewContentLoaded or in $routeChangeSuccess.
Thanks in advance.
So, Is there any way to make this synchronous like rootscope will be initialized before view loads.
Use the $controller service to manually create the controller, as in a unit test.
$controllerProvider.register('FooCtrl', FooCtrl);
ctrl = $controller('FooCtrl', {$scope: scope});
Or $broadcast a custom event from the main controller down to the child:
function mainCtrl($rootScope)
{
$rootScope.$broadcast('abc');
}
function secondCtrl($scope)
{
$scope.$on('abc', function(event) { $scope.initialize(); });
}
Or use a try/catch block and a recursive call with a timer.
These are more or less the steps that you would take to implement lazy loading in AngularJS. In summary, you would first define your app module to keep instances of the relevant providers. Then you would define your lazy artifacts to register themselves using the providers rather than the module API. Then using a ‘resolve’ function that returns a promise in your route definition, you would load all lazy artifacts and resolve the promise once they have been loaded. This ensures that all lazy artifacts will be available before the relevant route is rendered. Also, don’t forget to resolve the promise inside $rootScope.$apply, if the resolution will be happening outside of AngularJS. Then you would create a ‘bootstrap’ script that first loads the app module before bootstrapping the app. Finally, you would link to the bootstrap script from your ‘index.html’ file.
References
AngularJS source: controllerSpec.js
Ifeanyi Isitor: Lazy Loading In AngularJS
AngularJS Lazy Loading with Require.js
Split Large AngularJS Controllers using the Mixin Pattern

Angular: Inject dynamically into my controllers

I need to inject an extra object to my controllers dynamically so I thought it'd be best to do it in the run function like so:
angular.module("app").run([
"$rootScope", "$inject", "repository.user", function ($rootScope, userRepository) {
$rootScope.$on("$routeChangeStart", function (event, next, current) {
var controller = next.$$route.controller;
userRepository.getSession(function(data) {
// What do do now?
});
});
}
]);
I'd like to inject that returned data into my controllers but I'm not sure how to do it?
Take a look at the $routeProvider documentation, specifically the resolve configuration option for a route. Have you thought about using this to dynamically resolve your dependency?
resolve - {Object.=} - An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated. If all the promises are resolved successfully, the values of the resolved promises are injected and $routeChangeSuccess event is fired. If any of the promises are rejected the $routeChangeError event is fired. The map object is:
key – {string}: a name of a dependency to be injected into the controller.
factory - {string|function}: If string then it is an alias for a service. Otherwise if function, then it is injected and the return value is treated as the dependency. If the result is a promise, it is resolved before its value is injected into the controller. Be aware that ngRoute.$routeParams will still refer to the previous route within these resolve functions. Use $route.current.params to access the new route parameters, instead.
If I understand correctly, you are trying to collect user login state for every route change. Why not just save the data in $rootScope as something like $rootScope.currentUser = data.
This way you can access $rootScope.currentUser from any controller.

Resources