【发布时间】:2016-01-22 11:23:44
【问题描述】:
在我的 Angular 应用程序中,我定义了一个包含 <input type="range"> 的自定义滑块指令。我的自定义指令支持ngModel 将滑块的值绑定到变量。自定义指令还需要 fraction-size 属性。它对该值进行计算,然后使用结果设置包装后的<input> 的step 值。
当我结合这两个功能时,我看到了一个错误 - ngModel 和我的绑定属性值。它们以错误的顺序运行。
这是一个演示:
angular.module('HelloApp', []);
angular.module('HelloApp').directive('customSlider', function() {
var tpl = "2 <input type='range' min='2' max='3' step='{{stepSize}}' ng-model='theNum' /> 3";
return {
restrict: 'E',
template: tpl,
require: 'ngModel',
scope: {
fractionSize: '='
},
link: function(scope, element, attrs, ngModelCtrl) {
scope.stepSize = 1 / scope.fractionSize;
scope.$watch('theNum', function(newValue, oldValue) {
ngModelCtrl.$setViewValue(newValue);
});
ngModelCtrl.$render = function() {
scope.theNum = ngModelCtrl.$viewValue;
};
}
};
});
angular.module('HelloApp').controller('HelloController', function($scope) {
$scope.someNumber = 2.5;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<div ng-app="HelloApp" ng-controller="HelloController">
<h3>Custom slider</h3>
<custom-slider ng-model="someNumber" fraction-size="10"></custom-slider>
<h3>View/edit the slider’s value</h3>
<input ng-model="someNumber"></input>
</div>
上面的滑块应该从中间开始,代表2.5。但它实际上是从右边开始的(代表 3)。如果您拖动它,或者通过编辑文本字段更改其绑定值,则滑块会自行修复并允许值为 2.5。
我已经弄清楚为什么会在演示中发生这种情况 - 我只是不知道如何解决它。目前,当一个新的自定义滑块动态添加到页面时,包裹的<input> 的step 是未定义的,默认为1。然后ngModel 将值设置为2.5——但由于step 为1, input 中的值四舍五入为 3。最后,step 设置为 0.1 - 为时已晚。
如何确保step 属性的值在 ngModel 设置输入的value 之前绑定?
在上面的示例中,滑块在页面加载时位于页面上。在我的真实代码中,动态添加了多个新滑块。无论何时添加它们,它们都应该以正确的顺序绑定它们的step 和value。
我不喜欢的解决方法
一种解决方法是在模板中硬编码step,而不是动态设置它。如果我这样做,我的自定义滑块指令将毫无用处,我将删除它并直接使用 <input>:
<input type="range" min="2" max="3" step="0.1" ng-model="someNumber">
如果我使用它,滑块的value 设置正确,没有四舍五入。但我想保留我的自定义指令customSlider,以便抽象step 的计算。
我尝试过的方法不起作用
- 更改模板中
step和ng-model属性的顺序没有任何效果。 - 我开始尝试添加一个
compile函数,而不仅仅是一个link函数。但我不能在compile中设置stepSize,因为scope在编译阶段不可用。 - 我尝试将指令的
link函数拆分为预链接和后链接函数。但无论我将scope.stepSize设置为pre还是post,页面都像以前一样工作。 - 在设置
scope.stepSize后立即手动调用scope.$digest()只会抛出Error: [$rootScope:inprog] $digest already in progress。 - 我认为自定义指令的优先级不适合这种情况,因为只涉及一个自定义指令。我对
step值的绑定不是自定义指令,它只是原始的{{}}-binding。而且我认为绑定step是一项足够简单的任务,它不应该被包装在自己的指令中。
【问题讨论】:
-
作为 Icycool noted,有一个未解决的 Angular 问题,#6726 “ng-model does not set initial value and position of input[type=range]”。
标签: angularjs data-binding angular-ngmodel