53

plunker: http://plnkr.co/edit/wURNg8ByPYbEuQSL4xwg

example.js:

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

  $scope.open = function () {
    var modalInstance = $modal.open({
      templateUrl: 'modal.html',
      controller: 'ModalInstanceCtrl'
    });
  };
};

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

  $scope.ok = function () {
    alert($scope.text);
  };

  $scope.cancel = function () {
    $modalInstance.dismiss('cancel');
  };
};

index.html:

<!doctype html>
<html ng-app="plunker">
  <head>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js"></script>
    <script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.6.0.js"></script>
    <script src="example.js"></script>
    <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
  </head>
  <body>

  <div ng-controller="ModalDemoCtrl">
    <button class="btn" ng-click="open()">Open me!</button>
    <div ng-show="selected">Selection from a modal: {{ selected }}</div>
  </div>
 </body>
</html>

modal.html:

<div class="modal-header">
    <h3>I'm a modal!</h3>
</div>
<textarea ng-model="text"></textarea>
<div class="modal-footer">
    <button class="btn btn-primary" ng-click="ok()">OK</button>
    <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>

Why I can't get the $scope.text defined in ModalInstanceCtrl, even though I can use $scope.ok and $scope.cancel?

4
  • That's because you are alerting $scope.text before assignment. Just write $scope.text = "abc"; in your controller and you ll see that in the alert. Commented Sep 10, 2013 at 10:18
  • The input comes from modal.html. Please see the related plunker. Commented Sep 10, 2013 at 10:23
  • Oops! Sorry about that. Have a look at my answer. Commented Sep 10, 2013 at 10:29
  • QuantumUI modal is more flexiable and have good documentation. You can try it (angularui.net) Commented Mar 4, 2015 at 0:23

3 Answers 3

72

Looks like a scope issue. I got it to work like this:

var ModalInstanceCtrl = function ($scope, $modalInstance) {
    $scope.input = {};
    $scope.ok = function () {
        alert($scope.input.abc);
    };

    $scope.cancel = function () {
        $modalInstance.dismiss('cancel');
    };
};

HTML:

<textarea ng-model="input.abc"></textarea>
Sign up to request clarification or add additional context in comments.

7 Comments

Yes, initializing $scope.input = {}; fixes it. Strange thing though.
This is not strange, this is by design since the modal actually created a sub controller, and JS behavior is that if you try to get an object and your object doesn't have it than it will go to parent
Right, this is because of Javascript's prototypical inheritance. It can't find "input" on the current scope, so it checks that scope's prototype and so on until it happens upon an "input" property on one of the scopes in the prototype chain. This "input" property is an object so you have a reference to it and are able set an "abc" property on it. This little trick is often used in angular to manipulate a value somewhere in an ancestor scope.
True, nothing strange about it - although Javascript's prototypical inheritance might be difficult to grasp. I strongly recommend this read for clarification: github.com/angular/angular.js/wiki/Understanding-Scopes
I believe it's considered best practice in angular to bind all your scope variables as objects so that you don't have to worry about passing variables by value, and you maintain your 2 way binding
|
16

Update Nov 2014: the issue is fixed with angular-ui-bootstrap 0.12.0 - the transclusion scope is merged with the controller's scope. There is no need to do anything. Just stay with:

<textarea ng-model="text"></textarea>

Before 0.12.0:

Angular-UI modals are using transclusion to attach modal content, which means any new scope entries made within modal are created in child scope.

You should use inheritance and initialize empty text entry in parent $scope or you can explicitly attach the input to parent scope:

<textarea ng-model="$parent.text"></textarea>

Comments

3

Let'me try to explain the reason. ui-bootstrap modal sourcecode:

.directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
return {
  restrict: 'EA',
  scope: {
    index: '@',
    animate: '='
  },
  replace: true,
  transclude: true,
  templateUrl: function(tElement, tAttrs) {
    return tAttrs.templateUrl || 'template/modal/window.html';
  },

and the template sourcecode - window.html:

<div tabindex="-1" role="dialog" class="modal fade" ng-class="{in: animate}" ng-style="{'z-index': 1050 + index*10, display: 'block'}" ng-click="close($event)">
<div class="modal-dialog" ng-class="{'modal-sm': size == 'sm', 'modal-lg': size == 'lg'}"><div class="modal-content" modal-transclude></div></div>

there is a directive modal-transclude,your dialog content will insert into it, it's sourcecode:

.directive('modalTransclude', function () {
return {
  link: function($scope, $element, $attrs, controller, $transclude) {
    $transclude($scope.$parent, function(clone) {
      $element.empty();
      $element.append(clone);
    });
  }
};

})

now take a look at offical doc of $compile:

Transclusion Functions

When a directive requests transclusion, the compiler extracts its contents and provides 
a transclusion function to the directive's link function and controller. 
This transclusion function is a special linking function that will return the compiled 
contents linked to a **new transclusion scope.**

transclude will create a new scope of controller scope

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.