$ watch在字符串更改时第二次不工作

时间:2016-01-13 20:17:45

标签: javascript angularjs

我有一个简单的输入验证功能。每次用户点击submit时,都会验证输入。不知何故,$ watch仅在输入无效时第一次起作用,并且在第二次和后续时间不起作用。

代码如下。

<form ng-submit="validateInput(input)">
  <input ng-model="input.first">
  <input ng-model="input.second">
  <input type="submit" value="Submit">
</form>

function validateInput(input) {
  $scope.errorMessage = ""; //reset errorMessage to "" for every new submission.
  if (condition) {
    //http call
  } else {
    $scope.errorMessage = "Invalid input".
  }
}

app.directive('showError', function() {
  return {
    restrict: 'A',
    link: function(_scope, _element) {
      _scope.$watch('errorMessage', function(newVal) {
        if (newVal) {
          $(_element).find("#errorMessage").html(newVal);
          $(_element).slideDown().delay(3000).slideUp();
        }
      });

      $(_element).find(".hide-message").on("click", function() {
        $(_element).slideUp();
      });
    }
  };
});

showError指令是页面中面板的属性。当需要显示错误消息时,面板将从显示错误消息的页面顶部向下滑动,然后在3秒后向上滑动。

conditon==false第一次触发$ watch。但第二次(以及之后的时间),$ watch将不会被触发。

我使用了console.log($scope.errorMessage)来确保每次提交时,errorMessage首先设置为"",然后设置为"Invalid input"。但该小组将首次下滑,而不是以下时间。

奇怪的是:如果我只是将输入传递给后端进行验证并从后端返回errorMessage,它总是有效。

(感谢所有的帮助。简短的解决方案:如果$ digest循环被触发,$ watch将起作用。$ http调用将触发$ digest循环。在这种情况下,$ digest循环不会被触发,我必须使用$ apply或$ timeout手动触发它。)

3 个答案:

答案 0 :(得分:0)

您在单个$digest循环中将errorMessage从“无效输入”设置为“”并返回“无效输入”,因此$watch永远不会触发(因为据他所知,价值从未改变过。)

一个懒惰但可行的解决方案是简单地将errorMessage设置为勾选“无效输入”,因此$watch有时间注意到某些变化:

function validateInput(input) {
    $scope.errorMessage = ""; //reset errorMessage to "" for every new submission.
    if (condition) {
        //http call
    } else {
        $timeout(function() {
            $scope.errorMessage = "Invalid input";
        });
    });
}

(一个更好的解决方案可能是使用一个可以直接调用而不是$watch变量的错误处理程序,但听起来就像船只在那个特定的设计选择上航行......)

答案 1 :(得分:0)

是的,您可以通过手动触发摘要或等待下一个摘要来解决此问题。但不确定为什么要在Angular中构建自己的表单验证。 Angular为开箱即用的表单(验证等)提供了很好的实现。

查看:

<form name="form" novalidate>
  Name:
  <input type="text" ng-model="formDate.name" name="name" required="" />
  <br />
  <div ng-show="form.$submitted || form.name.$touched" custom-error>
    <div ng-show="form.name.$error.required">Tell us your name.</div>
    <div ng-show="form.name.$error.customError">For some reason our name needs to be a number.</div>
  </div>

  Age:
  <input type="number" ng-model="formDate.age" name="age" required="" />
  <br />
  <div ng-show="form.$submitted || form.name.$touched">
    <div ng-show="form.age.$error.required">Tell us your age.</div>
  </div>

  <br/>
  <button type="submit" ng-click="submitForm()">Submit</button>
</form>

控制器:

.controller('MainCtrl', function ($scope) {
    $scope.formData = {
       name: '',
       age: '' 
    }

    $scope.submit = function() {
        //http call accessin form data through our $scope model
    }
});

此处angular将处理隐藏或显示错误消息和表单错误。 Angular表单创建一个表单控制器,通过此控制器,您可以访问每个表单元素及其错误(即form.name。$ required)。像max,min,length,required等常规验证是开箱即用的,但结合编写自定义验证的能力,它变得非常强大(参见https://docs.angularjs.org/guide/forms#custom-validation)。

自定义验证指令

.directive('customError', function() {
        var CUSTOM_ERROR_REGEXP = /^\d+$/;
        return {
            require: 'ngModel',
            link: function(scope, elm, attrs, ctrl) {
                ctrl.$validators.customError = function(modelValue, viewValue) {
                if (ctrl.$isEmpty(modelValue)) {
                // consider empty models to be valid
                return true;
                }

                if (CUSTOM_ERROR_REGEXP.test(viewValue)) {
                // it is valid
                return true;
                }

                // it is invalid
                return false;
                };
            }
        };
    });

答案 2 :(得分:0)

创建自己的检查最好使用可重用的组件,例如指令use-form-error

查看jsfiddle的示例。

<form name="ExampleForm">
  <label>Password</label>
  <input ng-model="password" required />
  <br>
   <label>Confirm password</label>
  <input ng-model="confirmPassword" required />
  <div use-form-error="isSame" use-error-expression="password && confirmPassword && password!=confirmPassword" ng-show="ExampleForm.$error.isSame">Passwords Do Not Match!</div>
</form>