Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. Thanks in advance for any help...

like image 788
s.alem Avatar asked Oct 15 '14 13:10

s.alem


People also ask

How do I add an event listener to form?

Click EventListenersClick EventListener s are used when you need a user to click on an element to access additional information, go to a form, etc. For example, if you want the user to click on the “Next Page” button to go to the next page you would add a click EventListener to the button.

Which of the following events occurs when the submit button is clicked in a form?

The submit event fires when the user clicks a submit button ( <button> or <input type="submit">) or presses Enter while editing a field (e.g. <input type="text">) in a form. The event is not sent to the form when calling the form. submit() method directly.

Which of the below event will be triggered when a form is submitted?

The onsubmit event occurs when a form is submitted.

What is Onsubmit?

The onsubmit event is an event that occurs when you try to submit a form. You can put your form validation against this event type. The following example shows how to use onsubmit. Here we are calling a validate() function before submitting a form data to the web server.


1 Answers

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. 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.

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.

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)

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.

Note that I passed the form controller within the broadcast in the myForm directive. This ensures that you receive the parent form controller which is the from the form element. 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.

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. 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. The myForm controller below creates its own mechanism for storing the event handlers. 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. Using the implementation below, you can check if the required ?^myForm controller exists in the parent, notice the ? it represents an optional requirement. Additionally, setting scope to true in the myForm directive allows you to have the directive reusable, e.g. have multiple myForm directives inside a page..

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();
        });
      }
    };
  });
like image 123
ryeballar Avatar answered Oct 06 '22 06:10

ryeballar