Great idea to make a unit testable focusable form.
Solution angularjs < 1.6
HTML :
<div ng-app="myApp">
<form novalidate focus-first>
<div>
<input focusable ng-model="f1" name="f1" type="text" required />
</div>
<input focusable ng-model="f2" name="f2" type="text" required />
<input type="submit" />
</form>
</div>
JS :
const app = angular.module('myApp', []);
app.directive('focusable', () => ({
restrict: 'A',
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$$boundElement = element;
}
}));
app.directive('focusFirst', () => ({
restrict: 'A',
link: (scope, element, attrs) => {
element.on('submit', (event) => {
event.preventDefault();
const formCtrl = angular.element(event.target).controller("form");
const errorKeys = Object.keys(formCtrl.$error);
if (errorKeys.length > 0) {
errorKeys.forEach((errorKey) => {
// Pretty ugly, it needs to be improved
const boundElement = formCtrl.$error[errorKeys[0]][0].$$boundElement[0];
if (boundElement) {
boundElement.focus();
// boundElement.scrollIntoView(); // Drive Fiddle crazy
}
});
} else {
alert('OK !!!');
}
})
}
}));
Here is the fiddle : https://jsfiddle.net/jtassin/60gwL3eh/3/
This way you will avoid to use the scope in a focusMe directive.
Solution angularjs > 1.6
If you are using angularjs > 1.6, You can also use $$controls to get the inputs and you won't need the focusable directive anymore.
Here is the angular 1.6+ fiddle : https://jsfiddle.net/jtassin/60gwL3eh/5/
HTML :
<div ng-app="myApp">
<form novalidate focus-first>
<div>
<input ng-model="f1" name="f1" type="text" required />
</div>
<input ng-model="f2" name="f2" type="text" required />
<input type="submit" />
</form>
</div>
JS :
const app = angular.module('myApp', []);
app.directive('focusFirst', () => ({
restrict: 'A',
link: (scope, element, attrs) => {
element.on('submit', (event) => {
event.preventDefault();
const formCtrl = angular.element(event.target).controller("form");
formCtrl.$$controls.some((input) => {
if (input.$invalid) {
input.$$element[0].focus();
return true;
}
});
})
}
}));
In both solutions, during unit tests you can mock focus method of elements to check the focus call.