Skip to content

Commit a03dd6c

Browse files
committed
feat(uiScrollView) scroll page when ui-view is loaded
Replaces `$anchorScroll` call in `uiView` with a custom provider which scrolls the element attached to `uiView` into the current view instead. This should allow a finer default for designer by scrolling automatically to the activate `uiView` without manipulating anchors. Calling `$uiViewScrollProvider.useAnchorScroll()` will restore the current behavior or calling `$anchorScroll` instead.
1 parent b89e0f8 commit a03dd6c

7 files changed

+90
-19
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/viewScrollProvider.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/viewScrollProvider.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

+4-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* @requires $compile
77
* @requires $controller
88
* @requires $injector
9-
* @requires $anchorScroll
109
*
1110
* @restrict ECA
1211
*
@@ -16,8 +15,8 @@
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

@@ -122,10 +121,8 @@ function $ViewDirective( $state, $compile, $controller, $injector, $an
122121
viewScope.$emit('$viewContentLoaded');
123122
if (onloadExp) viewScope.$eval(onloadExp);
124123

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

src/viewScrollProvider.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @ngdoc directive
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 had 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 `$uiViewScroll.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/viewScrollProvider.js',
2425
'../src/viewDirective.js',
2526
'../src/stateDirectives.js',
2627
'../src/compat.js',

test/viewDirectiveSpec.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ describe('uiView', function () {
1818
beforeEach(module('ui.router'));
1919

2020
beforeEach(module(function ($provide) {
21-
$provide.decorator('$anchorScroll', function ($delegate) {
22-
return jasmine.createSpy('$anchorScroll');
21+
$provide.decorator('$uiViewScroll', function ($delegate) {
22+
return jasmine.createSpy('$uiViewScroll');
2323
});
2424
}));
2525

@@ -216,32 +216,32 @@ describe('uiView', function () {
216216
});
217217

218218
describe('autoscroll attribute', function () {
219-
it('should not autoscroll when unspecified', inject(function ($state, $q, $anchorScroll) {
219+
it('should autoscroll when unspecified', inject(function ($state, $q, $uiViewScroll) {
220220
elem.append($compile('<div ui-view></div>')(scope));
221-
$state.transitionTo(gState);
221+
$state.transitionTo(aState);
222222
$q.flush();
223-
expect($anchorScroll).not.toHaveBeenCalled();
223+
expect($uiViewScroll).toHaveBeenCalledWith(elem.find('div'));
224224
}));
225225

226-
it('should autoscroll when expression is missing', inject(function ($state, $q, $anchorScroll) {
226+
it('should autoscroll when expression is missing', inject(function ($state, $q, $uiViewScroll) {
227227
elem.append($compile('<div ui-view autoscroll></div>')(scope));
228-
$state.transitionTo(gState);
228+
$state.transitionTo(aState);
229229
$q.flush();
230-
expect($anchorScroll).toHaveBeenCalled();
230+
expect($uiViewScroll).toHaveBeenCalledWith(elem.find('div'));
231231
}));
232232

233-
it('should autoscroll based on expression', inject(function ($state, $q, $anchorScroll) {
233+
it('should autoscroll based on expression', inject(function ($state, $q, $uiViewScroll) {
234234
elem.append($compile('<div ui-view autoscroll="doScroll"></div>')(scope));
235235

236236
scope.doScroll = false;
237237
$state.transitionTo(aState);
238238
$q.flush();
239-
expect($anchorScroll).not.toHaveBeenCalled();
239+
expect($uiViewScroll).not.toHaveBeenCalled();
240240

241241
scope.doScroll = true;
242242
$state.transitionTo(bState);
243243
$q.flush();
244-
expect($anchorScroll).toHaveBeenCalled();
244+
expect($uiViewScroll).toHaveBeenCalledWith(elem.find('div'));
245245
}));
246246
});
247247

test/viewScrollProviderSpec.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)