I am learning AngularJS. Say I have /view1 using My1Ctrl and /view2 using My2Ctrl that can be navigated to using tabs where each view has its own simple but different form. How would I make sure that the values entered in the form of view1 are not reset when a user leaves view1 then returns to view1? What I mean is, how can the second visit to view1 have the exact same state of the model as I left it.
I took a bit of time to work out what the best way of doing this is. I also wanted to keep the state, when the user left the page and then pressed the backbutton and not just put all my data into the rootscope
the end result is to have a service for each controller. in the controller, you just have functions and variables that you dont care if they are cleared.
The service for the controller is injected by dependency injection. As services are singletons, their data is not destroyed like the data in the controller.
in the service, i have a model. the model ONLY has data, no functions. that way it can be converted back and forth from json to persist it. I used the html5 localstorage to persist it.
lastly i used window.onbeforeunload and $rootScope.$broadcast('saveState'); to let all the services know that they should save their state and $rootScope.$broadcast('restoreState') to let them know to restore their state. ( used for when the user leaves the page and presses the back button to return to the page respectively)
example service called userService for my userController
app.factory('userService', ['$rootScope', function ($rootScope) {
var service = {
model: {
name: '',
email: ''
},
SaveState: function () {
sessionStorage.userService = angular.toJson(service.model);
},
RestoreState: function () {
service.model = angular.fromJson(sessionStorage.userService);
}
}
$rootScope.$on("savestate", service.SaveState);
$rootScope.$on("restorestate", service.RestoreState);
return service;
}]);
userController example
function userCtrl($scope, userService) {
$scope.user = userService;
}
the view then uses binding like this
<h1>{{user.model.name}}</h1>
and in the app module, within the run function i handle the broadcasting of the saveState and restoreState
$rootScope.$on("$routeChangeStart", function (event, next, current) {
if (sessionStorage.restorestate == "true") {
$rootScope.$broadcast('restorestate'); //let everything know we need to restore state
sessionStorage.restorestate = false;
}
});
//let everthing know that we need to save state now.
window.onbeforeunload = function (event) {
$rootScope.$broadcast('savestate');
};
as i mentioned this took a while to come to this point. It is a very clean way of doing it, but it is a fair bit o engineering to do something that i would suspect is a very common issue when developing in angular.
I would love to see easier, but as clean ways to handle keeping state across controllers, including when the user leaves and returns to the page.
$rootScope is a big global variable, which is fine for one-off things, or small apps. Use a service if you want to encapsulate your model and/or behavior (and possibly reuse it elsewhere). In addition to the google group post the OP mentioned, see also https://groups.google.com/d/topic/angular/eegk_lB6kVs/discussion.
A bit late for an answer but just updated fiddle with some best practice
var myApp = angular.module('myApp',[]);
myApp.factory('UserService', function() {
var userService = {};
userService.name = "HI Atul";
userService.ChangeName = function (value) {
userService.name = value;
};
return userService;
});
function MyCtrl($scope, UserService) {
$scope.name = UserService.name;
$scope.updatedname="";
$scope.changeName=function(data){
$scope.updateServiceName(data);
}
$scope.updateServiceName = function(name){
UserService.ChangeName(name);
$scope.name = UserService.name;
}
}
I had the same problem, This is what I did: I have a SPA with multiple views in the same page (without ajax), so this is the code of the module:
var app = angular.module('otisApp', ['chieffancypants.loadingBar', 'ngRoute']);
app.config(['$routeProvider', function($routeProvider){
$routeProvider.when('/:page', {
templateUrl: function(page){return page.page + '.html';},
controller:'otisCtrl'
})
.otherwise({redirectTo:'/otis'});
}]);
I have only one controller for all views, but, the problem is the same as the question, the controller always refresh data, in order to avoid this behavior I did what people suggest above and I created a service for that purpose, then pass it to the controller as follows:
app.factory('otisService', function($http){
var service = {
answers:[],
...
}
return service;
});
app.controller('otisCtrl', ['$scope', '$window', 'otisService', '$routeParams',
function($scope, $window, otisService, $routeParams){
$scope.message = "Hello from page: " + $routeParams.page;
$scope.update = function(answer){
otisService.answers.push(answers);
};
...
}]);
Now I can call the update function from any of my views, pass values and update my model, I haven't no needed to use html5 apis for persistence data (this is in my case, maybe in other cases would be necessary to use html5 apis like localstorage and other stuff).
Angular doesn't really provide what you are looking for out of the box. What i would do to accomplish what you're after is use the following add ons
These two will provide you with state based routing and sticky states, you can tab between states and all information will be saved as the scope "stays alive" so to speak.
Check the documentation on both as it's pretty straight forward, ui router extras also has a good demonstration of how sticky states works.