AngularJS Custom Directive Validation on Array Model

我们常常需要自定义一些输入控件,如选择用户控件,在 angularjs 中允许我们添加自定义 directive,于是,
接下来我们就用 directive来实现选择用户控件。

app.directive('userSelect', function($q, $window) {
  function asyncOpenUserSelectDialog(users, opts) {
    var deferred = $q.defer();
    // here is a demo, replace it with your code
    $window.setTimeout(function() {
      deferred.resolve([{ name: 'bob' }, { name: 'john' }]);
    }, 100);
    return deferred.promise;
  }

  return {
    restrict: 'EAC',
    scope: {
      users: '='
    },
    template: '{% raw %}{{names}}{% endraw %} ' +
              ' ' +
              '',
    link: function(scope, elem, attrs) {
      function getNames(users) {
        if (!users) {
          return null;
        }
        return users.map(function(user) { return user.name; }).join(', ');
      }

      scope.open = function() {
        asyncOpenUserSelectDialog(scope.users).then(function(selectedUsers) {
          scope.users = selectedUsers;
        });
      };

      scope.clear = function() {
        scope.users = [];
      };

      scope.$watch('users', function(users) {
        scope.names = getNames(users);
      });
    }
  };
});

当 directive 添加好了之后,我们在 html 中使用这个 directive 了,非常方便,并且代码简洁。


or

or

然后,我们需要给选择用户控件添加一个多选的开关。

在 directive 的 scope 属性上添加 multi: '=',这样我们可以在 scope 中读取到 multi 的值,于是对
directive 进行如下修改。

app.directive('userSelect', function($q, $window) {
  function asyncOpenUserSelectDialog(users, opts) {
    var deferred = $q.defer();
    // here is a demo, replace it with your code
    $window.setTimeout(function() {
      var users = [{ name: 'bob' }, { name: 'john' }];
      deferred.resolve(opts.multi ? users : users[0]);
    }, 100);
    return deferred.promise;
  }

  return {
    restrict: 'EAC',
    scope: {
      users: '=',
      multi: '='
    },
    template: '{% raw %}{{names}}{% endraw %} ' +
              ' ' +
              '',
    link: function(scope, elem, attrs) {
      function getNames(users) {
        if (!users) {
          return null;
        }
        return users.map(function(user) { return user.name; }).join(', ');
      }

      function getName(user) {
        if (!user) {
          return null;
        }
        return user.name;
      }

      scope.open = function() {
        asyncOpenUserSelectDialog(scope.users, { multi: scope.multi }).then(function(selectedUsers) {
          scope.users = selectedUsers;
        });
      };

      scope.clear = function() {
        scope.users = scope.multi ? [] : null;
      };

      scope.$watch('users', function(users) {
        scope.names = scope.multi ? getNames(users) : getName(users);
      });
    }
  };
});

然后,在 html element 上添加 multi="true/false" 属性。


or

or

请注意:因为实际使用中我们不需要在运行中改变 mulit 的值,所以在我们的代码中没有监测 multi 值的变化,请根据需要添加监控该属性。

      scope.$watch('multi', function(value) {
        scope.clear();
      });

接下来,我们要把选择用户控件放到 form 中,并验证所选用户不能为空。

Angularjs 提供了 form 的 validate 功能,ng-required 用来验证 model 不为空,默认在 NgModelController 上提供了
$isEmpty 方法检查 model value 不是 undefined, '', null,但是我们的 model 可能是 array,所以我们需要覆盖默认的
$isEmpty 方法。

首先,我们需要使用 NgModelController,则需要在 directive 中添加 require: 'ngModel' 属性,然后 link 方法中的第
四个参数就会是 NgModelController 了,如 link: function(scope, elem, attrs, ctrl) 中的 ctrl 即 NgModelController。

同时,我们不能在使用 users 属性,而需要使用 ng-model 属性为 directive 设置 model。

所以,我们再次修改我们的 directive 代码。

app.directive('userSelect', function($q, $window) {
  function asyncOpenUserSelectDialog(users, opts) {
    var deferred = $q.defer();
    // here is a demo, replace it with your code
    $window.setTimeout(function() {
      var users = [{ name: 'bob' }, { name: 'john' }];
      deferred.resolve(opts.multi ? users : users[0]);
    }, 100);
    return deferred.promise;
  }

  return {
    restrict: 'EAC',
    scope: {
      ngModel: '=',
      multi: '='
    },
    require: 'ngModel',
    template: '{% raw %}{{names}}{% endraw %} ' +
              ' ' +
              '',
    link: function(scope, elem, attrs, ctrl) {
      function getNames(users) {
        if (!users) {
          return null;
        }
        return users.map(function(user) { return user.name; }).join(', ');
      }

      function getName(user) {
        if (!user) {
          return null;
        }
        return user.name;
      }

      scope.open = function() {
        asyncOpenUserSelectDialog(scope.users, { multi: scope.multi }).then(function(selectedUsers) {
          ctrl.$setViewValue(selectedUsers);
        });
      };

      scope.clear = function() {
        ctrl.$setViewValue(scope.multi ? [] : null);
      };

      scope.$parent.$watch(attrs.ngModel, function(users) {
        scope.users = users;
      });

      scope.$watch('users', function(users) {
        scope.names = scope.multi ? getNames(users) : getName(users);
      });

      ctrl.$isEmpty = function(value) {
        return !value || (scope.multi && value.length === 0);
      };
    }
  };
});

最后,在 html 中将选择用户控件放入到 form 中,并添加 name 属性和错误提示。

speaker is required!

完整代码示例请转到 jsfiddler 页面 Angularjs User Select Directive

你可能感兴趣的:(AngularJS Custom Directive Validation on Array Model)