3

I have a simple multi-view Angular application that implements a wizard, so each time a user clicks "Next" button, the app navigates to the next view, and same for "Back" button. I have a $routeProvider config that binds all views to the same controller. Each view represents a chunk of a huge form with input fields, and $routeProvider manages navigation across them:

app.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
    $routeProvider.when('/salespoints', {
        templateUrl: 'salespoints',
        controller: 'main'
    })
    .when('/users', {
        templateUrl: 'users',
        controller: 'main'
    })
    .otherwise({
        templateUrl: 'partner',
        controller: 'main'
    });

    $locationProvider.hashPrefix('');
}]);

The problem is that each time a user clicks "Next" or "Back", the controller is invoked, so all the $scope changes that were made on previous steps are simply lost:

app.controller('main', ['$scope', function ($scope) {
    console.log('controller invoked'); // appears each time a user presses "Next" or "Back", hence re-instantiation of $scope
}]);

The application is small enough to turn to $rootScope in case every other method fails, but I just would like to avoid that as an anti-pattern.

What's the best way to keep $scope in latest state, with all changes made from the very beginning of app instance's lifecycle, and without re-instantiating it on every change of view?

rishat
  • 8,206
  • 4
  • 44
  • 69

3 Answers3

5

Use a service to keep your form data and inject it into your controller.

angular.module('app').factory('formService', function () {
   var data = {};
   return {
      data: data
   };
});

angular.module('app').controller('ctrl', function(formService) {
   this.formData = formService.data;
});
Joao Leal
  • 5,533
  • 1
  • 13
  • 23
  • In this particular case, how should I catch the view switch event, if not explicitly listening to it? And if it's the only way, `ng-route` is of no use, and the navigation logic becomes too complicated. – rishat Jul 01 '15 at 12:55
  • You leave your routes as is, you just change the controller to pull the `formData` values from the service instead. – Joao Leal Jul 01 '15 at 13:11
1

Angular controllers are instanciated each time you have a new ng-controller in your template.

Angular services are instanciated only once, at the first time you need it, because they are singleton.

So angular services should be use to store data, especially data shared between multiple controllers instances (even if it's the same controller definition as in your example).

antoinestv
  • 3,286
  • 2
  • 23
  • 39
1

So, according to the advice of @joao-leal, I moved the data persistence to a new service and subscribed to $scope updates via $scope.$watch. The overall solution resulted into a quite simple code chunk:

app.factory('formPersistence', function () {
    var data = {};

    var get = function () {
        return data;
    };

    var set = function (updated) {
        data = updated;
    }

    var reset = function () {
        data = {};
    }

    return {
        get: get,
        set: set,
        reset: reset
    };
});

app.controller('main', ['$scope', 'formPersistence', function ($scope, formPersistence) {
    $scope.data = formPersistence.get();

    $scope.$watch('data', function () {                
        formPersistence.set($scope.data);
    });
}]);
Community
  • 1
  • 1
rishat
  • 8,206
  • 4
  • 44
  • 69