在 angular `redirectTo` 中保留 `$location.search()`?

Preserve `$location.search()` in angular `redirectTo`?

如何用

保存$location.search()
$routeProvider.otherwise({redirectTo: xxx})

我知道,我可以在那里使用一个函数,它将旧对象 $location.search() 作为第三个参数。我主要关心一个键,我们称它为a,我试过

function(a, b, search) {
    return "/mainpage?a=" + search[a];
}

但它总是以 /mainpage?a=undefined 结尾。更糟糕的是,它使我的调试器崩溃,从而减慢了我的速度。此外,这不适用于所有键(我稍后可能需要)。

主要问题是,在配置阶段(.config() 函数),您只能注入提供程序。由于 $location 不是提供者,因此您无法注入它。因此 $location.search() 不起作用。

但是,您可以使用普通 JavaScript 的 window.location.hash 来获取 URL 参数。但是,这不是 Angular 方式 并且它不会像您希望的那样工作。

例如,如果您使用以下配置代码:

angular.module('myApp').config(['$routeProvider', function($routeProvider) {

    // Return $location.search() equivalent object
    function locationSearch() {

        var search = {};
        var hash = window.location.hash;
        var index = hash.indexOf('?');

        // Has parameters
        if(index > -1) {

            // Extract only the params
            var params = hash.substring(index + 1);

            // Split parameters
            params = params.split('&');

            var tmp;
            for(var i = 0, len = params.length; i < len; i++) {

                tmp = params[i].split('=');
                search[tmp[0]] = tmp[1];

            }

        }

        return search;

    }

    // Generate default URL
    function defaultURL(search) {

        if(search.a === undefined) {

            return '/mainpage';

        } else {

            return '/mainpage?a=' + search.a;

        }

    }

    // Equivalent of $location.search()
    var search = locationSearch();

    // Set up routing
    $routeProvider

        // Events
        .when('/test', {
            templateUrl: 'test.html',
            controller: 'TestCtrl'
        })

        // Default page
        .otherwise({
            redirectTo: defaultURL(search)
        });

}]);

使用上面的代码,您可以访问 $location.search() 的等效项,并将动态 URL 编译为默认路由 - BUT ngRoute 转义.

例如,defaultURL() 函数生成如下内容:/mainpage?a=123。但是,ngRoute 会转义特殊字符(即 ?&),您会被重定向到 /mainpage%3Fa=123,这不会按预期工作。

此外,这仅在您 新鲜 打开包含该 URL 的页面时有效。当您已经在页面上并更改为无效 URL 时,上述代码不起作用,因为配置阶段已经完成 - .config() 每次页面加载仅触发一次。

TL;DR

据我所知,这不能通过在配置阶段使用 $routeProvider.otherwise() 方法来完成。

您可以创建自己的 provider,并在 configrun 阶段进行连接,以保留您的搜索参数。

假设您有以下条件:

angular.module('app', ['ngRoute'])
  .provider('myRoute', function myRouteProvider() {
    var defaultUrl;
    var search;

    // get default url with params (config phase)
    this.default = function() {
      var params = '';
      angular.forEach(search, function(value, key) {
        params = params + key + '=' + value + '&';
      });

      if (params.length > 0) {
        params = params.substring(0, params.length - 1);
      }

      var redirectUrl = defaultUrl + (params ? '?' + params : '');
      console.log(redirectUrl);
      return redirectUrl;
    };

    this.$get = function($rootScope) {
      var myRoute = {};

      // set default url (run phase)
      myRoute.setDefault = function(url) {
        if (defaultUrl) {
          throw 'Provider already initialized';
        }

        defaultUrl = url;

        // update params on route change
        $rootScope.$on('$routeChangeStart', function(event, next, current) {
          search = current.params;
        });
      };

      return myRoute;      
    };
  }) 

  .config(function($routeProvider, myRouteProvider) {
    $routeProvider
      .when('/main', {
        templateUrl: 'template.html',
        controller: 'Ctrl'
      })
      .when('/route1', {
        templateUrl: 'template.html',
        controller: 'Ctrl'
      })
      .when('/route2', {
        templateUrl: 'template.html',
        controller: 'Ctrl'
      })
      .otherwise({
        // redirect to default url with latest params
        redirectTo: myRouteProvider.default
      });
  })

  .run(function(myRoute) {
    // set default url (404)
    myRoute.setDefault('/main');
  })

  .controller('Ctrl', function($scope, $location) {
    $scope.$location = $location;
  });

其中 index.html 仅包含

<div>
  <a href="#/main">main</a><br>
  <a href="#/route1">route1</a><br>
  <a href="#/route2?param=abc">route2?param=abc</a><br>
  <a href="#/404">otherwise</a><br>
</div>
<hr>
<div ng-view></div> 

而路线的 template.html 就是

<div>
  path: {{ $location.path() }}
  <br>
  params: {{ $location.search() }}
</div>

现在,如果您在链接之间导航并点击 othwerwise,您将被重定向到 /main 以及之前路线中的任何 params


相关插件在这里http://plnkr.co/edit/cqR2Nh

我采用了一种新方法,如果上一个(不存在的)页面包含 重要,您可以在其中监听 $routeChangeStart 并修改 search()获取参数。可以在 this Plunker.

上找到完整的源代码和演示

这个概念类似于@Mikko 的解决方案,但实现有点不同(没有自定义提供程序),并且按照作者的说明工作。

精华

angular.module('myApp').run(['$rootScope', '$location', 'URL', function($rootScope, $location, URL) {

    // Previous search() value
    var prevSearch = {};

    // Listen to event
    $rootScope.$on('$routeChangeStart', function(evt, next, current) {

        // Have search() parameters and are going to default route
        if(prevSearch !== undefined && $location.path().endsWith(URL.defaultRoute)) {

            // Extract only important params
            var important = {};
            var importantCount = 0;

            for(var param in prevSearch) {

                if(URL.importantParams.indexOf(param) > -1) {

                    important[param] = prevSearch[param];
                    importantCount++;

                }

            }

            // Unset prevSearch
            prevSearch = {};

            // Apply important params, if any
            if(importantCount > 0) {

                $location.search(important);

            }


        } else {

            // Remember current search()
            prevSearch = $location.search();

        }

    });

}]);

run 函数为 $routeChangeStart 设置了一个侦听器,并且还记住了 search() 值(GET 参数)的先前值。

当应用程序被重定向到默认路由时,它会检查 search() 参数的先前值并仅提取重要的参数。如果有的话,它会覆盖 search() 值,因此将重要的 GET 参数设置为默认路由。

我制作了一个 URL 常量,可以在其中设置默认路由并指定重要参数。代码如下:

angular.module('myApp').constant('URL', {
    defaultRoute: '/mainpage',
    importantParams: ['a', 'b'],
});

演示

第一个 link 在不存在的 URL 上包含 3 个参数。页面被正确重定向到默认路由,并且只保留了 2 个重要的路由。

secondfourth links 不包含任何参数,并且 links 都没有' 应用任何附加参数(无论您在单击它们时位于哪个页面)。

third link 也包含默认路由上的 3 个参数,所有参数都被保留(没有重定向发生)。