我如何关注第一个无效的表单声明式方式?

时间:2017-03-13 19:20:52

标签: javascript angularjs

通过查询DOM来关注第一个无效表单字段并不是什么大问题。以下是如何做到的好答案:

Set focus on first invalid input in AngularJs form

Focus on the first field that is .ng-invalid at Submit - not working for radios-inline

它们不适合我的原因是我想要生成一个与DOM分离的可单元测试的解决方案。

第二个原因是我想编写更多的声明式方法(当然,如果值得付出努力)。

我尝试使用准备好的autofocus directive,但您可以提出自己的类似解决方案。

1 个答案:

答案 0 :(得分:0)

制作单元可测试的可聚焦形式的好主意。

解决方案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 !!!');
      }      
    })
  }
}));

这是小提琴:https://jsfiddle.net/jtassin/60gwL3eh/3/ 这样您就可以避免在focusMe指令中使用范围。

解决方案angularjs&gt; 1.6

如果您正在使用angularjs&gt; 1.6,您还可以使用$$控件来获取输入,您将不再需要可聚焦指令。 这是角度1.6+小提琴: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;
        }
      });            
    })
  }
}));

在两种解决方案中,在单元测试期间,您可以模拟元素的焦点方法来检查焦点调用。