Skip to content

Commit e3d5647

Browse files
committed
Merge pull request #715 from ysbaddaden/issue-110
feat(uiView) autoscroll attribute
2 parents 8ee2afb + 126bf11 commit e3d5647

File tree

7 files changed

+118
-8
lines changed

7 files changed

+118
-8
lines changed

Gruntfile.js

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ module.exports = function (grunt) {
3636
'src/urlRouter.js',
3737
'src/state.js',
3838
'src/view.js',
39+
'src/viewScroll.js',
3940
'src/viewDirective.js',
4041
'src/stateDirectives.js',
4142
'src/compat.js'

config/karma.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module.exports = function (karma) {
1919
'src/urlRouter.js',
2020
'src/view.js',
2121
'src/state.js',
22+
'src/viewScroll.js',
2223
'src/viewDirective.js',
2324
'src/stateDirectives.js',
2425
'src/stateFilters.js',
@@ -63,4 +64,4 @@ module.exports = function (karma) {
6364
// - PhantomJS
6465
browsers: [ 'PhantomJS' ]
6566
})
66-
};
67+
};

src/viewDirective.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,17 @@
66
* @requires $compile
77
* @requires $controller
88
* @requires $injector
9-
* @requires $anchorScroll
109
*
1110
* @restrict ECA
1211
*
1312
* @description
14-
* The ui-view directive tells $state where to place your templates.
13+
* The ui-view directive tells $state where to place your templates.
1514
* A view can be unnamed or named.
1615
*
1716
* @param {string} ui-view A view name.
1817
*/
19-
$ViewDirective.$inject = ['$state', '$compile', '$controller', '$injector', '$anchorScroll'];
20-
function $ViewDirective( $state, $compile, $controller, $injector, $anchorScroll) {
18+
$ViewDirective.$inject = ['$state', '$compile', '$controller', '$injector', '$uiViewScroll'];
19+
function $ViewDirective( $state, $compile, $controller, $injector, $uiViewScroll) {
2120
var $animator = $injector.has('$animator') ? $injector.get('$animator') : false;
2221
var viewIsUpdating = false;
2322

@@ -31,6 +30,7 @@ function $ViewDirective( $state, $compile, $controller, $injector, $an
3130
var viewScope, viewLocals,
3231
name = attr[directive.name] || attr.name || '',
3332
onloadExp = attr.onload || '',
33+
autoscrollExp = attr.autoscroll,
3434
animate = $animator && $animator(scope, attr),
3535
initialView = transclude(scope);
3636

@@ -121,9 +121,9 @@ function $ViewDirective( $state, $compile, $controller, $injector, $an
121121
viewScope.$emit('$viewContentLoaded');
122122
if (onloadExp) viewScope.$eval(onloadExp);
123123

124-
// TODO: This seems strange, shouldn't $anchorScroll listen for $viewContentLoaded if necessary?
125-
// $anchorScroll might listen on event...
126-
$anchorScroll();
124+
if (!angular.isDefined(autoscrollExp) || !autoscrollExp || scope.$eval(autoscrollExp)) {
125+
$uiViewScroll(element);
126+
}
127127
}
128128
};
129129
}

src/viewScroll.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @ngdoc provider
3+
* @name ui.router.state.$uiViewScroll
4+
*
5+
* @requires $anchorScroll
6+
* @requires $timeout
7+
*
8+
* @description
9+
* When called with a jqLite element, it scrolls the element into view (after a
10+
* `$timeout` so the DOM has time to refresh).
11+
*
12+
* If you prefer to rely on `$anchorScroll` to scroll the view to the anchor,
13+
* this can be enabled by calling `$uiViewScrollProvider.useAnchorScroll()`.
14+
*/
15+
function $ViewScrollProvider() {
16+
17+
var useAnchorScroll = false;
18+
19+
this.useAnchorScroll = function () {
20+
useAnchorScroll = true;
21+
};
22+
23+
this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) {
24+
if (useAnchorScroll) {
25+
return $anchorScroll;
26+
}
27+
28+
return function ($element) {
29+
$timeout(function () {
30+
$element[0].scrollIntoView();
31+
}, 0, false);
32+
};
33+
}];
34+
}
35+
36+
angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider);

test/debug.js

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module.exports = function(config) {
2121
'../src/urlRouter.js',
2222
'../src/view.js',
2323
'../src/state.js',
24+
'../src/viewScroll.js',
2425
'../src/viewDirective.js',
2526
'../src/stateDirectives.js',
2627
'../src/compat.js',

test/viewDirectiveSpec.js

+36
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ describe('uiView', function () {
1717

1818
beforeEach(module('ui.router'));
1919

20+
beforeEach(module(function ($provide) {
21+
$provide.decorator('$uiViewScroll', function ($delegate) {
22+
return jasmine.createSpy('$uiViewScroll');
23+
});
24+
}));
25+
2026
var aState = {
2127
template: 'aState template'
2228
},
@@ -209,4 +215,34 @@ describe('uiView', function () {
209215
}));
210216
});
211217

218+
describe('autoscroll attribute', function () {
219+
it('should autoscroll when unspecified', inject(function ($state, $q, $uiViewScroll) {
220+
elem.append($compile('<div ui-view></div>')(scope));
221+
$state.transitionTo(aState);
222+
$q.flush();
223+
expect($uiViewScroll).toHaveBeenCalledWith(elem.find('div'));
224+
}));
225+
226+
it('should autoscroll when expression is missing', inject(function ($state, $q, $uiViewScroll) {
227+
elem.append($compile('<div ui-view autoscroll></div>')(scope));
228+
$state.transitionTo(aState);
229+
$q.flush();
230+
expect($uiViewScroll).toHaveBeenCalledWith(elem.find('div'));
231+
}));
232+
233+
it('should autoscroll based on expression', inject(function ($state, $q, $uiViewScroll) {
234+
elem.append($compile('<div ui-view autoscroll="doScroll"></div>')(scope));
235+
236+
scope.doScroll = false;
237+
$state.transitionTo(aState);
238+
$q.flush();
239+
expect($uiViewScroll).not.toHaveBeenCalled();
240+
241+
scope.doScroll = true;
242+
$state.transitionTo(bState);
243+
$q.flush();
244+
expect($uiViewScroll).toHaveBeenCalledWith(elem.find('div'));
245+
}));
246+
});
247+
212248
});

test/viewScrollSpec.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
describe('uiView', function () {
2+
'use strict';
3+
4+
beforeEach(module('ui.router'));
5+
6+
describe('scrollIntoView', function () {
7+
var elem;
8+
9+
beforeEach(function () {
10+
elem = [{ scrollIntoView: jasmine.createSpy('scrollIntoView') }];
11+
});
12+
13+
it('should scroll element into view after timeout', inject(function ($uiViewScroll, $timeout) {
14+
$uiViewScroll(elem);
15+
expect(elem[0].scrollIntoView).not.toHaveBeenCalled();
16+
17+
$timeout.flush();
18+
expect(elem[0].scrollIntoView).toHaveBeenCalled();
19+
}));
20+
});
21+
22+
describe('useAnchorScroll', function () {
23+
beforeEach(module(function ($provide, $uiViewScrollProvider) {
24+
$provide.decorator('$anchorScroll', function ($delegate) {
25+
return jasmine.createSpy('$anchorScroll');
26+
});
27+
$uiViewScrollProvider.useAnchorScroll();
28+
}));
29+
30+
it('should call $anchorScroll', inject(function ($uiViewScroll, $anchorScroll) {
31+
$uiViewScroll();
32+
expect($anchorScroll).toHaveBeenCalled();
33+
}));
34+
});
35+
});

0 commit comments

Comments
 (0)