Mengubah rute tidak gulir ke atas di halaman baru

271

Saya telah menemukan beberapa perilaku yang tidak diinginkan, setidaknya bagi saya, ketika rute berubah. Pada langkah 11 tutorial http://angular.github.io/angular-phonecat/step-11/app/#/phones Anda dapat melihat daftar ponsel. Jika Anda menggulir ke bawah dan mengklik salah satu yang terbaru, Anda dapat melihat bahwa gulir itu tidak di atas, malah agak di tengah.

Saya telah menemukan ini di salah satu aplikasi saya juga dan saya bertanya-tanya bagaimana saya bisa mendapatkan ini untuk menggulir ke atas. Saya bisa melakukannya secara manual, tetapi saya pikir harus ada cara elegan lain untuk melakukan ini yang saya tidak tahu.

Jadi, adakah cara yang elegan untuk menggulir ke atas ketika rute berubah?

Matias Gonzalez
sumber

Jawaban:

578

Masalahnya adalah ngView Anda mempertahankan posisi gulir ketika memuat tampilan baru. Anda dapat menginstruksikan $anchorScrolluntuk "menggulir viewport setelah tampilan diperbarui" ( dokumen agak kabur, tetapi menggulir di sini berarti menggulir ke bagian atas tampilan baru).

Solusinya adalah menambahkan autoscroll="true"ke elemen ngView Anda:

<div class="ng-view" autoscroll="true"></div>
Konrad Kiss
sumber
1
Ini bekerja untuk saya, gulir halaman baik-baik saja ke atas, tetapi saya menggunakan scrollTo dengan directive smoothScroll, apakah ada cara untuk melakukan scroll smoth ke atas ?, selain itu solusi Anda gulir ke atas tampilan bukan ke halaman yang ada untuk gulir ke atas halaman?
xzegga
19
Status dokumen Jika atribut diatur tanpa nilai, aktifkan pengguliran . Jadi, Anda dapat mempersingkat kode menjadi adil <div class="ng-view" autoscroll></div>dan berfungsi sama (kecuali jika Anda ingin membuat scrolling bersyarat).
Daniel Liuzzi
7
itu tidak berhasil untuk saya, dokter tidak mengatakan itu akan bergulir ke atas
Pencilcheck
6
Saya menemukan bahwa jika autoscroll disetel ke true maka ketika pengguna 'kembali' mereka kembali ke bagian atas halaman, bukan di mana mereka meninggalkannya, yang merupakan perilaku yang tidak diinginkan.
axzr
4
ini akan menggulir tampilan Anda ke div ini. Jadi itu akan bergulir ke atas halaman hanya jika kebetulan berada di bagian atas halaman. Kalau tidak, Anda harus menjalankan $window.scrollTo(0,0)$ pendengar acara $ stateChangeSuccess
Filip Sobczak
46

Masukkan kode ini untuk dijalankan

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

    window.scrollTo(0, 0);

});
xmaster
sumber
6
$window.scrollTop(0,0)bekerja lebih baik untuk saya daripada autoscroll. Dalam kasus saya, saya memiliki pandangan yang masuk dan keluar dengan transisi.
IsidroGH
1
Ini juga berfungsi lebih baik untuk saya daripada autoscroll = "true". Autoscroll sudah terlambat karena alasan tertentu, itu akan bergulir ke atas setelah tampilan baru sudah terlihat.
Gregory Bolkenstijn
5
Ini bekerja paling baik untuk saya: $ rootScope. $ On ('$ stateChangeSuccess', function () {$ ("html, body"). Animate ({scrollTop: 0}, 200);});
James Gentes
$ window.scrollTop seharusnya $ window.scrollTo, kalau tidak dikatakan tidak ada. Solusi ini sangat bagus!
Perangkat Lunak Prophets
@ JamesGentes saya membutuhkan yang ini juga. Terima kasih.
Mackenzie Browne
36

Kode ini bekerja sangat baik untuk saya .. Saya harap ini juga akan bekerja dengan baik untuk Anda .. Yang harus Anda lakukan hanyalah menyuntikkan $ anchorScroll ke run run Anda dan menerapkan fungsi pendengar ke rootScope seperti yang telah saya lakukan dalam contoh di bawah ini ..

 angular.module('myAngularApp')
.run(function($rootScope, Auth, $state, $anchorScroll){
    $rootScope.$on("$locationChangeSuccess", function(){
        $anchorScroll();
    });

Berikut urutan pemanggilan Modul Angular:

  1. app.config()
  2. app.run()
  3. directive's compile functions (if they are found in the dom)
  4. app.controller()
  5. directive's link functions (again, if found)

RUN BLOCK dijalankan setelah injector dibuat dan digunakan untuk memulai aplikasi .. itu berarti ketika Anda diarahkan ke tampilan rute baru, pendengar di blok run memanggil

$ anchorScroll ()

dan sekarang Anda dapat melihat gulir mulai ke atas dengan tampilan perutean baru :)

Rizwan Jamal
sumber
Akhirnya, solusi yang bekerja dengan header-lengket! Terima kasih!
Fabio
31

Setelah satu atau dua jam mencoba setiap kombinasi ui-view autoscroll=true, $stateChangeStart, $locationChangeStart, $uiViewScrollProvider.useAnchorScroll(), $provide('$uiViewScroll', ...), dan banyak lainnya, saya tidak bisa scroll-to-top-on-baru-halaman bekerja seperti yang diharapkan.

Inilah yang akhirnya berhasil bagi saya. Ini menangkap pushState dan replaceState dan hanya memperbarui posisi gulir ketika halaman baru dinavigasi ke (tombol kembali / maju mempertahankan posisi gulir mereka):

.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');
})
wkonkel
sumber
Ini berfungsi baik untuk kasus penggunaan saya. Saya menggunakan Angular UI-Router dengan tampilan bersarang (beberapa level dalam). Terima kasih!
Joshua Powell
FYI - Anda harus berada dalam mode HTML5 di sudut untuk menggunakan pushState: $locationProvider.html5Mode(true); @wkronkel apakah ada cara untuk mencapai ini ketika tidak menggunakan mode HTML5 di sudut? Karena pemuatan ulang halaman menyebabkan 404 kesalahan karena mode html 5 sedang aktif
britztopher
@britztopher Anda dapat terhubung ke acara bawaan dan mempertahankan sejarah Anda sendiri, bukan?
Casey
2
Anda menambahkan ini .runke mana? sudut appobjek Anda ?
Philll_t
1
@ Philll_t: Ya, runfungsi adalah metode yang tersedia di setiap objek modul sudut.
Hinrich
18

Inilah solusi saya (yang tampaknya) kuat, lengkap dan (cukup) ringkas. Ini menggunakan gaya kompatibel minification (dan akses angular.module (NAME) ke modul Anda).

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

PS Saya menemukan bahwa hal autoscroll tidak berpengaruh apakah disetel ke true atau false.

James
sumber
1
Hmmm .. ini menggulirkan tampilan ke atas terlebih dahulu dan kemudian tampilan berubah di layar untuk saya.
Strelok
Solusi bersih yang saya cari. Jika Anda mengklik "kembali" itu akan membawa Anda ke tempat Anda tinggalkan - ini mungkin tidak diinginkan untuk beberapa orang, tapi saya suka dalam kasus saya.
mjoyce91
15

Menggunakan angularjs UI Router, yang saya lakukan adalah ini:

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

Dengan:

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

Itu tidak pernah gagal di halaman mana pun, dan itu bukan global.

Léon Pelletier
sumber
1
Ide bagus, Anda bisa membuatnya lebih pendek menggunakan:onEnter: scrollContent
zucker
2
Apa yang Anda letakkan di tempat Anda menulis // Your favorite scroll method here?
Agen Zebra
4
Waktu yang baik: Saya dua baris di atas komentar ini dalam kode asli, mencoba ui-router-title . Saya menggunakan $('html, body').animate({ scrollTop: -10000 }, 100);
Léon Pelletier
1
Haha, bagus! Aneh, itu tidak bekerja untuk saya sepanjang waktu. Saya memiliki beberapa view yang sangat pendek sehingga tidak memiliki scroll, dan dua view yang memiliki scroll, ketika saya letakkan onEnter: scrollContentdi setiap view, itu hanya bekerja pada view / rute pertama, bukan yang ke-2. Aneh.
Agen Zebra
1
Saya tidak yakin, tidak, itu hanya tidak gulir ke atas kapan seharusnya. Ketika saya mengklik antara 'rute' beberapa di antaranya masih bergulir ke tempat tampilan-ng, daripada bagian absolut dari halaman terlampir. Apakah itu masuk akal?
Agen Zebra
13

FYI untuk siapa pun yang menemukan masalah yang dijelaskan dalam judul (seperti yang saya lakukan) yang juga menggunakan plugin Router AngularUI ...

Seperti yang ditanyakan dan dijawab dalam pertanyaan SO ini, router angular-ui melompat ke bagian bawah halaman ketika Anda mengubah rute.
Tidak tahu mengapa halaman dimuat di bagian bawah? Masalah UI-Router otomatis sudut

Namun, seperti yang dinyatakan dalam jawabannya, Anda dapat mematikan perilaku ini dengan mengatakan autoscroll="false"pada Anda ui-view.

Sebagai contoh:

<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

mg1075
sumber
16
Milik saya tidak melompat ke bawah, itu hanya mempertahankan posisi gulir bukannya pergi ke atas.
Stephen Smith
5
Ini tidak menjawab pertanyaan. Saya punya masalah yang sama - ketika rute mengubah level gulir hanya tetap di tempat yang sama alih-alih menggulir ke atas. Saya bisa mendapatkannya untuk menggulir ke atas, tetapi hanya dengan mengubah hash, yang saya tidak ingin lakukan karena alasan yang jelas.
Will
7

Anda dapat menggunakan javascript ini

$anchorScroll()
CodeIsLife
sumber
3
Ya, itu tidak sesederhana itu di angularjs. Solusi Anda hanya valid dalam jquery. Apa yang saya cari adalah cara yang elegan untuk melakukan ini di angularjs
Matias Gonzalez
4
Sudahkah Anda mencoba menggunakan $ anchorScroll (), ini didokumentasikan docs.angularjs.org/api/ng.%24anchorScroll .
CodeIsLife
1
FYI Jika Anda menentukan lokasi dengan $location.hash('top')sebelum $anchorScroll()tombol browser maju / kembali default tidak lagi berfungsi; mereka terus mengarahkan Anda ke hash di URL
Roy
7

Jika Anda menggunakan ui-router, Anda dapat menggunakan (saat dijalankan)

$rootScope.$on("$stateChangeSuccess", function (event, currentState, previousState) {
    $window.scrollTo(0, 0);
});
Colzak
sumber
Saya tahu ini harus berhasil, saya banyak menggunakan "$ stateChangeSuccess", tetapi karena alasan tertentu $ window.scrollTo (0,0) tidak bekerja dengan itu.
Abhas
4

Saya menemukan solusi ini. Jika Anda pergi ke tampilan baru fungsi dijalankan.

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

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

Pengaturan autoScroll menjadi true bukan trik untuk saya, jadi saya memang memilih solusi lain. Saya membangun layanan yang menghubungkan setiap kali rute berubah dan yang menggunakan layanan $ anchorScroll bawaan untuk menggulir ke atas. Bekerja untuk saya :-).

Layanan:

 (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();
        }
    }
}());

Registrasi:

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

Sedikit terlambat ke pesta, tetapi saya sudah mencoba setiap metode yang mungkin, dan tidak ada yang berhasil. Inilah solusi elegan saya:

Saya menggunakan pengontrol yang mengatur semua halaman saya dengan ui-router. Ini memungkinkan saya untuk mengarahkan ulang pengguna yang tidak diautentikasi atau divalidasi ke lokasi yang sesuai. Kebanyakan orang meletakkan middleware mereka di konfigurasi aplikasi mereka, tetapi saya memerlukan beberapa permintaan http, oleh karena itu pengontrol global berfungsi lebih baik untuk saya.

Index.html saya terlihat seperti:

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

InitCtrl.js saya terlihat seperti:

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');
       });
    });
});

Saya sudah mencoba setiap opsi yang mungkin, metode ini bekerja paling baik.

BuffMcBigHuge
sumber
1
Ini adalah satu-satunya yang bekerja dengan bahan sudut bagi saya, juga menempatkan id="top-nav"elemen yang benar itu penting.
BatteryAcid
2

Semua jawaban di atas tidak sesuai dengan perilaku browser yang diharapkan. Apa yang diinginkan kebanyakan orang adalah sesuatu yang akan bergulir ke atas jika itu adalah halaman "baru", tetapi kembali ke posisi sebelumnya jika Anda sampai di sana melalui tombol Kembali (atau Maju).

Jika Anda mengasumsikan mode HTML5, ini ternyata mudah (walaupun saya yakin beberapa orang cerdas di luar sana dapat mengetahui cara membuatnya lebih elegan!):

// 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;
}

Cara kerjanya adalah router menganggap akan menggulir ke atas, tetapi menunda sedikit untuk memberi browser kesempatan untuk menyelesaikan. Jika browser kemudian memberi tahu kami bahwa perubahan itu disebabkan oleh navigasi Kembali / Maju, itu membatalkan batas waktu, dan gulir tidak pernah terjadi.

Saya menggunakan documentperintah mentah untuk menggulir karena saya ingin pindah ke seluruh bagian atas jendela. Jika Anda hanya ingin ui-viewmenggulir, maka atur di autoscroll="my_var"mana Anda mengontrol my_varmenggunakan teknik di atas. Tetapi saya pikir sebagian besar orang akan ingin menggulirkan seluruh halaman jika Anda pergi ke halaman sebagai "baru".

Di atas menggunakan ui-router, meskipun Anda bisa menggunakan ng-rute sebaliknya dengan menukar $routeChangeSuccessuntuk $stateChangeSuccess.

Dev93
sumber
Bagaimana Anda menerapkan ini? Di mana Anda akan meletakkannya dalam kode rute angular-ui biasa seperti berikut ini? var routerApp = angular.module('routerApp', ['ui.router']); routerApp.config(function($stateProvider, $urlRouterProvider, $locationProvider) { $urlRouterProvider.otherwise('/home'); $locationProvider.html5Mode(false).hashPrefix(""); $stateProvider // HOME STATES AND NESTED VIEWS ======================================== .state('home', { url: '/home', templateUrl: 'partial-home.html' }) });
Agen Zebra
hmmm ... tidak bekerja :-( Ini hanya membunuh aplikasi. .state('web', { url: '/webdev', templateUrl: 'partial-web.html', controller: function($scope) { $scope.clients = ['client1', 'client2', 'client3']; // I put it here } }) Saya hanya belajar hal ini, mungkin saya kehilangan sesuatu: D
Agen Zebra
@ AgentZebra Minimal, controller akan perlu mengambil $ timeout sebagai input (karena kode saya referensi itu), tetapi mungkin yang lebih penting, controller yang saya sebutkan perlu hidup pada level di atas kondisi individual (Anda dapat menyertakan pernyataan dalam pengontrol tingkat atas Anda). Kurva belajar AngularJS awal memang sangat sulit; semoga berhasil!
Dev93
2

Tidak ada jawaban yang diberikan yang menyelesaikan masalah saya. Saya menggunakan animasi antara tampilan dan gulir akan selalu terjadi setelah animasi. Solusi yang saya temukan sehingga bergulir ke atas terjadi sebelum animasi adalah arahan berikut:

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);
                }

            })
        }
    };
}]);

Saya memasukkannya pada tampilan saya sebagai berikut:

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

Solusi Sederhana, tambahkan scrollPositionRestorationmodul rute utama yang diaktifkan.
Seperti ini:

const routes: Routes = [

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

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

Saya akhirnya mendapatkan apa yang saya butuhkan.

Saya perlu menggulir ke atas, tetapi ingin beberapa transisi tidak

Anda dapat mengontrol ini pada level rute-per-rute.
Saya menggabungkan solusi di atas dengan @wkonkel dan menambahkan yang sederhananoScroll: true parameter ke beberapa deklarasi rute. Lalu saya menangkapnya dalam transisi.

Semua dalam semua: Ini melayang ke atas halaman pada transisi baru, itu tidak melayang ke atas pada Forward/ Backtransisi, dan memungkinkan Anda untuk menimpa perilaku ini jika perlu.

Kode: (solusi sebelumnya ditambah opsi noScroll tambahan)

  // 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');

Masukkan itu di app.runblok Anda dan suntikkan $state...myApp.run(function($state){...})

Kemudian, Jika Anda tidak ingin menggulir ke bagian atas halaman, buat rute seperti ini:

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

Ini bekerja untuk saya termasuk autoscroll

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

Teja
sumber