angularJS doesn't scroll to top after changing a view [duplicate] - angularjs

This question already has answers here:
Changing route doesn't scroll to top in the new page
(18 answers)
Closed 8 years ago.
For example:
A user scrolls down on view A;
Then the user clicks on a link, which takes the user to view B;
The view is changes,
but the user's vertical location remains lthe same, and must scroll manually to the top of the screen.
Is it an angular bug?
I wrote a small workaround that uses jquery to scroll to the top; but I don't find the correct event to bind it to.
edit after seeing the comment:
How and WHEN do i pull myself to the top? i'm using jquery but the $viewContentLoaded event is too soon (the method runs, but the page doesn't scroll at that time)

The solution is to add autoscroll="true" to your ngView element:
<div class="ng-view" autoscroll="true"></div>
https://docs.angularjs.org/api/ngRoute/directive/ngView

Angular doesn't automatically scroll to the top when loading a new view, it just keeps the current scroll position.
Here is the workaround I use:
myApp.run(function($rootScope, $window) {
$rootScope.$on('$routeChangeSuccess', function () {
var interval = setInterval(function(){
if (document.readyState == 'complete') {
$window.scrollTo(0, 0);
clearInterval(interval);
}
}, 200);
});
});
Put it in your bootstrap (usually called app.js).
It's a pure javascript solution (it's always better not to use jQuery if it's easy).
Explanation: The script checks every 200ms if the new DOM is fully loaded and then scrolls to the top and stops checking. I tried without this 200ms loop and it sometimes failed to scroll because the page was just not completely displayed.

It seems that you understand why the problem is happening based on #jarrodek's comment.
As for a solution, you could either follow #TongShen's solution of wrapping your function in a $timeout or you can put the function call within the partial that you're loading.
<!-- New partial-->
<div ng-init="scrollToTop()">
</div>
If you view change is fired after a click event, you could also put the function call on that element. Just comes down to timing though. Just depends on how things are set up.

Related

$location.hash() breaking my function

I'm very close to finishing up my page, and I ran into this odd bug.
When I use ng-include, the pages load fine, but they resume scrolling from the previous position, so they don't start at the top with every click.
I resorted to using anchor scroll, but it doesn't work properly. I have to click the link again, for it to load the contents, and if I click the same link again, it offsets the page to some weird position.
This is my code in the controller:
$scope.toPage = function (index, id) {
$scope.missiveIndex = index;
$scope.contentsDown();
$location.hash(id);
};
and this is the HTML part:
<article id="{{articles.ids}}" class="stories-anim" ng-repeat="articles in stories" ng-hide="!isMissiveSlideIndex($index)" ng-include="articles.content" [autoscroll]>
</article>
the id is passed in via ng-click="ng-click="toPage($index, button.ids);"
is there a way to put the location.hash in the [onload] expression and autoscroll that way?
thanks
Probably the anchor scroll didn't work because the content of the page was not fully loaded when it was called.
You can solve that by using a timeout.
$timeout(function() {
$anchorScroll();
},500);

Run code when link is clicked, even if I stay on the same page

I have a collapsible sidebar navigation, and any time users click a link on it, I have the sidebar hide automatically. I achieve this in the run method by using the following code:
app.run(function($rootScope) {
$rootScope.$on('$routeChangeSuccess', function () {
$rootScope.closeNavigation();
})
});
Where app is my Angularjs module.
The problem is that it gets a little unintuitive to use, when you click a link in the navigation for the page you're already on, and then nothing happens. What I would like is to have the sidebar close anyway, so that the users still get focus on the content, even if it's the same content.
But Angularjs doesn't execute the $routeChangeSucess event, if there is no route change happening. So what can I use instead?
You simply need to use ng-show = isPanelVisible and ng-click = closePanel() on the html sidepanel tag (e.g. a <div>).
Then, define $scope.closePanel() in the controller associated to the actual view. For instance
$scope.closePanel = function() {
$scope.isPanelVisible = false;
}
See an example here. Code is here. Note that it doesn't use ng-show, but the behaviour is the same.

Update scope half way through a scope method

EDIT: I've put together a JSFiddle: http://jsfiddle.net/U3pVM/18137/
I want the black to go up AND THEN the green to slide across... I do not know how to achieve this... Read on for more information:
Side note: The code is in Typescript (re: () => {} )
The following context shows a web page that has an overlaying div, and a couple of divs behind it. It also denotes the kind of animation that is occuring via a css class.
I have the following code (see references to diagram):
$scope.Resizing = true; // This prevents animations from happening (as a css class is linked up to it (see below))
$scope.MoveToPage( col, row ); // 2
$scope.$applyAsync(() => { // Update the view to remove the class and then start sliding up the overlay div
$scope.FirstPageTop = -$scope.PageMap.WindowInfo.Height; // 3
$scope.Resizing = false; // Re-enable animations // Finish
});
What it is supposed to do is:
When Resizing is true, it removes a css class that animates stuff.
It then runs the MoveToPage method that makes a position left equal a new number.. usually it would animate to the new position but in this instance i don't want it to (thus the Resizing attribute).
We can see the above expressed in my html:
<div class="column-wrap"
ng-class="{ 'column-wrap-animation' : !Resizing }"
ng-style="{ 'width': PageMap.ContainerWidth + 'px',
'left': PageMap.ColumnWrap.Left + 'px'}">
The procedure then applies the scope asynchronously (because if i use a simple apply, it throws an error) and as a callback to this method, I am then doing pulling an overlay off of the top of the page... and then setting Resizing back to false to re-apply animations.
The problem:
I need to slide off an overlay AFTER positioning the content underneath it. What is actually happening is, it slides the overlay off of the top, while the under page animates to where i've told it to go. How do i get around this css animation digestion problem...
Perhaps an even simpler question is, why might the class not be being removed when Resizing is set to false and actively i am telling it to update the scope?
Explaining my context:
You can apparently use setTimeout to deal with the $scope.$apply issue:
http://jsfiddle.net/U3pVM/18138/
$scope.Resizing = true;
$scope.MoveToPage( col, row );
setTimeout( function() {
$scope.FirstPageTop = -$scope.PageMap.WindowInfo.Height;
$scope.Resizing = false;
$scope.$apply() // This is important
}, 1);

Call function when a tab in tabbar is first opened (onsen-ui)

I need to load a specific JavaScript function after one of the tabs in the tabbar finishes loading (after it is clicked for the first time). I don't really understand how to set up an event listener with the onsen-ui tabbar (ons-tabbar), and the docs were not very clear.
Basically, what I am trying to do, is create a few graphs on one of the pages. This page is loaded up when a ons-tab is selected and the partial loads. However, I need the JavaScript that creates the graphs to only load after the page is loaded (the JavaScript looks for the elements to position the graph in the partial, hence my problem right now is that the JS is loading first and not finding the html elements in the partial because they have't been loaded yet). How do I create an event listener to detect when one of the tabs are selected to run a specific code of JavaScript?
To be a little bit more clear, I am using the persistent attribute on this specific tab, so essentially I only need this event listener to run the JS code once, when the tab is first opened. However, I don't understand how to get the once event listener to work...
For the reference: http://onsen.io/reference/ons-tabbar.html#methods-summary
There are three tabbar methods related to events: on, once and off.
That means the way to use them is myTabbar.on(...), for example. You can see in that link their parameters as well. Also, right below there are the three tabbar events. In this case I think you want to use postchange, right?
In your HTML you create a tabbar and assign it to a variable with var attribute, as I am sure you already know:
<ons-tabbar var="myTabbar">
<ons-tab page="tab0.html" label="Tab0" active="true"></ons-tab>
<ons-tab page="tab1.html" label="Tab1"></ons-tab>
<ons-tab page="tab2.html" label="Tab2"></ons-tab>
</ons-tabbar>
Now, you have to wait until the app is initialised and Onsen UI is loaded in order to set the event listeners, and you can use ons.ready() for that.
Going back to the tabbar events method, since you only need to trigger the functionality with a specific tab you need to filter the tabs inside the listener. Therefore, if you use myTabbar.once('postchange', ...) the listener itself will be triggered only once regardless the filter you set inside, so perhaps it will be triggered with a different tab and then deleted for the tab you want. You cannot use it in this case.
Instead of that, you can set a listener with myTabbar.on('postchange', ...) that triggers always, then filter the tabs inside and remove the listener only when you want with myTabbar.off('postchange'). Something like this:
ons.ready(function() {
myTabbar.on('postchange', function(event) {
// Only with the tab we want, we can use the index or the name
if (event.index === 2) {
console.log('postchange and do stuff with the tab');
console.log(event.tabItem);
// Delete this listener so it's only triggered the first time
myTabbar.off('postchange');
}
});
Working here: http://codepen.io/frankdiox/pen/QbYEaq
In case you have other listener over postchange, you can use the second parameter of myTabbar.off(...) to remove the exact listener you want and not the others.
Hope it helps!

Angular: Fixed Top Menu, switching page but not scrolling top

I have a fixed top menu in Angular but when I try to switch page (within the app) the browser windows is not scrolling up.
I try to do this in my controller (this code is loaded every time i switch page within the app):
$('body').scroll();
window.scrollTo(0, 0);
$("html, body").animate({ scrollTop: 0 }, 100);
None of which is working.
Is there any trick that i am missing?
I tried this as well:
$location.hash('#top');
// call $anchorScroll()
$anchorScroll();
but without success
First of all, you should never attach DOM events or modify DOM from controller. You can use directives for that.
As per you question - AngularJS has an event called '$routeChangeSuccess'.
You can attach handler to this event with $scope.$on('$routeChangeSuccess', function () {...}) and run scrolling code inside.

Resources