Reputation: 2297
I'm new to AngularJS. I'm using AngularJS 1.2.5 and Bootstrap 3.0. I'm trying to include ScrollSpy in my app. However, I'm having some challenges. I'm trying to incorporate the code found here. Currently, my code looks like this:
index.html
<div class="row" scroll-spy>
<div class="col-md-3 sidebar">
<ul style="position:fixed;" class="nav nav-pills nav-stacked">
<li class="active" spy="overview"><a href="#overview">Overview</a></li>
<li spy="main"><a href="#main">Main Content</a></li>
<li spy="summary"><a href="#summary">Summary</a></li>
<li spy="links"><a href="#links">Other Links</a></li>
</ul>
</div>
<div class="col-md-9 content">
<h3 id="overview">Overview</h3>
Lorem Ipsum Text goes here...
<h3 id="main">Main Body</h3>
Lorem Ipsum Text goes here...
<h3 id="summary">Summary</h3>
Lorem Ipsum text goes here...
<h3 id="links">Other Links</h3>
</div>
</div>
index.html.js
angular.module('td.controls.scrollSpy', [])
.directive('spy', function ($location) {
return {
restrict: 'A',
require: '^scrollSpy',
link: function (scope, elem, attrs, scrollSpy) {
var _ref;
if ((_ref = attrs.spyClass) == null) {
attrs.spyClass = 'current';
}
elem.click(function () {
return scope.$apply(function () {
return $location.hash(attrs.spy);
});
});
return scrollSpy.addSpy({
id: attrs.spy,
'in': function () {
return elem.addClass(attrs.spyClass);
},
out: function () {
return elem.removeClass(attrs.spyClass);
}
});
}
};
})
.directive('scrollSpy', function ($location) {
return {
restrict: 'A',
controller: function ($scope) {
$scope.spies = [];
return this.addSpy = function (spyObj) {
return $scope.spies.push(spyObj);
};
},
link: function (scope, elem, attrs) {
var spyElems;
spyElems = [];
scope.$watch('spies', function (spies) {
var spy, _i, _len, _results;
_results = [];
for (_i = 0, _len = spies.length; _i < _len; _i++) {
spy = spies[_i];
if (spyElems[spy.id] == null) {
_results.push(spyElems[spy.id] = elem.find('#' + spy.id));
} else {
_results.push(void 0);
}
}
return _results;
});
return $($window).scroll(function () {
var highlightSpy, pos, spy, _i, _len, _ref;
highlightSpy = null;
_ref = scope.spies;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
spy = _ref[_i];
spy.out();
spyElems[spy.id] = spyElems[spy.id].length === 0 ? elem.find('#' + spy.id) : spyElems[spy.id];
if (spyElems[spy.id].length !== 0) {
if ((pos = spyElems[spy.id].offset().top) - $window.scrollY <= 0) {
spy.pos = pos;
if (highlightSpy == null) {
highlightSpy = spy;
}
if (highlightSpy.pos < spy.pos) {
highlightSpy = spy;
}
}
}
}
return highlightSpy != null ? highlightSpy['in']() : void 0;
});
}
};
})
;
When I run this in the browser I get several errors. When I initially run it, I see the following errors in my browser console:
TypeError: Object function (spyObj) { return $scope.spies.push(spyObj); } has no method 'addSpy'
ReferenceError: $window is not defined
I can't figure out a) Why I'm getting these errors or b) how to get this basic example to work. I really like this approach to using scrollspy with AngularJS. It's the cleanest implementation I've seen. For that reason, I'd love to figure out how to get this working.
Upvotes: 1
Views: 1402
Reputation: 1551
if you wont that it working with ng-include change the follow condition
if (spyElems[spy.id].offset() === undefined) {
continue;
}
on this
if (spyElems[spy.id].offset() === undefined) {
// try to refind it
spyElems[spy.id] = elem.find('#' + spy.id);
if(spyElems[spy.id].offset() === undefined)
continue;
}
Upvotes: 0
Reputation: 10624
I recently ran across Alexander's solution as well and went through the process of translating it.
To answer your direct question: You need to import $window
into your scrollSpy
directive.
.directive('scrollSpy', function ($location, $window) {
Below is the complete translation I did of Alexander's code:
app.directive('scrollSpy', function ($window) {
return {
restrict: 'A',
controller: function ($scope) {
$scope.spies = [];
this.addSpy = function (spyObj) {
$scope.spies.push(spyObj);
};
},
link: function (scope, elem, attrs) {
var spyElems;
spyElems = [];
scope.$watch('spies', function (spies) {
var spy, _i, _len, _results;
_results = [];
for (_i = 0, _len = spies.length; _i < _len; _i++) {
spy = spies[_i];
if (spyElems[spy.id] == null) {
_results.push(spyElems[spy.id] = elem.find('#' + spy.id));
}
}
return _results;
});
$($window).scroll(function () {
var highlightSpy, pos, spy, _i, _len, _ref;
highlightSpy = null;
_ref = scope.spies;
// cycle through `spy` elements to find which to highlight
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
spy = _ref[_i];
spy.out();
// catch case where a `spy` does not have an associated `id` anchor
if (spyElems[spy.id].offset() === undefined) {
continue;
}
if ((pos = spyElems[spy.id].offset().top) - $window.scrollY <= 0) {
// the window has been scrolled past the top of a spy element
spy.pos = pos;
if (highlightSpy == null) {
highlightSpy = spy;
}
if (highlightSpy.pos < spy.pos) {
highlightSpy = spy;
}
}
}
// select the last `spy` if the scrollbar is at the bottom of the page
if ($(window).scrollTop() + $(window).height() >= $(document).height()) {
spy.pos = pos;
highlightSpy = spy;
}
return highlightSpy != null ? highlightSpy["in"]() : void 0;
});
}
};
});
app.directive('spy', function ($location, $anchorScroll) {
return {
restrict: "A",
require: "^scrollSpy",
link: function(scope, elem, attrs, affix) {
elem.click(function () {
$location.hash(attrs.spy);
$anchorScroll();
});
affix.addSpy({
id: attrs.spy,
in: function() {
elem.addClass('active');
},
out: function() {
elem.removeClass('active');
}
});
}
};
});
The above code also supports highlight the last spy
element in the menu if the browser is scrolled to the bottom, which the original code did not.
Upvotes: 2