<strong>Absolute URL (G):</strong> {{$parent.locationAbsUrl}}<br /> <strong>Protocol (G):</strong> {{$parent.locationProtocol}}<br /> <strong>Host (G):</strong> {{$parent.locationHost}}<br /> <strong>Port (G):</strong> {{$parent.locationPort}}<br /> <br /> <strong>URL (G/S):</strong> {{$parent.locationUrl}}<br /> <button ng-click="$parent.setUrl()">Set URL</button> <label>Replace: <input type="checkbox" ng-model="$parent.replaceUrl" /></label><br /> <br /> <strong>Path (G/S):</strong> {{$parent.locationPath}}<br /> <button ng-click="$parent.setPath()">Set Path</button><br /> <br /> <strong>Search (G/S):</strong> {{$parent.locationSearch}}<br /> <button ng-click="$parent.setSearch(1)">Set Search 1: single parameter and single value</button><br /> <button ng-click="$parent.setSearch(2)">Set Search 2: single parameter and multiple values</button><br /> <button ng-click="$parent.setSearch(3)">Set Search 3: remove parameter</button><br /> <button ng-click="$parent.setSearch(4)">Set Search 4: hash object (multiple parameters)</button><br /> <button ng-click="$parent.setSearch(5)">Set Search 5: clear search parameters</button><br /> <br /> <strong>Hash (G/S):</strong> {{$parent.locationHash}}<br /> <button ng-click="$parent.setHash()">Set Hash</button><br /> <br /> <strong>locationChangeStartCounter:</strong> {{$parent.locationChangeStartCounter}}<br /> <strong>locationChangeSuccessCounter:</strong> {{$parent.locationChangeSuccessCounter}}
angular.module("mainModule", []) .controller("mainController", function ($scope, $location) { function refreshValues() { $scope.locationAbsUrl = $location.absUrl(); $scope.locationUrl = $location.url(); $scope.locationProtocol = $location.protocol(); $scope.locationHost = $; $scope.locationPort = $location.port(); $scope.locationPath = $location.path(); $scope.locationSearch = $; $scope.locationHash = $location.hash(); } var urlValueCounter = 0; var pathValueCounter = 0; var hashValueCounter = 0; $scope.locationChangeStartCounter = 0; $scope.locationChangeSuccessCounter = 0; refreshValues(); $scope.$on("$locationChangeStart", function (event) { $scope.locationChangeStartCounter++; }); $scope.$on("$locationChangeSuccess", function (event) { $scope.locationChangeSuccessCounter++; refreshValues(); }); $scope.setUrl = function () { $location.url("newurl" + (++urlValueCounter)); if ($scope.replaceUrl === true) { $location.replace(); } }; $scope.setPath = function () { $location.path("newpathvalue" + (++pathValueCounter)); }; $scope.setSearch = function (setMode) { switch (setMode) { case 1: // Single parameter and single value $"param3", "value3"); break; case 2: // Single parameter and multiple values $"param3", ["value3-1", "value3-2", "value3-3"]); break; case 3: // Remove parameter $"param3", null); break; case 4: // Hash object (multiple parameters) ${ param1New: "value1New", param2New: "value2New", param3New: ["value3-1New", "value3-2New", "value3-3New"] }); break; case 5: // Clear search parameters var currParams = $; for (var paramName in currParams) { $, null); } break; } }; $scope.setHash = function () { $location.hash("newhashvalue" + (++hashValueCounter)); }; });
<!DOCTYPE html> <html> <head> <script src="../../../angularjs/angular.js"></script> <script src="../script.js"></script> <script src="script.js"></script> </head> <body ng-app="hashbangModule"> <div ng-controller="mainController"> <h3>1. Hashbang mode</h3> <div ng-include="'../common.html'"></div> </div> </body> </html>
angular.module("hashbangModule", ["mainModule"]) .config(function ($locationProvider) { $locationProvider.html5Mode(false).hashPrefix("!"); });
<!DOCTYPE html> <html> <head> <script src="../../../angularjs/angular.js"></script> <script src="../script.js"></script> <script src="script.js"></script> </head> <body ng-app="html5Module"> <div ng-controller="mainController"> <h3>2. HTML5 mode</h3> <div ng-include="'../common.html'"></div> </div> </body> </html>
angular.module("html5Module", ["mainModule"]) .config(function ($locationProvider) { $locationProvider.html5Mode(true).hashPrefix("!"); });
<IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule (.*)$ /code/examples/routing/01_LocationService/html5/index.html [NC,L] </IfModule>



The $location service is very useful whenever you need to read or change the URL (or any part of it) in the browser. It can be configured to work in two modes: hashbang mode or HTML5 mode (it falls back to hashbang mode in case the HTML5 history API is not supported by the browser). In this example, we see both modes at work. Since the mode must be configured in advance in a configuration block of an AngularJS module, we need to decide how to start the web application, so the example opens the starting page in a different tab/window with one of the two modes (opening the example in a new tab/windows also makes it easier to see how the browser's URL changes depending on what is done in the application).

Now let's take a look at the structure of the code in this example. The main entry points of the web application are hashbang/index.html and html5/index.html. Both of them use common.html as the web page content and include that file with the ng-include directive. In the same way, the common code is inside script.js in the example's root directory. That file defines mainModule and mainController with all the common code. hashbang/index.html and html5/index.html both use a different script.js file that just defines a different module that depends on mainModule and configures the web application startup mode (hashbang or HTML5). So, hashbang/index.html defines hashbangModule that configures the application to start in hashbang mode, while html5/index.html defines html5Module and configures the application to start in HTML5 mode. Since the $location service is a provider, it's available in the configuration blocks of modules, but we must remember to add the Provider suffix to its name (check the example about the module's configuration and run phases for more information). If we don't explicitly configure the mode, the default is hashbang mode.

How do the URLs look like in hashbang and HTML5 mode in our example application? Let's take a look. This is an example URL in hashbang mode:!/mypath?param1=value1#hashvalue

This is the same URL in HTML5 mode:

You can identify the same URL parts in different places depending on the chosen mode. In hashbang mode, we see that #! is used in the URL and this is because we've configured the application to use the ! symbol together with # by writing hashPrefix("!") in the configuration blocks of both hashbang and HTML5 mode. Why do we need it also in HTML5 mode? That's because in case the browser doesn't support the HTML5 history API, AngularJS falls back to hashbang mode and we've configured hashPrefix for that case.

At this point, we could enumerate all the different methods provided by the $location service, but trying out the example and playing with it is actually much more effective to understand their effects on the URL. So, open both the hashbang mode and the HTML5 mode of the example. Here you can see what the available methods of the $location service return. You can see in parentheses either a G, S or both. This means that a specific method can be used as a getter (G) or a setter (S) or both. When a setter is available, you can try it by pressing the corresponding button to see its effect.

We need to add a little note about the Replace checkbox for the Set URL button because what it does might not be immediately clear. When the checkbox is not set (default), whenever you press the Set URL button, the URL is changed and a new item is added to the back button of your browser (navigation history), while if the checkbox is set, then the replace() method is called and that means that a new history item will not be added (check the history with the back button of your browser). The replace() method could be used to avoid changes in the history when the $location service changes something in the URL, not only after the url() method.

Let's give also a little explanation about the buttons that manipulate the search part of the URL. Here we have five different examples:

  • Set Search 1: a single parameter param3 is set in the search string with the value value3;
  • Set Search 2: a single parameter param3 is set in the search string, but with multiple values (an array);
  • Set Search 3: the parameter param3 is removed from the search string;
  • Set Search 4: multiple parameters are set at the same time and they completely replace the previous search string;
  • Set Search 5: all the search parameters are removed from the search string (we remove them individually with a loop).

As you can see, we intercept also a couple of events ($locationChangeStart and $locationChangeSuccess) in the example just to show how we can define a handler for them in our script.

You might have noticed that in our example there's also the html5/.htaccess file and it's only in the html5 directory. Why do we need that? While working in HTML5 mode, we often have URLs that don't point to a real path on the server, so when we try to load them in the browser, all we get is a "404 Not Found" error. In this example, with the .htaccess file, we're telling the server that every URL that points to that html5 directory must return the /code/examples/routing/01_LocationService/html5/index.html file to the browser, so for example /code/examples/routing/01_LocationService/html5/somethingElse will still work in the browser (if you access the URL directly or do a full page reload) and will return the main entry point of the web application. That entry point will be the page of our example that is able to interpret the URL and show us the result. URL rewriting is often needed in HTML5 mode, while we don't need it in hashbang mode because what's before the # symbol will typically point to a real path on the server and we change only what's after the # symbol. Anyway, if the URL in HTML5 mode is changed completely and the base part doesn't point to the html5 directory (for example /myCompletelyDifferentURL), that .htaccess file won't have any effect and we either need another .htaccess file in the specified path or the path must be recognized and correctly treated by the server (by returning a real resource in that path for example).

While working with the $location service configured to work in HTML5 mode, you should also be aware of the links rewriting that AngularJS can do for us in a browser that doesn't support the HTML5 history API (fallback to hashbang mode). Since you need to decide how to properly write the links in the pages of your web application, please take a look at the official AngularJS documentation about HTML link rewriting and experiment a bit before writing all the links in your pages.