Initialize Angular 1.4 app with ngNewRouter with asynchronous data - angularjs

I know about this question here: AngularJS : Initialize service with asynchronous data which I have used in the past on 1.1 and 1.2.
But this requires you need to use old ngRoute module. I'm using ngNewRouter (available for angular 1.x) and don't have a resolve method for the routeProvider.
However it is possible to be done in components, not with the main controller:
//before navigation finishes, useful to load required data
appController.prototype.activate = function() {
console.log ('loading data for the user');
console.log (this.authService.requestCurrentUser());
return this.authService.requestCurrentUser();
}
Still, the main controller it's not a component, so $componentLoaderProvider is not available.
How do I make the app wait for async data, without adding return this.authService.requestCurrentUser() to every component?
Thank you!

Related

Set app.config within a controller

So, I have my app.controller and inside of it I have a request. When request is successful, I want to set retrieved data to some $provider property, i.e.
app.controller('someCtrl', function(){
app.config(function($someProvider){
$someProvider.property = response.data;
$someProvider.function(response.data.status);
})
})
I'm trying to set this within controller, but it does nothing. Any tips, guys? :)
if default injecting doesn't do the job
maybe store your provider at config time to some other variable and use this var in controller later
but angular says:
During application bootstrap, before Angular goes off creating all services, it configures and instantiates all providers. We call this
the configuration phase of the application life-cycle. During this
phase, services aren't accessible because they haven't been created
yet.
Once the configuration phase is over, interaction with providers is
disallowed and the process of creating services starts. We call this
part of the application life-cycle the run phase.
so i don't know if this will work
Newly added config blocks aren't executed after the ending of config phase, the same applies to other app methods because they result in additional config blocks internally.
It is possible with:
app.config(($provide, $someProvider) => {
$provide.value('$someProvider', $someProvider);
});
app.controller('someCtrl', ($someProvider) => {
$someProvider...
});
This voids the warranty and may indicate XY problem that should be solved the other way, depending on what $someProvider is and how it functions.
If $some is injected and used before setting $someProvider, this will result in race condition. If $some was changed at some point to not watch for $someProvider properties after instantiation, this will result in breaking changes without notice that will make the tests fail.
It is acceptable for badly designed third-party services that don't cause ill effects when being treated like that. And as any other hack, it should be thoroughly covered with tests.
Your question comes down to how can I postpone angular's bootstrap process until I have some async data.
For the async part, you can use (or do) whatever you want, it doesn't really matter. Fun fact: You can even use $http before bootstrapping angular just by getting it via angular.injector(["ng"]).get("$http");.
This being said, when you have all your async data, all that's left to do is to bootstrap angular. This can be achieved via angular.bootstrap - source.
Here's a working example in which I asynchronously attach a controller (remember, you can do whatever you want: attach various constants, config blocks, etc). I've used setTimeout for simplicity's sake.
// Initial Angular Code
angular.module('myApp', []);
// Async function. I've used setTimeout for simplicity's sake
setTimeout(function() {
angular
.module('myApp')
.controller('MyCtrl', ['$scope',
function($scope) {
$scope.value = 'Angular has started!';
}
]);
// Boostrap AngularJS
angular.bootstrap(document.getElementById('boostrap-me'), ['myApp']);
}, 1000);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<div id="boostrap-me" ng-controller="MyCtrl">
<div ng-bind="value">Angular hasn't yet bootstrapped as we're waiting for some async data!</div>
</div>

$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

Right way to tell all directives that data has already loaded

I have SPA and on the first page I load a big data object from the REST service.
The first page consists of the main part which resolved by controller, set of directives in the current scope which render some parts of received object and a header directive in the $rootscope which also render some part of received data.
I call API in the controller and when all data will be loaded I should notify about it all related directives for rendering loaded data.
Now I use $watch() and $watchGroup() for the same scope directives and $rootScope.$broadcast() for the header from the $rootscope.
Is there any more gracefully solution for it?
What is the best way to do this?
This sounds like a good use case for ngResource, which is an official Angular module for REST resources. I'd recommend you create a service for your resource like:
app.factory('Widget', function ($resource) {
return $resource('/api/v1/widgets/:id');
});
Then you can use it in your controller to handle the loading of your data.
app.controller('WidgetController', function ($scope, Widget) {
$scope.widgets = Widget.query({active: true});
});
In your views/templates/whatever, can bind right to widgets, which will be an array of widgets that match the query. The array will be empty while the widgets load, then it will be populated with the results -- and the binding will automatically update.
You can also bind to widgets.$resolved which (essentially) indicates whether the resource has finished loading or not.
Check out more about ngResource here.

Wait for Firebase to completely load in Angular Service

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.

Populating $scope using .getJSON()

I started playing with Angular.js recently, and got a demo project working pretty well. However, when I attempted to load the data from a backend web service versus just a hard coded array I started getting hung up. Specifically the page doesnt seem to properly data bind after i set the $scope using $.getJSON().done(...). Instead of just assigning a value to $scope after .getJSON is done, should I be doing it somewhere else/differently? I searched high and low and really couldnt find any good examples of angular thats pulling intial data from a backend.
Thanks in advance for any help with this!
Since you are trying to update the $scope outside of Angular you will have to make your model changes using the $apply method on the scope.
Maybe something like:
$.getJSON('ajax/test.json', function(data) {
$scope.$apply(function(){
$scope.modelData = data;
});
});
The preferred way to access a backend with AngularJS would be to use the $http or $resource service in place of jQuery. You won't have to use $scope.$apply you can just update your $scope.modelData directly.
This post has a good fiddle of updating a model outside of Angular code.
or instead of wrapping with apply, just call it at the end of the callback function like
$.getJSON('ajax/test.json', function(data) {
$scope.data = data;
$scope.$apply();
});

Resources