Click here to Skip to main content
15,897,291 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am creating an angular ui slider directive. But unable to add tooltip for slider control. Please suggest

What I have tried:

angular.module('lt.directives').directive('slider', ['$templateCache', '$parseOptions', function ($templateCache, $parseOptions) {
    // Move these defaults into a value object
    var UPDATE_DELAY_MS = 100,
        NUM_TICKS = 20,
        MIN = 0,
        MAX = 5000,
        VALUE = 1500,
        STEPS = 100,
        DEFAULT_HTML = '<div class="ui-slider-bwrap">' +
                                '<div class="ui-slider-ticks">' +
                                '   <span class="ui-tick-mark" ng-style="{left: (5 * $index) + \'%\'}" ng-repeat="tick in ticks" />' +
                                '</div>' +
                                '<div class="ui-slider"></div>' +
                                '<div class="ui-slider-labels">' +
                                '   <span class="minLabel">{{labelMin}}</span>' +
                                '   <span class="maxLabel">{{labelMax + (value == max && showLabelMaxPlus && "+" || "")}}</span>' +
                                '</div>' +
                            '</div>';

    return {
        require: 'ngModel',
        scope: {
            value: '=ngModel',
            defaultValue: '=?',
            sliderData: '=?',
            min: '=?',
            max: '=?',
            step: '=?',
            labelMin: '@',
            labelMax: '@'
        },
        template: function (element, attrs) {
            // In the future, vary the template based on attrs.slider
            return DEFAULT_HTML;
        },
        link: function (scope, element, attrs, ngModel) {
            // Set defaults if none exist
            _.defaults(scope, {
                value: scope.defaultValue,
                labelMin: '0%',
                labelMax: '100%'
            });

            // Don't set isolate scope if min/max are undefined, 
            // since it'll result in a non-assignable expression error.
            if (_.every([scope.min, scope.max], angular.isDefined)) {
                _.defaults(scope, {
                    min: MIN,
                    max: MAX
                });
            }

            // Only check for existance of the attribute
            scope.showLabelMaxPlus = angular.isDefined(attrs.showLabelMaxPlus);

            var staggerStepObj = scope.$eval(attrs.staggerStep),
                modelMapObj = scope.$eval(attrs.modelMap),
                parsedOptions = angular.isDefined(attrs.sliderOptions) ? $parseOptions(attrs.sliderOptions) : undefined,
                slider = element.find('.ui-slider'),
                sliderObj,
                optionsList = [],
                valuesArr = [];

            // Stagger-step and ng-options sliders are both mapped value sliders
            var buildMappedValueSlider = function () {
                // Since the slider uses a different value system, find the index
                // that the ngModel maps to.
                // Ensure index is less than valuesArr length..
                var newVal = parseFloat(scope.value, 10);
                var initialIndex = Math.min(_.sortedIndex(valuesArr, scope.value), valuesArr.length - 1);

                // .. and greater than 0
                initialIndex = Math.max(initialIndex, 0);

                // Normalize the value
                scope.value = valuesArr[initialIndex];
                if (newVal < valuesArr[initialIndex] && (valuesArr[initialIndex] % 2) !== 0 && initialIndex > 0) {
                    scope.value = valuesArr[initialIndex-1];
                }

                // Set real min and max on scope if no attrs exist
                if (angular.isUndefined(scope.min)) {
                    scope.min = valuesArr[0];
                }
                if (angular.isUndefined(scope.max)) {
                    scope.max = valuesArr[valuesArr.length - 1];
                }
                
                // Attach label to parent scope (bad!)
                if (angular.isDefined(parsedOptions)) {
                    scope.$parent.sliderLabel = _.findWhere(optionsList, { value: scope.value }).label;
                }

                sliderObj = {
                    range: 'min',
                    min: 0,
                    max: valuesArr.length - 1,
                    value: initialIndex
                };

                slider.slider(sliderObj);
            };

            // Watch ng-model, min, and max - if any change, update the slider

            if (angular.isDefined(parsedOptions)) {
                // Values may be async, so wait until it returns before
                // initializing the slider.
                // Use scope.$parent so that we can access data outside of the
                // isolate scope.
                // Assumption: "value as label for obj in array"-like syntax is being used
                parsedOptions.valuesFn(scope.$parent, ngModel).then(function (values) {
                    optionsList = values;
                    valuesArr = _.pluck(values, 'value');

                    buildMappedValueSlider();
                });
            }
            else if (angular.isObject(staggerStepObj)) {
                var staggerStepKeys = _.map(_.keys(staggerStepObj), function (key) {
                    return parseFloat(key, 10);
                });

                // Create the full array of values
                for (var i = 0, ii = staggerStepKeys.length; i < ii; i++) {
                    var rangeMin = staggerStepKeys[i],
                        rangeMax = (i < staggerStepKeys.length - 1) ? staggerStepKeys[i + 1] : scope.max,
                        rangeStep = staggerStepObj[staggerStepKeys[i]],
                        range = _.map(_.range(rangeMin, rangeMax, rangeStep), function (step) {
                            // _.range doesn't create float ranges correctly, so fix this
                            return parseFloat(accounting.toFixed(step, 3));
                        });

                    valuesArr = valuesArr.concat(range);
                }

                // Pare down the list so that it's between min & max
                valuesArr = _.filter(valuesArr, function (value) {
                    return value >= scope.min && value < scope.max;
                });

                // Add the max value for good measure
                valuesArr.push(scope.max);

                buildMappedValueSlider();
            }
            else {
                sliderObj = {
                    range: 'min',
                    min: scope.min,
                    max: scope.max,
                    value: angular.isObject(modelMapObj) ? _.invert(modelMapObj)[scope.value] : scope.value,
                    step: scope.step || (scope.max - scope.min) / STEPS
                };

                slider.slider(sliderObj);
            }

            var throttledModelUpdate = _.throttle(function (sliderValue) {
                var modelValue = sliderValue;

                // Find the real model value
                if (angular.isObject(staggerStepObj) || angular.isDefined(parsedOptions)) {
                    modelValue = valuesArr[sliderValue];
                }

                // If we've defined a model mapping, perform the translation
                // before setting the model value
                modelValue = angular.isObject(modelMapObj) ? modelMapObj[modelValue] : modelValue;

                // Attach label to parent scope (bad!)
                if (angular.isDefined(parsedOptions)) {
                    scope.$parent.sliderLabel = _.findWhere(optionsList, { value: modelValue }).label;
                }

                scope.$apply(function () {
                    // Push slider value (or mapped value) out to the model
                    ngModel.$setViewValue(modelValue);
                });
            }, UPDATE_DELAY_MS);

            slider.bind({
                slide: function (event, ui) {
                    throttledModelUpdate(ui.value); 
                }
            });

            // Initialize the slider
            // TODO: Watch these values and re-init the slider based on outside changes
            // so that the slider can be updated dynamically
            if (attrs.reset === "on") {
                scope.$watch(function () {
                    return ngModel.$modelValue;
                }, function (newVal, oldVal) {
                    if (newVal !== oldVal) {
                        buildMappedValueSlider();
                    }
                });
            }

            scope.ticks = _.range(NUM_TICKS);
        }
    };
} ]);



angular.module('fossa').run(['$templateCache', 'variation', function ($templateCache, variation) {
    if (variation.getVariationByName('style') === 'tactile') {
        $templateCache.put('ig-desired-loan-amount-slider',
            '<label for="desired-loan-amount-slider" class="control-label" ng-bind-html="groupUtil.getProp(\'label\', \'How much would you like to borrow?\')"></label>' +
            '<div class="row">' +
                '<div class="col-xs-10 col-xs-offset-1">' +
                    '<div slider label-tip reset={{resetSlider}} ng-model="inputs.desiredLoanAmount" label-min="$0" label-max="$400K" show-label-max-plus slider-options="item.value as item.name for item in lists.desiredLoanAmount" />' +
                    '<span ng-show="formData.returnToHE" class="range-update">Range has been updated</span>' +
                '</div>' +
            '</div>'
        );
    }
}]);
Posted

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900