Ray Shan
Ray Shan

Reputation: 1675

AngularUi jQuery Passthrough - call method to get plugin value

I'm using AngularJS, AngularUI's jQuery passthrough uiJq and jQuery's noUiSlider plugin, but the question is really about accessing a method. The html portion correctly renders a slider, and I need to 2-way bind to the slider's output value. I tried the below javascript code and variation of them in controller unsuccessfully.

Seems like uiJq doesn't work with all jQuery plugins. I'm wondering if noUiSlider is one that doesn't work and I need to write a custom directive. Also not sure if I need to deal with Deferred Execution or need uiRefresh to manually $watch things.

HTML

<div class="noUiSlider" id="abc" ui-jq="noUiSlider"
    ui-options="{range: [0, 100], start: 50, handles: 1}"></div>

JS

app.controller('MainCtrl', function ($scope) {
    $scope.selectionValue = $('#abc').noUiSlider().val();
    // error, seems to override whatever is in html
    $scope.selectionValue = $('#abc').val();
    // no error but no value is returned
});

Thanks!

Upvotes: 0

Views: 1876

Answers (2)

Jason
Jason

Reputation: 564

Here is a quick dirty directive that wraps JQuery UI Slider. Just set ngModel as you always would for bi-directional binding, and add min max orientation and animation attributes to your directive tag.

Sample Usage:

<div si-slider ng-model="TargetModel.SomeIntProperty" min="100" max="2000" orientation="horizontal" animate="true"></div>

And the code:

var directives = angular.module('si.directives', []);    
directives.directive('siSlider', function ($rootScope) {
    var directiveDefinitionObject = {
        restrict: 'EA',
        transclude: 'false',
        template: '<div class="slider"></div>',
        replace: true,
        scope: { Model:"=ngModel"},
        link: function (scope, element, attrs) {
            var change = function () {
                scope.Model = (element).slider("value");
                if (!$rootScope.$$phase) {
                    scope.$apply();
                }
            };
            element.slider({
                value: scope.Model,
                animate: attrs.animate,
                orientation: attrs.orientation,
                min: parseInt(attrs.min, 10),
                max: parseInt(attrs.max, 10),
                slide: change,
                change: change
            });
            scope.$watch('Model', function (value) {
                element.slider("value", value);
            });
        }
    };

    return directiveDefinitionObject;
});

Upvotes: 0

ProLoser
ProLoser

Reputation: 4616

Okay so you're running into a lot of assorted issues all at once and it's hard to figure out where to start. FIRST, read this: https://github.com/angular/angular.js/wiki/Understanding-Directives

Second, DON'T use jQuery in your controller. The controller fires ONCE, and it fires BEFORE the template has 'rendered'. This means that you are retrieving DOM values before they intialized (or even exist) and you're getting an error because .noUiSlider() was never run on the DOM element.

You have to think ASYNCHRONOUSLY when it comes to AngularJS. Templates are constantly changing and updating and refreshing and you must code with this expectation in mind.

Anyway, long story short, you're better off using the slide callback function to update the model, although this is a bit hackish and if you don't mind getting your nose wet, you can try making a new directive altogether that requires: 'ngModel'

ui-options="{range, [0,100], start: 50, handles: 1, slide: slideCallback }"
...
$scope.slideCallback = function() {
  $scope.myModel = $(this).val();

  // this tells angular to refresh since an async event occurred outside of angular
  $scope.$apply(); 
};

Upvotes: 3

Related Questions