Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refresh Bootstrap ScrollSpy after Angular model changes

I am attempting to use the bootstrap scollspy to highlight list items generated by an angular repeater.

The problem I'm running into is that I'm refreshing the scrollspy plugin from an angular controller, before angular has applied the model changes to the view.

What is the angular way to ensure the scrollspy('refresh') call happens after the DOM itself has been updated (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')
 }
like image 578
Steve Goodman Avatar asked Jan 11 '13 18:01

Steve Goodman


2 Answers

Without knowing anything about scroll spy, here's how you generally want to use a 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 generic, but it will basically apply your plugin to an element, and call 'refresh' or whatever on it every time $scope.foo changes.

I hope that helps.

like image 122
Ben Lesh Avatar answered Sep 28 '22 08:09

Ben Lesh


How I solved this, 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 watched scope var is an object or array. Unfortunately, this solution still results in the unrecognized expression problem randomguy mentions in the comments. I ultimately resolved this by using a timeout in the watch function.

like image 33
Steve Goodman Avatar answered Sep 28 '22 09:09

Steve Goodman