2

I'm trying to build an Angular directive that creates a keyboard-accessible slider widget. I found several Angular sliders on Github, but none of them are keyboard-accessible. So, I'm trying to convert the jQuery UI slider to an Angular directive.

Here is how I'd build the widget without Angular.

HTML:

<div id="slider"></slider>
<input type="text" id="amount"/>

Javascript:

$("#slider").slider({
  min: 1,
  max: 10,
  slide: function(event, ui) {
    $("#amount").val(ui.value);
  }
});

And this is how I'd like the Angular directive to work:

<slider min="1" max="10" ng-model="sliderValue"/>
<input type="text" ng-model="sliderValue"/>

As an example, here is a jsFiddle that shows how someone converted a jQuery UI date widget to an Angular directive.

http://jsfiddle.net/xB6c2/121/

I'd like to do something similar to this for the slider widget. Thanks!

3 Answers 3

6

Here is a solution that works. Thank you to Duc Hong - his answer was instrumental in helping me to figure this out. Please let me know if there is a better way to do this.

app.directive('slider', function() {
    return {
        restrict: 'AE',
        link: function(scope, element, attrs) {
            element.slider({
                value: scope[attrs.ngModel],
                min: parseInt(attrs.min),
                max: parseInt(attrs.max),
                step: parseFloat(attrs.step),
                slide: function(event, ui) {
                    scope.$apply(function() {
                        scope[attrs.ngModel] = ui.value;
                    });
                }
            });
        }
    };
});
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, very helpful. The only difference for me have been 2 lines value: scope[attrs.ngModel].price, and scope[attrs.ngModel].price= ui.value;, was using from within ng-repeat.
I had to modify this directive slightly as I was getting an error that said "Uncaught TypeError: Cannot read property 'addClass' of undefined" when I tried to drag the slider. Turns out that it was because I was using a 'getterSetter' function as the model. I added support for getterSetter functions in this gist: gist.github.com/NoxHarmonium/d53d07bda7417d6d8004 If somebody knows a better way please let me know it seems a bit clunky. Also I can't seem to get it to work unless I use the slider keyword as an attribute (<div slider></div>) syntax rather than <slider></slider>
1

you can write up a directive for your UI slider, it works something like this:

<input type="text" ng-model="setMin" />
<input type="text" ng-model="setMax" />
<slider min="setMin" max="setMax"></slider>

and your directive would be something like this:

app.directive("slider", function() {
    return {
        restrict: "AE",
        scope:{
           min:"=",
           max:"="
        },
        link: function(scope, ele, attrs) {

            ele.slider({
                min: scope.min,
                max: scope.max,
                range: "min",
                value: "5",
                slide: function(event, ui) {
                    scope.$apply(function() {
                        //update on the view using ui.value;
                        //scope.number =  = ui.value;
                    });
                }
            });

           // You can watch the value min,max and then reapply your UI slider 
            scope.$watch('setMin', function(newValue, oldValue) {
                if (newValue !== null && newValue !== undefined) {
                   // re-apply UI slider
                }
            }, true);

            scope.$watch('setMax', function(newValue, oldValue) {
                if (newValue !== null && newValue !== undefined) {
                   // re-apply UI slider
                }
            }, true);

        }
    }
});

Hopefully you get the idea, cheers

Comments

1

A small improvement over the Rodney's solution:

app.directive('slider', function() {
    return {
        restrict: 'AE',
        link: function(scope, element, attrs) {
            element.slider({
                value: scope[attrs.ngModel],
                min: parseInt(attrs.min),
                max: parseInt(attrs.max),
                step: parseFloat(attrs.step),
                slide: function(event, ui) {                    
                    scope.$apply(function() {           
                        scope[attrs.ngModel] = ui.value;
                    });
                }
            });

            scope.$watch(attrs.ngModel, function(newVal, oldVal){
                element.slider({value: newVal});
            });
        }      
    };
});

It allows to detect and update the slider position if the model changes.

If your model contains dots (for instance, ngmodel = "Configuration.Size"), scope[attrs.ngModel] no longer works. A workaround is to use _.get and _.set from lodash library instead.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.