当前位置: 首页 > 面试题库 >

如何获得“后退”按钮以与AngularJS ui路由器状态机一起使用?

宇文灿
2023-03-14
问题内容

我已经使用ui-router实现了angularjs单页应用程序。

最初,我使用不同的URL来标识每个状态,但这是针对不友好的GUID打包的URL。

因此,我现在将站点定义为一个更简单的状态机。状态不是由url标识的,而是仅根据需要转换为,例如:

定义嵌套状态

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

该系统运行良好,我喜欢它的简洁语法。但是,由于我没有使用网址,因此后退按钮不起作用。

如何保持整洁的ui-router状态机但启用后退按钮功能?


问题答案:

注意

建议使用的变体形式的答案$window.history.back()都忽略了问题的关键部分:如何随着历史记录的跳转(后退/前进/刷新)
将应用程序的状态恢复 到正确的状态位置。考虑到这一点; 请继续阅读。

是的,可以在运行纯ui-router状态机时使浏览器前进/后退(历史记录)并刷新,但是这需要花些时间。

您需要几个组件:

  • 唯一网址 。当您更改URL时,浏览器仅启用后退/前进按钮,因此您必须为每个访问状态生成唯一的URL。这些url不必包含任何状态信息。

  • 会话服务 。每个生成的url都与特定状态相关联,因此您需要一种存储url-state对的方法,以便在通过后退/前进或刷新单击重新启动角度应用后,您可以检索状态信息。

  • 国家历史 。简单的ui-router状态字典,由唯一的url键输入。如果可以依靠HTML5,则可以使用HTML5历史记录API,但如果像我一样不能这样做,则可以自己用几行代码实现它(请参见下文)。

  • 定位服务 。最后,您需要能够管理由代码内部触发的ui路由器状态更改,以及通常由用户单击浏览器按钮或在浏览器栏中键入内容而触发的正常浏览器URL更改。所有这些都可能会有些棘手,因为很容易混淆触发事件的原因。

这是我对所有这些要求的实现。我将所有内容捆绑为三个服务:

会话服务

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单页应用程序处理页面刷新

国家历史服务处

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所产生的,唯一的网址键入的历史状态的存储和检索后的外观。它实际上只是字典样式对象的便利包装。

国家位置服务

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 。当更改浏览器的位置时,通常是在按下后退/前进/刷新按钮时,或者在应用首次启动时,或者在用户键入URL时,将调用此方法。如果当前location.url的状态存在于,StateHistoryService则它用于通过ui-router的恢复状态$state.go

  • stateChange 。当您在内部移动状态时会调用此方法。当前状态的名称和参数存储在StateHistoryService由生成的URL键入的键中。生成的url可以是您想要的任何内容,它可以或可以不以人类可读的方式标识状态。就我而言,我使用的是状态参数,以及从guid派生的随机生成的数字序列(有关guid生成器代码段,请参见脚)。生成的url显示在浏览器栏中,并且至关重要的是,使用添加到浏览器的内部历史记录堆栈中@location.url url。它将url添加到浏览器的历史记录堆栈中,从而启用前进/后退按钮。

这种技术的最大问题是,在调用@location.url urlstateChange方法会触发$locationChangeSuccess事件,那么调用该locationChange方法。同样地调用@state.gofrom
locationChange将触发$stateChangeSuccess事件以及stateChange方法。这变得非常混乱,并弄乱了浏览器的历史记录。

解决方案非常简单。您可以看到该preventCall数组被用作堆栈(poppush)。每次调用其中一个方法时,都会阻止另一个方法仅被一次性调用。该技术不会干扰$事件的正确触发,并使一切保持直线。

现在,我们要做的就是HistoryService在状态转换生命周期中的适当时间调用方法。这是在AngularJS Apps
.run方法中完成的,如下所示:

Angular app.run

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

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

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

生成指南

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

完成所有这些操作后,前进/后退按钮和刷新按钮均可按预期工作。



 类似资料:
  • 我是一个react初学者,我有一个使用react路由器(4.2.2)、Redux的项目,我最近添加了react路由器Redux,希望它能解决我的问题。我有一些组件,它们的redux存储状态需要根据与浏览器后退和前进按钮的交互进行更改。 例如,我有一个屏幕,其中包含用户单击的元素的详细信息。如果我使用browser back(浏览器返回)按钮并导航到历史上打开该详细信息视图的另一个点,它将只显示最新

  • 问题内容: 有没有办法获取当前状态的先前状态? 例如,我想知道当前状态B之前的先前状态是什么(以前的状态本来是状态A)。 我无法在ui-router github文档页面中找到它。 问题答案: ui-router转换后不会跟踪以前的状态,但是会在状态更改时广播该事件。 您应该能够从该事件中捕获先前状态(即将离开的状态):

  • 问题内容: 我在aspx页面的c#中使用asp.net和c#,在此面板中有一个更新面板,我有一些指向其他站点的链接,这些链接在同一窗口中打开。在单击这些链接后,当我通过浏览器的后退按钮返回时,在更新面板上没有得到相同的结果… 问题答案: 我已经在以下文章中实现了相同的方法,如果您需要进一步的帮助,请Plz告诉我,我将提供代码 http://rchern.wordpress.com/2008/05/

  • 问题内容: 我有一个页面可以根据用户按下按钮来动态加载内容: 动态内容运行良好,我可以整天获取页面。但是,当您链接到另一个页面的链接,然后按浏览器的“后退”按钮返回该页面后,该页面将完全重置,就好像从未加载任何动态内容一样。 我发誓以前见过不同的行为,但也许我疯了。浏览器是否应该保留页面状态,而不是重新呈现它? 编辑:顺便说一句,我正在使用播放!框架,如果有关系的话。 问题答案: 浏览器将在首次接

  • 问题内容: 我正在用php做在线测验应用程序。我想限制用户再次参加考试。我尝试了以下脚本,但是它停止了我的计时器。我该怎么办? 我已经包含了源代码。计时器存储在cdtimer.js中 我有一个考试计时器,该计时器从 mysql 值中获取考试时间。计时器相应地启动,但是当我放入用于禁用后退按钮的代码时,计时器停止。我怎么了 问题答案: 有许多原因导致禁用后退按钮无法真正起作用。最好的选择是警告用户:

  • 我正在使用android导航组件库。我用导航图设置了两个片段。我想在我的片段内按下后退按钮时做一些额外的动作。 我使用这段代码来处理工具栏上的按钮点击,但我也想处理后退按钮按下。我该怎么做?