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

Angular JS: URL parameter appears before the `#!/` and doesn't appear in $location.search()

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

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

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

正如您在上面看到的,我需要传递一个名为 returnUrl 的 URL 参数。然后我需要在 Angular JS 应用程序中获取这个值,使用:

$location.search()

然而,这实际上returns一个空对象。我认为这是因为 Angular JS 在 URL URL 参数之后附加 #!/login,像这样:

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

如果我在浏览器中修改 URL,将 returnUrl 参数移动到 URL 的末尾并刷新页面,它会起作用 $location.search() returns {returnUrl: "/original/location/"}。但是,据我所知,我无法在我的网站上更改它,因为该参数是 link 到 Angular JS 应用程序的一部分,并且 #!/login 是之后自动添加的.

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

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

以下是您问题的答案:

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);

您在 url 中看到的 # 实际上就是所谓的锚点 link 或片段。通常通过其 id 跳转到特定元素,例如导航单击 <a href="#footer"> 将直接滚动您的页面,如果您有与此 id 匹配的元素。

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

关于为什么查询参数在散列之前,只是因为那实际上是您在 link 中指定的。 Angular 将您正在加载的 url 视为项目的根,并在初始化后将其自己的 # 添加到当前路由器。您可能有一个服务器仅在指定此查询参数时为 Angular 应用程序提供服务,因此保持它的重要性。如果查询参数位于散列之后,它被视为 Angular 应用程序路由的一部分,因此在您调用 $location.search().

时返回

我建议您在 link 中明确指定 #,或者使用 html5 模式。 Html5 模式意味着您不会在 url 中获得散列形式,而是 "normal" 不那么难看的散列形式。

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

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

并在你的头脑中添加元基础

<base href="/" />

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

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

您可以使用 $location.absUrl() : 它将 return 完整 URL 所有段都根据 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 返回控制器。

What can I do to fix this?

您可以试试这个方法来解决这个问题。

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 注入您的控制器。

检查这个,

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);

你只需要使用window.location.search

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

$location.search() 仅当您的路线定义了它时才会给您值。

What is going on here? And why is the URL parameter before the #!/login?

你可以把#!前面的querystring参数想成服务器端参数,[=44之后的querystring参数=]#! 作为 客户端 参数。这是完全有效的:

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

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

Why does this prevent $location.search() from returning anything? If I move the param to the end of the resulting URL, it works.

因为在您将值移到那里之前,没有为 客户端 参数指定任何内容。

What can I do to fix this?

  1. 您可以将链接更改为您修改它们以使其正常工作的方式:<a href="example.com/path/to/app/#!/login?returnUrl=/original/location">Login.</a> 或复制参数以便它在服务器和客户端上都可用<a href="example.com/path/to/app/?returnUrl=/original/location#!/login?returnUrl=/original/location">Login.</a>
  2. 您可以注入 $window 并从中获取 服务器端 $window.location.search
  3. 您可以注入 $location 并使用 $location.absUrl()

这是一个示例,说明如何从 服务器端 参数设置 客户端 参数:

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>