在表单级别使用$ setValidity后,有效性将被置于false状态

时间:2015-12-22 21:13:25

标签: javascript angularjs forms validation

我在Angular中写了一个相当复杂的搜索表单。

表单分为几部分 - 您可以按ID,URL,文本字符串或位置进行搜索。当您同时按文本字符串和位置搜索时,我需要验证您是否一起提交纬度和经度。

换句话说,通过文本字符串搜索很好。通过lat / lng搜索很好。通过文本字符串和纬度搜索但省略经度是不行的。

我尽可能使用HTML5和角度指令来验证各个字段'内容,但是我试图通过使用范围观察器,查看表单对象来验证特定的值组合,并且如果我发现当前搜索模式与特定组合不兼容,则使用$setValidity()字段。

我目前的问题是,一旦我使用$setValidity()一次,该验证状态就会被卡住"。当用户切换出'textOrLocation'搜索模式时,我想让angular回到默认的验证行为。我不明白为什么它没有这样做 - 我在$setValidity()模式下检查表单后,只调用范围更改'textOrLocation'

使用Javascript:

$scope.search = {
    mode: 'id'
};

$scope.$watch(textOrLocationValid, function() {});

function textOrLocationValid() {
    var usingTextOrLocation = $scope.search.mode == 'textOrLocation';
    if (usingTextOrLocation) {
        var textModel = $scope.form.searchText || {},
            textValid = textModel.$valid,
            textValue = textModel.$modelValue,
            latModel = $scope.form.searchLat || {},
            latValid = latModel.$valid,
            latValue = latModel.$modelValue,
            lngModel = $scope.form.searchLng || {},
            lngValid = lngModel.$valid,
            lngValue = lngModel.$modelValue,
            formValid = (textValid && latValid && lngValid) && // No invalid fields
            ((latValue && 1 || 0) + (lngValue && 1 || 0) != 1) && // Either both lat and long have values, or neither do
            (textValue || latValue); // Either text or location are filled out
        if (formValid) {
            // Explicitly set form validity to true
            $scope.form.$setValidity('textOrLocation', true);
        } else {
            // Explicitly set form validity to false
            $scope.form.$setValidity('textOrLocation', false);
        }
    }
}

HTML

<form name="form">
    <div ng-if="search.mode == 'id'">
        <input type="text" name="searchId" required>
    </div>
    <div ng-if="search.mode == 'textOrLocation'">
        <input type="text" name="searchText">
        <input type="number" name="searchLat" min="-90" max="90" step="0.000001">
        <input type="number" name="searchLng" min="-180" max="180" step="0.000001">
    </div>
    <button ng-disabled="form.$invalid">Submit</button>
</form>

3 个答案:

答案 0 :(得分:1)

我的理解是,因为正在观看该功能,所以它实际上是在每次摘要期间由Angular进行评估的。一个简单的解决方案可能是在特定表单不在焦点时将textOrLocation的有效性设置为true。这将允许按钮状态取决于id形式中字段的有效性。

function textOrLocationValid() {
    var usingTextOrLocation = $scope.search.mode == 'textOrLocation';
    if (usingTextOrLocation) {
        var textModel = $scope.form.searchText || {},
            textValid = textModel.$valid,
            textValue = textModel.$modelValue,
            latModel = $scope.form.searchLat || {},
            latValid = latModel.$valid,
            latValue = latModel.$modelValue,
            lngModel = $scope.form.searchLng || {},
            lngValid = lngModel.$valid,
            lngValue = lngModel.$modelValue,
            formValid = (textValid && latValid && lngValid) && // No invalid fields
            ((latValue && 1 || 0) + (lngValue && 1 || 0) != 1) && // Either both lat and long have values, or neither do
            (textValue || latValue); // Either text or location are filled out
        if (formValid) {
            // Explicitly set form validity to true
            $scope.form.$setValidity('textOrLocation', true);
        } else {
            // Explicitly set form validity to false
            $scope.form.$setValidity('textOrLocation', false);
        }
    }
    else{
        // Explicitly set form validity to true because form is not active
        $scope.form.$setValidity('textOrLocation', true);
    }
}

答案 1 :(得分:1)

要创建复杂的测试,您可以使用小指令use-form-error,这对您将来也可能有用。

使用此指令,您可以写:

<div ng-form="myForm">
  <div>
    <input type="text" ng-model="searchText" name="searchText">
    <input type="number" ng-model="searchLat" name="searchLat" min="-90" max="90" step="0.000001">
    <input type="number" ng-model="searchLng" name="searchLng" min="-180" max="180" step="0.000001">
    <span use-form-error="textOrLocation" use-error-expression="textOrLocationValid()" use-error-input="myForm"></span>
  </div>
  {{myForm.$error}}
  <br>
  <button ng-disabled="myForm.$invalid">Submit</button>
</div>

和JS

angular.module('ExampleApp', ['use']).controller('ExampleController', function($scope) {
$scope.search = {
  mode: 'textOrLocation'
};
$scope.textOrLocationValid = function() {
  var usingTextOrLocation = $scope.search.mode == 'textOrLocation';
  if (usingTextOrLocation) {
    var textModel = $scope.myForm.searchText || {},
      textValid = textModel.$valid,
      textValue = textModel.$modelValue,
      latModel = $scope.myForm.searchLat || {},
      latValid = latModel.$valid,
      latValue = latModel.$modelValue,
      lngModel = $scope.myForm.searchLng || {},
      lngValid = lngModel.$valid,
      lngValue = lngModel.$modelValue,
      formValid = (textValid && latValid && lngValid) && // No invalid fields
      ((latValue && 1 || 0) + (lngValue && 1 || 0) != 1) && // Either both lat and long have values, or neither do
      (textValue || latValue); // Either text or location are filled out
    return !formValid;
  } else {
    // Explicitly set form validity to true because form is not active
    return false;
  }
}

});

jsfiddle上的实例:

答案 2 :(得分:0)

我开始工作,您可以在此Plunker

中找到代码

有几个问题(我正在假设,你正在尝试在控制器中处理你的表单,而不是在自定义指令中):

    我们尝试在控制器中访问的
  1. $scope.form与视图中的格式不同。表格有自己的范围。在控制器中无法直接访问。要解决此问题,我们可以将form附加到$scope.forms - 在控制器中声明的对象(有关继承模式的详细信息here
  2. 我们应该将ng-model附加到输入,因此我们可以观看它们,因为无法直接观看表单(阅读更多here
  3. 此外,我们还必须观看$scope.search更改。
  4. 它绝对不是处理自定义验证的最优雅的解决方案......将尝试为此提出自定义指令。