Search code examples
angularjsangular-bootstrap

Angular bootstrap modal controller scope


I'm trying to get modals woking with angular bootstrap. I can launch the modal just fine, but I'm having some scope issues dismissing the modal.

When I launch the modal, I can specify a controller that I can call functions from, which works, but it seems to be a copy of the controller without a $parent and without any controller-local variables.

I need access to the return value of $uibModal.open() in order to close the modal, so I'm trying to store it in var modalInstance, which works fine when I'm within the scope of the controller, but the copy of the controller passed into the $uibModal service doesn't have the local variable modalInstance set.

I can get around this by storing the return object in the $rootScope, but that seems like a bad idea. Am I wrong? What's the best way to get access to modalInstance from the click handler passed into the $uibModal service? Can I avoid using the $rootScope?

var app = angular.module('main', ['ui.bootstrap']);

app.controller('MainCtrl', function($scope, $rootScope, $uibModal) {
  var modalInstance;

  $scope.launch = function() {
    console.log('launch');
    modalInstance = $uibModal.open({
      template: '<div>Modal Content - <a ng-click="close()">Close</a></div>',
      controller: 'MainCtrl',
    });

    // Wouldn't need to do this if I could access modalInstance in close handler
    $rootScope.modalInstance = modalInstance;
  }

  $scope.close = function () {
    console.log('close');
    console.log(modalInstance);

    // Works, but should I be using $rootScope like this?
    //$rootScope.modalInstance.close();

    // Doesn't work, modalInstance is undefined
    modalInstance.close();
  }
});

Solution

  • Angular instantiates a new instance of a controller whenever it is used, and it is the same for modal. So when you specify controller: 'MainCtrl' you're telling angular you want to instantiate one of those for your modal, which is rarely what you want.

    Instead you should create a separate controller for the dialog, which can return values on closing using the $uibModalInstance service.

    var app = angular.module('main', ['ui.bootstrap']);
    
    app.controller('MainCtrl', function($scope, $rootScope, $uibModal) {
      var modalInstance;
    
      $scope.launch = function() {
        console.log('launch');
        modalInstance = $uibModal.open({
          template: '<div>Modal Content - <a ng-click="close()">Close</a></div>',
          controller: 'DialogCtrl',
        });
        ....
    
      }
    
    });
    
    app.controller('DialogCtrl', function($scope, $uibModalInstance) {
      $scope.theThingIWantToSave = [];
      $scope.cancel = function () {
        $uibModalInstance.close($scope.theThingIWantToSave);
      };
    });