Saya telah menerapkan aplikasi satu halaman angularjs menggunakan ui-router .
Awalnya saya mengidentifikasi setiap negara menggunakan url yang berbeda tetapi ini dibuat untuk url yang dikemas GUID yang tidak ramah.
Jadi saya sekarang telah mendefinisikan situs saya sebagai mesin negara yang jauh lebih sederhana. Status tidak diidentifikasi oleh url tetapi hanya dialihkan ke sesuai kebutuhan, seperti ini:
Tentukan Status Bersarang
angular
.module 'app', ['ui.router']
.config ($stateProvider) ->
$stateProvider
.state 'main',
templateUrl: 'main.html'
controller: 'mainCtrl'
params: ['locationId']
.state 'folder',
templateUrl: 'folder.html'
parent: 'main'
controller: 'folderCtrl'
resolve:
folder:(apiService) -> apiService.get '#base/folder/#locationId'
Transisi ke Status yang Ditentukan
#The ui-sref attrib transitions to the 'folder' state
a(ui-sref="folder({locationId:'{{folder.Id}}'})")
| {{ folder.Name }}
Sistem ini bekerja dengan sangat baik dan saya suka sintaksnya yang bersih. Namun, karena saya tidak menggunakan url, tombol kembali tidak berfungsi.
Bagaimana cara menjaga agar mesin status ui-router saya tetap rapi tetapi mengaktifkan fungsi tombol kembali?
javascript
angularjs
coffeescript
angular-ui-router
biofraktal
sumber
sumber
Jawaban:
Catatan
Jawaban yang menyarankan penggunaan variasi
$window.history.back()
semuanya telah melewatkan bagian penting dari pertanyaan: Bagaimana cara mengembalikan status aplikasi ke lokasi-negara yang benar saat sejarah melompat (mundur / maju / refresh). Dengan pemikiran itu; tolong, baca terus.Ya, adalah mungkin untuk membuat browser mundur / maju (riwayat) dan menyegarkan saat menjalankan mesin
ui-router
negara murni tetapi itu membutuhkan sedikit kerja.Anda membutuhkan beberapa komponen:
URL unik . Browser hanya mengaktifkan tombol mundur / maju saat Anda mengubah url, jadi Anda harus membuat url unik untuk setiap status yang dikunjungi. Url ini tidak perlu berisi informasi negara bagian apa pun.
Sebuah Layanan Sesi . Setiap url yang dihasilkan berkorelasi dengan kondisi tertentu sehingga Anda memerlukan cara untuk menyimpan pasangan status-url Anda sehingga Anda dapat mengambil informasi status setelah aplikasi sudut Anda dimulai ulang dengan klik mundur / maju atau segarkan.
Sejarah Negara . Kamus sederhana dari status ui-router yang dikunci oleh url unik. Jika Anda dapat mengandalkan HTML5 maka Anda dapat menggunakan API Riwayat HTML5 , tetapi jika, seperti saya, Anda tidak dapat menerapkannya sendiri dalam beberapa baris kode (lihat di bawah).
Sebuah Layanan Lokasi . Terakhir, Anda harus dapat mengelola kedua perubahan status ui-router, yang dipicu secara internal oleh kode Anda, dan perubahan url browser normal biasanya dipicu oleh pengguna yang mengklik tombol browser atau mengetik sesuatu ke dalam bilah browser. Ini semua bisa menjadi sedikit rumit karena mudah untuk menjadi bingung tentang apa yang memicu apa.
Berikut adalah implementasi saya untuk masing-masing persyaratan tersebut. Saya telah menggabungkan semuanya menjadi tiga layanan:
Layanan Sesi
class SessionService setStorage:(key, value) -> json = if value is undefined then null else JSON.stringify value sessionStorage.setItem key, json getStorage:(key)-> JSON.parse sessionStorage.getItem key clear: -> @setStorage(key, null) for key of sessionStorage stateHistory:(value=null) -> @accessor 'stateHistory', value # other properties goes here accessor:(name, value)-> return @getStorage name unless value? @setStorage name, value angular .module 'app.Services' .service 'sessionService', SessionService
Ini adalah pembungkus untuk
sessionStorage
objek javascript . Saya telah memotongnya untuk kejelasan di sini. Untuk penjelasan lengkap tentang ini, silakan lihat: Bagaimana cara menangani penyegaran halaman dengan Aplikasi Halaman Tunggal AngularJSLayanan Sejarah Negara
class StateHistoryService @$inject:['sessionService'] constructor:(@sessionService) -> set:(key, state)-> history = @sessionService.stateHistory() ? {} history[key] = state @sessionService.stateHistory history get:(key)-> @sessionService.stateHistory()?[key] angular .module 'app.Services' .service 'stateHistoryService', StateHistoryService
The
StateHistoryService
terlihat setelah penyimpanan dan pengambilan negara sejarah mengetik dengan yang dihasilkan, url yang unik. Ini benar-benar hanya pembungkus kenyamanan untuk objek gaya kamus.Layanan Lokasi Negara
class StateLocationService preventCall:[] @$inject:['$location','$state', 'stateHistoryService'] constructor:(@location, @state, @stateHistoryService) -> locationChange: -> return if @preventCall.pop('locationChange')? entry = @stateHistoryService.get @location.url() return unless entry? @preventCall.push 'stateChange' @state.go entry.name, entry.params, {location:false} stateChange: -> return if @preventCall.pop('stateChange')? entry = {name: @state.current.name, params: @state.params} #generate your site specific, unique url here url = "/#{@state.params.subscriptionUrl}/#{Math.guid().substr(0,8)}" @stateHistoryService.set url, entry @preventCall.push 'locationChange' @location.url url angular .module 'app.Services' .service 'stateLocationService', StateLocationService
The
StateLocationService
menangani dua peristiwa:locationChange . Ini dipanggil saat lokasi browser diubah, biasanya saat tombol kembali / maju / segarkan ditekan atau saat aplikasi pertama kali dimulai atau saat pengguna mengetikkan url. Jika status untuk location.url saat ini ada,
StateHistoryService
maka status itu digunakan untuk memulihkan status melalui ui-router$state.go
.stateChange . Ini dipanggil saat Anda memindahkan status secara internal. Nama negara bagian saat ini dan params disimpan dalam
StateHistoryService
kunci oleh url yang dihasilkan. Url yang dihasilkan ini dapat berupa apa pun yang Anda inginkan, mungkin atau mungkin tidak mengidentifikasi negara dengan cara yang dapat dibaca manusia. Dalam kasus saya, saya menggunakan param negara ditambah urutan digit yang dihasilkan secara acak yang berasal dari pedoman (lihat kaki untuk cuplikan generator pedoman). Url yang dihasilkan ditampilkan di bilah browser dan, yang terpenting, ditambahkan ke tumpukan riwayat internal browser menggunakan@location.url url
. Ini menambahkan url ke tumpukan riwayat browser yang memungkinkan tombol maju / mundur.Masalah besar dengan teknik ini adalah bahwa menelepon
@location.url url
dalamstateChange
metode akan memicu$locationChangeSuccess
acara dan memanggillocationChange
metode. Dengan cara yang sama memanggil@state.go
fromlocationChange
akan memicu$stateChangeSuccess
kejadian dan begitu pulastateChange
metode. Ini menjadi sangat membingungkan dan mengacaukan riwayat browser tanpa akhir.Solusinya sangat sederhana. Anda dapat melihat
preventCall
array yang digunakan sebagai tumpukan (pop
danpush
). Setiap kali salah satu metode dipanggil, ia mencegah metode lain disebut hanya satu kali. Teknik ini tidak mengganggu pemicu peristiwa $ yang benar dan menjaga semuanya tetap lurus.Sekarang yang perlu kita lakukan adalah memanggil
HistoryService
metode pada waktu yang tepat dalam siklus hidup transisi status. Ini dilakukan dalam.run
metode Aplikasi AngularJS , seperti ini:App.run sudut
angular .module 'app', ['ui.router'] .run ($rootScope, stateLocationService) -> $rootScope.$on '$stateChangeSuccess', (event, toState, toParams) -> stateLocationService.stateChange() $rootScope.$on '$locationChangeSuccess', -> stateLocationService.locationChange()
Hasilkan Panduan
Math.guid = -> s4 = -> Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1) "#{s4()}#{s4()}-#{s4()}-#{s4()}-#{s4()}-#{s4()}#{s4()}#{s4()}"
Dengan semua ini di tempatnya, tombol maju / mundur dan tombol segarkan semuanya berfungsi seperti yang diharapkan.
sumber
@setStorage
dan@getStorage
daripada `@ get / setSession '.$window.history.back()
telah melewatkan bagian penting dari pertanyaan. Intinya adalah untuk mengembalikan aplikasi negara ke negara-lokasi yang benar sebagai sejarah melompat (belakang / maju / refresh). Ini biasanya dicapai dengan menyediakan data status melalui URI. Pertanyaan tersebut menanyakan bagaimana cara melompat di antara lokasi negara bagian tanpa data status URI (eksplisit). Dengan adanya batasan ini, tidak cukup hanya mereplikasi tombol kembali karena ini bergantung pada data status URI untuk membangun kembali status-lokasi.app.run(['$window', '$rootScope', function ($window , $rootScope) { $rootScope.goBack = function(){ $window.history.back(); } }]); <a href="#" ng-click="goBack()">Back</a>
sumber
$window.history.back
adalah melakukan keajaiban bukan$rootScope
... jadi kembali bisa terikat ke lingkup direktif navbar Anda jika Anda mau.$window.history.back();
fungsi in untuk dipanggilng-click
.Setelah menguji berbagai proposal, saya menemukan bahwa cara termudah seringkali yang terbaik.
Jika Anda menggunakan angular ui-router dan Anda memerlukan tombol untuk kembali terbaik adalah ini:
<button onclick="history.back()">Back</button>
atau
// Peringatan jangan setel href atau jalurnya akan rusak.
Penjelasan: Misalkan aplikasi manajemen standar. Cari objek -> Lihat objek -> Edit objek
Menggunakan solusi sudut Dari keadaan ini:
Kepada:
Nah itulah yang kami inginkan kecuali jika sekarang Anda mengklik tombol kembali browser Anda akan berada di sana lagi:
Dan itu tidak logis
Namun menggunakan solusi sederhana
<a onclick="history.back()"> Back </a>
dari:
setelah klik tombol:
setelah klik tombol kembali browser:
Konsistensi dihormati. :-)
sumber
Jika Anda mencari tombol "kembali" yang paling sederhana, Anda dapat menyiapkan perintah seperti ini:
.directive('back', function factory($window) { return { restrict : 'E', replace : true, transclude : true, templateUrl: 'wherever your template is located', link: function (scope, element, attrs) { scope.navBack = function() { $window.history.back(); }; } }; });
Ingatlah bahwa ini adalah tombol "kembali" yang cukup tidak cerdas karena menggunakan riwayat browser. Jika Anda menyertakannya di halaman arahan Anda, itu akan mengirim pengguna kembali ke url mana pun mereka berasal sebelum mendarat di url Anda.
sumber
solusi tombol mundur / maju browser
Saya mengalami masalah yang sama dan saya menyelesaikannya menggunakan
popstate event
dari objek $ window danui-router's $state object
. Acara popstate dikirim ke jendela setiap kali entri riwayat aktif berubah.Acara
$stateChangeSuccess
dan$locationChangeSuccess
tidak dipicu pada klik tombol browser meskipun bilah alamat menunjukkan lokasi baru.Jadi, dengan asumsi Anda sudah navigasikan dari negara-negara
main
untukfolder
kemain
lagi, ketika anda menekanback
pada browser, Anda harus kembali kefolder
rute. Path diperbarui tetapi tampilan tidak dan masih menampilkan apa pun yang Anda milikimain
. coba ini:angular .module 'app', ['ui.router'] .run($state, $window) { $window.onpopstate = function(event) { var stateName = $state.current.name, pathname = $window.location.pathname.split('/')[1], routeParams = {}; // i.e.- $state.params console.log($state.current.name, pathname); // 'main', 'folder' if ($state.current.name.indexOf(pathname) === -1) { // Optionally set option.notify to false if you don't want // to retrigger another $stateChangeStart event $state.go( $state.current.name, routeParams, {reload:true, notify: false} ); } }; }
tombol mundur / maju harus bekerja dengan lancar setelah itu.
catatan: periksa kompatibilitas browser untuk window.onpopstate () untuk memastikan
sumber
Dapat diselesaikan dengan petunjuk sederhana "kembali-sejarah", yang satu ini juga menutup jendela jika tidak ada riwayat sebelumnya.
Penggunaan arahan
<a data-go-back-history>Previous State</a>
Deklarasi direktif sudut
.directive('goBackHistory', ['$window', function ($window) { return { restrict: 'A', link: function (scope, elm, attrs) { elm.on('click', function ($event) { $event.stopPropagation(); if ($window.history.length) { $window.history.back(); } else { $window.close(); } }); } }; }])
Catatan: Bekerja menggunakan ui-router atau tidak.
sumber
Tombol Kembali juga tidak berfungsi untuk saya, tetapi saya menemukan bahwa masalahnya adalah saya memiliki
html
konten di dalam halaman utama saya, diui-view
elemen.yaitu
<div ui-view> <h1> Hey Kids! </h1> <!-- More content --> </div>
Jadi saya memindahkan konten ke
.html
file baru , dan menandainya sebagai template di.js
file dengan rute.yaitu
.state("parent.mystuff", { url: "/mystuff", controller: 'myStuffCtrl', templateUrl: "myStuff.html" })
sumber
history.back()
dan beralih ke kondisi sebelumnya seringkali memberikan efek yang tidak diinginkan. Misalnya, jika Anda memiliki formulir dengan tab dan setiap tab memiliki statusnya sendiri, ini hanya mengganti tab sebelumnya yang dipilih, bukan kembali dari formulir. Dalam kasus status bersarang, Anda biasanya perlu memikirkan tentang witch status induk yang ingin Anda kembalikan.Arahan ini memecahkan masalah
angular.module('app', ['ui-router-back']) <span ui-back='defaultState'> Go back </span>
Ini kembali ke keadaan, yang aktif sebelum tombol ditampilkan. Opsional
defaultState
adalah nama negara yang digunakan ketika tidak ada keadaan sebelumnya di memori. Juga mengembalikan posisi gulirKode
class UiBackData { fromStateName: string; fromParams: any; fromStateScroll: number; } interface IRootScope1 extends ng.IScope { uiBackData: UiBackData; } class UiBackDirective implements ng.IDirective { uiBackDataSave: UiBackData; constructor(private $state: angular.ui.IStateService, private $rootScope: IRootScope1, private $timeout: ng.ITimeoutService) { } link: ng.IDirectiveLinkFn = (scope, element, attrs) => { this.uiBackDataSave = angular.copy(this.$rootScope.uiBackData); function parseStateRef(ref, current) { var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed; if (preparsed) ref = current + '(' + preparsed[1] + ')'; parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/); if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'"); let paramExpr = parsed[3] || null; let copy = angular.copy(scope.$eval(paramExpr)); return { state: parsed[1], paramExpr: copy }; } element.on('click', (e) => { e.preventDefault(); if (this.uiBackDataSave.fromStateName) this.$state.go(this.uiBackDataSave.fromStateName, this.uiBackDataSave.fromParams) .then(state => { // Override ui-router autoscroll this.$timeout(() => { $(window).scrollTop(this.uiBackDataSave.fromStateScroll); }, 500, false); }); else { var r = parseStateRef((<any>attrs).uiBack, this.$state.current); this.$state.go(r.state, r.paramExpr); } }); }; public static factory(): ng.IDirectiveFactory { const directive = ($state, $rootScope, $timeout) => new UiBackDirective($state, $rootScope, $timeout); directive.$inject = ['$state', '$rootScope', '$timeout']; return directive; } } angular.module('ui-router-back') .directive('uiBack', UiBackDirective.factory()) .run(['$rootScope', ($rootScope: IRootScope1) => { $rootScope.$on('$stateChangeSuccess', (event, toState, toParams, fromState, fromParams) => { if ($rootScope.uiBackData == null) $rootScope.uiBackData = new UiBackData(); $rootScope.uiBackData.fromStateName = fromState.name; $rootScope.uiBackData.fromStateScroll = $(window).scrollTop(); $rootScope.uiBackData.fromParams = fromParams; }); }]);
sumber