简体   繁体   English

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

[英]Listen for form submit event in directive

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. 我想知道的是,是否有类似formCtrl.onsubmit(...)或任何解决方法来获得相同的功能。 Thanks in advance for any help... 在此先感谢任何帮助......

Instead of watching the $submitted property, you can create a directive that has the same name as the form directive which is attached with an event handler for form submit that broadcasts an angular event that you can listen in your myDirective directive. 而是看的$submitted属性,你可以创建一个具有相同的名称作为一个指令form其连接与形式的事件处理程序提交广播,你可以在你听角事件指令myDirective指令。 You don't have to worry about overriding the angular implementation of the form directive, it will simply append your behavior not overwrite the built-in implementation. 您不必担心覆盖form指令的角度实现,它只会附加您的行为而不会覆盖内置实现。

DEMO DEMO

Note: You can also choose not to append functionality to the form directive 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? 什么是最有效的方法来检查具有“my-directive”属性的元素是否具有“my-form”(如果我将“form”指令命名为“myForm”)属性为其父表单? So I can either use "myDirective" with or without "myForm" (and behave accordingly of course) 所以我可以使用“myDirective”,有或没有“myForm”(当然也有相应的行为)

There are several ways to do it: 有几种方法可以做到:

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

Note that I passed the form controller within the broadcast in the myForm directive. 请注意,我在myForm指令的广播中传递了form控制器。 This ensures that you receive the parent form controller which is the from the form element. 这可以确保您收到来自form元素的父表单控制器。 There are certain use cases wherein you would use the myDirective inside a nested form via ng-form , so instead of setting form.$setPristine() to the form element form controller you'd be setting the ngForm form controller. 在某些用例中,您可以通过ng-form在嵌套表单中使用myDirective ,而不是将form.$setPristine()设置为form元素表单控制器,您将设置ngForm表单控制器。

DEMO 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 myForm directive which stores form event handlers to be iterated when a form event is triggered. myForm指令中创建一个控制器,该指令存储表单事件处理程序,以便在触发表单事件时进行迭代。 Instead of using the $broadcast angular event which is actually slower than the implementation below because it traverses each scope from the form element down to the last scope chain. 而不是使用实际比下面的实现慢的$broadcast角度事件,因为它遍历每个范围从form元素到最后一个范围链。 The myForm controller below creates its own mechanism for storing the event handlers. 下面的myForm控制器创建了自己的存储事件处理程序的机制。 As implemented in #1, using the .data() - inheritedData() is slow when the myDirective is buried deep and nested from a lot of elements, since it traverses the DOM upwards until it finds that specific data . 正如在#1中实现的那样,使用.data() - inheritedData()myDirective被深埋并嵌套在很多元素中时很慢,因为它向上遍历DOM直到找到特定data Using the implementation below, you can check if the required ?^myForm controller exists in the parent, notice the ? 使用下面的实现,您可以检查父项中是否存在所需的?^myForm控制器,注意? it represents an optional requirement. 它代表一个可选的要求。 Additionally, setting scope to true in the myForm directive allows you to have the directive reusable, eg have multiple myForm directives inside a page.. 另外,在myForm指令中将scope设置为true允许您使指令可重用,例如在页面内有多个myForm指令。

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

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

https://docs.angularjs.org/api/ng/directive/ngSubmit可能就是你要找的东西。

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. 这篇文章可能已经死了,但是基于上面的内容,我发现form指令没有正确地向其他指令广播,所以我将所有内容都包含在一个指令中。

Here is a simple functions that generates an alert based on form.$error if the form is invalid:- 这是一个基于表单生成警报的简单函数。如果表单无效,则为$ 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();
            });
        }
    };
}])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM