在我的Angular应用程序中,我定义了一个包装<input type="range">
的自定义滑块指令。我的自定义指令支持ngModel
将滑块的值绑定到变量。自定义指令还需要fraction-size
属性。它对值进行计算,然后使用结果设置包装step
的{{1}}值。
当我将这两个功能组合在一起时,我发现了一个错误 - <input>
和我的绑定属性值。它们以错误的顺序运行。
这是一个演示:
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;
});
上面的滑块应该从中间开始,表示2.5。但它实际上从右边开始(代表3)。滑块固定自身,如果您拖动它,则允许值为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>
的{{1}}未定义,默认为1.然后<input>
将值设置为2.5 - 但是step
为1,ngModel
中的值四舍五入为3.最后,step
设置为input
- 对于它来说太晚了。
如何在 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
,因为在编译阶段stepSize
不可用。compile
功能拆分为预链接和后链接功能。但是,无论我在scope
还是在link
中设置scope.stepSize
,该页面都会像以前一样工作。pre
之后立即手动拨打post
只会抛出scope.$digest()
。scope.stepSize
的值的绑定不是自定义指令,它只是原始Error: [$rootScope:inprog] $digest already in progress
- 绑定。我认为绑定step
是一个足够简单的任务,它不应该包含在它自己的指令中。答案 0 :(得分:2)
在link
函数中,通过添加step
属性来操作DOM。
您还可以通过将ngModel
放入theNum: '=ngModel'
来简化与外部scope
的绑定。
var app = angular.module('HelloApp', []);
app.directive('customSlider', function () {
var tpl = "2 <input type='range' min='2' max='3' ng-model='theNum' /> 3";
return {
restrict: 'E',
template: tpl,
require: 'ngModel',
scope: {
fractionSize: '=',
theNum: '=ngModel'
},
link: function (scope, element, attrs, ngModelCtrl) {
var e = element.find('input')[0];
var step = 1 / scope.fractionSize;
e.setAttribute('step', step);
scope.$watch('fractionSize', function (newVal) {
if (newVal) {
var step = 1 / newVal;
e.setAttribute('step', step);
}
});
}
};
});
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> {{ someNumber }}
</div>
答案 1 :(得分:2)
进一步采用@pixelbits' answer直接DOM操作,我根本不会在内部输入上有另一个ng-model
,而是直接在输入上设置/获取属性
从输入设置/获取值时,您需要考虑一个抽象级别。原始DOM事件和元素。
因此,您不仅限于Angular允许这些元素的内容(实际上:它似乎无法在没有解决方法的情况下处理您的用例)。如果浏览器允许它在范围输入上,您可以这样做。
在一个UI小部件上播放2个ngModelControllers,设置一个值可能会让人感到有些困惑,至少对我而言!
如果您需要/想要使用解析器和验证器,您仍然可以访问外部ngModelController管道及其所有功能。
您可以节省额外的观察员(但这可能是微观/过早优化)。
请参阅下面的示例。
angular.module('HelloApp', []);
angular.module('HelloApp').directive('customSlider', function() {
var tpl = "2 <input type='range' min='2' max='3' /> 3";
return {
restrict: 'E',
template: tpl,
require: 'ngModel',
scope: {
fractionSize: '='
},
link: function(scope, element, attrs, ngModelCtrl) {
var input = element.find('input');
input.prop('step', 1 / scope.fractionSize);
input.on('input', function() {
scope.$apply(function() {
ngModelCtrl.$setViewValue(input.prop('value'));
});
});
ngModelCtrl.$render = function(value) {
input.prop('value', ngModelCtrl.$viewValue);
};
}
};
});
angular.module('HelloApp').controller('HelloController', function($scope) {
$scope.someNumber = 2.5;
});
&#13;
<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>
&#13;
获取
答案 2 :(得分:0)
我不确定这是否是正确的方法,但是向主控制器添加超时可以解决问题。
$timeout(function() {
$scope.someNumber = 2.5;
});
编辑:虽然它起初看起来像是一个肮脏的黑客,但请注意,(最终)$ scope变量通常比模板分配更常见,因为需要额外的ajax调用来检索值。