web-dev-qa-db-fra.com

Changer de route ne défile pas en haut de la nouvelle page

J'ai trouvé un comportement indésirable, du moins pour moi, lorsque la route change . À l'étape 11 du tutoriel http://angular.github.io/angular-phonecat/step-11/app/ #/phones vous pouvez voir la liste des téléphones. Si vous faites défiler vers le bas et cliquez sur l'une des dernières, vous pouvez voir que le défilement n'est pas en haut, mais plutôt au milieu.

Je l'ai aussi trouvé dans l'une de mes applications et je me demandais comment puis-je le faire défiler vers le haut. Je peux le faire manuellement, mais je pense qu'il devrait exister une autre manière élégante de le faire que je ne connais pas.

Alors, y a-t-il un moyen élégant de faire défiler vers le haut lorsque la route change?

267
Matias Gonzalez

Le problème est que votre ngView conserve la position de défilement lorsqu'il charge une nouvelle vue. Vous pouvez demander à $anchorScroll de "faire défiler la fenêtre une fois la vue mise à jour" (les -docs sont un peu vagues, mais faire défiler signifie ici faire défiler vers le haut de la nouvelle vue).

La solution consiste à ajouter autoscroll="true" à votre élément ngView:

<div class="ng-view" autoscroll="true"></div>
574
Konrad Kiss

Il suffit de mettre ce code à courir

$rootScope.$on("$routeChangeSuccess", function (event, currentRoute, previousRoute) {

    window.scrollTo(0, 0);

});
44
xmaster

Après une heure ou deux d'essayer toutes les combinaisons de ui-view autoscroll=true, $stateChangeStart, $locationChangeStart, $uiViewScrollProvider.useAnchorScroll(), $provide('$uiViewScroll', ...) et de nombreux autres, je ne pouvais pas obtenir le défilement vers le haut de la nouvelle page pour fonctionner comme prévu.

C'était finalement ce qui a fonctionné pour moi. Il capture pushState et replaceState et ne met à jour que la position de défilement lorsque de nouvelles pages sont consultées (le bouton Précédent/Suivant conserve les positions de défilement):

.run(function($anchorScroll, $window) {
  // hack to scroll to top when navigating to new URLS but not back/forward
  var wrap = function(method) {
    var orig = $window.window.history[method];
    $window.window.history[method] = function() {
      var retval = orig.apply(this, Array.prototype.slice.call(arguments));
      $anchorScroll();
      return retval;
    };
  };
  wrap('pushState');
  wrap('replaceState');
})
31
wkonkel

Voici ma solution (apparemment) robuste, complète et (assez) concise. Il utilise le style compatible avec la minification (et l'accès angular.module (NAME) à votre module).

angular.module('yourModuleName').run(["$rootScope", "$anchorScroll" , function ($rootScope, $anchorScroll) {
    $rootScope.$on("$locationChangeSuccess", function() {
                $anchorScroll();
    });
}]);

Post-scriptum j'ai trouvé que la chose autoscroll n'avait aucun effet si défini sur true ou false.

18
James

En utilisant angularjs UI Router, voici ce que je fais:

    .state('myState', {
        url: '/myState',
        templateUrl: 'app/views/myState.html',
        onEnter: scrollContent
    })

Avec:

var scrollContent = function() {
    // Your favorite scroll method here
};

Il n'échoue jamais sur aucune page, et ce n'est pas global.

15
Léon Pelletier

Pour votre information, si vous rencontrez le problème décrit dans le titre (comme je le faisais) qui utilise également Le AngularUI Router plugin ...

Comme demandé et répondu à cette question SO, le routeur angular-ui passe au bas de la page lorsque vous modifiez les itinéraires.
Vous ne pouvez pas comprendre pourquoi la page se charge en bas? Problème de défilement automatique de routeur d'interface utilisateur angulaire

Toutefois, comme l'indique la réponse, vous pouvez désactiver ce comportement en indiquant autoscroll="false" sur votre ui-view

Par exemple: 

<div ui-view="pagecontent" autoscroll="false"></div>
<div ui-view="sidebar" autoscroll="false"></div> 

http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.directive:ui-view

13
mg1075

vous pouvez utiliser ce javascript

$anchorScroll()
7
CodeIsLife

Si vous utilisez le routeur ui, vous pouvez utiliser (en marche)

$rootScope.$on("$stateChangeSuccess", function (event, currentState, previousState) {
    $window.scrollTo(0, 0);
});
7
Colzak

J'ai trouvé cette solution. Si vous accédez à une nouvelle vue, la fonction est exécutée.

var app = angular.module('hoofdModule', ['ngRoute']);

    app.controller('indexController', function ($scope, $window) {
        $scope.$on('$viewContentLoaded', function () {
            $window.scrollTo(0, 0);
        });
    });
4
Vince Verhoeven

Un peu tard pour la fête, mais j'ai essayé toutes les méthodes possibles et rien ne fonctionnait correctement. Voici ma solution élégante:

J'utilise un contrôleur qui régit toutes mes pages avec ui-router. Cela me permet de rediriger les utilisateurs qui ne sont pas authentifiés ou validés vers un emplacement approprié. La plupart des gens ont mis leur middleware dans la configuration de leur application, mais j'avais besoin de quelques requêtes http. Par conséquent, un contrôleur global me convient mieux.

Mon index.html ressemble à:

<main ng-controller="InitCtrl">
    <nav id="top-nav"></nav>
    <div ui-view></div>
</main>

Mon initCtrl.js ressemble à:

angular.module('MyApp').controller('InitCtrl', function($rootScope, $timeout, $anchorScroll) {
    $rootScope.$on('$locationChangeStart', function(event, next, current){
        // middleware
    });
    $rootScope.$on("$locationChangeSuccess", function(){
        $timeout(function() {
            $anchorScroll('top-nav');
       });
    });
});

J'ai essayé toutes les options possibles, cette méthode fonctionne le mieux.

3
BuffMcBigHuge

Définir autoScroll sur true ne me convenait pas, alors j'ai choisi une autre solution. J'ai créé un service qui prend en compte chaque changement d'itinéraire et qui utilise le service intégré $ anchorScroll pour faire défiler jusqu'en haut. Travaille pour moi :-).

Un service:

 (function() {
    "use strict";

    angular
        .module("mymodule")
        .factory("pageSwitch", pageSwitch);

    pageSwitch.$inject = ["$rootScope", "$anchorScroll"];

    function pageSwitch($rootScope, $anchorScroll) {
        var registerListener = _.once(function() {
            $rootScope.$on("$locationChangeSuccess", scrollToTop);
        });

        return {
            registerListener: registerListener
        };

        function scrollToTop() {
            $anchorScroll();
        }
    }
}());

Enregistrement:

angular.module("mymodule").run(["pageSwitch", function (pageSwitch) {
    pageSwitch.registerListener();
}]);
3
asp_net

Aucune des réponses fournies n'a résolu mon problème. J'utilise une animation entre les vues et le défilement aura toujours lieu après l'animation. La solution que j'ai trouvée pour que le défilement vers le haut se produise avant l'animation est la directive suivante:

yourModule.directive('scrollToTopBeforeAnimation', ['$animate', function ($animate) {
    return {
        restrict: 'A',
        link: function ($scope, element) {
            $animate.on('enter', element, function (element, phase) {

                if (phase === 'start') {

                    window.scrollTo(0, 0);
                }

            })
        }
    };
}]);

Je l'ai inséré dans ma vue comme suit: 

<div scroll-to-top-before-animation>
    <div ng-view class="view-animation"></div>
</div>
2
aBertrand

Toutes les réponses ci-dessus annulent le comportement attendu du navigateur. Ce que la plupart des gens veulent, c’est quelque chose qui fera défiler vers le haut s’il s’agit d’une «nouvelle» page, mais qui revient à la position précédente si vous y accédez par le bouton Précédent (ou Suivant).

Si vous supposez que le mode HTML5 est simple, cela s'avère facile (même si je suis sûr que certaines personnes brillantes peuvent comprendre comment rendre cela plus élégant!):

// Called when browser back/forward used
window.onpopstate = function() { 
    $timeout.cancel(doc_scrolling); 
};

// Called after ui-router changes state (but sadly before onpopstate)
$scope.$on('$stateChangeSuccess', function() {
    doc_scrolling = $timeout( scroll_top, 50 );

// Moves entire browser window to top
scroll_top = function() {
    document.body.scrollTop = document.documentElement.scrollTop = 0;
}

La façon dont cela fonctionne est que le routeur suppose qu'il fera défiler vers le haut, mais attend un peu pour donner au navigateur une chance de finir. Si le navigateur nous informe ensuite que le changement est dû à une navigation Précédent/Suivant, il annule le délai et le défilement ne se produit jamais.

J'ai utilisé des commandes brutes document pour faire défiler parce que je veux passer en haut de la fenêtre. Si vous souhaitez simplement que votre ui-view défile, définissez autoscroll="my_var" dans lequel vous contrôlez my_var en utilisant les techniques ci-dessus. Mais je pense que la plupart des gens voudront faire défiler la page entière si vous allez sur la page en tant que "nouveau".

Ce qui précède utilise ui-router, bien que vous puissiez utiliser ng-route à la place en échangeant $routeChangeSuccess pour$stateChangeSuccess.

2
Dev93

Solution simple, ajoutez scrollPositionRestoration dans le module de route principale activé.
Comme ça:

const routes: Routes = [

 {
   path: 'registration',
   loadChildren: () => RegistrationModule
},
];

 @NgModule({
  imports: [
   RouterModule.forRoot(routes,{scrollPositionRestoration:'enabled'})
  ],
 exports: [
 RouterModule
 ]
 })
  export class AppRoutingModule { }
1
Farida Anjum

Essayez ceci http://ionicframework.com/docs/api/service/ $ ionicScrollDelegate /

Il fait défiler vers le haut de la liste scrollTop ()

0
ToughPal

Cela a fonctionné pour moi, y compris autoscroll

<div class="ngView" autoscroll="true" >

0
Teja

J'ai enfin obtenu ce dont j'avais besoin.

Je devais faire défiler vers le haut, mais je voulais certaines transitions ne pas à  

Vous pouvez contrôler cela au niveau de chaque itinéraire.
Je combine la solution ci-dessus de @wkonkel et ajoute un simple paramètre noScroll: true à certaines déclarations de route. Ensuite, je prends ça dans la transition. 

Tout compte fait: Cela flotte en haut de la page sur les nouvelles transitions, il ne flotte pas en haut des transitions Forward/Back et vous permet de remplacer ce comportement si nécessaire.

Le code: (solution précédente plus une option supplémentaire noScroll)

  // hack to scroll to top when navigating to new URLS but not back/forward
  let wrap = function(method) {
    let orig = $window.window.history[method];
    $window.window.history[method] = function() {
      let retval = orig.apply(this, Array.prototype.slice.call(arguments));
      if($state.current && $state.current.noScroll) {
        return retval;
      }
      $anchorScroll();
      return retval;
    };
  };
  wrap('pushState');
  wrap('replaceState');

Mettez cela dans votre bloc app.run et injectez $state... myApp.run(function($state){...})

Ensuite, si vous ne voulez pas faire défiler vers le haut de la page, créez un itinéraire comme celui-ci:

.state('someState', {
  parent: 'someParent',
  url: 'someUrl',
  noScroll : true // Notice this parameter here!
})
0
Augie Gardner