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

You just need to use window.location.search

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

$location.search() gives you values only if your route has defined it.


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

You can think of querystring parameters before the #! as a server-side parameters, and querystring parameters after the #! as client-side parameters. This is perfectly valid:

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

The server-side parameters allow you to configure how the page is served from the server and then, once server page is served and the Angular app is loaded, use the client-side parameters to supply what that particular Angular app is expecting.

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

Because nothing was specified for the client-side parameters until you moved the values there.

What can I do to fix this?

  1. You could change the links to be like how you modified them to get them to work: <a href="example.com/path/to/app/#!/login?returnUrl=/original/location">Login.</a> or duplicate the parameter so it's available on both the server and the client <a href="example.com/path/to/app/?returnUrl=/original/location#!/login?returnUrl=/original/location">Login.</a>
  2. You can inject $window and get the server-side values from $window.location.search
  3. You can inject $location and use $location.absUrl()

Here is an example of how you can set the client-side parameters from the server-side parameters:

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="[email protected]" 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>