Update Bootstrap ScrollSpy after changing Angular model

I am trying to use bootstrap scollspy to highlight the list items created by the angular repeater.

The problem I am facing is that I am updating the scrollspy plugin from the angular controller before angular applies the model changes to the view.

What is the angular way to provide a call to scrollspy ('refresh') after updating the DOM itself (and not just the angular model)?

Template:

<div class="span2" id="addressList"> <ul class="nav nav-tabs nav-stacked affix"> <li ng-repeat="addr in addresses"><a href="#{{addr.id}}">{{addr.id}}</a></li> </ul> </div> 

Controller:

 $scope.httpSuccessCallback = function (data) $scope.addresses.push(data); $('[data-spy="scroll"]').scrollspy('refresh'); //calls $('#addressList .nav > li > a') } 
+6
angularjs jquery-plugins twitter-bootstrap
source share
4 answers

How I solved it using Blesh's answer

Template:

  <body ng-app="address" ng-controller="AddressCtrl" scroll-spy="addresses"> <div class="container"> <form ng-submit="lookupAddress()"> <input type="text" ng-model="addressText" /> <button id="addressQueryBtn">Submit</button> </form> <div id="addressList"> <ul> <li ng-repeat="addr in addresses">{{addr}}</li> </ul> </div> </div> </body> 

Angular JS:

 angular.module('address', []). directive('scrollSpy', function($timeout){ return function(scope, elem, attr) { scope.$watch(attr.scrollSpy, function(value) { $timeout(function() { elem.scrollspy('refresh') }, 200); }, true); } }); function AddressCtrl($scope, $http) { $scope.addresses = []; $scope.lookupAddress = function() { $scope.addresses.push($scope.addressText); $scope.addressText = ''; }; } 

The third argument to scope.watch (...) is needed when the observed region var is an object or an array. Unfortunately, this solution still leads to the unrecognized problem of expressing randomguy in the comments. I eventually resolved this using a timeout in the clock function.

+4
source share

Without knowing anything about espionage scrolling, here is how you usually want to use the jQuery plugin in Angular:

 app.directive('scrollSpy', function (){ return { restrict: 'A', link: function(scope, elem, attr) { elem.scrollSpy({ /* set up options here */ }); //watch whatever expression was passed to the //scroll-spy attribute, and refresh scroll spy when it changes. scope.$watch(attr.scrollSpy, function(value) { elem.scrollSpy('refresh'); }); } }; }); 

Then in HTML:

 <div scroll-spy="foo">Do something with: {{foo}}</div> 

The above example is VERY general, but it will basically apply your plugin to the element and call "refresh" or something else on it every time you change the $ scope.foo parameter.

I hope this helps.

+5
source share

There are a few issues with using Scrollspy in Angular (perhaps why AngularUI still doesn't include Scrollspy as of August 2013).

  • You must update scrollspy every time the contents of the DOM change.

    This can be done, as suggested here, through $, observing the element and setting a timeout for calling scrollspy ('refresh').

  • You need to override the default behavior of the navigation items if you want to use the navigation items to move around the scroll area.

This can be done with preventDefault to prevent navigation and the attachment of the scrollTo function.

I ran a working example on Plunker: http://plnkr.co/edit/R0a4nJi3tBUBsluBJUo2?p=preview

+1
source share

I have been looking for a solution to this problem for a while, but I could not find it. I ended up implementing my own by creating a scrolling spy service with several directives.

The service will track all sections viewed and their offset. The directives will create a scroll event, highlight navigation items and tell the service where all the sections are located. Since this implementation does not consider the scope variable, the application must broadcast a message to update the offsets of all sections.

You can find my example on Github.

-one
source share

All Articles