我可以将Angular表单验证移动到指令中的其他DOM元素

时间:2014-06-25 16:04:02

标签: angularjs angularjs-directive

我有一个货币输入指令。它执行一些基本的数字验证,$parse - 返回一个数字。另外,我想在输入之前使用CSS $放置:before。问题是,您无法在:before元素上使用<input>

我的解决方案是创建以下指令(为简单起见,我除了required之外还删除了验证):

angular.module('myApp')
  .directive('myCurrencyInput', function () {
    return {
      restrict: 'A',
      require: ['ngModel', '^form'],
      scope: {
        ngModel: '='
      },
      replace: true,
      templateUrl: 'scripts/directives/myCurrencyInput/myCurrencyInput.html',
      compile: function(element, attrs) {

        // The internal input to the directive
        var $input = element.children('input');

        if(attrs.required) {
          element.removeAttr('required');
          attrs.required = undefined;
          $input.attr('required', 'required');
        }

        if(attrs.name) {
          element.removeAttr('name');
          $input.attr('name', attrs.name);
          attrs.name = undefined;
        }

        return function postLink(scope, element, attrs, ctrls) {
          var ngModelCtrl = ctrls[0];
          var formCtrl = ctrls[1];
          var internalCtrl = element.find('input').controller('ngModel');

          if($input.attr('required') === 'required') {
            // formCtrl doesn't have ngModelCtrl attached at this point
            formCtrl.$removeControl(ngModelCtrl);
          }

          // Bind internal model changes to the outside ngModel
          scope.$watch('internalModel', function(newVal) {
            if(newVal !== undefined && newVal !== scope.ngModel) {
              scope.ngModel = newVal;
            }
          });

          // Bind external model changes to the inside ngModel (internalModel)
          scope.$watch('ngModel', function(newVal) {
            if(newVal !== undefined && newVal !== scope.internalModel) {
              scope.internalModel = newVal;
            }
          });
        };
      }
    };
  });

模板scripts/directives/myCurrencyInput/myCurrencyInput.html是:

<div class="my-currency-input">
  <input type="text" ng-model="internalModel" />
</div>

问题是,现在<div>(原始<input>元素)和新<input>被视为FormController的需要(它处理表单级别的验证) )。当我在链接函数中运行formCtrl.$removeControl(ngModelCtrl)时,ngModelCtrl尚未附加到表单,但到表单提交时,它已被添加。

此时我唯一能想到的就是将formCtrl.$removeControl(ngModelCtrl)包裹在$timeout中,但这似乎不是正确的做法。什么是正确的方法?

我使用的是Angular 1.0.7

2 个答案:

答案 0 :(得分:1)

如何让它简单地显示为文本框中的“$”符号? See here

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
  <style>
  .currency-input{
    padding:.3em;
    display:inline-block;
    background-color:#fff;
    box-shadow:inset 0px 0px 3px #ddd;
    border:solid 1px #aaa;
    font-size:14px;
  }
  .currency-input input{
    border:0px;
    outline:0px;
    font-size:14px;
  }
  <style>
</head>
<body>  
  <div class='currency-input'>
    $<input />
  </div>
</body>
</html>

答案 1 :(得分:0)

事实证明,这可以通过将formCtrl.$removeControl(ngModelCtrl)包装在没有超时值的$timeout中来实现。所以

if($input.attr('required') === 'required') {
  // formCtrl doesn't have ngModelCtrl attached at this point
  formCtrl.$removeControl(ngModelCtrl);
}

变为

if($input.attr('required') === 'required') {
  // formCtrl doesn't have ngModelCtrl attached at this point
  $timeout(function() {
    formCtrl.$removeControl(ngModelCtrl);
  });
}

我不确定是什么阻止了同步运行,但是现在它会做(在工作中,不能花太多时间在它上面)。