97

I want to pass the userName from a list of userNames a logged in user clicks on to twitter bootstrap modal. I am using grails with angularjs, where data is rendered via angularjs.

Configuration

My grails view page encouragement.gsp is

<div ng-controller="EncouragementController">
    <g:render template="encourage/encouragement_modal" />
    <tr ng-cloak
                  ng-repeat="user in result.users">
                   <td>{{user.userName}}</rd>
                   <td>
                      <a class="btn btn-primary span11" href="#encouragementModal" data-toggle="modal">
                            Encourage
                       </a>
                  </td>
                </tr>

My encourage/_encouragement_modal.gsp is

<div id="encouragementModal" class="modal hide fade">
  <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal"
      aria-hidden="true">&times;</button>
    <h3>Confirm encouragement?</h3>
  </div>
  <div class="modal-body">
      Do you really want to encourage <b>{{aModel.userName}}</b>?
  </div>
  <div class="modal-footer">
    <button class="btn btn-info"
      ng-click="encourage('${createLink(uri: '/encourage/')}',{{aModel.userName}})">
      Confirm
    </button>
    <button class="btn" data-dismiss="modal" aria-hidden="true">Never Mind</button>
  </div>
</div>

So, how can I pass userName to encouragementModal?

1
  • 1
    You should handle these things using angularjs. checkout ng-include.
    – zs2020
    Commented Sep 2, 2013 at 15:31

8 Answers 8

149

To pass the parameter you need to use resolve and inject the items in controller

$scope.Edit = function (Id) {
   var modalInstance = $modal.open({
      templateUrl: '/app/views/admin/addeditphone.html',
      controller: 'EditCtrl',
      resolve: {
         editId: function () {
           return Id;
         }
       }
    });
}

Now if you will use like this:

app.controller('EditCtrl', ['$scope', '$location'
       , function ($scope, $location, editId)

in this case editId will be undefined. You need to inject it, like this:

app.controller('EditCtrl', ['$scope', '$location', 'editId'
     , function ($scope, $location, editId)

Now it will work smooth, I face the same problem many time, once injected, everything start working!

6
  • As anything Angular, injection is the solution. Thanks! Commented Jul 2, 2015 at 14:21
  • this undefined error was doing my head in until i saw your answer! Works like a charm!
    – Dan
    Commented Aug 8, 2015 at 12:35
  • How do you do if instead of a simply var you want to pass some timeout object? the resolve won't fire untill the timeout has been resolved, which happens at the end of the timeout
    – DJ22T
    Commented Oct 9, 2015 at 20:09
  • The thing I was missing in my solution was to use $scope and $location between simple quotes.... I didn´t know those arguments had to be strings
    – rshimoda
    Commented Feb 15, 2016 at 23:29
  • 1
    Doing this I get "angular.js:10071 Error: [$injector:unpr] Unknown provider: editIdProvider <- editId". Any ideas?
    – Oskar Lund
    Commented Apr 29, 2016 at 15:02
57

The other one doesn't work. According to the docs this is the way you should do it.

angular.module('plunker', ['ui.bootstrap']);
var ModalDemoCtrl = function ($scope, $modal) {

    var modalInstance = $modal.open({
      templateUrl: 'myModalContent.html',
      controller: ModalInstanceCtrl,
      resolve: {
        test: function () {
          return 'test variable';
        }
      }
    });
};

var ModalInstanceCtrl = function ($scope, $modalInstance, test) {

  $scope.test = test;
};

See plunkr

3
  • 1
    You shouldn't wrap this like ['$scope', '$modalInstance', function() to inject dependencies?
    – joseramonc
    Commented Aug 12, 2014 at 2:34
  • 1
    Thanks for the comment, Jose, that was exactly my issue. If you're minifying your Angular code, yes, you have to use the array syntax for the ModalInstanceCtrl.
    – mcole
    Commented Aug 29, 2014 at 14:49
  • Jose, +1 It was my problem too. And I think of everyone using AngularUI! Commented Aug 18, 2015 at 9:38
51

Alternatively you can create a new scope and pass through params via the scope option

var scope = $rootScope.$new();
 scope.params = {editId: $scope.editId};
    $modal.open({
        scope: scope,
        templateUrl: 'template.html',
        controller: 'Controller',
    });

In your modal controller pass in $scope, you then do not need to pass in and itemsProvider or what ever resolve you named it

modalController = function($scope) {
    console.log($scope.params)
}
3
  • 2
    $rootScope.$new(); <=== this should be upvoted. very neat trick. thanks!
    – reflog
    Commented Aug 2, 2014 at 16:17
  • 3
    +1 Since this allows for passing data without having to define a controller for the modal. BTW you can just do $scope.$new(true) to create a new isolated scope without the need to inject $rootScope.
    – Santosh
    Commented Jul 29, 2015 at 15:24
  • @Mwayi: Thanks for this tip! The problem with classical injection (using resolve) is that the arguments are mandatory so every time you open a modal, you must resolve all arguments or the call to $modal.open() will fail because the controller wants its arguments. By using this neat scope trick, dependencies become optional.
    – stackular
    Commented Aug 12, 2015 at 12:18
24

You can also easily pass parameters to modal controller by added a new property with instance of modal and get it to modal controller. For example:

Following is my click event on which i want to open modal view.

 $scope.openMyModalView = function() {
            var modalInstance = $modal.open({
                    templateUrl: 'app/userDetailView.html',
                    controller: 'UserDetailCtrl as userDetail'
                });
                // add your parameter with modal instance
                modalInstance.userName = 'xyz';
        };

Modal Controller:

angular.module('myApp').controller('UserDetailCtrl', ['$modalInstance',
                function ($modalInstance) {
                    // get your parameter from modal instance
                    var currentUser = $modalInstance.userName;
                    // do your work...
                }]);
1
  • Exactly what I needed. Worked perfectly.
    – theFish
    Commented May 9, 2017 at 15:27
17

I tried as below.

I called ng-click to angularjs controller on Encourage button,

               <tr ng-cloak
                  ng-repeat="user in result.users">
                   <td>{{user.userName}}</rd>
                   <td>
                      <a class="btn btn-primary span11" ng-click="setUsername({{user.userName}})" href="#encouragementModal" data-toggle="modal">
                            Encourage
                       </a>
                  </td>
                </tr>

I set userName of encouragementModal from angularjs controller.

    /**
     * Encouragement controller for AngularJS
     * 
     * @param $scope
     * @param $http
     * @param encouragementService
     */
    function EncouragementController($scope, $http, encouragementService) {
      /**
       * set invoice number
       */
      $scope.setUsername = function (username) {
            $scope.userName = username;
      };
     }
    EncouragementController.$inject = [ '$scope', '$http', 'encouragementService' ];

I provided a place(userName) to get value from angularjs controller on encouragementModal.

<div id="encouragementModal" class="modal hide fade">
  <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal"
      aria-hidden="true">&times;</button>
    <h3>Confirm encouragement?</h3>
  </div>
  <div class="modal-body">
      Do you really want to encourage <b>{{userName}}</b>?
  </div>
  <div class="modal-footer">
    <button class="btn btn-info"
      ng-click="encourage('${createLink(uri: '/encourage/')}',{{userName}})">
      Confirm
    </button>
    <button class="btn" data-dismiss="modal" aria-hidden="true">Never Mind</button>
  </div>
</div>

It worked and I saluted myself.

1
  • 3
    I +1 and I salute you. If someone needs to pass the whole user to the modal rather than a single string for her name, in the controller, remember to use angular.copy(user).
    – youri
    Commented Oct 10, 2013 at 16:15
10

You should really use Angular UI for that needs. Check it out: Angular UI Dialog

In a nutshell, with Angular UI dialog, you can pass variable from a controller to the dialog controller using resolve. Here's your "from" controller:

var d = $dialog.dialog({
  backdrop: true,
  keyboard: true,
  backdropClick: true,
  templateUrl:  '<url_of_your_template>',
  controller: 'MyDialogCtrl',
  // Interesting stuff here.
  resolve: {
    username: 'foo'
  }
});

d.open();

And in your dialog controller:

angular.module('mymodule')
  .controller('MyDialogCtrl', function ($scope, username) {
  // Here, username is 'foo'
  $scope.username = username;
}

EDIT: Since the new version of the ui-dialog, the resolve entry becomes:

resolve: { username: function () { return 'foo'; } }

4
  • Hi, I didn't try this way. But +1 for a suggestion. Commented Sep 3, 2013 at 10:39
  • 3
    actually that should be resolve: function(){return {username: 'foo'}}
    – SET001
    Commented Oct 13, 2013 at 12:28
  • SETs answer won't work. bicycle has it right below, it should be- resolve: {data:function(){return 'foo';}}
    – bornytm
    Commented Feb 11, 2014 at 23:47
  • 1
    @bornytm Nowadays you're right. But when my answer was written, the syntax was: resolve: { username: 'foo'} Commented Feb 12, 2014 at 7:51
4

You can simply create a controller funciton and pass your parameters with the $scope object.

$scope.Edit = function (modalParam) {
var modalInstance = $modal.open({
      templateUrl: '/app/views/admin/addeditphone.html',
      controller: function($scope) {
        $scope.modalParam = modalParam;
      }
    });
}
1

If you're not using AngularJS UI Bootstrap, here's how I did it.

I created a directive that will hold that entire element of your modal, and recompile the element to inject your scope into it.

angular.module('yourApp', []).
directive('myModal',
       ['$rootScope','$log','$compile',
function($rootScope,  $log,  $compile) {
    var _scope = null;
    var _element = null;
    var _onModalShow = function(event) {
        _element.after($compile(event.target)(_scope));
    };

    return {
        link: function(scope, element, attributes) {
            _scope = scope;
            _element = element;
            $(element).on('show.bs.modal',_onModalShow);
        }
    };
}]);

I'm assuming your modal template is inside the scope of your controller, then add directive my-modal to your template. If you saved the clicked user to $scope.aModel, the original template will now work.

Note: The entire scope is now visible to your modal so you can also access $scope.users in it.

<div my-modal id="encouragementModal" class="modal hide fade">
  <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal"
      aria-hidden="true">&times;</button>
    <h3>Confirm encouragement?</h3>
  </div>
  <div class="modal-body">
      Do you really want to encourage <b>{{aModel.userName}}</b>?
  </div>
  <div class="modal-footer">
    <button class="btn btn-info"
      ng-click="encourage('${createLink(uri: '/encourage/')}',{{aModel.userName}})">
      Confirm
    </button>
    <button class="btn" data-dismiss="modal" aria-hidden="true">Never Mind</button>
  </div>
</div>

Not the answer you're looking for? Browse other questions tagged or ask your own question.