In my app I want to use a custom scrollbar for a div. So I used ng-scrollbar, it is working fine with static data. But whenever I get the data using ng-repeat it is not working.
please try ng-scroll... another plugin, but without need of manual adjust. mentioned on:
AngularJS with ng-scroll and ng-repeat
This might be a bit late.
The problem is even though you have added the content to scope variable, angular has not finished adding p tags to your DOM. If you try a simple console log like
console.log($('.well').find('p').length);
After pushing content to $scope.me
, you will understand what is happening. (Need jQuery at least to debug)
The solution is far more complicated than you can imagine.
STEP 1:
Add a ng-controller
to your ng-repeat
(Yes. It is allowed)
<p ng-repeat="mi in me" ng-controller="loopController">{{mi.name}}</p>
STEP 2: Define loopController
demoApp.controller('loopController', function($scope) {
$scope.$watch('$last', function(new_val) {
new_val && $scope.$emit('loopLoaded', $scope.$index);
});
});
This controller function is triggered whenever ng-repeat
manipulates DOM. I'm watching $last
which is a scope variable for ng-repeat
. This will be set to true whenever, ng-repeat
loads last element in DOM. When $last
is set to true
I emit one event loopLoaded
. Since you are pushing values into $scope.me
using a loop, this event will be triggered for every push.
STEP 3: Event handling
In your SimpleController (not simple anymore, eh?)
$scope.$on('loopLoaded', function(evt, index) {
if (index == $scope.me.length-1) {
$scope.$broadcast('rebuild:me');
}
});
Once all the p elements are loaded, index
sent to event will be equal to $scope.me.length-1
. So you call scroll rebuild. That's it.
Here's a reference I used - AngularJS - Manipulating the DOM after ng-repeat is finished
If you use jQuery, you can try jQuery Scrollbar - it has more options and fully CSS customizable.
Example with ng-repeat is here
JavaScript
var demoApp = angular.module('demoApp', ['jQueryScrollbar']);
demoApp.controller('SimpleController', function($scope){
$scope.me = [];
for(var i=1;i<=20;i++){
$scope.me.push({"name":i});
}
$scope.add = function(){
$scope.me.push({"name":$scope.me.length+1});
}
$scope.remove = function(){
$scope.me.pop();
}
$scope.jqueryScrollbarOptions = {
"onUpdate":function(container){
setTimeout(function(){
// scroll to bottom. timeout required as scrollbar restores
// init scroll positions after calculations
container.scrollTop(container.prop("scrollHeight"));
}, 10);
}
};
});
HTML
<div data-ng-app="demoApp">
<div data-ng-controller="SimpleController">
<button class="btn btn-info" ng-click="add();">add</button>
<button class="btn btn-warning" ng-click="remove();">remove</button>
<div class="scrollbar-dynamic" data-jquery-scrollbar="jqueryScrollbarOptions">
<h1>Scroll me down!</h1>
<p ng-repeat="mi in me">{{mi.name}}</p>
</div>
</div>
</div>
CSS
.scrollbar-dynamic {
border: 1px solid #FCC;
max-height: 300px;
overflow: auto;
}
Try adding the broadcast
call to the end of your controller so it fires on controller load. If that doesn't work, try adding:
$timeout(function () {
$scope.$broadcast('rebuild:me');
}, 0);
// 0 optional, without it the time is assumed 0 which means next digest loop.
at the end of your controller code, not inside the add function. If this works but the previous approach doesn't then that means ngRepeat didn't finish rendering it's dynamic content in time for the ngScrollbar to properly update.
UPDATE: in general, you might have to wrap the broadcast inside of the add() function in a timeout as well. The reason I say this is that I suspect what's going on is that you add data to the scope variable and then broadcast all in the same function call. What might be happening is that the broadcast event is caught and scrollbar recalculates before ngRepeat sees the updated scope data and adds its extra DOM elements. Btw, if you want to recalculate the scrollbar on add(), then you also want to do this on remove() as well.
So your add function would become:
$scope.add = function(){
$scope.me.push({"name":$scope.me.length+1});
// wait until next digest loop to send event, this way ngRepeat has enough time to update(?)
$timeout(function () {
$scope.$broadcast('rebuild:me');
});
}