Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I've made a directive for a special type of submit button that watches when its form is submitted, and then that buttons is disabled and gets a nice animated progress bar.

This all works fine when the form is submitted by pressing the submit button or pressing enter in one of the fields, the onsubmit handler is called just fine.

The problem: in one of my forms I have a textarea which I want to submit when the user presses the enter key. So I made an onEnter directive that just looks for the right key press and then executes a function.

<form name="form" ng-submit="controller.submit()">
    <textarea ng-model="controller.newMessage.content" autofocus on-enter="controller.submit()"
    <progress-button type="submit">Post</progress-button>
</form>

The problem of course is that this doesn't trigger the onsubmit handler, and thus the button isn't disabled or anything. How could I solve this? I've tried something like document.form.submit(), but that submits the form in the old fashioned HTML way, of course bypassing all Angular / JS code and handlers. Should I find the submit button and simulate a click? That feels very hackish too.

Sadly $scope.form is very useless, nothing there to submit it.

Edit 1: Just so the problem is clear: yes, the controller.submit() function is called just fine via the on-enter directive. However, the form doesn't get a submit event which my button is listening for.

Edit 2: Here is a gist with my button directive: https://gist.github.com/kevinrenskers/d66dbebe43c8533efaf2. The button currently needs a "pb-click" attribute, or its form needs a "pb-submit" attribute. Those functions need to return a promise.

Moving this logic to a scope variable that's set from these functions might not be a big deal since that means we can use standard ng-click and ng-submit, don't need to return promises, etc. On the other hand, if you have 5 buttons on one page you then need to create 5 scope variables. Not the best idea either. Or keep using pb-click and for forms use a scope variable?

share|improve this question
    
just call controller.submit() at the end of your key press event? –  Quad May 22 '14 at 17:31
    
Also a fiddle could help –  Quad May 22 '14 at 17:32
    
Can you not use ng-form? –  J.Wells May 22 '14 at 17:32
    
Just calling controller.submit() executes the code, yes, but bypasses the onsubmit handler of the form itself. –  mixedCase May 22 '14 at 17:33
    
Using ng-form makes no difference. –  mixedCase May 22 '14 at 17:35

3 Answers 3

You can subscribe to a "submit" from your progress-button directive and broadcast/emit that event from triggered actions. I put together a quick sample with a regular input, text area, and a progress-button that disabled. The controller.submit and onEnter directives both raise the event of interest that progress-button subscribes to (jsfiddle).

JavaScript:

var mod = angular.module("myApp", []);

mod.controller("MainController", function ($scope, $rootScope) {
    var isSubmitted = false;
    $scope.controller = {};
    $scope.controller.submit = function () {
        if (!isSubmitted) {
            alert("controller.submit called");
            $rootScope.$broadcast("progressButton.submit");
        }
        isSubmitted = true;
    }
});

mod.directive("onEnter", function ($rootScope) {
    return {
        restrict: "A",
        scope: {onEnter: "&"},
        link: function (scope, element, attrs) {
            element.on("keypress", function (event) {
                var whichKey = event.which;

                //enter pressed
                if (whichKey === 13) {
                    scope.onEnter();
                    $rootScope.$broadcast("progressButton.submit");
                }
            });
        }
    }
});

mod.directive("progressButton", function ($timeout) {
    return {
        restrict: "E",
        template: '<button ng-transclude></button>',
        transclude: true,
        replace: true,
        link: function (scope, element, attrs) {
            //function to disable the button when clicked or progressButton.submit event
            //is broadcast.
            var disableButton = function () {
                //disabling after a timeout so submit handler can be called
                $timeout(function () { 
                    element.attr("disabled", true);
                }, 0);
            };

            if (!attrs.type) {
                element.type = "submit";
            }
            else {
               element.type = attrs.type;
            }

            //disable the button when form is submitted or explicitly clicked.
            element.on("click", function () {
                disableButton();
            });

            //disable the button when progressButton.submit event is received.
            scope.$on("progressButton.submit", function () {
                    disableButton();
            });
        }
    }
});

HTML:

<div ng-app="myApp" ng-controller="MainController">
    <form ng-submit="controller.submit()">
        <input ng-model="data.name"><br>
        <textarea on-enter="controller.submit()"></textarea><br>
        <progress-button type="submit">Submit Me</progress-button>
    </form>
</div>
share|improve this answer
    
Thanks for the very detailed answer, but in the end going with a scope variable seemed a little bit cleaner. –  mixedCase May 24 '14 at 14:16

I've managed to achieve this by adding $submit method to FormController:

module.directive('form', function($parse) {
    return {
       require: 'form',
       restrict: 'E',
       link: function(scope, element, attrs, formController) {          
           formController.$submit = $parse(attrs.ngSubmit);
       }
    };
});

You can then invoke form's ng-submit expression by calling $scope.myForm.$submit($scope) from the controller.

share|improve this answer
    
got a quick workaround :D feel free check it out :) stackoverflow.com/questions/28773057/… –  Chen Feb 27 at 20:08
    
Properties starting with a $ are reserved by angular. Better to use a name other than $submit in case this gets added to angular proper in the future. –  Jackson Apr 17 at 20:32

The $parse in aikoven's answer didn't seem to be working for me, so I modified it to use scope.$eval instead. I also added form.$setSubmitted() so the form properly gets the .ng-submitted class after you submit it.

app.directive('form', function() {
    return {
        require: 'form',
        restrict: 'E',
        link: function(scope, elem, attrs, form) {
            form.$submit = function() {
                form.$setSubmitted();
                scope.$eval(attrs.ngSubmit);
            };
        }
    };
});
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.