Angular JS:URL参数出现在`#!/`之前,并没有出现在$ location.search()中

时间:2017-05-24 13:56:06

标签: javascript angularjs angular-routing

我正在将一个由其他人制作的小型现有Angular JS应用程序集成到一个不是使用Angular构建的现有网站中。

所有Angular JS应用程序文件都保存在一个完全独立的目录中,然后用户可以单击链接将它们带到该子目录的索引。例如:

<a href="/path/to/angular-app/?returnUrl=/original/location">Login.</a>

如上所示,我需要传递一个名为returnUrl的URL参数。然后我需要在Angular JS应用程序中使用:

获取此值
$location.search()

然而,这实际上返回一个空对象。我认为这是因为Angular JS在 URL参数之后在URL 上附加#!/login,如下所示:

example.com/path/to/app?returnUrl=/original/location/#!/login

如果我在浏览器中修改了网址,将returnUrl参数移到了网址的末尾并刷新了网页,则可以正常工作,$location.search()会返回{returnUrl: "/original/location/"}。但是,据我所知,我无法在我的网站中更改此内容,因为该参数是Angular JS应用程序链接的一部分,之后会自动添加#!/login

我是新的AngularJS,所以请尽可能清楚地解释:

  1. 这里发生了什么?为什么URL参数之前 #!/login
  2. 为什么这会阻止$location.search()返回任何内容?如果我将参数移动到结果URL的末尾,它就会起作用。
  3. 我该怎么做才能解决这个问题?

6 个答案:

答案 0 :(得分:3)

您只需使用window.location.search

即可
> window.location.search
> "?returnUrl=/original/location/"

$location.search()仅在路线定义了值时为您提供值。

答案 1 :(得分:2)

  

这里发生了什么?为什么#!/ login?

之前的URL参数

您可以将#!之前的查询字符串参数视为服务器端参数,并将#!之后的查询字符串参数视为< em>客户端参数。这完全有效:

http://example.com?serverParam=client1#!angularRoute?angularParam=someValue

服务器端参数允许您配置从服务器提供页面的方式,然后,在提供服务器页面并加载Angular应用程序后,使用客户端参数提供特定Angular应用程序所期望的内容。

  

为什么这会阻止$ location.search()返回任何内容?如果我   将参数移动到结果URL的末尾,它可以工作。

因为在您移动值之前没有为客户端参数指定任何内容。

  

我该怎么做才能解决这个问题?

  1. 您可以将链接更改为与修改它们的方式相同,以使其工作:<a href="example.com/path/to/app/#!/login?returnUrl=/original/location">Login.</a>
  2. 您可以注入$window并从中获取服务器端$window.location.search
  3. 您可以注入$location并使用$location.absUrl()
  4. 以下是如何从服务器端参数设置客户端参数的示例:

    var app = angular.module('YOURAPPNAME', []);
    
    app.run(['$location', function($location) {
      // I'm going to fake out the url so it runs in the SnippetEditor.
      var url = "example.com/path/to/app?returnUrl=/original/location/#!/login"; //$location.absUrl();
      var hashbangIdx = url.indexOf('#!');
      var serverStr = hashbangIdx > -1 ? url.substring(0, hashbangIdx) : url;
      var qIdx = serverStr.indexOf("?");
      var p = qIdx > -1 ? serverStr.substring(qIdx).split(/[&||?]/) : [];
      for (var i = 0; i < p.length; i += 1) {
        if (p[i].indexOf('=') > -1) {
          var param = p[i].split('=');
          $location.search(param[0], param[1]);
        }
      }
    }]);
    
    app.controller('MainCtrl', ['$scope', '$location', function($scope, $location) {
      $scope.params = $location.search();
    }]);
    <!DOCTYPE html>
    <html ng-app="YOURAPPNAME">
    
    <head>
      <meta charset="utf-8" />
      <title>AngularJS</title>
      <script data-require="angular.js@1.5.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js" data-semver="1.5.11"></script>
      <script src="app.js"></script>
    </head>
    
    <body ng-controller="MainCtrl">
      <h4>Parameters:</h4>
      <p ng-repeat="(name, val) in params">
        {{name}}: {{val}}
      </p>
    </body>
    
    </html>

答案 2 :(得分:1)

以下是您的问题的答案:

1)AngularJS将#添加到加载页面的URL的末尾。因此,如果加载页面的url已经具有returnUrl的查询参数,这似乎就是这种情况,它将始终显示在#的左侧。

2)AngularJS应用程序通常只关注url中#后面发生的事情。 AngularJS使用它来在状态和更类似的东西之间路由和传递查询参数。 $ location.search()用于检索#。

之后的所有查询参数

3)以下是如何提取在#

之前发生的查询参数
function getParamFromURL (paramName) {
    var searchStr = window.location.search.substring(1);
    var keyValues = searchStr.split('&');
    for(var i=0; i<keyValues.length; i++) {
        var split = keyValues[i].split('=');
        if(split[0] === paramName) {
            return decodeURIComponent(split[1]);
        }
    }
}

var value = getParamFromURL('returnUrl');
console.log(value);

答案 3 :(得分:1)

您在网址中看到的#实际上是什么称为锚链接或片段。通常通过其ID跳转到特定元素,例如,如果您的元素与此ID匹配,则导航点击<a href="#footer">将直接滚动您的页面。

Angular和其他框架(如React)利用此功能通过将哈希用作当前活动路由来创建所谓的哈希路由。这样做的好处是它可以实现非常快速的设置,并且可以在非HTML5浏览器上开箱即用。

关于查询参数在哈希之前的原因,只是因为它实际上是您在链接中指定的内容。 Angular会将您正在加载的url视为项目的根目录,并在初始化后将当前路由器添加自己的#。您可能只有在指定此查询参数时才有服务器为Angular应用程序提供服务,因此保持其位置的重要性。如果查询参数位于哈希之后,则它被视为Angular应用程序路由的一部分,因此在您调用$location.search()时会返回。

我建议您做的是在链接中明确指定#,或使用html5模式。 Html5模式意味着您不会在您的网址中获取哈希表单,而不是#34;正常&#34;那些不那么难看的。

为此,您必须在应用中启用html5模式

.config(function ($locationProvider) {
  $locationProvider.html5Mode(true);
}

并在脑中添加元库

<base href="/" />

问题是,现在,如果您尝试加载不是主页的页面,您将会遇到404问题。

为防止这种情况发生,您必须为应用程序的每个子路径提供Angular条目。有多种方法,具体取决于您在服务器上使用/想要使用的内容。这是展示大部分内容的the Angular FAQ

答案 4 :(得分:1)

您可以使用$location.absUrl()它将返回完整的网址,其中所有网段均按照RFC 3986中指定的规则进行编码。

var resultURl = GetURLParameter('returnUrl');
console.log(resultURl);

function GetURLParameter(param) {
    // var URL =  $location.absUrl();
    var URL = "example.com/path/to/app?returnUrl=/original/location/#!/login";
    var allVariables = URL.split('?');
    for (var i = 0; i < allVariables.length; i++) {
        var paramName = allVariables[i].split('=');
        if (paramName[0] == param) {
            return paramName[1].split("#")[0];
        }
    }
}

============================== ========= ===========================

RouteProviderrouteParams适用于您的情况。

您可以在路由中将returnUrl作为路由参数传递,然后使用$routeParams.returnUrl返回控制器。

  

我该怎么做才能解决这个问题?

您可以尝试这种方法来解决此问题。

HTML:

<a href="#login/original/location">Login Return Url</a>

路由文件:

$routeProvider.when('/login/:returnUrl', {
    templateUrl: 'partials/partial.html',    
    controller: 'MyCtrl'
});

在控制器中,注入$routeParams

.controller('MyCtrl', ['$scope','$routeParams', function($scope, $routeParams) {
  var url = $routeParams.returnUrl;
}]);

注意:

如果您使用的是ngRoute,则可以将$routeParams注入您的控制器。

如果您使用的是angular-ui-router,则可以将$stateParams注入您的控制器。

答案 5 :(得分:-1)

检查一下,

function getValue(key, url) {
    var regex = new RegExp("[?&]" + key + "(=([^&#]*)|&|#|$)"),
        results = regex.exec(url);
    return results[2];
}
var value = getValue("returnUrl","example.com/path/to/app?returnUrl=/original/location/#!/login");
console.log(value);