如何在 ui-router 中将浏览器上的可选状态参数持久化?

How to persist optional state parameter on browser back in ui-router?

我有一个父状态,其中有两个子状态,我将根据 URL.

显示一个状态

在这两个 states 中,一个必须使用 param1param2 之类的参数,我在内部使用了 ui-router 的 params 选项状态定义。

$stateProvider.state('tabs.account', {
    url: '/account',
    views: {
        'content@tabs': {
            templateUrl: 'account.html',
            controller: function($scope, $stateParams) {
                //This params are internally used to make ajax and show some data.
                $scope.param1 = $stateParams.param1;
                $scope.param2 = $stateParams.param2;
            },
        }
    },
    params: {
        param1: { value: null }, //this are optional param
        param2: { value: null } //because they are not used in url
    }
});

如果您查看我的路线,URL 中并未真正引入 params 选项,这就是为什么我将 then 视为可选的原因。

问题

看看 plunkr,我显示了两个选项卡 Account & Survey,

  1. 单击“调查”选项卡,然后在显示的textarea 中添加一些数据。
  2. 单击 转到帐户,这会将那些 textarea 值传递给 另一个 Account 选项卡通过在 anchor

  3. 上执行 ui-sref="tabs.account({param1: thing1, param2: thing2})"
  4. 现在您将看到 html 上的 param1param2 值已分配给 $stateParams

    的范围
  5. 现在再次单击调查选项卡,您将进入调查页面。
  6. 只需单击浏览器返回,您会注意到 param 值没有得到 null

Problem Plunkr

相信你已经明白我想问的了,为什么可选参数值没有被存储?因为他们是国家的一部分。

我知道我可以通过以下两种解决方案解决这个问题。

我已经尝试过 angular-ui-routers sticky states 但这似乎对我不起作用。什么是更好的方法?

有什么方法可以使我的用例正常工作,任何想法都将不胜感激。

Github Issue Link Here

我相信如果不使用您提供的两种解决方案中的一种,您将无法实现您想要实现的目标。

浏览器后退按钮仅保留 URL 历史记录。他对 ui-路由器内部状态一无所知,只会强制 URL 改变。

强制 URL 改变会触发内部 ui-router 机器,但不幸的是 ui-router 会看到 URL 改变,就像有人会手动更改 url。

Ui-router 将触发一个新的路由更改到 URL 指向的路由。这意味着他不知道你想去 "back" 并且只会在没有任何参数的情况下将状态更改为新状态。

总结

单击后退按钮将根据 URL 将状态更改为 状态,而不是返回到之前的状态。

这就是将参数添加到 URL 解决问题的原因。由于 URL 具有歧视性,您最终会到达您想要的状态。

希望对您有所帮助。

一个解决方法 解决方案是缓存状态参数并在进入tabs.account 状态时有条件地加载它们。 UI 路由器状态配置实际上允许您为这些类型的 "do something on entering the state" 情况提供 onEnter 回调。

这是使用 localStorage as the cache, with working Plunker here 的基本逻辑:

  • 当您进入 tabs.account 状态时,检查您的状态参数
    • 如果有,将它们缓存到本地存储
    • 如果不这样做,请将它们从本地存储加载到 $stateParams

这里有一个示例代码片段供参考(取自 Plunker):

  $stateProvider.state('tabs.account', {
    ...
    onEnter: ['$stateParams', '$window', function($stateParams, $window) {
      if($stateParams.param1) {
        $window.localStorage.setItem('tabs.account.param1', $stateParams.param1);
      } else {
        $stateParams.param1 = $window.localStorage.getItem('tabs.account.param1');
      }
      if($stateParams.param2) {
        $window.localStorage.setItem('tabs.account.param2', $stateParams.param2);
      } else {
        $stateParams.param2 = $window.localStorage.getItem('tabs.account.param2');
      }
    }],
    ...
  }

一个 警告 是您的参数将无限期保留(例如,跨刷新和会话)。要解决此问题,您可以像 app.run.

那样在应用程序加载时清除缓存

最后要注意的是,在 Plunker 中,我正在直接访问本地存储(通过 Angular $window 服务)。您可能想使用一些 AngularJS 模块 - 我在生产中使用了 angular-local-storage

我会将 params 定义移至父状态,以便在您的两个子状态之间共享可选状态参数。

子状态将从您的父状态继承 $stateParams,因此不需要真正的 'workaround'。

只需像往常一样在您的子控制器中注入 $stateParams,您就可以完全访问传递的参数。如果您不想在特定子状态下使用参数,只需避免注入它们即可。

这适用于;

  • 后退按钮
  • 前进按钮
  • ui-sref (没有参数(将保持原样))
  • ui-sref (带有参数(将覆盖))

$stateProvider
  .state('parent', {
    params: { p1: null, p2: null }
  })
  .state('parent.childOne', {
    url: '/one',
    controller: function ($stateParams) {
      console.log($stateParams); // { p1: null, p2: null }
    }
  })
  .state('parent.childTwo', {
    url: '/two',
    controller: function ($stateParams) {
      console.log($stateParams); // { p1: null, p2: null }
    }
  })

如果您在 parent 的状态树中移动时在任何时候想要 清除 参数,则必须 手动执行.

这将是 唯一 我可以通过使用此解决方案看到的真正警告。

我知道你提出的情况下可能不希望手动清除,但你没有采取积极的立场反对,所以我觉得这个建议是有价值的。

updated plunker

对我来说这听起来像 X Y problem。有一些建议的方法可以使状态参数持久化,而问题出在这个表面之外。

根据问题的定义,有些数据应该独立于状态。因此,相对于国家而言,它是一种全球性的。因此,应该有一个服务来保存它,并且两个状态的控制器都根据需要维护它。只是不要在状态参数中传递超出一个状态范围的数据。

粘性状态很适合这种方法,因为它允许在另一个状态处于活动状态时保持 DOM 和 $scope。但是当状态被重新激活时,它与状态参数无关。