【问题标题】:Custom checkbox directive the angularJS way?自定义复选框指令angularJS方式?
【发布时间】:2023-03-27 20:52:01
【问题描述】:

我正在尝试不使用<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');
          }
        };
      }
    }
  }]);

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

有谁知道这是否是正确的方法,或者是否有更类似于角度的方法仍然可以解决我的要求?

【问题讨论】:

    标签: angularjs angularjs-directive


    【解决方案1】:

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

    话虽如此,添加和删除类的角度方式实际上是将 then 作为表达式或作为 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解释我的意思

    【讨论】:

    • 我真的不明白为什么我被否决了。我提供了一个带有示例的工作解决方案,而不仅使用角度也没有使用 jquery。在任何情况下,都应该通过评论来证明反对票是合理的。
    • 我没有对你投反对票,但你的解决方案的问题是多个复选框不能在同一页面上,它们将共享 isChecked 范围变量。
    • 您好,感谢您的评论。我没有使用隔离范围以防止增加混乱。我只是想演示如何在不使用 jQuery 的情况下操作 dom。我现在已经更新了我的答案(和 plunker)以提供一个孤立的范围。如果您不想绑定到外部范围内的某些内容,则绑定也是可选的。它也可以通过依赖 ngModel 值来完成。
    • 谢谢,但这仍然不能完全回答我的问题。我知道作用域和隔离作用域是如何工作的,但我的问题是关于ngModel$ctrl.render(),这似乎不适用于隔离作用域。您的解决方案不适用于ngModel(以及随附的验证指令,例如required)。
    • 这是个好主意。将其发布为答案,我会接受它:)
    【解决方案2】:

    $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 可以根据您的需要进行调整。

    【讨论】:

      【解决方案3】:

      $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/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>

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

      【讨论】:

        猜你喜欢
        • 2013-09-23
        • 1970-01-01
        • 2014-04-04
        • 2014-06-24
        • 2015-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多