title: AngularJS: Refactoring a confirmation modal directive
body: <p>I need some advice on refactoring a modal directive I have. I am just getting started with directives, so any other approach to my problem is welcome. </p>
  
  <p>My program needs a confirmation modal, where we can confirm or cancel the desired action. It will appear in many places and needs to be able to have a programmable button. Cancel is consistent in that it will only hide the modal, the confirmation button needs to perform whatever action required. </p>
  
  <p>I am currently using <code>$rootScope</code> to show / hide / configure the modal. Is this a bad idea? Please tell me.</p>
  
  <p>This is what I am working with right now (roughly, as I have cut out a lot of the other unnecessary code):</p>
  
  <p><strong>index.html</strong></p>
  
  <pre><code>&lt;!doctype html&gt;
  &lt;html lang="en"&gt;
      &lt;head&gt;
          &lt;title&gt;My App&lt;/title&gt;
      &lt;/head&gt;
      &lt;body ng-controller="MenuCtrl"&gt;
  
          &lt;confirmmodal ng-show="$root.confirmModal.isVisible"&gt;&lt;/confirmmodal&gt;
  
          &lt;ul&gt;
              &lt;li&gt;Home&lt;/li&gt;
              &lt;li&gt;About&lt;/li&gt;
              &lt;li&gt;Contact&lt;/li&gt;
          &lt;/ul&gt;
  
          &lt;div ng-view&gt;&lt;/div&gt;
  
          &lt;!-- build:js scripts/main.js --&gt;
          &lt;script data-main="scripts/main" src="lib/requirejs/require.js"&gt;&lt;/script&gt;
          &lt;!-- endbuild --&gt;
      &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
  <p>So my modal sits atop the <code>ng-view</code> and can be called from anywhere. It is inside a pseudo global controller, called <code>MenuCtrl</code>.</p>
  
  <p>Here is the modal directive code:</p>
  
  <p><strong>directives.js</strong></p>
  
  <pre><code>/* Confirm Modal */
  .directive('confirmmodal', [function() {
      return {
          restrict: 'E',
          templateUrl: 'view/templates/modal-confirm.tpl.html'
      };
  }])
  </code></pre>
  
  <p>It serves as a template for the following code:</p>
  
  <p><strong>modal-confirm.tpl.html</strong></p>
  
  <pre><code>&lt;!-- Confirm Modal Template --&gt;
  &lt;div class="overlay"&gt;
      &lt;div class="overlay-content extended"&gt;
          &lt;span&gt;{{$root.confirmModal.content}}&lt;/span&gt;
          &lt;div class="buttons"&gt;
              &lt;button class="btn btn-default" ng-click="$root.confirmModal.secondary.action()"&gt;{{$root.confirmModal.secondary.content}}&lt;/button&gt;
              &lt;button class="btn btn-primary" ng-click="$root.confirmModal.primary.action()"&gt;{{$root.confirmModal.primary.content}}&lt;/button&gt;
          &lt;/div&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p>I set a bunch of defaults in the <code>app.run</code> function:</p>
  
  <p><strong>app.js</strong></p>
  
  <pre><code>app.run(['$rootScope', function ($rootScope) {
      _.extend($rootScope, {
          confirmModal: {
              isVisible: false,
              content: '',
              primary: {
                  action: function() {
                      console.log('hello world');
                  },
                  content: 'Submit'
              },
              secondary: {
                  action: function() {
                      $rootScope.confirmModal.isVisible = false;
                  },
                  content: 'Cancel'
              }
          }
      });
  }]);
  </code></pre>
  
  <p>So I've also coded a modal trigger directive, the idea being that I can create different triggers that perform different actions with the modal.</p>
  
  <p><strong>directives.js</strong></p>
  
  <pre><code>/* Resolve Event */
  .directive('resolveevent', ['RequestService', '$location', function (RequestService, $location) {
      return {
          restrict: 'A',
          scope: {
              eventtype: '@',
              eventid: '@',
              index: '@'
          },
          controller: ['$scope', function($scope) {
  
              $scope.remove = function(id) {
                  // remove the event from the events array
                  $scope.$parent.$parent.$parent.$parent.events.splice(id, 1);
              },
  
              $scope.config = function(config) {
                  _.extend($scope.$root.confirmModal, config);
              },
  
              $scope.isVisible = function() {
                  $scope.$apply(function() {
                      $scope.$root.confirmModal.isVisible = true;
                  });
              }
          }],
          link: function( $scope, element, attrs ) {
              var config = {
                  content: 'Are you sure you wish to resolve this event?',
                  primary: {
                      action: function() {
                          var config = {
                              url: '/Events/' + $scope.eventid,
                              method: 'PUT',
                              data: {
                                  event_status: 'resolved'
                              },
                              cache: false
                          }
  
                          /* Update event with resolved status */
                          RequestService.makeApiRequest(config).success(function(response) {
                              $scope.$root.confirmModal.isVisible = false;
                              $scope.remove($scope.index);
                          });
                      },
                      content: 'Resolve Event'
                  }
              }
  
              element.on('click', function() {
                  if (!$scope.$root.confirmModal.isVisible) {
                      $scope.config(config);
                      $scope.isVisible();
                  }
              });
          }
      }
  }]);
  </code></pre>
  
  <p>And then I use a button on the view where my <code>ng-repeat</code> is found which is able to trigger the modal:</p>
  
  <p><strong>eventlist.html</strong></p>
  
  <pre><code>&lt;li ng-repeat="event in events"&gt;
  
      &lt;p&gt;Event: {{ event.number }}&lt;/p&gt;
      &lt;p&gt;Group: {{ event.group_name }}&lt;/p&gt;
      &lt;p&gt;Record Date: {{ event.event_date | moment: 'MM/DD/YYYY h:mm A' }}&lt;/p&gt;
  
      &lt;button resolveevent index="{{$index}}" eventid="{{ event.number }}" class="btn btn-default"&gt;Resolve&lt;/button&gt;
  &lt;/li&gt;
  </code></pre>
  
  <p>This is what I've got, and it is working, however it seems like overkill, inefficient, and a nightmare to maintain. Can anyone chime in on a way to improve this? I appreciate any help, thanks in advance.</p>