Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

ng-model does not set initial value and position of input[type=range] #6726

Closed
fschwiet opened this issue Mar 17, 2014 · 35 comments
Closed

ng-model does not set initial value and position of input[type=range] #6726

fschwiet opened this issue Mar 17, 2014 · 35 comments

Comments

@fschwiet
Copy link

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.

@IgorMinar IgorMinar self-assigned this Mar 17, 2014
@IgorMinar IgorMinar added this to the Backlog milestone Mar 17, 2014
@IgorMinar
Copy link
Contributor

This is still an issue with Angular 1.2.14: http://jsfiddle.net/HVp2J/7/

@IgorMinar IgorMinar removed their assignment Mar 17, 2014
@fschwiet
Copy link
Author

As a work-around, one can set the model value within a $timeout handler.

@Rajat-Chowdhary
Copy link

$timeout doesn' help, the issue remains.

@fschwiet : http://jsfiddle.net/HVp2J/9/

@fschwiet
Copy link
Author

@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.

@ardf69
Copy link

ardf69 commented Apr 24, 2014

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);
            });
        }
    };
});

@ardf69
Copy link

ardf69 commented Jun 23, 2014

Here there is the new version of ngRange without private scope:

angular
.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);
                }
            });
        }
    };
});

@btford btford removed the gh: issue label Aug 20, 2014
@zavidovych
Copy link

this is still an issue, right?

http://plnkr.co/edit/MAT5KPkXyWblAJ71L8nK?p=preview

@sabhiram
Copy link

Yep +1

@kelle62819
Copy link

Yes, still an issue.

@mpromonet
Copy link

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.

@bastianwegge
Copy link

+1

@Yimiprod
Copy link

Yimiprod commented Jun 3, 2015

+1, and it became worst when the input range is coupled with an input number.

@mrbinky3000
Copy link

+1 for this issue

@Tooa
Copy link

Tooa commented Jun 14, 2015

Facing the same problem: +1

@larpo1
Copy link

larpo1 commented Jun 15, 2015

yep - same problem +1

@onemenny
Copy link

this is basic html5 - whats the status on this, is anyone working on this?

@onemenny
Copy link

taking under consideration this: danielcrisp/angular-rangeslider#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();
        }
    };
}
]);

@radek-anuszewski
Copy link

+1, I have the same problem when switching[input type=range] with ng-if

@roryokane
Copy link

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.

@4string
Copy link

4string commented Nov 17, 2015

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.

@jwittner
Copy link

+1 This is very annoying.

@berkaytheunicorn
Copy link

+1

2 similar comments
@westcode
Copy link

+1

@Sithdown
Copy link

+1

@davedx
Copy link
Contributor

davedx commented Mar 1, 2016

Another lazy workaround hack:

      $timeout(function () {
        angular.element('.slider-input').val(amount);
      });

@cue232s
Copy link

cue232s commented Mar 14, 2016

+1

@roryokane
Copy link

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.

@nagarajumusini
Copy link

I have solved my problem using ondrag method by removing ng-model.
check this http://codepen.io/nagarajumusini/pen/BKpGJz

@marianmazarovici
Copy link

+1

1 similar comment
@felixschul
Copy link

+1

@Jerome2606
Copy link

Jerome2606 commented May 22, 2016

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:

http://jsfiddle.net/jestho/8dymV/7/

@Narretz
Copy link
Contributor

Narretz commented Jul 5, 2016

We are working on this (finally). Closing as a duplicate of #5892

@jacobbullock95
Copy link

$(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.

@mrdulin
Copy link

mrdulin commented Aug 3, 2016

@davedx Maybe this is the best way before the issue is solved.

@AhmadRedha
Copy link

AhmadRedha commented Oct 1, 2016

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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests