自定义组件使表单无效| AngularJS

时间:2013-02-25 04:19:27

标签: forms angularjs angularjs-directive

我已经搜索过了,但无法弄清楚这一点。我做了一个指令manyToOneSelect(自定义组件),它从服务器加载项目,向用户显示它们并让用户选择一个。这很有效,但如果用户没有选择任何项目,我无法弄清楚如何阻止提交表单,即如何使表单无效。

以下几乎是指令:

angular.module('myApp.directives').
    directive('manyToOneSelect', function(entityService) {
        return {
            restrict:'E',
            templateUrl:'partials/control/n21select.html',
            scope:{
                entityName:'@',
                entityField:'@',
                bindVariable:'='
            },
            compile:function (tElement, tAttrs, transclude) {
                return function (scope, element, attrs) {
                    var inner = element.children("#n21select");
                    scope.entities = [];
                    scope.$watch('entityName', function ($new, $old) {
                        entityService.getList(scope.entityName, function (data) {
                            scope.entities = data;
                        }, []);
                    }, true);
                    scope.lookup = function(uuid) {
                        for(var i in scope.entities) {
                            if(scope.entities[i].uuid == uuid) {
                                return scope.entities[i];
                            }}}}}}});

以下是相应的部分partials/control/n21select.html

<select ng-hide="disable" ng-options="entity.uuid as entity[entityField] for entity in entities" ng-model="bindVariable" required></select>
<span ng-show="disable">{{lookup(bindVariable)[entityField]}}</span>

以下是我如何使用该指令:

<form ng-href="#/" ng-submit="save()">
<many-to-one-select entity-name="customer" entity-field="name"
    bind-variable="entity.customerUuid"></many-to-one-select>
...

我的问题似乎缺乏策略,而不是“不完全让它工作”,因此您在我上面发布的代码中看不到任何尝试。那么这就是一个相当开放的问题:如何做到这一点? :)非常感谢!

1 个答案:

答案 0 :(得分:1)

有几种方法可以做到这一点。

考虑到你已经构建了指令,一种方法是为表单本身添加一个scope属性。类似的东西:

scope: {
   form: '='
}

然后你就像这样传递你的表单元素:

<form name="myForm" ng-submit="whatever()">
   <my-directive-name form="myForm"></my-directive-name>
</form>

在您的指令中,您希望使表单无效,您只需在其上调用$ setValidity:

link: function(scope, elem, attr) {
  if(somethingIsWrong) scope.form.$setValidity('reason', false);
}

这是一种方法,如果您可以重新设计指令,这是一种更好方式:

另一种可能是首选的方法是让你的指令需要ngModel。然后你将对你的验证有更多的控制,因为ngModel的控制器将被传入,你可以使用它来使表单和表单上的单个字段无效:

   app.directive('bettererWay', function() {
      return {
         require: 'ngModel',
         restrict: 'E',
         link: function(scope, elem, attr, ngModel) {
             if(somethingIsBad()) ngModel.$setValidity('somethingIsBad', false);
         }
      };
   });

简而言之,这就是你如何做到的。希望这能让你开始朝着正确的方向前进。


编辑:提交时出现的奇怪问题,无论有效性如何(在评论中)

这显然是Angular试图遵守HTML规范引起的问题。

来自代码approx. line 214 here中的评论:

* To prevent double execution of the handler, use only one of ngSubmit or ngClick directives. This
 * is because of the following form submission rules coming from the html spec:
 *
 * - If a form has only one input field then hitting enter in this field triggers form submit
 * (`ngSubmit`)
 * - if a form has has 2+ input fields and no buttons or input[type=submit] then hitting enter
 * doesn't trigger submit
 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)

因此,鉴于上述情况,将指令绑定到页面上隐藏类型的输入元素而不是它自己的元素可能是个好主意。如果表单上有多个元素,则无效就会阻止提交。