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.
Related
I'm upgrading my applications Angular UI-Router to 1.0. The migration guide states
We no longer process resolve blocks that are declared inside a views
Fair enough. All resolve blocks should be put in the parent state however there is a limitation to this, which I'll explain.
My project currently uses named views and resolves data inside those views. This data gets passed to the controller. The issue that I'm having with upgrading to 1.0 is that some of my states re-use the same view and controller. The difference between the views is the data I pass to them via the views resolve block.
Here is an overly simplified example of what my application currently does
https://embed.plnkr.co/SFCzutU7N0AGsxpk9c6r/
As you can see, if was to move the resove block to the parent state, I would no longer be able to customise the data passed to each controller instance.
What is an alternative or workaround for this breaking change?
No answers :( Perhaps the question wasn't clear. Anyway, this is the solution I came up with. Not that happy with it but it works.
https://embed.plnkr.co/1YxFvHGjowYMvUhXmVsj/
I created a directive that I use on the ui-view element.
<div ui-view="blue-view" ui-view-param="blueView"></div>
This adds the given value to the parent scope.
I can then use this value to get the correct configuration.
$scope.color = viewParams[$scope.uiViewParam].color;
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.
i'm very confuse about how ember controller works.
I'm starting with ember and ember-cli now, and i would like to understand more about how controller works.
If i have an nested route called new, inside a events resource, i should i have:
models/event
routes/events/new
templates/events/new
What about controllers?? I just work one simple controller, or should i use controllers/events/new too?
There isn't any generator command that will create every resource for me? I need call one by one?
Thanks.
What about controllers?? I just work one simple controller, or should i use controllers/events/new too?
This mainly depends on what is your controller needs to do. If it's only the essential stuff the controller does anyways, Ember will create that controller under the hood for you and automatically bubble actions up to its parent controller.
No better place than Ember guides to read what a controller is used for:
The simplest definition is:
Controllers allow you to decorate your models with display logic.
This means that you basically use them as the main communication layer between your route and your template. Essentially, you model comes from your route, through your controller and into your template. Actions happening in the template go up to the controller and then to the route. Therefore, controller is essentially the middle layer where you user your model (and other data) to control what is shown to the user, control what a user can do, control where can they navigate etc.
However, be aware of the plan for the future:
Controllers are very much like components, so much so that in future versions of Ember, controllers will be replaced entirely with components. At the moment, components cannot be routed to, but when this changes, it will be recommended to replace all controllers with components.
This means, that right now, controller responsibility is limited to two things:
Maintaining application state based on the current route
Handling or bubbling user actions that pass through the controller layer when moving from a component to a route.
All actions triggered on a template are first looked up on the controller, if it is not handled or bubbled (by return true) in the controller, they are looked up on the route.
Therefore, controllers for your /events or events/new routes aren't necessary at all, only if you want to handle things happening on those routes right away (in a smaller scope) instead of allowing everything to bubble up to the ApplicationController.
There isn't any generator command that will create every resource for me? I need call one by one?
Yes. Since, if you don't specifically create a controller, Ember just generates one for you behind the scenes. You need to specify where you want to handle things yourself.
You should visit the link I gave above (or here it is again) to the Ember guides that have many more examples in much more detail.
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.
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()