观察具有完整名称未知的多个变量

时间:2016-04-20 10:36:00

标签: angularjs

我有一个Angular控制器如下:

function SignUpController($window, accountService) {

  var vm = this;
  vm.errors = null;
  vm.user = null;

  vm.submit = function (user) {

    accountService.signup(user)

      .then(function (response) {     
        $window.location.href = "/";
      })

    .catch(function (response) {
      vm.errors = response.data.errors;
    }

  };

}

API会返回错误列表(如果存在),如下所示:

[
  {code: "name", "message": "the name is required"}
]

如果有错误,我将此列表添加到vm.errors。在HTML中我有:

<form name="form" ng-controller="SignUpController as vmsignup" ng-submit="vmsignup.submit(vmsignup.user)">
  <input ng-model="vmsignup.user.email" name="vmsignup.user.email" type="text" />
  <span class="error" validator="vmsignup.user.email"></span>
  <!-- Other form fields -->
</form>

该指令正在观察vm.errors并使用验证器填充所有跨度并使用正确的错误消息。

在我的控制器中,我总是使用vm.errors,但在我的html中,我经常使用其他东西,比如vmsignup ,因为我可能有多个控制器。

因此,在此示例中,该指令必须具有vmsignup.errors才能使其正常工作:

angular.module("app").directive("validator", validator);
validator.$inject = ["$parse"];

function validator($parse) {

  var validator = {
    link: link,
    replace: false,
    restrict: "A"
  };

  return validator;

  function link(scope, element, attributes) {

  scope.$watch("vmsignup.errors", function (errors) {

    if (errors) {

      var result = errors.filter(function (error) {

        if (error.code == null) 
          return false;

        var position = attributes.validator.lastIndexOf(".");

        if (position > -1)
          return attributes.validator.slice(position + 1).toLowerCase() === error.code.toLowerCase();
        else
          return attributes.validator.toLowerCase() === error.code.toLowerCase();

      });          

      if (result.length > 0) {
        element.show().text(result[0].message);
        return;
      }

    }

    element.hide();

  });
}

我希望该指令具有类似的内容:

scope.$watch("vm*.errors", ...

我想这是不可能的,但必须有办法解决这个问题,不是吗?

我认为解决方案可能是在表单上使用新的指令“validation”来说明模型是什么:

<form name="form" ng-controller="SignUpController as vmsignup" validation-model="vmsignup">

然后我会在验证器指令上使用它:

scope.$watch(validator.model + ".errors", function (errors) {

这是一个不错的选择吗?可以这样做吗?有人可以帮助我吗?

3 个答案:

答案 0 :(得分:0)

您可以将$ watchGroup用于此目的

答案 1 :(得分:0)

您无法在*表达式中使用$watch作为通配符,但您可以查看包含$watchCollection的集合...

一系列属性:

$scope.$watchCollection('[prop1, prop2]', function(newValues, oldValues){
  var newProp1 = newValues[0];
  var newProp2 = newValues[1];
});

返回值的函数数组:

$scope.$watchCollection([
    function(){
        return prop1;
    }, function(){
        return prop2;
    }
], function(newValues, oldValues){
  var newProp1 = newValues[0];
  var newProp2 = newValues[1];
});

<强>更新

如@Ambegodas所说,您可以将errors集合作为参数传递给您的指令。所以你的代码变成了:

angular
  .module("app")
  .directive("validator", validator);

validator.$inject = ["$parse"];

function validator($parse) {

  var validator = {
    link: link,
    replace: false,
    restrict: "A"
  };

  return validator;

  function link(scope, element, attributes) {

    scope.$watch(attributes.errors, function (errors) {

      if (errors) {

        var result = errors.filter(function (error) {
          if (error.code == null) return false;
          var position = attributes.validator.lastIndexOf(".");
          if (position > -1) {
            return attributes.validator.slice(position + 1).toLowerCase() === error.code.toLowerCase();
          }
          return attributes.validator.toLowerCase() === error.code.toLowerCase();
        });

        if (result.length > 0) {
          element.show().text(result[0].message);
          return;
        }
      }

      element.hide();

    }, true); // << objectEquality == true

  }
}

不同的建议方法

您可以为那些只能通过客户端验证验证的输入创建服务器端验证。

HTML:

<form name="form" novalidate ng-submit="vm.submit(vm.account)">

  <div class="form-group" mt-show-errors>
    <label class="control-label" for="username">Username</label>
    <input type="text" mt-validate-username class="form-control" name="username" id="username" ng-model="vm.account.username" required>
    <div ng-show="form.$submitted || form.username.$touched">
      <span ng-show="form.login.$error.required" class="help-block">Username is required</span>
      <span ng-show="form.login.$error.mtValidateUsername" class="help-block">Username already taken</span>
    </div>
  </div>

  <!-- more inputs -->

</form>

指令:

  angular
    .module('app.directives')
    .directive('mtValidateUsername', mtValidateUsername);

  function mtValidateUsername($timeout, MyAccountService) {
    return {
      require: 'ngModel',
      scope: false,
      link: function (scope, element, attrs, modelCtrl) {
        var timeoutId;
        modelCtrl.$parsers.push(function (inputValue) {
          if (!inputValue) return '';
          $timeout.cancel(timeoutId);
          timeoutId = $timeout(function () {
            MyAccountService.validateUsername(inputValue).then(function () {
              modelCtrl.$setValidity('mtValidateUsername', true);
            }).catch(function(){
              modelCtrl.$setValidity('mtValidateUsername', false);
            });
          }, 1000); // 1 second throttle
          return inputValue;
        });
      }
    };
  }

答案 2 :(得分:0)

请尝试以下方法。

      var app = angular.module('MyApp',[]);

  app.controller('MyCtrl',function($scope){
    $scope.errors =1;
    $scope.change = function(){
      $scope.errors = 2;
   }

});

  app.directive('myDirecitive',function(){

     return {
       restrict:'A',
       link:linkFunction
    }

    function linkFunction(scope,element,atrrs){            

       scope.$watch(atrrs.varName,function(newval,oldval){
         console.log("watcher called");
      });
    }

 });

  <body ng-app="MyApp", ng-controller="MyCtrl">  
   <h1>Sample Application</h1>
   <input type="text" my-direcitive var-name="errors"/>     
   <button ng-click="change()">Watch</button>     
  </body>