3

I have a form, and I'm navigating only with TAB. Tab order should be input > select > button, but because of the ng-disable on the SUBMIT, on certain browsers the TAB out of the select will kick you somewhere else.

HTML

<div ng-app="myApp">
    <div ng-controller="FirstCtrl">
        <form name="myForm" ng-submit="submit()" novalidate>    
            First Name: <input type="text" ng-model="Data.FirstName" required><br>
            Last Name: <select ng-model="Data.LastName" required>
                <option value="Bigglesworth">Bigglesworth</option>
                <option value="Burgermeister">Burgermeister</option>
            </select><br>
            <input type="submit" value="Submit" ng-disabled="myForm.$invalid" />
        </form>
    </div>
</div>

JS

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

myApp.factory('Data', function(){
    return {
        FirstName: '',
        LastName: ''
    };
});

myApp.controller('FirstCtrl', function( $scope, Data ){
    $scope.Data = Data;
    $scope.submit = function() {
        console.log('you just submitted, foolio');
    }
});

JsFiddle here.

On Mac FF the final tab kicks you to the address bar before enabling the submit button. Mac Chrome works as you'd expect, focusing on the submit button after final tab. I know Windows is janky, but don't have exact specs to post.

Thoughts? How can I do this in a fool-proof fashion?

EDIT I've selected @David B.'s answer as it's the best Angular solution. I ended up using a somewhat hidden element right after the the submit button so the focus would stay in the same general area. Lame and hacky, I know, but for a tight deadline it worked.

<h3><button class="fakebtn_hack">Confirmation</button></h3>
<style>.fakebtn_hack {background:none; border:none; color: #FF6319; cursor: default; font-size: 1em; padding: 0;}</style>
johnkeese
  • 4,048
  • 3
  • 18
  • 15

1 Answers1

2

This happens because Firefox doesn't send a change event on key-driven changes of the select. Angular doesn't see the change until the tab is hit, so the submit button isn't enabled until after the tab has been processed by the browser (and focus sent to some other element, e.g., the address bar). The W3C standard suggests not sending the event until the control loses focus, although Chrome sends one for any change and Firefox does if the change was mouse-driven.

See the angularjs issue tracker for more: https://github.com/angular/angular.js/issues/4216

As suggested in the issue tracker, solve it by manually issuing the change event via the following select directive (http://jsfiddle.net/j5ZzE/):

myApp.directive("select", function () {
    return {
        restrict: "E",
        require: "?ngModel",
        scope: false,
        link: function (scope, element, attrs, ngModel) {
            if (!ngModel) {
                return;
            }
            element.bind("keyup", function () {
                element.trigger("change");
            })
        }
    }
})

You'll need JQuery loaded before AngularJS to have the trigger function available on the element object.

Manually include an empty option (<option value=""></option>) in your select or the first option will be auto-selected when the control receives focus.

Unlike the default behavior, this empty option will not disappear after selecting a real option. I suppose you could remove the empty option by declaring all the options via ng-options or ng-repeat and then removing the empty one from the bound scope once a real option has been selected, but I've never tried it.

David B.
  • 5,700
  • 5
  • 31
  • 52
  • for some reason your jsfiddle fails if I remove jQuery. Is jQuery required for this solution? http://jsfiddle.net/j5ZzE/1/ – johnkeese May 22 '14 at 14:26
  • Unfortunately. jQuery provides the `.trigger()` element method used in the directive. The jqLite library included with Angular doesn't provide it. I'm not a javascript guru, but maybe you can send the change event with pure js: http://stackoverflow.com/q/5658849/448970 or http://stackoverflow.com/q/2490825/448970 – David B. May 22 '14 at 15:37