问题
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...
回答1:
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:
- Use the
.data()
method in yourmyForm
directive during the compile phase, and access it in the link function in yourmyDirective
using the.inheritedData()
method if the data assigned in theform
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();
});
}
};
});
- 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 theform
element down to the last scope chain. ThemyForm
controller below creates its own mechanism for storing the event handlers. As implemented in #1, using the.data()
-inheritedData()
is slow when themyDirective
is buried deep and nested from a lot of elements, since it traverses theDOM
upwards until it finds that specificdata
. 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 themyForm
directive allows you to have the directive reusable, e.g. have multiplemyForm
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();
});
}
};
});
回答2:
You can use ng-submit
with a broadcast or something similar, but maybe first give $setUntouched()
a try, or perhaps manually set $submitted
back to false
after you are done with the current submission.
回答3:
https://docs.angularjs.org/api/ng/directive/ngSubmit is probably what you're looking for.
回答4:
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:-
// 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();
});
}
};
}])
来源:https://stackoverflow.com/questions/26383507/listen-for-form-submit-event-in-directive