I\'ve just upgraded to ui-router 0.2.8
from 0.2.0
and I\'ve noticed that when the state changes, the scroll position jumps to the top of te child <
If you wanted to do this with views conditionally, you could place this in the controller view like so:
.state('exampleState', {
url: '/exampleURL',
controller: 'ExampleCtrl as examplectrl',
onEnter: function($rootScope) {
$rootScope.$on('$viewContentLoaded',function(){
jQuery('html, body').animate({ scrollTop: 0 }, 200);
});
}
}).
or optionally which might keep your state.js file cleaner place the above in the controller for given view:
function ExampleCtrl ($scope, $rootScope) {
$rootScope.$on('$viewContentLoaded',function(){
jQuery('html, body').animate({ scrollTop: 0 }, 200);
});
}
Hope this helps someone, please let me know if I'm missing something. I used the latter and works great for me.
So I had this same problem. I have a fixed-top nav bar. If I put autoscroll="true" in the ui-view it would scroll to the top minus the height of the height of the scroll bar.
So I got rid of the style that added the padding to the body for the top navbar
// fixed navigation at top
//body { padding-top: 100px; }
And applied it to the ui-view
[ui-view=main] {
padding-top: 100px;
}
Now autoscroll="true" works as expected.
I think that we don't need scrolling to top if navigating state is child state, so I wrote this:
$rootScope.$on('$stateChangeSuccess',function(_, _, _, os){
if(!$state.includes(os) || $state.is(os))
$("html, body").animate({ scrollTop: 0 }, 200);
});
1) I think the easiest way it to put autoscroll="false"
on the ui-view and manipulate the scrolling in the $viewContentLoaded
event.
2) This is the browser's default behavior on anchors
If one combines Angular + Material Design, this is also required to scroll to top:
app.run(function ($rootScope) {
$rootScope.$on('$viewContentLoaded', function () {
$("md-content").animate({ scrollTop: 0 }, "fast"); /* <------- Notice this line */
jQuery('html, body').animate({ scrollTop: 0 }, 200);
});
});
Most of the time it's not enough just to scroll to the top of the page. It is always a good idea to respect anchor links and to scroll to the specific place in the loaded content designated by location's hash.
Here's the code I use to implement this strategy:
module.run(function (
$rootScope,
$timeout,
$location,
$uiViewScroll
) {
// Scrolling when screen changed.
$rootScope.$on('$viewContentLoaded', function () {
$timeout(performAutoScroll, 0);
});
function performAutoScroll () {
var hash = $location.hash();
var element =
findDomElement('#' + hash)
|| findDomElement('a[name="' + hash + '"]')
|| angular.element(window.document.body)
;
$uiViewScroll(element);
}
});
$viewContentLoaded
is an event generated by Angular when content is loaded inside of the ui-view
element. Actually, we need to postpone the execution of our code in order to move it to a next digest cycle. That way the content of the view element will be actually placed in the DOM tree, so we can query it. $timeout
with zero delay trick is used for this purpose.
$uiViewScroll
service, provided by UI Router is used to actually do the scrolling. We can pass a jQuery/jqLite element to it and it will scroll to it's top border.
We are getting the currect hash from the $location
service and using it to find the proper element inside of the DOM tree. It could be either a element with id
attribute or a link with name
attribute. In case we can't find an element to scroll to we falling back to a body
element (i.e. will scroll to the top of the page).
And the findDomElement
is just a syntactic sugar. It defined like this:
/**
* @param {string} selector
* @returns {jQuery|null}
*/
function findDomElement (selector) {
var result = $(selector);
return (result.length > 0 ? result : null);
}
I hope this approach makes sense and will be useful to someone out there. Cheers!