Search code examples
javascriptangularjsangular-directive

Angular Directive only works on one element at a time


I have the following angular directive. It uses a range slider to scroll a horizontal div of items. It works if applied to only one row. But if applied to multiple...nothing happens. The transclusion works, but function never runs and no errors are given. What can I do to this to make it be more angular universal and work on multiple elements? Here is repro:CodePen

app.directive('bob', function () {
        return {
            restrict: 'A',
            transclude: true,
            template:
            `<div style="background-color: #333"><input type="range" value="0" id="scroll-rangeb"><div id="photo-containerb" style="display: flex; overflow-x: scroll; flex-direction: row; align-items: center; height: 90%;" ng-transclude></div></div>`,
            link: function (scope, element, attrs) {
                var scroll = document.getElementById("scroll-rangeb");
                scroll.oninput = function () {
                    var panel = document.getElementById("photo-containerb");
                    var total = panel.scrollWidth - panel.offsetWidth;
                    var percentage = total * (this.value / 100);
                    panel.scrollLeft = percentage;
                }
            }
        };
    });

Solution

  • #scroll-rangeb is a unique element (in theory), if you override the oninput on every directive it clearly will not work, it'll remain only the first one found. Anyways, you mustn't use multiple components with the same id at all. Try to find it from the element parameter given on the link function instead, using classes or somthing else.

    For example, I could get it solved by using element[0].getElementsByClassName('scroll-rangeb') :

    angular.module('app', [])
      .directive('bob', function() {
        return {
          restrict: 'A',
          transclude: true,
          template: `
              <div style="background-color: #333">
                <input type="range" value="0" class="scroll-rangeb">
                <div
                  class="photo-containerb"
                  style="display: flex; overflow-x: scroll; flex-direction: row; align-items: center; height: 90%;"
                  ng-transclude>
                </div>
              </div>`,
          link: function(scope, $element, attrs) {
            var
              element = $element[0],
              scroll = element.getElementsByClassName("scroll-rangeb")[0],
              panel = element.getElementsByClassName("photo-containerb")[0];
    
            scroll.oninput = function() {
              var total = panel.scrollWidth - panel.offsetWidth;
              var percentage = total * (this.value / 100);
    
              panel.scrollLeft = percentage;
            }
          }
        };
      });
    img {
      border-radius: 50%;
    }
    
    .box {
      display: block;
      width: 50px;
      height: 50px;
      min-width: 50px;
      margin: 10px;
    }
    <div ng-app="app">
      <div bob>
        <img class="box" ng-repeat="img in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">
      </div>
      <div bob>
        <img class="box" ng-repeat="img in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==">
      </div>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.js"></script>