Initialize Angular $scope on initial load - javascript

I would like to init the user session with some data. On loading the main outer controller (I have a "global" controller that is run for the entire app and individual controllers for each state/route) I currently check if the local session data is empty and, if so, fetch from server and return a promise.
The issue that I have is that I use directives which need this session data. Currently they run before the scope is populated. I need a solution that does not involve modifying all the directives to use promises/callbacks/etc.
On researching this I found many requests but no viable solutions. I am using UI router and looked at the grandfather state idea but that just seems to act as a master resolve that still needs to be addressed in each controller.
I have also looked at manually bootstrapping but I need to populate the scope and I don't see how to do that from angular.element.
I should add that this needs to a pause the Angular process until loading. I see suggestions for global configuration but not sure how do pause besides deferring bootstrapping.
Suggestions?

Can you do your work in main module's .run method. and set whatever you want at $rootScope.
That way it will be visible everywhere.
Angular Modules

You can use value receipe, ngtutorial.com/learn/value

You can write your provider which will perform you some functionality.
You can take a look here - Documentation for providers how to do it. From the link you can see:
You should use the Provider recipe only when you want to expose an API for application-wide configuration that must be made before the application starts.
By the way, if you want to access scope from angular.element = you can use:
angular.element(<yourElement>).scope()

Related

Passing $scope to angularJs service/factory a bad idea?

I am working on a project where in there are almost 90+ modules.
All modules has a set of input fields and on submit the data should be saved on the server.
At any given point in time only one module is active. But there can be open modules in the background.
Submit button is common to all modules, meaning there is only one Submit button throughout the application.
Below picture explains it more.
The prime motto is to keep the individual module changes to minimum and a way to handle certain things(validation, reload etc) in the module from a central place.
The current approach I am planning is,
Use a 'moduleInit' directive that all module should include in its
partial.
The directive takes the $scope of the module and pass it to a
common service/factory (pushConfigService)
The pushConfigService stores and keep this scope as long as the
module is open. Once the scope is destroyed the reference of the
same will be removed from the pushConfigService.
The footer panel is another directive with Submit button in it and
calls a save function in the pushConfigService which in turn calls
a $scope function in the module to get the form data.
pushConfigService talks to a bunch of other services like
dirtyChecker, apiGenerator and finally post data to the server.
Each module will have a set of scope methods defined with some standard names. Eg: _submit, _onSubmit, _cancel, _reload etc.
Another way to handle this, broadcast the submit event and each module listens to the same. There is possibility more actions will be added to the footer panel.
So I am little bit hesitant to use the broadcast approach.
My question, Is it a good idea to pass controller scope to a service? Any alternate suggestions?
Thanks in advance.
I believe your core concept is a nice way to handle this setup. Yet I'd suggest to split business logic from UI. I don't have a sample of your code so it is a little hard to build an exact example. Yet since you're using the $scope variable I'm going to assume you're not using a styleguide like or similar to John Papa's. His ways encourage you to not use the $scope and to stay close to actual JavaScript "classes".
How does this make a difference?
Instead of passing the whole scope, you'd be able to just pass the instance of your specific module. For one it is less confusing to you and colleagues to have a concrete interface to operate on instead of having to figure out the composition of given scope. In addition it prevents services from being able to alter the $scope.
The latter could be considered a good practice. Having just the controllers alter the scope make it easy to find the code which alters and manages the UI. From there on the controller could access services to do the actual logic.
Taking it one step further
So passing the class instance instead of scope should be an easy adjustment to the already proposed setup. But please consider the following setup as well.
It seems there are quite some different ways to handle and process the data provided by the module/end user. This logic is now implemented in the controller. One might think some of these modules share similar handling methods (big assumption there). You could move this logic to, so to speak, saving strategies, in services. On activation of a module, this module will set its preferred saving strategy in the service which handles the submit button click. Or more precisely, the save data method which should be called from the onClick handler in the controller.
Now these services/strategies might be shared among controllers, potentially setting up for a better workflow and less duplicated code.

Angular initial data load, defer directives until after initialise

I need to load some data into my angular app the first time it is loaded. The data needs to come from the server.
My app currently consists of a number of directives on a single view in an accordion. The directives are forms whose criteria needs to be met before the user can progress to the next accordion group. The first accordion group needs to use the data from the server to display to the user, there is a dependency on the data for the view.
What is the best way to inject this data in the first instance?
I could move the initial data call into the first directive but it doesn't really feel like it is 'its' job. I could create another directive to handle this load but then I would need manage the load order of the directives. I have had a look at ng-init which works well until the server call is made, during this call the directives run before it completes.
Any of the above would work for me in theory but i'm not sure that they are the best approach. What would you suggest is the best approach?
Thanks
You should use a resolve in your state, so, both views and controllers are called when the resolution is done.
have a look at this link: http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx
You can decorate your directives with ng-if attribute and, as value, pass a scope property that will indicate data load ex.

Why would you ever use the $rootScope?

Ok so I was reading here
basically when I have this
MyApp = MyApp || {};
MyApp.settings = {
isFooEnabled: false
}
if I use the rootscope and want to check if isFooEnabled I have to inject the rootScope into whatever object I want to do the check.
How does that make sense?
What is the advantage of using $rootScope.isFooEnabled over using straight standard javascript MyApp.isFooEnabled?
what is better for what?
when should I use one over the other?
The $rootScope is the top-most scope. An app can have only one $rootScope which will be shared among all the components of an app. Hence it acts like a global variable. All other $scopes are children of the $rootScope.
The rootScope's variable is set when the module initializes, and then each of the inherited scope's get their own copy which can be set independently.
NOTE:
When you use ng-model with $rootScope objects then AngularJS updates those objects under a specific $scope of a controller but not
at global level $rootScope.
The $rootScope shouldn't be used to share variables when we have things like services and factories.
Finally, Angular FAQ says this at the bottom of the page: "Conversely, don't create a service whose only purpose in life is to store and return bits of data." See from here.
Actually, I would argue that you shouldn't use $rootScope in this case, you should create a separate service (or factory) that stores your settings, however, the usage and reasons are the same.
For simply storing values, the primary reason is consistency. Modules and dependency injection are a big part of angular to ensure you write testable code, and these all use dependency injection so that unit tests can be written easily (dependencies can be mocked). Whilst there are not many obvious gains from injecting a simple object, it's consistent with the way more complex code is accessed, and there is a lot to be said for that. On a similar note, if you were to upgrade your settings object to fetch data from the server (e.g. for environment specific settings), you might want to start unit testing that functionality, which you can't really do properly without modularising it.
There is also the (frankly weak) namespacing argument - what if another library you import uses window.MyApp?
TL;DR: It's a strongly recommended best-practice. It might seem a bit clunky now, but you'll benefit from doing it in the long run.

Angular - Lazy loading an entire page from CMS

I'm attempting to build an angular app that is driven by the CaaS/CMS known as Prismic.io.
Phase one of this project will be a straight forward content-silo (CMS-managed), and phase two will add on more complex web app components. Knowing this, I've decided that Angular would be my best bet, but I'm struggling to think of a good solution to have all of the content lazy-loaded from the Prismic API.
One solution I've decided to explore would be to have a standardized $scope variable, let's call it $scope.loaded. Each controller will do what it must to query my Prismic service for its respective content, and once it's completed, it would set $scope.loaded = true.
The part I'm stuck on with this approach is how exactly to display the page while all of these components are loading. The easiest way would be to include ng-if directives that reference this loaded value, but I feel like there'd be a massive flash of unstyled content. And yes I could use spinners, but the idea of having 90% of the page covered in spinners seems chintzy.
Then I got to wondering: what if I pull up a loading screen for the app until all controllers' $scope.loaded values are truthy? In that case, how would I know which controllers are currently active on the page and reference their respective scopes?
(If you have comments about why this approach is bad, I'd love to hear them as replies rather than answers. I imagine this could create too many http request, for example).
A couple of options here:
Have you looked at ngCloak to see if will help you here with the flicker problem? https://docs.angularjs.org/api/ng/directive/ngCloak.
If you're using jquery, you could have a global spinner that works on concurrent ajax requests http://api.jquery.com/category/ajax/global-ajax-event-handlers/.
Or have a look at something like this global angular spinner https://github.com/monterail/angular-global-spinner/tree/master/src.
If none of these work, you could always create an array on the root scope where each controller/directive registers itself and sets its loading flag. Then add a watch to that variable to see when all components in that array are finished loading.

Deferring angular controllers until Ajax call is completed

First of all, I have googled and searched on SO for this question. None of the answers I've seen are really satisfactory for me. I am aware of resolve & manually bootstrapping angular and neither are great solutions for us.
Essentially our app is constructed such that at the top level, we have a
<body ng-controller="applicationController">
that instantiates a bunch of stuff. Part of that is getting some data from our server and setting it so that children controllers have access to it. So the issue is sometimes the children controllers are executed before the server has responded.
We use angular UI's ui-router so we can take advantage of states. However resolve hasn't been a great solution for us because we have a bunch of routes and a user can enter the app from any of them. So far my only solutions are to create a very high level parent state and putting a resolve on that, or putting a resolve a multitude of states. Is there a way to make the ajax call before the angular controllers are run?
I upvoted both answers here because, in my opinion, the most elegant solution is a combination of both (without redirecting back and forth). The core of this problem is the fact that angular only allows synchronised dependency injection on controller arguments via the route provider. We can however wrap all our initialisation in a single service and let our main controller to be dependent on this.
See this plunker for a live demo.
What about ng-include (http://docs.angularjs.org/api/ng.directive:ngInclude)? Remove all the subcontroller stuff from your html file and put it in a template file. Now you may load some data from your server and if they all have arrived set the ng-include src.
With this solution you also have the possibility showing another template file during the loading phase.
If you don't want your content in another file you may consider writing a directive that can handle inline templates:
<script type="text/ng-template" id="my-awesome-content.html">
...
</script>
If you want to delay the instantiation of your controller, you need at some point to take advantage of promises and $routeProvider.
Misko Hevery (the creator of AngularJS) answered such question here: Delaying AngularJS route change until model loaded to prevent flicker
He updated the "AngularJS getting started tutorial" to conform to that pattern; it is worth reading it.
I run into the same question a few months ago; I architected my app to follow that principle.
Basically set a url/entry point in your app that resolves the instantiation of your controllers.
If your app is accessible from other locations, redirect the call to that entry point/url; after the init stuff has been resolved, redirect to the requested url.

Categories

Resources