我如何获得Back按钮与AngularJS UI路由器状态机工作? [英] How do I get the Back Button to work with an AngularJS ui-router state machine?

查看:572
本文介绍了我如何获得Back按钮与AngularJS UI路由器状态机工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经实现了使用 UI路由器的angularjs单页的应用程序。

I have implemented an angularjs single page application using ui-router.

本来我确定使用的是不同的URL然而,这对于不友好的每次状态,GUID包装的URL。

Originally I identified each state using a distinct url however this made for unfriendly, GUID packed urls.

所以,我现在已经定义我的网站作为一个更简单的状态机。这些状态不被识别的网址,但只是过渡根据需要,像这样的:

So I have now defined my site as a much simpler state-machine. The states are not identified by urls but are simply transitioned to as required, like this:

定义嵌套美国

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'

转换为定义的状态

#The ui-sref attrib transitions to the 'folder' state

a(ui-sref="folder({locationId:'{{folder.Id}}'})")
    | {{ folder.Name }}

本系统工作得很好,我喜欢它的简洁的语法。然而,正如我不使用的URL后退按钮不起作用。

This system works very well and I love its clean syntax. However, as I am not using urls the back button does not work.

我如何保持我的整洁UI路由器状态机,但启用后退按钮功能?

How do I keep my neat ui-router state-machine but enable the back button functionality?

推荐答案

是的,这是可能有浏览器的后退/前进(历史)和刷新,同时运行的纯 UI路由器状态机,但它需要多一点的做的。

Yes, it is possible to have the browser back/forward (history) and refresh whilst running a pure ui-router state-machine but it takes a bit of doing.

您需要几个组件:


  • 唯一URL 的。浏览器只允许前进/后退按钮,当您更改URL,因此,您必须生成每访问国家唯一的URL。这些URL不需要包含任何状态信息虽然。

  • Unique URLs. The browser only enables the back/forward buttons when you change urls, so you must generate a unique url per visited state. These urls need not contain any state information though.

一个会话服务的。所以你需要一种方法来存储您的网址状态对这样就可以检索您的角度应用后的状态信息通过前进/后退或刷新点击已重新启动每个生成的URL被关联到一个特定的状态。

A Session Service. Each generated url is correlated to a particular state so you need a way to store your url-state pairs so that you can retrieve the state information after your angular app has been restarted by back / forward or refresh clicks.

一个国家历史的。 UI路由器的一个简单的字典州内唯一的URL关键字。如果你可以依靠HTML5,那么你可以使用 HTML5 API历史的,但如果像我一样,你不能,那么你可以自己实现它在code几行(见下文)。

A State History. A simple dictionary of ui-router states keyed by unique url. If you can rely on HTML5 then you can use the HTML5 History API, but if, like me, you can't then you can implement it yourself in a few lines of code (see below).

位置服务的。最后,你需要能够同时管理UI路由器状态的变化,你的code内部触发,通常由用户点击浏览器按钮或输入的东西到浏览器栏触发正常的浏览器的URL变化。这都可以得到一个有点棘手,因为它很容易混淆关于什么触发的。

A Location Service. Finally, you need to be able manage both ui-router state changes, triggered internally by your code, and normal browser url changes typically triggered by the user clicking browser buttons or typing stuff into the browser bar. This can all get a bit tricky because it is easy to get confused about what triggered what.

下面是我的每一个执行这些要求的。我已经一切都捆绑成三个服务:

Here is my implementation of each of these requirements. I have bundled everything up into three services:

会话服务

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

这是JavaScript的的sessionStorage 对象的包装。我在这里剪下来的清晰度。有关此的完整说明,请参阅:如何处理清爽页面与AngularJS单页应用程序

This is a wrapper for the javascript sessionStorage object. I have cut it down for clarity here. For a full explanation of this please see: How do I handle page refreshing with an AngularJS Single Page Application

国家历史服务

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

StateHistoryService 所产生的,唯一的网址键入的历史状态的存储和检索后的样子。它实际上只是一个字典样式对象的便捷包装器。

The StateHistoryService looks after the storage and retrieval of historical states keyed by generated, unique urls. It is really just a convenience wrapper for a dictionary style object.

国家位置服务

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

StateLocationService 处理两个事件:


  • locationChange 的。当浏览器的位置被改变这就是所谓的,通常在后退/前进/刷新按钮是pressed或当应用程序启动后或者当用户键入一个URL。如果当前location.url的状态存在 StateHistoryService 则用于通过UI路由器的 $ state.go

  • locationChange. This is called when the browsers location is changed, typically when the back/forward/refresh button is pressed or when the app first starts or when the user types in a url. If a state for the current location.url exists in the StateHistoryService then it is used to restore the state via ui-router's $state.go.

stateChange 的。当你在内部移动的状态这就是所谓的。目前国家的名字,而params存储在 StateHistoryService 通过生成的URL关键字。这个生成的URL可以是你想要的任何东西,它可能会或可能不会识别人类可读的方式的状态。就我而言,我使用的状态参数加上从GUID衍生的数字随机生成的序列(请参阅GUID生成片段英尺)。生成的URL显示在浏览器栏和关键的是,使用 @ location.url网址添加到浏览器的内部历史堆栈。其加入的网址到浏览器的历史堆栈,使前进/后退按钮。

stateChange. This is called when you move state internally. The current state's name and params are stored in the StateHistoryService keyed by a generated url. This generated url can be anything you want, it may or may not identify the state in a human readable way. In my case I am using a state param plus a randomly generated sequence of digits derived from a guid (see foot for the guid generator snippet). The generated url is displayed in the browser bar and, crucially, added to the browser's internal history stack using @location.url url. Its adding the url to the browser's history stack that enables the forward / back buttons.

使用此技术的最大问题是,调用 @ location.url网址 stateChange 方法将引发 $ locationChangeSuccess 事件,因此调用 locationChange 方法。同样调用 @ state.go locationChange 将触发 $ stateChangeSuccess 事件等等 stateChange 方法。这变得很混乱,搅乱浏览器的历史不到尽头。

The big problem with this technique is that calling @location.url url in the stateChange method will trigger the $locationChangeSuccess event and so call the locationChange method. Equally calling the @state.go from locationChange will trigger the $stateChangeSuccess event and so the stateChange method. This gets very confusing and messes up the browser history no end.

的解决方案是很简单的。你可以看到 preventCall 数组被用作堆栈(弹出)。每次的方法之一是把它叫做prevents被称为一次性只另一种方法。这种技术不与$事件的正确触发干扰,并保持笔直的一切

The solution is very simple. You can see the preventCall array being used as a stack (pop and push). Each time one of the methods is called it prevents the other method being called one-time-only. This technique does not interfere with the correct triggering of the $ events and keeps everything straight.

现在我们需要做的是在调用的状态转换生命周期适当的时候 HistoryService 方法。这是在AngularJS应用做了 .RUN 的方法,如:

Now all we need to do is call the HistoryService methods at the appropriate time in the state transition life-cycle. This is done in the AngularJS Apps .run method, like this:

角app.run

angular
.module 'app', ['ui.router']
.run ($rootScope, stateLocationService) ->

    $rootScope.$on '$stateChangeSuccess', (event, toState, toParams) ->
        stateLocationService.stateChange()

    $rootScope.$on '$locationChangeSuccess', ->
        stateLocationService.locationChange()

生成GUID

Math.guid = ->
    s4 = -> Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1)
    "#{s4()}#{s4()}-#{s4()}-#{s4()}-#{s4()}-#{s4()}#{s4()}#{s4()}"

有了这一切到位,前进/后退按钮和刷新按钮预期的所有工作。

With all this in place, the forward / back buttons and the refresh button all work as expected.

这篇关于我如何获得Back按钮与AngularJS UI路由器状态机工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆