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: '<span>{% raw %}{{names}}{% endraw %}</span> ' +
              '<button ng-click="open()">select</button> ' +
              '<button ng-click="clear()">clear</button>',
    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 了,非常方便,并且代码简洁。

<user-select users="users"></user-select>
or
<span user-select users="users"></span>
or
<div user-select users="users"></div>

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

在 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: '<span>{% raw %}{{names}}{% endraw %}</span> ' +
              '<button ng-click="open()">select</button> ' +
              '<button ng-click="clear()">clear</button>',
    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" 属性。

<user-select users="users" multi="true"></user-select>
or
<span user-select users="users" multi="true"></span>
or
<div user-select users="users" multi="true"></div>

请注意:因为实际使用中我们不需要在运行中改变 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: '<span>{% raw %}{{names}}{% endraw %}</span> ' +
              '<button ng-click="open()">select</button> ' +
              '<button ng-click="clear()">clear</button>',
    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 属性和错误提示。

<form name="editForm">
  <div>
    <label>users:</label>
    <span user-select name="users" ng-model="users" multi="true" ng-required="true" class="form-input"></span>
    <span class="error" ng-show="editForm.users.$error.required">speaker is required!</span>
  </div>
</form>

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容