user1128272
user1128272

Reputation:

Reusing angular events in controllers

I use the following event attached to the scope in a controller:

  $scope.$on('$locationChangeStart', function( event ) {
      var answer = confirm('Are you sure?.')
      if (!answer) {
          event.preventDefault();
      }

How do I make this event logic reusable so I can use it in multiple controllers? Is a service or factory the way to go? Is it even better to use a directive and attach it as elements, attributes or classes in the relevant views. I suspect the directive option is a mild violation of separation of concerns.

Upvotes: 0

Views: 107

Answers (2)

bhantol
bhantol

Reputation: 9616

This event being global event broadcasted before a URL change you only need to define the handler once.

How do I make this event logic reusable so I can use it in multiple controllers?

That means this does not need to be defined in multiple controllers.

Use module.run() for this. You may use $rootScope which seems appropriate for this kind of event you are dealing with.

Module.run() is executed once "per page" as your application gets initialized. If you later make your app SPA you are still golden.

If you follow TDD refer to the following link on ideas:-

Testing event chains and module.run() in Angular + Jasmine

Why not in Controllers ?

Strictly their job should be to make the Model available to the View.

Is a service or factory the way to go?

Service, Factory etc should be to just objects that help your controllers, directives. They don't have access to the DOM scopes.

Is it even better to use a directive and attach it as elements, attributes or classes in the relevant views. I suspect the directive option is a mild violation of separation of concerns.

Directives are local. They enhance HTML elements. Event like URL change is global. What possible directive can be created for the browser's location field ?

Upvotes: 0

meriadec
meriadec

Reputation: 2983

You can create a param in your route configuration. For my example, needConfirmation.

$routeProvider
  .when('/route1', {
    templateUrl: 'page1.html',
    controller: 'Page1Ctrl'
  })
  .when('/route2', {
    templateUrl: 'page2.html',
    controller: 'Page2Ctrl',
    needConfirmation: true
  })
  .otherwise({
    redirectTo: '/route1'
  });

Then in your run() phase :

$rootScope.$on('$routeChangeStart', function (event, next) {
  if (next.needConfirmation) {
    if (!confirm('Are you sure?')) {
      event.preventDefault();
    }
  }
});

Here is a fully working example :

angular.module('demo', ['ngRoute']);

angular.module('demo')
  .config(function ($routeProvider) {
  
    $routeProvider
      .when('/route1', {
        template: '<h1>this is the page 1</h1> <a href="#/route2">go to page 2</a>',
        controller: 'Page1Ctrl'
      })
      .when('/route2', {
        template: '<h1>this is the page 2</h1> <a href="#/route1">go to page 1</a>',
        controller: 'Page2Ctrl',
        needConfirmation: true
      })
      .otherwise({
        redirectTo: '/route1'
      });
  
  });

angular.module('demo')
  .run(function ($rootScope) {
    $rootScope.$on('$routeChangeStart', function (event, next) {
      if (next.needConfirmation) {
        if (!confirm('Are you sure?')) {
          event.preventDefault();
        }
      }
    });
  });

angular.module('demo')
  .controller('Page1Ctrl', function () {
  });

angular.module('demo')
  .controller('Page2Ctrl', function () {
  });
<script src="https://code.angularjs.org/1.3.5/angular.js"></script>
<script src="https://code.angularjs.org/1.3.5/angular-route.js"></script>

<div ng-app="demo">
  <div ng-view></div>  
</div>

Upvotes: 1

Related Questions