Hinweis
Die Antworten, die die Verwendung von Variationen von vorschlagen, $window.history.back()
haben alle einen entscheidenden Teil der Frage übersehen: Wie kann der Status der Anwendung beim Springen des Verlaufs an den richtigen Status zurückgesetzt werden (Zurück / Vorwärts / Aktualisieren)? In diesem Sinne; Bitte lesen Sie weiter.
Ja, es ist möglich, den Browser vor / zurück (Verlauf) zu halten und zu aktualisieren, während eine reine ui-router
Zustandsmaschine ausgeführt wird, aber es ist ein wenig erforderlich.
Sie benötigen mehrere Komponenten:
Eindeutige URLs . Der Browser aktiviert die Vor- / Zurück-Schaltflächen nur, wenn Sie URLs ändern. Daher müssen Sie pro besuchtem Status eine eindeutige URL generieren. Diese URLs müssen jedoch keine Statusinformationen enthalten.
Ein Sitzungsdienst . Jede generierte URL ist mit einem bestimmten Status korreliert, sodass Sie eine Möglichkeit zum Speichern Ihrer URL-Status-Paare benötigen, damit Sie die Statusinformationen abrufen können, nachdem Ihre eckige App durch Zurück / Vorwärts- oder Aktualisierungsklicks neu gestartet wurde.
Eine Staatsgeschichte . Ein einfaches Wörterbuch mit UI-Router-Status, das durch eine eindeutige URL gekennzeichnet ist. Wenn Sie sich auf HTML5 verlassen können, können Sie die HTML5-Verlaufs-API verwenden . Wenn Sie dies jedoch nicht können, können Sie sie selbst in wenigen Codezeilen implementieren (siehe unten).
Ein Ortungsdienst . Schließlich müssen Sie in der Lage sein, sowohl Statusänderungen des UI-Routers, die intern durch Ihren Code ausgelöst werden, als auch normale Änderungen der Browser-URL zu verwalten, die normalerweise durch das Klicken des Benutzers auf Browser-Schaltflächen oder das Eingeben von Inhalten in die Browserleiste ausgelöst werden. Dies kann alles etwas schwierig werden, da es leicht zu verwechseln ist, was was ausgelöst hat.
Hier ist meine Implementierung jeder dieser Anforderungen. Ich habe alles in drei Dienste gebündelt:
Der Sitzungsdienst
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
Dies ist ein Wrapper für das Javascript- sessionStorage
Objekt. Ich habe es hier aus Gründen der Klarheit gekürzt. Eine vollständige Erklärung hierzu finden Sie unter: Wie gehe ich mit der Seitenaktualisierung mit einer AngularJS-Einzelseitenanwendung um?
Der Staatliche Geschichtsdienst
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
Das StateHistoryService
kümmert sich um das Speichern und Abrufen von historischen Zuständen, die durch generierte, eindeutige URLs verschlüsselt sind. Es ist wirklich nur ein Convenience-Wrapper für ein Objekt im Wörterbuchstil.
Der staatliche Ortungsdienst
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
Das StateLocationService
behandelt zwei Ereignisse:
locationChange . Dies wird aufgerufen, wenn der Speicherort des Browsers geändert wird, normalerweise wenn die Schaltfläche Zurück / Vorwärts / Aktualisieren gedrückt wird oder wenn die App zum ersten Mal gestartet wird oder wenn der Benutzer eine URL eingibt. Wenn ein Status für die aktuelle location.url in der vorhanden StateHistoryService
ist, wird er verwendet, um den Status über den UI-Router wiederherzustellen $state.go
.
stateChange . Dies wird aufgerufen, wenn Sie den Status intern verschieben. Der Name und die Parameter des aktuellen Status werden in der StateHistoryService
von einer generierten URL eingegebenen URL gespeichert . Diese generierte URL kann alles sein, was Sie wollen, sie kann den Zustand auf eine für Menschen lesbare Weise identifizieren oder nicht. In meinem Fall verwende ich einen Statusparameter plus eine zufällig generierte Folge von Ziffern, die von einem Guid abgeleitet wurden (siehe Fuß für das Guid-Generator-Snippet). Die generierte URL wird in der Browserleiste angezeigt und mit entscheidend zum internen Verlaufsstapel des Browsers hinzugefügt @location.url url
. Es fügt die URL zum Verlaufsstapel des Browsers hinzu, der die Vorwärts- / Zurück-Schaltflächen aktiviert.
Das große Problem bei dieser Technik ist, dass das Aufrufen @location.url url
der stateChange
Methode das $locationChangeSuccess
Ereignis auslöst und somit die locationChange
Methode aufruft . Gleiches Ausrufen von @state.go
from locationChange
löst das $stateChangeSuccess
Ereignis und damit die stateChange
Methode aus. Dies wird sehr verwirrend und bringt den Browserverlauf ohne Ende durcheinander.
Die Lösung ist sehr einfach. Sie können sehen, dass das preventCall
Array als Stapel ( pop
und push
) verwendet wird. Jedes Mal, wenn eine der Methoden aufgerufen wird, wird verhindert, dass die andere Methode nur einmal aufgerufen wird. Diese Technik beeinträchtigt nicht die korrekte Auslösung der $ -Ereignisse und hält alles gerade.
Jetzt müssen wir nur noch die HistoryService
Methoden zum richtigen Zeitpunkt im Lebenszyklus des Zustandsübergangs aufrufen . Dies geschieht in der AngularJS Apps- .run
Methode wie folgt :
Winkel app.run
angular
.module 'app', ['ui.router']
.run ($rootScope, stateLocationService) ->
$rootScope.$on '$stateChangeSuccess', (event, toState, toParams) ->
stateLocationService.stateChange()
$rootScope.$on '$locationChangeSuccess', ->
stateLocationService.locationChange()
Generieren Sie eine Guid
Math.guid = ->
s4 = -> Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1)
"#{s4()}#{s4()}-#{s4()}-#{s4()}-#{s4()}-#{s4()}#{s4()}#{s4()}"
Wenn dies alles vorhanden ist, funktionieren die Vorwärts- / Rückwärts-Tasten und die Aktualisierungs-Taste wie erwartet.