有没有一种简单的方法可以在网页上放置一个三态复选框并将其绑定到布尔模型,以便后者可以采用true
,false
或null
值?
到目前为止,我找到的最接近的解决方案是http://jsfiddle.net/HB7LU/454/,但在设置初始视图状态时存在缺陷(因为在第一次渲染期间无法获取模型值)。任何其他建议都处理多个子复选框,并通过观察它们来解决问题。
有没有一种简单的方法可以在网页上放置一个三态复选框并将其绑定到布尔模型,以便后者可以采用true
,false
或null
值?
到目前为止,我找到的最接近的解决方案是http://jsfiddle.net/HB7LU/454/,但在设置初始视图状态时存在缺陷(因为在第一次渲染期间无法获取模型值)。任何其他建议都处理多个子复选框,并通过观察它们来解决问题。
http://jsfiddle.net/xJhEG/我是在一个商业项目中制作的。三态是真、假、空(不是“未知”)
.directive('indeterminate', [function() {
return {
require: '?ngModel',
link: function(scope, el, attrs, ctrl) {
var truthy = true;
var falsy = false;
var nully = null;
ctrl.$formatters = [];
ctrl.$parsers = [];
ctrl.$render = function() {
var d = ctrl.$viewValue;
el.data('checked', d);
switch(d){
case truthy:
el.prop('indeterminate', false);
el.prop('checked', true);
break;
case falsy:
el.prop('indeterminate', false);
el.prop('checked', false);
break;
default:
el.prop('indeterminate', true);
}
};
el.bind('click', function() {
var d;
switch(el.data('checked')){
case falsy:
d = truthy;
break;
case truthy:
d = nully;
break;
default:
d = falsy;
}
ctrl.$setViewValue(d);
scope.$apply(ctrl.$render);
});
}
};
}])
您已经利用<input type="checkbox">
.
MDN web docs:
存在一种不确定的复选框状态,即未选中或未选中,但未确定。这是通过 JavaScript 使用 HTMLInputElement 对象的不确定属性设置的(不能使用 HTML 属性设置)。
HTML
<label>
<input type="checkbox" ng-model="state" indeterminate /> {{state}}
</label>
指示
app.directive('indeterminate', function() {
return {
restrict: 'A',
scope: {
model: '=ngModel'
},
link: function(scope, el, attrs, ctrl) {
var states = [true, false, undefined];
var index = states.indexOf(scope.model);
setIndeterminate();
el.bind('click', function() {
scope.model = states[++index % 3];
setIndeterminate();
});
function setIndeterminate() {
scope.$applyAsync(function() {
el[0].indeterminate = (scope.model === undefined);
});
}
}
};
});
我创建了指令,您可以使用它。 GitHub 上的三态复选框 AngularJS 指令
还有一个帖子,它是如何构建的:Creating Angular Directive "Three-state checkbox
你可以试试DEMO
该指令如下所示:
angular.module("threeStateCheckbox", [])
.directive("threeStateCheckbox", ['$compile', function($compile){
return {
restrict: "A",
transclude: true,
require: 'ngModel',
link: function(scope, element, attrs, ngModel){
var states = [true, false, null];
var classNames = ["checked", "unchecked", "clear"];
scope.click = function(){
var st;
states.map(function(val, i){
if(ngModel.$modelValue === val){
st = states[(i+1)%3];
}
});
ngModel.$setViewValue(st);
ngModel.$render();
};
scope.tscClassName = function(){
var className;
states.map(function(val, i){
if(ngModel.$modelValue=== val){
className = classNames[i];
}
});
return className;
};
element.attr("class", "tri-sta-che ");
element.attr("ng-click", "click()");
element.attr("ng-class", "tscClassName()");
element.removeAttr("three-state-checkbox");
$compile(element)(scope);
}
};
}]);
因为自 AngularJS 1.7 以来所有先前的答案都不起作用(复选框模型现在只允许布尔值,其他所有内容都转换为布尔值),我现在找到了一个在 v1.8 之前有效的解决方案:感谢@The.Bear 的基础.
要使用它,只需包含<tristate label="someOptionalText" ng-model="yourVariableToBindTo" />
和相应的指令。
var app = angular.module('tristatedemo', []);
app.controller('MyCtrl', function() {
this.state = null; //initial state
});
app.directive('tristate', function() {
return {
restrict: 'E',
scope: {
model: '=ngModel',
label: '@'
},
template: '<input type="checkbox" /> {{label}}',
link: function(scope, el, attrs, ctrl) {
var states = [true, false, null];
var index = states.indexOf(scope.model);
setIndeterminate();
el.bind('click', function() {
scope.model = states[++index % 3];
setIndeterminate();
});
function setIndeterminate() {
scope.$applyAsync(function() {
var cb = el.find('input')[0];
cb.checked = scope.model;
cb.indeterminate = (scope.model === null);
});
}
},
};
});
<!DOCTYPE html>
<html ng-app="tristatedemo">
<head>
<script data-require="angular.js@1.8.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.2/angular.min.js" data-semver="1.8.2"></script>
</head>
<body ng-controller="MyCtrl as ctrl">
{{(ctrl.state === null) ? "null" : ctrl.state}}<br> <!-- only for demo -->
<tristate label="abc" ng-model="ctrl.state" />
</body>
</html>