【问题标题】:Listen for form submit event in directive在指令中监听表单提交事件
【发布时间】:2014-10-15 13:12:28
【问题描述】:

我想在指令中监听表单提交。假设我有这样的指令:

app.directive('myDirective', function () {
    return {
        restrict: 'A',
        require: '^form',
        scope: {
            smth: '='
        },
        link: function (scope, el, attrs, formCtrl) {
            scope.$watch(function(){
                return formCtrl.$submitted;
            },function(currentValue){
                console.log('submitted');
            });
        }
    }
});

通过上述方法,我可以看到第一次提交,但不能看到其余的。我试图做这样的事情:

scope.$watch(function () {
    return formCtrl.$submitted;
}, function (currentValue) {
    if (currentValue) {
        console.log('submitted');
        formCtrl.$setPristine(); // Watch this line!
    }
});

但是问题是,如果我在一个表单中多次使用该指令,它只适用于第一次使用。 我想知道是否有类似formCtrl.onsubmit(...) 或任何解决方法来获得相同的功能。在此先感谢您的帮助...

【问题讨论】:

    标签: javascript angularjs forms angularjs-directive


    【解决方案1】:

    您可以创建一个与form 指令同名的指令,而不是查看$submitted 属性,该指令附加一个用于表单提交的事件处理程序,该事件处理程序广播一个角度事件,您可以在您的@ 中收听987654326@ 指令。您不必担心覆盖form 指令的角度实现,它只会附加您的行为而不是覆盖内置实现。

    DEMO

    注意:您也可以选择不向 form 指令附加功能,而是选择另一个指令名称,只需确保将该指令名称作为属性附加到表单标记中以触发事件。

    Javascript

    .directive('form', function() {
    
      return {
        restrict: 'E',
        link: function(scope, elem) {
          elem.on('submit', function() {
             scope.$broadcast('form:submit');
          });
        }
      };
    
    })
    
    .directive('myDirective', function() {
      return {
        require: '^form',
        link: function(scope, elem, attr, form) {
          scope.$on('form:submit', function() {
            form.$setPristine();
          });
        }
      };
    });
    

    更新

    鉴于以下评论中提出的问题:

    什么是检查元素是否具有的最有效方法 “my-directive”属性具有“my-form”(如果我将“form”指令命名为 “myForm”)属性在它的父表单中?所以我可以使用 带有或不带有“myForm”的“myDirective”(并相应地表现 当然)

    有几种方法可以做到:

    1. 在编译阶段在myForm 指令中使用.data() 方法,如果在form 指令中分配的数据存在,则使用.inheritedData() 方法在myDirective 的链接函数中访问它。

    请注意,我在myForm 指令的广播中传递了form 控制器。这确保您收到来自form 元素的父表单控制器。在某些用例中,您可以通过ng-form 在嵌套表单中使用myDirective,因此您可以设置ngForm 表单控制器,而不是将form.$setPristine() 设置为form 元素表单控制器。

    DEMO

      .directive('myForm', function() {
    
        return {
          require: 'form',
          compile: function(tElem, tAttr) {
    
            tElem.data('augmented', true);
    
            return function(scope, elem, attr, form) {
              elem.on('submit', function() {
                 scope.$broadcast('form:submit', form);
              });
            }
          }
        };
    
      })
    
      .directive('myDirective', function() {
        return {
          link: function(scope, elem, attr) {
    
            if(!elem.inheritedData('augmented')) {
              return;
            }
    
            scope.$on('form:submit', function(event, form) {
              console.log('submit');
              form.$setPristine();
            });
          }
        };
      });
    
    1. 另一种可能是针对此特定用例高度优化的方法。在myForm 指令中创建一个控制器,该指令存储表单事件处理程序,以便在触发表单事件时进行迭代。而不是使用$broadcast 角度事件,它实际上比下面的实现要慢,因为它遍历从form 元素到最后一个作用域链的每个作用域。下面的myForm 控制器创建了自己的机制来存储事件处理程序。正如在#1 中实现的那样,当myDirective 被深埋并嵌套在许多元素中时,使用.data() - inheritedData() 会很慢,因为它会向上遍历DOM,直到找到特定的data。使用下面的实现,您可以检查所需的?^myForm 控制器是否存在于父级中,注意? 它表示一个可选要求。此外,在 myForm 指令中将范围设置为 true 允许您使指令可重用,例如在一个页面中有多个 myForm 指令..

    DEMO

      .directive('myForm', function() {
    
        return {
          require: ['form', 'myForm'],
          scope: true,
    
          controller: function() {
    
            this.eventHandlers = {
              submit: [],
              change: []
            };
    
            this.on = function(event, handler) {
              if(this.eventHandlers[event]) {
                this.eventHandlers[event].push(handler);
              }
            };
    
          },
    
          link: function(scope, elem, attr, ctrls) {
            var form = ctrls[0],
                myForm = ctrls[1];
    
    
            angular.forEach(myForm.eventHandlers, function(handlers, event) {
              elem.on(event, function(eventObject) {
                angular.forEach(handlers, function(handler) {
                  handler(eventObject, form);
                });
              });
            });
    
          }
    
        };
    
      })
    
      .directive('myDirective', function() {
        return {
          require: '?^myForm',
          link: function(scope, elem, attr, myForm) {
    
            if(!myForm) {
              return;
            }
    
            myForm.on('submit', function(event, form) {
              console.log('submit');
              form.$setPristine();
            });
          }
        };
      });
    

    【讨论】:

    • 谢谢你:)。我知道这可能不在这个问题的范围内,但是检查具有“my-directive”属性的元素是否具有“my-form”(如果我将“form”指令命名为“myForm”)属性的最有效方法是什么以它的父形式?所以我可以使用带有或不带有“myForm”的“myDirective”(当然也可以相应地表现)。
    • 您想检查 myForm 指令是否作为父指令存在于您的 myDirective 指令中?
    • 是的,但它不必是直接父母。我的意思是它也可能是这样的:form(with myForm)>div>div>input(with myDirective) 或其他任何东西......
    【解决方案2】:

    您可以将ng-submit 与广播或类似的东西一起使用,但可以先尝试$setUntouched(),或者在完成当前提交后手动将$submitted 设置回false

    【讨论】:

    • 我认为这需要在控制器中完成,对吧?我觉得@ryeballar 的解决方案更加通用和干燥......
    【解决方案3】:

    https://docs.angularjs.org/api/ng/directive/ngSubmit 可能就是您要找的。​​p>

    【讨论】:

    • 这很可能不是我想要的。我发送表单或从控制器访问它没有问题。问题是从指令中监听这个提交......
    • 在这种情况下,您可以尝试 Mosho 所说的,并将 ng-submit 放入您的
      标记中,然后将事件广播到子 $scope(指令的那个)
    【解决方案4】:

    这篇文章可能已经死了,但是在上面的基础上,我发现表单指令没有正确广播到其他指令,所以我将所有内容都包含在一个指令中。

    下面是一个简单的函数,如果表单无效,它会根据 form.$error 生成警报:-

    // automated handling of form submit errors
    myApp.directive('form', [ function() {
        return {
            restrict: 'E',
            require: '^form',
            link: function (scope, elem, attr, form) {
                elem.on('submit', function () {
                    if(form.$invalid){
                        console.log('form.$error: ', form.$error);
    
                        Object.keys(form.$error).forEach(error => {
                            form.$error[error].forEach(elem => {
                                console.log('error elem is: ', elem);
                                alert(error + ' for ' + elem.$name + ' is invalid! Current: ' + elem.$modelValue);
                            })
                        })
                    }
                    form.$setPristine();
                });
            }
        };
    }])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-24
      • 1970-01-01
      • 2011-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-17
      • 1970-01-01
      相关资源
      最近更新 更多