77

我想将我的表单字段封装在一个指令中,所以我可以简单地这样做:

<div ng-form='myForm'>
  <my-input name='Email' type='email' label='Email Address' placeholder="Enter email" ng-model='model.email' required='false'></my-input>

</div>

如何访问myForm指令中的 ,以便进行验证检查,例如myForm.Email.$valid

4

5 回答 5

155

要在指令中访问 FormController:

require: '^form',

然后它将作为链接函数的第四个参数提供:

link: function(scope, element, attrs, formCtrl) {
    console.log(formCtrl);
}

fiddle

不过,您可能只需要访问 NgModelController:

require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
     console.log(ngModelCtrl);
}

fiddle

如果您需要访问两者:

require: ['^form','ngModel'],
link: function(scope, element, attrs, ctrls) {
    console.log(ctrls);
}

fiddle

于 2013-07-12T17:59:04.007 回答
32

这是一个完整的示例(使用 Bootstrap 3.1 进行样式设置)

它包含一个带有多个输入(姓名、电子邮件、年龄和国家/地区)的表单。姓名、电子邮件和年龄是指令。Country 是“常规”输入。

当用户没有输入正确的值时,每个输入都会显示一条帮助消息。

表单包含一个保存按钮,如果表单包含至少一个错误,该按钮将被禁用。

<!-- index.html -->
<body ng-controller="AppCtrl">
  <script>
    var app = angular.module('app', []);

    app.controller('AppCtrl', function($scope) {
      $scope.person = {};
    });
  </script>
  <script src="inputName.js"></script>
  <script src="InputNameCtrl.js"></script>
  <!-- ... -->

  <form name="myForm" class="form-horizontal" novalidate>
    <div class="form-group">
      <input-name ng-model='person.name' required></input-name>
    </div>

    <!-- ... -->

    <div class="form-group">
      <div class="col-sm-offset-2 col-sm-4">
        <button class="btn btn-primary" ng-disabled="myForm.$invalid">
          <span class="glyphicon glyphicon-cloud-upload"></span> Save
        </button>
      </div>
    </div>
  </form>

  Person: <pre>{{person | json}}</pre>
  Form $error: <pre>{{myForm.$error | json}}</pre>
  <p>Is the form valid?: {{myForm.$valid}}</p>
  <p>Is name valid?: {{myForm.name.$valid}}</p>
</body>

// inputName.js
app.directive('inputName', function() {
  return {
    restrict: 'E',
    templateUrl: 'input-name.html',
    replace: false,
    controller: 'InputNameCtrl',
    require: ['^form', 'ngModel'],

    // See Isolating the Scope of a Directive http://docs.angularjs.org/guide/directive#isolating-the-scope-of-a-directive
    scope: {},

    link: function(scope, element, attrs, ctrls) {
      scope.form = ctrls[0];
      var ngModel = ctrls[1];

      if (attrs.required !== undefined) {
        // If attribute required exists
        // ng-required takes a boolean
        scope.required = true;
      }

      scope.$watch('name', function() {
        ngModel.$setViewValue(scope.name);
      });
    }
  };
});

// inputNameCtrl
app.controller('InputNameCtrl', ['$scope', function($scope) {
}]);

带有指令的 AngularJS 表单

于 2014-03-10T03:11:38.703 回答
17

编辑 2:我会留下我的答案,因为它可能对其他原因有所帮助,但 Mark Rajcok 的另一个答案是我最初想做的,但未能开始工作。显然这里的父控制器是form,不是ngForm


你可以在你的指令中使用一个属性来传递它,尽管这会变得相当冗长。

例子

这是一个工作的、简化的 jsFiddle

代码

HTML:

<div ng-form="myForm">
    <my-input form="myForm"></my-input>
</div>

该指令的基本部分:

app.directive('myInput', function() {
    return {
        scope: {
            form: '='
        },
        link: function(scope, element, attrs) {
            console.log(scope.form);
        }
    };
});

发生了什么

我们已经要求 Angularform通过使用'='.

这样做可以将实际表单与输入指令分离。

注意:我尝试使用require: "^ngForm",但是 ngForm 指令没有定义控制器,并且不能以这种方式使用(这太糟糕了)。


话虽如此,我认为这是一种非常冗长和混乱的处理方式。您最好向表单元素添加一个新指令,并使用它require来访问该项目。我会看看我能不能把东西放在一起。

编辑:使用父指令

好的,这是我能想到的最好的使用父指令的方法,我稍后会解释更多:

使用父指令工作jsFiddle

HTML:

<div ng-app="myApp">
    <div ng-form="theForm">
        <my-form form="theForm">
            <my-input></my-input>
        </my-form>
    </div>
</div>

JS(部分):

app.directive('myForm', function() {
    return {
        restrict: 'E',
        scope: {
            form: '='
        },
        controller: ['$scope', function($scope) {
            this.getForm = function() {
                return $scope.form;
            }
        }]
    }
});

app.directive('myInput', function() {
    return {
        require: '^myForm',
        link: function(scope, element, attrs, myForm) {
            console.log(myForm.getForm());
        }
    };
});

这将表单存储在父指令范围 ( myForm) 中,并允许子指令通过要求父表单 ( require: '^myForm') 访问它,并在链接函数 ( myForm.getForm()) 中访问指令的控制器。

好处:

  • 您只需要在一个地方识别表格
  • 您可以使用您的父控制器来存放公共代码

负面:

  • 你需要一个额外的节点
  • 您需要输入两次表格名称

我更喜欢什么

我试图使用表单元素上的属性让它工作。如果这有效,您只需将该指令添加到与ngForm.

但是,我在范围内遇到了一些奇怪的行为,myFormName变量将在 内可见$scope,但是undefined当我尝试访问它时。那个让我很困惑。

于 2013-07-12T15:37:22.960 回答
8

从 AngularJS 1.5.0 开始,有更简洁的解决方案(而不是link直接使用该函数)。如果您想FormController在子组件的指令控制器中访问表单,您可以简单地require在指令上添加属性,如下所示:

return {
  restrict : 'EA',
  require : {
    form : '^'
  },
  controller : MyDirectiveController,
  controllerAs : 'vm',
  bindToController : true,
  ...
};

接下来,您将能够像访问任何其他范围变量一样在模板或指令控制器中访问它,例如

function MyDirectiveController() {
  var vm = this;
  console.log('Is the form valid? - %s', vm.form.$valid);
}

请注意,要使其正常工作,您还需要bindToController: true在指令上设置属性。有关更多信息,请参阅问题的文档。$compile

文档中的相关部分:

要求

需要另一个指令并将其控制器作为第四个参数注入链接函数。require 属性可以是字符串数组对象

如果 require 属性是一个对象并且bindToController是真实的,则使用 require 属性的键将所需的控制器绑定到控制器。如果所需控制器的名称与本地名称(键)相同,则名称可以省略。例如,{parentDir: '^parentDir'}等价于{parentDir: '^'}

于 2016-12-10T04:29:59.267 回答
2

使您的“我喜欢的”小提琴工作!出于某种原因,您可以在 console.log 中看到“$scope.ngForm”字符串,但直接记录它不起作用,导致未定义。但是,如果将属性传递给控制器​​函数,则可以得到它。

app.directive('myForm', function() {
return {
    restrict: 'A',
    controller: ['$scope','$element','$attrs', function($scope,$element,$attrs) {
        this.getForm = function() {
            return $scope[$attrs['ngForm']];
        }
    }]
}
});

http://jsfiddle.net/vZ6MD/20/

于 2013-12-25T11:27:27.873 回答