Basically, a range input is set up with an initial value using ng-model. The actual value of the HTML element, and the position of the range slider, are incorrect. I would expect the slider position and html input value to be in sync with the model variable.
Here is a repro: http://jsfiddle.net/fschwiet/HVp2J/
Verified on chrome and firefox running on Windows 8.
I see there are other issues opened for input[type=range], its not clear they are related.
This is still an issue with Angular 1.2.14: http://jsfiddle.net/HVp2J/7/
As a work-around, one can set the model value within a $timeout handler.
$timeout doesn' help, the issue remains.
@fschwiet : http://jsfiddle.net/HVp2J/9/
@Rajat-Chowdhary Sorry, I should have included a code sample showing how $timeout can work as work-around: http://jsfiddle.net/fschwiet/YC4av/
The workaround does feel dicey since the model value cannot be initialized until the $timeout callback for the workaround to work.
It seems I found the problem.
Angular sets value before setting min, max and step so it seems that the range is in the wrong position. To solve the problem I used this directive
angular.module('angular-range', []).directive('range', function() {
return {
replace: true,
restrict: 'E',
scope: {
value: '=ngModel',
min: '=rangeMin',
max: '=rangeMax',
step: '=rangeStep',
},
template: '<input type="range"/>',
link: function(scope, iElement, iAttrs) {
scope.$watch('min', function() { setValue(); });
scope.$watch('max', function() { setValue(); });
scope.$watch('step', function() { setValue(); });
scope.$watch('value', function() { setValue(); });
function setValue() {
if (
angular.isDefined(scope.min) &&
angular.isDefined(scope.max) &&
angular.isDefined(scope.step) &&
angular.isDefined(scope.value)
) {
iElement.attr("min", scope.min);
iElement.attr("max", scope.max);
iElement.attr("step", scope.step);
iElement.val(scope.value);
}
}
function read() {
scope.value = iElement.val();
}
iElement.on('change', function() {
scope.$apply(read);
});
}
};
});
Here there is the new version of ngRange without private scope:
.module('ngRange', [])
.directive('ngRange', function() {
return {
replace: true,
restrict: 'E',
require: 'ngModel',
template: '<input type="range"></input>',
link: function(scope, element, attrs, ngModel) {
var ngRangeMin;
var ngRangeMax;
var ngRangeStep;
var value;
if (!angular.isDefined(attrs.ngRangeMin)) {
ngRangeMin = 0;
} else {
scope.$watch(attrs.ngRangeMin, function(newValue, oldValue, scope) {
if (angular.isDefined(newValue)) {
ngRangeMin = newValue;
setValue();
}
});
}
if (!angular.isDefined(attrs.ngRangeMax)) {
ngRangeMax = 100;
} else {
scope.$watch(attrs.ngRangeMax, function(newValue, oldValue, scope) {
if (angular.isDefined(newValue)) {
ngRangeMax = newValue;
setValue();
}
});
}
if (!angular.isDefined(attrs.ngRangeStep)) {
ngRangeStep = 1;
} else {
scope.$watch(attrs.ngRangeStep, function(newValue, oldValue, scope) {
if (angular.isDefined(newValue)) {
ngRangeStep = newValue;
setValue();
}
});
}
if (!angular.isDefined(ngModel)) {
value = 50;
} else {
scope.$watch(
function () {
return ngModel.$modelValue;
},
function(newValue, oldValue, scope) {
if (angular.isDefined(newValue)) {
value = newValue;
setValue();
}
}
);
}
function setValue() {
if (
angular.isDefined(ngRangeMin) &&
angular.isDefined(ngRangeMax) &&
angular.isDefined(ngRangeStep) &&
angular.isDefined(value)
) {
element.attr("min", ngRangeMin);
element.attr("max", ngRangeMax);
element.attr("step", ngRangeStep);
element.val(value);
}
}
function read() {
if (angular.isDefined(ngModel)) {
ngModel.$setViewValue(value);
}
}
element.on('change', function() {
if (angular.isDefined(value) && (value != element.val())) {
value = element.val();
scope.$apply(read);
}
});
}
};
});
this is still an issue, right?
Yep +1
Yes, still an issue.
I asked something similar in stackoverflow How to initialize the value of an input[range] using AngularJS when value is over 100 . Using 1.4.0-beta.build.3791 this bug is still present.
+1
+1, and it became worst when the input range is coupled with an input number.
+1 for this issue
Facing the same problem: +1
yep - same problem +1
this is basic html5 - whats the status on this, is anyone working on this?
taking under consideration this: https://github.com/danielcrisp/angular-rangeslider/issues/51
where Error: ngModel:numfmt range slider is thrown
i adjusted @ardf69 solution to this:
angular.module('common').directive('rangeSlider', [function () {
return {
replace: true,
restrict: 'E',
require: 'ngModel',
template: '<input type="range"/>',
link: function (scope, element, attrs, ngModel) {
var ngRangeMin;
var ngRangeMax;
var ngRangeStep;
var value;
function init() {
if (!angular.isDefined(attrs.ngRangeMin)) {
ngRangeMin = 0;
} else {
scope.$watch(attrs.ngRangeMin, function (newValue, oldValue, scope) {
if (angular.isDefined(newValue)) {
ngRangeMin = newValue;
setValue();
}
});
}
if (!angular.isDefined(attrs.ngRangeMax)) {
ngRangeMax = 100;
} else {
scope.$watch(attrs.ngRangeMax, function (newValue, oldValue, scope) {
if (angular.isDefined(newValue)) {
ngRangeMax = newValue;
setValue();
}
});
}
if (!angular.isDefined(attrs.ngRangeStep)) {
ngRangeStep = 1;
} else {
scope.$watch(attrs.ngRangeStep, function (newValue, oldValue, scope) {
if (angular.isDefined(newValue)) {
ngRangeStep = newValue;
setValue();
}
});
}
if (!angular.isDefined(ngModel)) {
value = 50;
} else {
scope.$watch(
function () {
return ngModel.$modelValue;
},
function (newValue, oldValue, scope) {
if (angular.isDefined(newValue)) {
value = newValue;
setValue();
}
}
);
}
if (!ngModel) {
return;
}
ngModel.$parsers.push(function (value) {
var val = Number(value);
if (val !== val) {
val = undefined;
}
return val;
});
}
function setValue() {
if (
angular.isDefined(ngRangeMin) &&
angular.isDefined(ngRangeMax) &&
angular.isDefined(ngRangeStep) &&
angular.isDefined(value)
) {
element.attr("min", ngRangeMin);
element.attr("max", ngRangeMax);
element.attr("step", ngRangeStep);
element.val(value);
}
}
function read() {
if (angular.isDefined(ngModel)) {
ngModel.$setViewValue(value);
}
}
element.on('change', function () {
if (angular.isDefined(value) && (value != element.val())) {
value = element.val();
scope.$apply(read);
}
});
init();
}
};
}
]);
+1, I have the same problem when switching[input type=range] with ng-if
I also ran into this problem, and wrote a Stack Overflow question about it, “In Angular, bind attribute from scope variable _before_ ngModel binds the input’s value
”. The question includes a runnable code snippet that reproduces this problem.
Some workarounds have been posted as answers on Stack Overflow. The one applicable to my case involves using DOM manipulation with $inputElement.prop()
to set the values of min
, max
, and step
(whichever attributes are needed). By doing this DOM manipulation before my custom directive’s underlying <input>
’s value
is first set, I avoid the problem.
My lazy way of addressing this bug was to divide the min/max and step values by 100. So 300 becomes 3.0, etc. and values fall below 100. Then I multiply things back as needed. Perhaps it's useful to someone.
+1 This is very annoying.
+1
+1
+1
Another lazy workaround hack:
$timeout(function () {
angular.element('.slider-input').val(amount);
});
+1
With GitHub’s new Reactions feature, you no longer need to comment “+1”. Instead, you can mark your interest by clicking the “+:smile:” at the top-right of the first comment in this issue, then choosing :+1: (“+1”). This avoids sending a useless notification to everyone subscribed to this issue.
I have solved my problem using ondrag method by removing ng-model.
check this http://codepen.io/nagarajumusini/pen/BKpGJz
+1
+1
Adding ng-init to input works for me to set initial value
<input type="range" class="slider-years" min="0" max="30" step="1" value="0" data-ng-model="metier.anneeExperience" data-ng-init="metier.anneeExperience ? metier.anneeExperience = metier.anneeExperience : metier.anneeExperience = 0" />
Found solution in:
We are working on this (finally). Closing as a duplicate of https://github.com/angular/angular.js/issues/5892
$(document).ready(function () {
angular.element('.yourclasshere').val(valueoftheslideronload);
});
this jQuery workaround works so you can set a value when the page loads but whatever ithe value of the slider controls wont sync until it's touched.
@davedx Maybe this is the best way before the issue is solved.
<input type="range" min="15"
data-ng-init="signup.age=30" value="30" max="85" step="1"
name="ageInput" class="form-control input-lg" ng-model="signup.age"
oninput="ageOutputId.value = ageInput.value" />
<output name="ageOutputName" id="ageOutputId">30</output>
Most helpful comment
With GitHub’s new Reactions feature, you no longer need to comment “+1”. Instead, you can mark your interest by clicking the “+:smile:” at the top-right of the first comment in this issue, then choosing :+1: (“+1”). This avoids sending a useless notification to everyone subscribed to this issue.