ui-router reinitializing angular controllers on state change - angularjs

Is there an easy way using ui-router to force all my controllers to only be initialized once? Like a singleton. As of right now, every time I change to a state, the controller linked to that state is reinitialized. I don't want this to happen. This seems like it should be simple, but I could not find a solution to this anywhere online.

A common way to handle things like one-time or global operations is to have an application level controller on an element that wraps all your ui-views:
<html ng-app="app">
<head>...</head>
<body ng-controller="ApplicationController">
<div ui-view></div>
</body>
</html>
ApplicationController can then reponsible for one-time operations. It gets initialised once (when the app starts) and will persist between route and state changes.
The controllers associated with your states should only be concerned with constructing their own views, not performing one-time operations. If the state controllers need to access shared data, then that data should be stored in a service as suggested by ABOS. State controllers should request that shared data from the service, and the service should be the one deciding if it should return cached data, or make a fresh service call.

Related

"Synchronise" loading of components in a page

I have an Ionic 1.3 mobile application in which I am using Angular 1.5. Each page is comprised of one or more Angular components and each one of these components may in turn contain other components, thus creating a component tree within the page.
The problem I have is that some of these components might require data from the server, which are loaded in the component's $onInit function. Since this data is retrieved asynchronously (and there might be delays) the result is for the components to appear one after the other in the page (depending on when each one manages to initialise) which doesn't make for a very "native" mobile experience. What I would like to do is find a way to know when all components have finished loading so that I can display the page only after it is really ready (all components are fully rendered).
My original idea was to "register" each component with a service when its controller initialises and then notify the service that the component is ready once the data is loaded. The service can then be responsible for showing the page when all components are ready. My problem with doing this is that components seem to initialise sequentially (ie. component1's controller initialises and then $onInit executes, then component2's controller initialises and $onInit executes, etc.) which means that my service can't really know how many components it is waiting for. Not only that, but also a component's children controllers initialise and their $onInit methods execute after the parent component has finished initialising, which means that the component also can't keep track of whether its children have initialised or not.
An alternative would be to hard-code the number of components somewhere in the page and pass it to the service through the page controller so that it knows how many components to wait for without the components needing to register themselves, but ideally I would like something a bit more maintainable (ie. something that doesn't require me to keep track of components and keep this number up to date myself).
Can anyone think of any other way I can keep track of when my page is ready using either Angular or Ionic? Ideally it would be something that can also be ported to Angular2 / Ionic2 since I expect the app to get upgraded at some point, but any suggestion is welcome.
You can try to use resolve in ui.router or ngRoute:
angular.module('app', ['ui.router'])
.config(['$stateProvider', function($stateProvider) {
$stateProvider.state('mainstate', {
url: 'your-url'
,templateUrl: 'yout_template.html'
,controller: 'YourController'
,resolve: {
yourFirstData: ['serviceDeps', function(serviceDeps){
return serviceDeps.getFirstData();
}]
,yourSecondData: ['serviceDeps', 'yourFistData', function(serviceDeps, yourSecondData){
return serviceDeps.getSecondData(yourFirstData);
}
}
}
}]);
The resolve will get the data and resolve promises before loading controller.
This is an ideia.

Persisting controller data while switching views in angular

I'm using angular with UI-router and whenever I switch to a different view and then return back to the original, the controller is re instantiated.
I know that I can save the state via a service and to repopulate it afterwards, but is there some sort of option in ui-router that allows me to not reinitialize the controller?
If you have one controller for multiple views then you can uses the nested state feature of ui-router to keep track of states. See the documentation here

Initialization webservice in angularJS for all routes

I need to get some information from a webservice before anything is loaded, for all routes. In fact, that webservice is in charge of getting the proper "theme", so it must be known before anything loads.
Until now i solved this kind of issues with the "resolve" property of each route. I don't know if this would be enough tough.
Anyway, adding that resolve condition manually to all routes doesn't sound good to me. Is there a ellegant way to do this?
You could have an ng-init in your AppCtrl that you load in your index.html (where you have your main ng-view). Consequently, you could store the state/result in the rootScope or in another way in which it is accesible to all your controllers.
<div ng-app="myApp" ng-controller="AppCtrl" ng-init="callWebservice()">
<div ng-view></div>
</div>

AngularJS - Multiple controllers watching route changes

From what I've read, Angular doesn't support multiple views out of the box for URL changes.
What I really want is to have a set of controllers in charge of different parts of the application UI, that each respond in their own way to route changes.
Is there a common solution for this, or am I thinking about the application structure in the wrong way?
The ui-router plugin doesn't appear (to me) to solve this particular problem in the way I'd like - it's a state-first approach with optional URL changes, as opposed to URL-first.
Angular actually does support multiple views out of the box, what it doesn't support is multiple ng-view out of the box. You can use ng-include and place a controller on that element and watch for any route changes you need.
Essentially you'd do something like this:
<ng-include src='"menu.html"' ng-controller='MenuCtrl'></ng-include>
<div ng-view></div>
The ng-include's controller you would be watching for route changing and doing whatever is needed.
The ng-view of course is driven by the route changes setup in your app config.

How do I share data across controllers in AngularJS?

I am doing a project that uses AngularJS. I have created partial pages where each page has its own, different controller.
Site Structure
/ProjectRoot
/css
/js
angular.js
jquery.js
/partials
login.html
page1.html
index.html
index.html
<!DOCTYPE html>
<html ng-app="demoapp" >
<head>
scripts
</head>
<body ng-controller="maincontroller">
<ng-view></ng-view>
</body>
</html>
login.html
<div class="main-page" ng-controller="logincontroller">
--code---
</div>
So the full login page is rendered as:
<!DOCTYPE html>
<html ng-app="demoapp" >
<head>
scripts
</head>
<body ng-controller="maincontroller">
<div class="main-page" ng-controller="logincontroller">
--code---
</div>
</body>
</html>
I need to share data between the controllers. I tried using $scope.$parent.variablename but when I refresh page1.html it contains a blank value.
My questions are:
How do I create global variables which I can use throughout the project, that is with all partial pages and their controllers (perhaps $rootScope)?
Is the use of a cache is better option or not? (security concern)
To share state between controllers you should use an Angular service. Services are easy to define and use (see example below) and since they are singletons they are a great place to store shared state information. No need for caches or anything complex.
But then you also said:
I tried using $scope.$parent.variablename but when I refresh
page1.html it contains a blank value.
Refreshing an Angular app is always going to be a problem.
This is because your typical Angular app is a Single Page Application (SPA). Your state information persists only as long as the lifetime of the app, which for an SPA is the lifetime of the page. Thus, if you refresh the page then you restart the application and all your state information is lost.
To solve this problem you can use sessionStorage. This technology is well supported across browsers and allows you to persist your state data between page refreshes. This is especially important in Angular apps because they should always gracefully support the user refreshing the page (which includes clicking on the back and forward buttons).
You can combine all this into a simple service that persists its data into sessionStorage. This gives you state data that is shared between controllers and persisted between page refreshes.
Here is an example (in coffeescript) that stores a state datum called userToken:
Service
angular
.module 'app'
.service 'storageService', StorageService
class StorageService
setStorage:(key, value)->
json = if value is undefined then null else JSON.stringify value
sessionStorage.setItem key, json
getStorage:(key)->
JSON.parse sessionStorage.getItem key
userToken:(value=null)->
if value is null
#getStorage 'userToken'
else
#setStorage 'userToken', value
You can add as many data values to this service as you like, you can even store complex objects
Once you have defined a service you can use Angular's dependency injection to easily get hold of the service instance. Here are a couple of example controllers that show the storageService being injected and used:
Controller
angular
.module 'app'
.controller 'logincontroller', ($scope, storageService)->
$scope.setUserToken = (token)-> storageService.userToken(token)
.controller 'maincontroller', ($scope, storageService)->
$scope.userToken = storageService.userToken()

Resources