Scope issue in AngularJS using AngularUI Bootstrap Modal

AngularjsAngular UiAngular Ui-Bootstrap

Angularjs Problem Overview


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?

Angularjs Solutions


Solution 1 - Angularjs

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>

Solution 2 - Angularjs

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>

Solution 3 - Angularjs

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionManuel BittoView Question on Stackoverflow
Solution 1 - AngularjsAlwaysALearnerView Answer on Stackoverflow
Solution 2 - AngularjsgertasView Answer on Stackoverflow
Solution 3 - Angularjs笑笑十年View Answer on Stackoverflow