angular bootstrap modal masks forms

2019-07-07 04:07发布

问题:

I am trying to get at an angular form in scope to verify validations etc.

Base Case

Let us say I have the following HTML:

 <body ng-controller='MyAwesomeController'>
   <form name="fooForm">
     <textarea ng-model="reason" required=""></textarea>
   </form>
   <div class='btn btn-primary' ng-click="submit()" ng-class="{'btn-disabled': true}">Awesome Submit Button</div>
 </body>

And the following controller

 angular.module('MyAwesomeController', '$scope', function(scope){
   scope.submit = function(){
      console.debug(scope)
   }
 })

This will work, and upon inspection, scope will contain a fooForm key.

Let us now say that I introduce an angular ui bootstrap modal into the mix like so:

Broken Case

 <body ng-controller="AwesomeParentController">
   <div class="btn btn-primary" ng-click="open()">Open the Modal</div>
 </body>

with the following two controllers:

.controller('AwesomeParentController', ['$scope', '$modal', function(scope, modal){
   scope.open = function(){
     options = {
       windowClass: 'modal discontinue-modal',
       templateUrl: 'modal.html',
       controller: 'AwesomeModalController'
     }
     modalInstance = modal.open(options)

     modalInstance.result.then(function(){
       console.debug("result!")
     })
   }  
 }])

 .controller("AwesomeModalController", ['$scope', '$modalInstance', function(scope, modalInstance){
   scope.submit = function(){
     console.debug(scope)
   }  
 }])

with the following modal.html:

<form name="fooForm">
  <textarea ng-model="reason" required=""></textarea>
</form>
<div class='btn btn-primary' ng-click="submit()">Awesome Submit Button</div>

When the first button is clicked, a modal opens, the second button click prints a scope, which does NOT contain fooForm, rather fooForm resides on scope.$$childTail

Plunkr: http://embed.plnkr.co/jFGU0teIbL3kUXdyTPxR/preview

Possible Fix

<form name="fooForm">
  <div ng-controller ="JankyFormController">
    <textarea ng-model="reason" required=""></textarea>
    <div class='btn btn-primary' ng-click="submit()">Awesome Submit Button</div>  
  </div>
</form>

.controller('JankyFormController', ['$scope', function(scope){
  scope.models['fooForm'] = scope.fooForm
}])

Plunkr: http://embed.plnkr.co/BAZFbS7hFRhHm8DqOpQy/preview

Why is the form being masked? What would be a clean way to get at it without having to create a nested child controller? what if I want to bind ng-class to the forms validity? Would I now have to construct a watch around ($$childTail).fooForm.$valid?

回答1:

Update: angular ui-bootstrap 0.12.0 fixes the problem - transclusion scope becomes the same as controller's scope, no need for $parent.. Just upgrade.

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. This happens with form directive.

This is known issue: https://github.com/angular-ui/bootstrap/issues/969

I proposed the quick workaround which works for me, with Angular 1.2.16:

<form name="$parent.userForm">

The userForm is created and available in modal's controller $scope. Thanks to scope inheritance userForm access stays untouched in the markup.

<div ng-class="{'has-error': userForm.email.$invalid}"}>