javascript 在指令中监听表单提交事件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26383507/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 05:53:59  来源:igfitidea点击:

Listen for form submit event in directive

javascriptangularjsformsangularjs-directive

提问by s.alem

I want to listen for form submitting in a directive. Say I have a directive like this:

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

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');
            });
        }
    }
});

With the above method I can watch for first submit, but not the rest. I tried to do something like this:

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

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

But then the problem is, if I use the directive in a form more than once, it works only for the first usage. What I want to know is if there is something like formCtrl.onsubmit(...)or any workaround to get the same functionality. Thanks in advance for any help...

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

回答by ryeballar

Instead of watching the $submittedproperty, you can create a directive that has the same name as the formdirective which is attached with an event handler for form submit that broadcasts an angular event that you can listen in your myDirectivedirective. You don't have to worry about overriding the angular implementation of the formdirective, it will simply append your behavior not overwrite the built-in implementation.

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

DEMO

演示

Note: You can also choose not to append functionality to the formdirective and instead choose another directive name, just make sure to attach that directive name as an attribute in the form tag to trigger the event.

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

Javascript

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();
      });
    }
  };
});

Update

更新

In light of the question raised in the comment below:

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

what's the most efficient way to check if the element that has "my-directive" attribute has "my-form" (if I name "form" directive to "myForm") attribute in it's parent form? So I can either use "myDirective" with or without "myForm" (and behave accordingly of course)

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

There are several ways to do it:

有几种方法可以做到:

  1. Use the .data()method in your myFormdirective during the compile phase, and access it in the link function in your myDirectiveusing the .inheritedData()method if the data assigned in the formdirective exists.
  1. 使用.data()在你的方法myForm指令在编译阶段,并获得它在你的链接功能myDirective使用.inheritedData()方法,如果在指定的数据form存在指令。

Note that I passed the formcontroller within the broadcast in the myFormdirective. This ensures that you receive the parent form controller which is the from the formelement. There are certain use cases wherein you would use the myDirectiveinside a nested form via ng-form, so instead of setting form.$setPristine()to the formelement form controller you'd be setting the ngFormform controller.

请注意,我formmyForm指令的广播中传递了控制器。这确保您收到来自form元素的父表单控制器。有一定的使用情况,其中,你可以使用myDirective经由嵌套形式内ng-form,因此,而不是设置form.$setPristine()form元件形式控制器你会设置ngForm形式的控制器。

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. Another way which is probably something that is highly optimized for this specific use case. To create a controller in the myFormdirective which stores form event handlers to be iterated when a form event is triggered. Instead of using the $broadcastangular event which is actually slower than the implementation below because it traverses each scope from the formelement down to the last scope chain. The myFormcontroller below creates its own mechanism for storing the event handlers. As implemented in #1, using the .data()- inheritedData()is slow when the myDirectiveis buried deep and nested from a lot of elements, since it traverses the DOMupwards until it finds that specific data. Using the implementation below, you can check if the required ?^myFormcontroller exists in the parent, notice the ?it represents an optional requirement. Additionally, setting scope to true in the myFormdirective allows you to have the directive reusable, e.g. have multiple myFormdirectives inside a page..
  1. 另一种方式可能是针对此特定用例进行了高度优化的方式。在myForm指令中创建一个控制器,该控制器存储在触发表单事件时要迭代的表单事件处理程序。而不是使用$broadcast实际上比下面的实现慢的angular 事件,因为它遍历从form元素到最后一个作用域链的每个作用域。myForm下面的控制器创建了自己的机制来存储事件处理程序。正如在#1 中实现的那样,当被深埋并嵌套在许多元素中时,使用.data()- 会inheritedData()很慢myDirective,因为它会DOM向上遍历直到找到特定的data. 使用下面的实现,您可以检查是否需要?^myForm控制器存在于父级中,请注意?它代表一个可选要求。此外,在myForm指令中将 scope 设置为 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();
        });
      }
    };
  });

回答by Mosho

You can use ng-submitwith a broadcast or something similar, but maybe first give $setUntouched()a try, or perhaps manually set $submittedback to falseafter you are done with the current submission.

您可以使用ng-submit广播或类似的东西,但也许先$setUntouched()尝试一下,或者在您完成当前提交后手动设置$submittedfalse

回答by mr-wildcard

回答by Julien Bongars

This post is probably dead but to build on the above, I found the form directive was not broadcasting to the other directives properly so I included everything in one directive.

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

Here is a simple functions that generates an alert based on form.$error if the form is invalid:-

这是一个简单的函数,如果表单无效,它会根据 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();
            });
        }
    };
}])