3

I am using the angular-rating-icons to generate a star system.

<div class="rating-filter">
    <h4>{{'Rating: '|translate}} {{filter.ratings}}</h4>
    <div data-angular-rating-icons 
    ng-model="filter.ratings" color-base="orange"
    on-change="fn()"></div>
</div>

I am looking to get a tooltip on each individual star similar to how it is implemented in uib-tooltip.

<div class="btn-group">
    <button type="button" class="btn btn-price" uib-tooltip="₹0 - ₹500" ng-click="applyFilter(1)">$</button>
    <button type="button" class="btn btn-price" uib-tooltip="₹500 - ₹1000" ng-click="applyFilter(2)">$$</button>
    <button type="button" class="btn btn-price" uib-tooltip="₹1000 - ₹5000" ng-click="applyFilter(3)">$$$</button>
    <button type="button" class="btn btn-price" uib-tooltip="₹5000+" ng-click="applyFilter(4)">$$$$</button>
</div>

I cannot seem to figure this out as angular-rating-icons takes in String to determine which icon is to be used for the rating system i.e. icon-full="fa-star" rather then passing in a <div> object. And I cannot seem to target individual stars since they are generated to be the minified CSS.

I tried overriding the css for .fa-star but that doesn't seem to work so well either. Is there a way to display a tooltip on hover for each button?

JSFiddle: https://jsfiddle.net/y4b1skdw/1/

You might have to hit the Load Type of the snippet to No wrap - bottom of <body> for the plugin to load.

2
  • 1
    can you share a MVC stackblitz, i'tll make it easier to help Commented May 12, 2019 at 4:28
  • I added a JSFiddle if it helps, I am unable to setup stackblitz as this is an adhoc task and I have no experience in Angular. Essentially, I'm looking to get tool tips for each stars in the JSFiddle code snippet. Commented May 12, 2019 at 4:44

1 Answer 1

1

Q: Is there a way to display a tooltip on hover for each button?

A: yes - we add the tooltip class to the <li ng-repeat="icon in icons track by $index">... and it is this <li> element which is repeated 10 times, and twice on each star.

Following is the code snippet for your tooltip with tooltip classes added in the css

var app = angular.module("myApp", []);

app.directive('angularRating', function() {
  return {
    replace: true,
    require: 'ngModel',
    scope: {
      ngModel: '=',
      onChangeFunction: '&onChange'
    },
    template: '' +
      '<ul ng-class="[listClass, decimal]">' +
      '<li ng-repeat="icon in icons track by $index"  class="tooltip"' +
      'ng-style="getListItemStyle($index)" ' +
      'ng-click="setValue($index)" ' +
      'ng-mouseenter="paintIcons($index)" ' +
      'ng-mouseleave="resetIcons()" ' +
      '>' +
      '<span class="tooltiptext">sample Tooltip text for {{$index}}</span>' +
      '<i ng-class="getClass($index)" ng-style="getIconStyle($index)"></i>' +
      '</li>' +
      '</ul>',

    link: function(scope, element, attrs, controller) {
      // Settings
      scope.icons = new Array(+attrs.max || 5);
      scope.value = controller.$viewValue || (+attrs.defaultValue || 0);
      scope.size = +attrs.iconSize || 20;
      scope.spacing = +attrs.iconSpacing || 5;
      scope.listClass = 'angular-rating-icons';
      scope.readOnly = !(attrs.readonly === undefined);
      scope.decimal = !(attrs.decimal === undefined) ? 'angular-rating-icons-decimal' : undefined;

      // Colors
      var colorBase = attrs.colorBase || 'black';
      var colorSelected = attrs.colorSelected || 'orange';
      var colorHover = attrs.colorHover || 'orange';

      // Different states
      var iconBase = attrs.iconBase || 'fa';
      var iconEmpty = attrs.iconEmpty || 'fa-star-o';
      var iconFull = attrs.iconFull || 'fa-star';
      var iconHover = attrs.iconHover || 'fa-star';

      // Model
      controller.$render = function() {
        scope.value = controller.$viewValue === 0 ? 0 : controller.$viewValue || scope.value;

        // update model safeguard/fallback should it not be initialized before
        controller.$setViewValue(scope.value);
      };

      /**
       * Returns the appropriate class for the icon.
       * Changes if it's meant to be full or empty.
       * All indexes above the given value will be empty, all bellow or equal will be full.
       *
       * @param {int} index - the icon's index
       * @return {string} - the icon class to use
       */
      scope.getClass = function(index) {
        return iconBase + ' ' + (index >= scope.value ? iconEmpty : iconFull);
      };

      /**
       * Returns the appropriate style for the icon's color.
       * Changes if it's meant to be full or empty.
       * If it's decimal type, modifies the style to reduce the icon size by 2px, and move the odd index icons
       * half of their size minus 2, to the left.
       *
       * @param {int} index - the icon's index
       * @return {Object} - the icon style to use
       */
      scope.getIconStyle = function(index) {
        var css = {
          color: index >= scope.value ? colorBase : colorSelected
        };

        if (!scope.decimal) {
          return css;
        }

        css.height = scope.size - 2 + 'px';
        css.width = scope.size - 2 + 'px';
        css.left = index % 2 ? '-' + (scope.size - 2) / 2 + 'px' : '';

        return css;
      };

      /**
       * Returns the appropriate style fo the list item's font-size and padding-right.
       * If it's decimal type, modifies the style to reduce the height and width by 2 px, and the only the width
       * by half of that result. Also for every even index it removes the right padding.
       *
       * @param {int} index - the list item's index
       * @return {object} - the list item's style to use
       */
      scope.getListItemStyle = function(index) {
        var css = {
          'font-size': scope.size + 'px',
          'padding-right': index !== scope.icons.length - 1 ? scope.spacing + 'px' : '0'
        };

        if (!scope.decimal) {
          return css;
        }

        css.height = scope.size - 2 + 'px';
        css.width = (scope.size - 2) / 2 + 'px';

        if (!(index % 2)) {
          css['padding-right'] = '0';
        }

        return css;
      };

      /**
       * Doesn't run if set to readonly.
       * Sets the directive's scope value to the clicked icon plus 1.
       * List item's indexes go from 0 to 9, whilst real values should go from 1 to 10.
       * Sets the model's value to the directive's scope value.
       * Runs the onChangeFunction function.
       *
       * @param {int} index - the clicked icon's index
       */
      scope.setValue = function(index) {
        if (scope.readOnly) {
          return;
        }

        controller.$setViewValue(scope.value = index + 1);
        scope.onChangeFunction();
      };

      /**
       * Runs the paintIcon function to paint the icons only up to the current scope value - 1,
       * since the indexes range from 0 to 9 but the real values range from 1 to 10.
       */
      scope.resetIcons = function() {
        scope.paintIcons(scope.value - 1, true);
      };

      /**
       * Doesn't run if set to readonly.
       * Changes the icon's classes accordingly to their index.
       * Cycles all the icons, and if the current index is smaller than the cycle number, it gives the icon the
       * empty class, otherwise gives it the hover class and sets the color to the hover color.
       * If reset is true, the above first case scenario also sets the color to the base color, and the second
       * adds the class full and paints with the selected color instead.
       *
       * @param {int} index - the clicked icon's index
       * @param {boolean} reset - if icon's paint should be reset
       */
      scope.paintIcons = function(index, reset) {
        if (scope.readOnly) {
          return;
        }

        var items = element.find('li').find('i');
        for (var i = 0; i < items.length; i++) {
          var icon = angular.element(items[i]);

          if (index >= i) {
            icon.removeClass(iconEmpty)
              .addClass(reset ? iconFull : iconHover)
              .css('color', reset ? colorSelected : colorHover);
          } else {
            icon.removeClass(iconFull)
              .addClass(iconEmpty)
              .css('color', reset ? colorBase : icon.css('color'));
          }

          if (reset && iconHover !== iconFull) {
            icon.removeClass(iconHover);
          }
        }
      };
    }
  };
});

app.controller("MyCtrl", ['$scope', function($scope) {
  $scope.maxValue = 10;

  $scope.fn = function() {
    if ($scope.rating.value === 10) {
      $scope.callbackText = 'This text was changed through the callback function! ' +
        'And it was validated to say "YOU\'RE AWESOME" ' +
        'if the value was 10!';
      return;
    }
    $scope.callbackText = 'The new rating is ' + $scope.rating.value + '. ' +
      'This text was changed through a callback function! ' +
      'Try hitting the max value star!';
  }
}]);
.angular-rating-icons {
  text-align: left;
  display: inline-block;
  padding: 0;
  list-style: none;
}

.angular-rating-icons>li {
  display: inline-block;
  padding: 0;
  cursor: pointer;
}

.angular-rating-icons-decimal>li {
  overflow: hidden;
  position: relative;
}

.angular-rating-icons-decimal>li>i {
  position: absolute;
}

.tooltip {
  position: relative;
  display: inline-block;
  border-bottom: 1px dotted black;
}

.tooltip .tooltiptext {
  visibility: hidden;
  width: 120px;
  background-color: black;
  color: #fff;
  text-align: center;
  border-radius: 6px;
  padding: 5px 0;
  /* Position the tooltip */
  position: fixed;
  font-size: 14px;
  margin-top: 45px;
  z-index: 1;
}

.tooltip:hover .tooltiptext {
  visibility: visible;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script src="https://use.fontawesome.com/2bd9c462bc.js"></script>

<div ng-app="myApp">
  <div ng-controller="MyCtrl">
    <h3>Hello, Superhero!</h3>

    <div>Rating: {{rating.value}}</div>

    <div data-angular-rating ng-model='rating.value' max={{maxValue}} default-value=2 icon-empty="fa-star-o" icon-full="fa-star" icon-hover="fa-star" color-base="black" color-selected="orange" color-hover="orange" icon-size="50" on-change="fn()" decimal></div>

    <div>{{callbackText}}</div>
  </div>
</div>

Sign up to request clarification or add additional context in comments.

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.