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...
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.
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.
The onsubmit event occurs when a form is submitted.
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.
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();
});
}
};
});
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:
.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();
});
}
};
});
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();
});
}
};
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With