自定义复选框指令的angularJS方式?

时间:2013-07-23 07:53:41

标签: angularjs angularjs-directive

我正在尝试使用<input type="checkbox">构建一个自定义复选框指令(以便能够根据我的喜好设置它)。它应该像人们期望的那样工作;更新模型应更新视图,单击该框应更新模型,它应该监听required指令(验证应该有效)。

我目前有以下指令,完全符合预期:

'use strict';

angular.module('directives.checkbox', [])
  .directive('checkbox', [function () {
    return {
      restrict: 'A',
      require: 'ngModel',
      replace: true,
      template: '<span class="checkbox"></span>',
      link: function (scope, elem, attrs, ctrl) {
        elem.bind('click', function () {
          scope.$apply(function () {
            elem.toggleClass('checked');
            ctrl.$setViewValue(elem.hasClass('checked'));
          });
        });

        ctrl.$render = function () {
          if (!elem.hasClass('checked') && ctrl.$viewValue) {
            elem.addClass('checked');
          } else if (elem.hasClass('checked') && !ctrl.$viewValue) {
            elem.removeClass('checked');
          }
        };
      }
    }
  }]);

它只是<span>的CSS类checkbox和另一个CSS类checked的已检查状态。但是,使用jQuery切换类并像我一样更新视图似乎没有像角度一样(或最佳实践)。我宁愿使用ng-classng-click指令,但这意味着我需要使用一个隔离的范围(以避免在同一页面上使用多个复选框时范围状态冲突)。出于某种原因,隔离范围会使角度停止在$render()上调用ctrl

有没有人知道这是否是正确的方法,或者是否有更像角度的方法仍能解决我的要求?

3 个答案:

答案 0 :(得分:1)

首先,我相信如果样式是你的问题,你肯定可以通过css自定义输入,而不是创建一个必须模仿输入复选框的指令。

话虽这么说,添加和删除类的角度方式实际上是在模板中包含作为表达式或作为ngClass的参数。 您还可以通过在模板中包含ngClick来避免绑定。

app.directive('myCheckbox', function() {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    template: '<span class="checkbox" ng-class="{checked: isChecked}" ng-click="toggleMe()"></span>',
    scope: { isChecked: '=?'},
    link: function(scope, elem, attrs) {
      scope.isChecked = true;

    scope.toggleMe = function() {
        scope.isChecked = !(scope.isChecked);
    }
 }
 });

Here is a little plunk解释我的意思

答案 1 :(得分:0)

不为复选框调用

$ render函数。我们可以监视模型更改并检查元素状态并相应地更新UI,而不是提供渲染功能。

以下是指令代码。

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

app.controller('MainCtrl', function($scope) {
  $scope.name = 'World';
});

app.directive('radioOrCheck', radioOrCheckDirective);

function radioOrCheckDirective() {
  return {
    require: 'ngModel',
    restrict: 'A',
    scope: false,
    link: link
  };

  function link(scope, element, attrs) {
    element.addClass('hidden-input').wrap('<label/>');
    element.parent().addClass(attrs.type).append('<span/>');

    scope.$watch(attrs.ngModel, function updateUI() {
      element.parent().toggleClass('checked', jQuery(element).is(':checked'));
    });

    attrs.$observe('label', function(newLabel) {
      element.parent().find('span').text(newLabel);
    });
  }
}
.hidden-input {
  border: 0;
  clip: rect(0 0 0 0);
  height: 0;
  margin: 0;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 0;
}
.radio {
  display: block;
  cursor: pointer;
  color: red;
}
.radio.checked {
  color: green;
}
.checkbox {
  display: block;
  cursor: pointer;
  color: red;
}
.checkbox.checked {
  color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input radio-or-check="" type="checkbox" ng-model="cbModel" data-label="CB Label" />
<br/>
<input radio-or-check="" type="checkbox" ng-model="cbModel" data-label="CB Label" />
<br/>
<input radio-or-check="" type="checkbox" ng-model="cbModel" data-label="CB Label" />
<br/>
<br/>
<input radio-or-check="" type="radio" ng-model="rModel" data-label="R Label" value="1" />
<br/>
<input radio-or-check="" type="radio" ng-model="rModel" data-label="R Label" value="2" />
<br/>
<input radio-or-check="" type="radio" ng-model="rModel" data-label="R Label" value="3" />
<br/>

Here is the working plunker。这里的输入字段是模拟默认值,CSS可以根据您的需要进行调整。

答案 2 :(得分:0)

不为复选框调用

$ render函数。我们可以监视模型更改并检查元素状态并相应地更新UI,而不是提供渲染功能。

以下是指令代码。

&#13;
&#13;
var app = angular.module('RadioOrCheck', []);

app.controller('MainCtrl', function($scope) {
  $scope.name = 'World';
});

app.directive('radioOrCheck', radioOrCheckDirective);

function radioOrCheckDirective() {
  return {
    require: 'ngModel',
    restrict: 'A',
    scope: false,
    link: link
  };

  function link(scope, element, attrs) {
    element.addClass('hidden-input').wrap('<label/>');
    element.parent().addClass(attrs.type).append('<span/>');

    scope.$watch(attrs.ngModel, function updateUI() {
      element.parent().toggleClass('checked', jQuery(element).is(':checked'));
    });

    attrs.$observe('label', function(newLabel) {
      element.parent().find('span').text(newLabel);
    });
  }
}
&#13;
.hidden-input {
  border: 0;
  clip: rect(0 0 0 0);
  height: 0;
  margin: 0;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 0;
}
.radio {
  display: block;
  cursor: pointer;
  color: red;
}
.radio.checked {
  color: green;
}
.checkbox {
  display: block;
  cursor: pointer;
  color: red;
}
.checkbox.checked {
  color: green;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="RadioOrCheck">
<input radio-or-check="" type="checkbox" ng-model="cbModel" data-label="CB Label" />
<br/>
<input radio-or-check="" type="checkbox" ng-model="cbModel" data-label="CB Label" />
<br/>
<input radio-or-check="" type="checkbox" ng-model="cbModel" data-label="CB Label" />
<br/>
<br/>
<input radio-or-check="" type="radio" ng-model="rModel" data-label="R Label" value="1" />
<br/>
<input radio-or-check="" type="radio" ng-model="rModel" data-label="R Label" value="2" />
<br/>
<input radio-or-check="" type="radio" ng-model="rModel" data-label="R Label" value="3" />
<br/>
</div>
&#13;
&#13;
&#13;

Here is the working plunker。这里的输入字段是模拟默认值,CSS可以根据您的需要进行调整。