2

What is the right way to change md-virtual-repeat's scroll to watch events from an other scroll ?

I have tried some manual triggering

And i readed this question but it is not exactly i want.

So is there a way to select a scrool to virtual-repeat container ?

EDIT 2

I need to change scrollbar of virtual repeat with scrollbar of Body

Live example

(function() {
  'use strict';

  var app = angular.module('MyApp', ['ngMaterial', 'ngMessages']);

  app.filter('to_trusted', ['$sce', function($sce) {
    return function(text) {
      return $sce.trustAsHtml(text);
    };
  }]);

  app.controller('AppCtrl', function($timeout) {
    this.itemSize = 529.39;

    var DynamicItems = function() {
      this.loadedPages = {};
      this.numItems = 0;
      this.PAGE_SIZE = 50;
      this.fetchNumItems_();
    };

    DynamicItems.prototype.getItemAtIndex = function(index) {
      var pageNumber = Math.floor(index / this.PAGE_SIZE);
      var page = this.loadedPages[pageNumber];

      if (page) {
        return page[index % this.PAGE_SIZE];
      } else if (page !== null) {
        this.fetchPage_(pageNumber);
      }
    };

    DynamicItems.prototype.getLength = function() {
      return this.numItems;
    };

    DynamicItems.prototype.fetchPage_ = function(pageNumber) {
      this.loadedPages[pageNumber] = null;

      $timeout(angular.noop, 300).then(angular.bind(this, function() {
        this.loadedPages[pageNumber] = [];
        var pageOffset = pageNumber * this.PAGE_SIZE;
        //console.log(pageNumber);

        for (var i = pageOffset; i < pageOffset + this.PAGE_SIZE; i++) {
          var obj = {};
          obj.name = 'Ad ' + i;
          obj.surname = 'Soyad ' + i;
          obj.age = i;
          obj.image = 'http://lorempixel.com/75/75/cats?' + i;
          obj.list = [];
          obj.clicked = false;
          obj.status = "asd";
          obj.html = '' + i + '---Lorem Ipsum, dizgi ve baskı endüstrisinde kullanılan mıgır metinlerdir. Lorem Ipsum, adı bilinmeyen bir matbaacının bir hurufat numune kitabı oluşturmak üzere bir yazı galerisini alarak karıştırdığı 1500 lerden beri endüstri standardı sahte metinler olarak kullanılmıştır. Beşyüz yıl boyunca varlığını sürdürmekle kalmamış, aynı zamanda pek değişmeden elektronik dizgiye de sıçramıştır. 1960 larda Lorem Ipsum pasajları da içeren Letraset yapraklarının yayınlanması ile ve yakın zamanda Aldus PageMaker gibi Lorem Ipsum sürümleri içeren masaüstü yayıncılık yazılımları ile popüler olmuştur.' + i;
          for (var j = 0; j < 1000; j++) {
            obj.list.push('http://lorempixel.com/75/75/city/?' + j);
          }

          this.loadedPages[pageNumber].push(obj);
        }

        //this.setItemNum(this.numItems + this.PAGE_SIZE);
      }));
    };

    DynamicItems.prototype.fetchNumItems_ = function() {
      $timeout(angular.noop, 300).then(angular.bind(this, function() {
        //console.log("fetchNumItems_");
        this.numItems = 10000;
      }));
    };

    DynamicItems.prototype.setItemNum = function(number) {
      this.numItems = number;
    };

    this.dynamicItems = new DynamicItems();
    //console.log(this.dynamicItems);
  });
}());
.virtualRepeatdemoDeferredLoading #vertical-container {
  width: 100%;
}
.virtualRepeatdemoDeferredLoading .repeated-item {
  border-bottom: 1px solid #ddd;
  box-sizing: border-box;
  padding-top: 10px;
}
.virtualRepeatdemoDeferredLoading md-content {
  margin: 16px;
}
.virtualRepeatdemoDeferredLoading md-virtual-repeat-container {
  border: solid 1px grey;
}
.virtualRepeatdemoDeferredLoading .md-virtual-repeat-container .md-virtual-repeat-offsetter div {
  padding-left: 16px;
}
.virtualRepeatdemoHorizontalUsage #horizontal-container {
  height: 100px;
  width: 100%;
}
.virtualRepeatdemoHorizontalUsage .repeated-item {
  border-right: 1px solid #ddd;
  box-sizing: border-box;
  display: inline-block;
  height: 84px;
  padding-top: 10px;
  text-align: center;
  width: 100px;
}
.virtualRepeatdemoHorizontalUsage md-content {
  margin: 16px;
}
.virtualRepeatdemoHorizontalUsage md-virtual-repeat-container {
  border: solid 1px grey;
}
<!DOCTYPE html>

<html class=''>

<head>
  <meta charset='UTF-8'>
  <meta name="robots" content="noindex">
  <link rel="shortcut icon" type="image/x-icon" href="//production-assets.codepen.io/assets/favicon/favicon-8ea04875e70c4b0bb41da869e81236e54394d63638a1ef12fa558a4a835f1164.ico" />
  <link rel="mask-icon" type="" href="//production-assets.codepen.io/assets/favicon/logo-pin-f2d2b6d2c61838f7e76325261b7195c27224080bc099486ddd6dccb469b8e8e6.svg" color="#111" />
  <link rel="canonical" href="http://codepen.io/anon/pen/OWXMXW" />
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">
  <link rel='stylesheet prefetch' href='https://cdn.gitcdn.link/cdn/angular/bower-material/v1.1.1/angular-material.css'>
  <link rel='stylesheet prefetch' href='https://material.angularjs.org/1.1.1/docs.css'>
  <script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js'></script>
  <script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.min.js'></script>
  <script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-route.min.js'></script>
  <script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-aria.min.js'></script>
  <script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-messages.min.js'></script>
  <script src='https://cdn.gitcdn.link/cdn/angular/bower-material/v1.1.1/angular-material.js'></script>

</head>

<body>
  <h1> Virtual Repeat Test </h1>
  <div ng-controller="AppCtrl as ctrl" ng-cloak="" flex layout-fill layout-padding class="virtualRepeatdemoDeferredLoading" ng-app="MyApp">

    <md-content layout="column" flex layout-fill>

      <md-virtual-repeat-container flex layout-fill md-auto-shrink="false" layout id="vertical-container">

        <div md-virtual-repeat="item in ctrl.dynamicItems" id="repeat_item" md-on-demand="true" md-item-size="ctrl.itemSize" layout-fill>

          <img ng-src="{{item.image}}" class="md-avatar" alt="{{item.name}}" />
          <br> Adı : {{item.name}} <br> Soyadı : {{item.surname}} <br> Yaşı : {{item.age}} <br> is Clicked : {{ item.clicked }} <br> status : {{ item.status }}
          <p ng-bind-html=" item.html | to_trusted "> </p>

          <h5 ng-click="item.clicked = !item.clicked; item.status = item.status + item.status; "> List Items </h5>

          <md-content ng-if="item.clicked == true" class="virtualRepeatdemoHorizontalUsage" layout="column">

            <md-virtual-repeat-container id="horizontal-container" md-orient-horizontal="">
              <div md-virtual-repeat="picture in item.list" class="repeated-item" flex="">
                <img ng-src="{{ picture }}" class="md-avatar" alt="{{ picture }}" />
              </div>
            </md-virtual-repeat-container>
          </md-content>

          <br>

        </div>

      </md-virtual-repeat-container>
    </md-content>

  </div>

</body>

</html>

3
  • Regarding your edit, is the md-virtual-repeat going to take up the whole area in the window? If so, would it be easier to just hide the body scrollbar and use md-virtual-repeat scrollbar as the "official" scrollbar? Commented Jun 6, 2017 at 16:09
  • No because there would be multiple virtual-repeat on page at the same time. Commented Jun 7, 2017 at 13:41
  • I see, in that case I'd use the updated directive in my answer, but I'd still use a separate div that stretches the height of the window as the scrollbar and disable the body scrollbar so that your md-virtual-repeat tags aren't inside the thing that you're scrolling. Commented Jun 7, 2017 at 13:47

1 Answer 1

1
+50

If I understand your question correctly, you just want to scroll the virtual repeater div using a scrollbar somewhere else on your page? One way to do that would be to create a directive that listens to scroll event of the div you want to use as the visible scrollbar and then just scroll the other div the same percentage.

Here is one such directive. Add it to the md-virtual-repeat-container and point it to the div you want to scroll it. The divs can even be different heights.

Update I changed the directive to fall back to the body tag if an element wasn't specified.

app = angular.module('myApp', ['ngMaterial']);

app.controller("myController", function($scope) {
  $scope.items = [];
  for (var i = 0; i < 5000000; i++) {
    $scope.items.push(i);
  }
});

app.directive("scrollFrom", function($window, $document) {
  return {
    scope: {
      elTarget: '@scrollFrom'
    },
    link: function(scope, element, attrs) {
      var body = $document[0].body;
      angular.element(scope.elTarget || $window).bind("scroll", function() {
        var percentScrolled = scope.elTarget ?
          this.scrollHeight > 0 ? this.scrollTop / (this.scrollHeight - element.height()) : 0 :
          body.scrollHeight > 0 ? body.scrollTop / (body.scrollHeight - element.height()) : 0;
        var trgt = angular.element(element).find('.md-virtual-repeat-scroller')[0];
        trgt.scrollTop = trgt.scrollHeight * percentScrolled;
      });
    }
  };
});
#vertical-container {
  height: 292px;
  width: 100%;
  max-width: 400px;
  position: fixed;
  top: 10px;
}

#vertical-container .md-virtual-repeat-scroller {
  overflow: hidden;
}

.repeated-item {
  border-bottom: 1px solid #ddd;
  box-sizing: border-box;
  height: 40px;
  padding-top: 10px;
}

md-virtual-repeat-container {
  border: solid 4px grey;
}
<html ng-app="myApp">

<head>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.4/angular-material.min.css" />
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-animate.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-aria.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.4/angular-material.min.js"></script>
</head>

<body style="height: 5000000px">
  <div ng-controller="myController">
    <md-virtual-repeat-container id="vertical-container" scroll-from>
      <div md-virtual-repeat="item in items" class="repeated-item" flex>
        {{item}}
      </div>
    </md-virtual-repeat-container>
  </div>
</body>

</html>

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

8 Comments

Your answer can solve this problem but i need a scrollFrom directive not scroll for. And somehow with your way scrollFor directive cant see virtualRepeats scroll. I tried to target all containers even containers after dom render but it not worked for me..
@BurakAkyıldız, please see my updated answer that uses an md-virtual-repeat and changes the directive to a scrollFrom as requested.
I tried your directive but it not worked for document's scroll. I fixed it but the problem i got is now the height of scroll. Document has so small scroll and virtual-repeat has 5000000 or bigger height. I think a watcher scroll is not the solve. I need to change the virtual-repeat's scroll to document's scroll orginally
@BurakAkyıldız, sorry, I don't understand. It's not working because the document is so small so you want to change it to use the document's scrollbar? If the document is too small, how would changing it to use the document's scrollbar help?
Document has 1000 scrollHeight virtual-repeat-container has 5000000 scrollHeight so when i scroll on document virtual-repeat scrolls so much because your directive works with percent naturally. I need same scroll height (by the way it is out of the scope of my question)
|

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.