aboutsummaryrefslogtreecommitdiffstats
path: root/docs/content/guide/dev_guide.services.$location.ngdoc
diff options
context:
space:
mode:
authorVojta Jina2011-07-22 14:58:53 +0200
committerVojta Jina2011-09-08 23:00:59 +0200
commit8fa79066e2cea470086769aa59e7cc9d3aa30d81 (patch)
tree08e84901b62396ff0bcc37b10515a9cf9e98cf2a /docs/content/guide/dev_guide.services.$location.ngdoc
parent909415d5ed817ea3dc2ddcfded00f808df4b5849 (diff)
downloadangular.js-8fa79066e2cea470086769aa59e7cc9d3aa30d81.tar.bz2
doc($location): $location docs + using $location guide
Diffstat (limited to 'docs/content/guide/dev_guide.services.$location.ngdoc')
-rw-r--r--docs/content/guide/dev_guide.services.$location.ngdoc634
1 files changed, 634 insertions, 0 deletions
diff --git a/docs/content/guide/dev_guide.services.$location.ngdoc b/docs/content/guide/dev_guide.services.$location.ngdoc
new file mode 100644
index 00000000..4e0e8548
--- /dev/null
+++ b/docs/content/guide/dev_guide.services.$location.ngdoc
@@ -0,0 +1,634 @@
+@workInProgress
+@ngdoc overview
+@name Developer Guide: Angular Services: Using $location
+@description
+
+# What does it do?
+
+The `$location` service parses the URL in the browser address bar (based on the {@link
+https://developer.mozilla.org/en/window.location window.location}) and makes the URL available to
+your application. Changes to the URL in the address bar are reflected into $location service and
+changes to $location are reflected into the browser address bar.
+
+**The $location service:**
+
+- Exposes the current URL in the browser address bar, so you can
+ - Watch and observe the URL.
+ - Change the URL.
+- Synchronizes the URL with the browser when the user
+ - Changes the address bar.
+ - Clicks the back or forward button (or clicks a History link).
+ - Clicks on a link.
+- Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
+
+
+## Comparing $location to window.location
+
+<table>
+<thead>
+
+ <tr>
+ <td class="empty-corner-lt"></td>
+ <td>window.location</td>
+ <td>$location service</td>
+ </tr>
+
+</thead>
+<tbody>
+
+ <tr>
+ <td class="head">purpose</td>
+ <td>allow read/write access to the current browser location</td>
+ <td>same</td>
+ </tr>
+
+ <tr>
+ <td class="head">API</td>
+ <td>exposes "raw" object with properties that can be directly modified</td>
+ <td>exposes jQuery-style getters and setters</td>
+ </tr>
+
+ <tr>
+ <td class="head">integration with angular application life-cycle</td>
+ <td>none</td>
+ <td>knows about all internal life-cycle phases, integrates with $watch, ...</td>
+ </tr>
+
+ <tr>
+ <td class="head">seamless integration with html5 API</td>
+ <td>no</td>
+ <td>yes (with a fallback for legacy browsers)</td>
+ </tr>
+
+ <tr>
+ <td class="head">aware of docroot/context from which the application is loaded</td>
+ <td>no - window.location.path returns "/docroot/actual/path"</td>
+ <td>yes - $location.path() returns "/actual/path"</td>
+ </tr>
+
+</tbody>
+</table>
+
+## When should I use $location?
+Any time your application needs to react to a change in the current URL or if you want to change
+the current URL in the browser.
+
+## What does it not do?
+Does not cause a full page reload when the browser URL is changed. To reload the page after
+changing the URL, use the lower-level API, `$window.location.href`.
+
+
+# General overview of the API
+
+The `$location` service can behave differently, depending on the configuration that was provided to
+it when it was instantiated. The default configuration is suitable for many applications, for
+others customizing the configuration can enable new features.
+
+Once the `$location` service is instantiated, you can interact with it via jQuery-style getter and
+setter methods that allow you to get or change the current URL in the browser.
+
+## $location service configuration
+
+To configure the `$location` service, you define the `$config` service which is an object with
+configuration properties:
+
+- **html5Mode**: {boolean}<br />
+ `true` - see Html5 mode<br />
+ `false` - see Hashbang mode<br />
+ default: `false`
+
+- **hashPrefix**: {string}<br />
+ prefix used for Hashbang URLs (used in Hashbang mode or in legacy browser in Html5 mode)<br />
+ default: `'!'`
+
+### Example configuration
+<pre>
+angular.service('$config', function() {
+ return {
+ html5mode: true,
+ hashPrefix: '!'
+ };
+});
+</pre>
+
+## Getter and setter methods
+
+`$location` service provides getter methods for read-only parts of the URL (absUrl, protocol, host,
+port) and getter / setter methods for url, path, search, hash:
+<pre>
+// get the current path
+$location.path();
+
+// change the path
+$location.path('/newValue')
+</pre>
+
+All of the setter methods return the same `$location` object to allow chaining. For example, to
+change multiple segments in one go, chain setters like this:
+<pre>$location.path('/newValue').search({key: value});</pre>
+
+All setter methods take an optional boolean flag parameter, which signifies whether current history
+record should be replaced or if a new record should be created (default). To change the current URL
+without creating a new browser history record you can call:
+<pre>$location.path('/newVal', true);</pre>
+
+Note that the setters don't update `window.location` immediately. Instead, `$location` service is
+aware of the {@link api/angular.scope scope} life-cycle and coalesces multiple `$location`
+mutations into one "commit" to the `window.location` object during the scope `$flush` phase. Since
+any of the setters can take the replace flag, it's enough for one setter to use this flag in order
+to make the entire "commit" a replace operation rather than addition to the browser history.
+
+### Setters and character encoding
+You can pass special characters to `$location` service and it will encode them according to rules
+specified in {@link http://www.ietf.org/rfc/rfc3986.txt RFC 3986}. When you access the methods:
+
+- All values that are passed to `$location` setter methods, `path()`, `search()`, `hash()`, are
+encoded.
+- Getters (calls to methods without parameters) return decoded values for the following methods
+`path()`, `search()`, `hash()`.
+- When you call the `absUrl()` method, the returned value is a full url with its segments encoded.
+- When you call the `url()` method, the returned value is path, search and hash, in the form
+`/path?search=a&b=c#hash`. The segments are encoded as well.
+
+
+# Hashbang and Html5 Modes
+
+`$location` service has two configuration modes which control the format of the URL in the browser
+address bar: **Hashbang mode** (the default) and the **Html5 mode** which is based on using the
+Html5 {@link http://www.w3.org/TR/html5/history.html History API}. Applications use the same API in
+both modes and the `$location` service will work with appropriate URL segments and browser APIs to
+facilitate the browser URL change and history management.
+
+<img src="img/guide/hashbang_vs_regular_url.jpg">
+
+<table>
+<thead>
+
+ <tr>
+ <td class="empty-corner-lt"></td>
+ <td>Hashbang mode</td>
+ <td>Html5 mode</td>
+ </tr>
+
+</thead>
+<tbody>
+
+ <tr>
+ <td class="head">configuration</td>
+ <td>the default</td>
+ <td>{ html5Mode: true }</td>
+ </tr>
+
+ <tr>
+ <td class="head">URL format</td>
+ <td>hashbang URLs in all browsers</td>
+ <td>regular URLs in modern browser, hashbang URLs in old browser</td>
+ </tr>
+
+ <tr>
+ <td class="head">&lt;a href=""&gt; link rewriting</td>
+ <td>no</td>
+ <td>yes</td>
+ </tr>
+
+ <tr>
+ <td class="head">requires server-side configuration</td>
+ <td>no</td>
+ <td>yes</td>
+ </tr>
+</tbody>
+</table>
+
+## Hashbang mode (default mode)
+
+In this mode, `$location` uses Hashbang URLs in all browsers.
+
+### Example
+
+<pre>
+angular.service('$config', function() {
+ return {
+ html5Mode: false,
+ hashPrefix: '!'
+ };
+});
+
+// open http://host.com/base/index.html#!/a
+$location.absUrl() == 'http://host.com/base/index.html#!/a'
+$location.path() == '/a'
+
+$location.path('/foo')
+$location.absUrl() == 'http://host.com/base/index.html#!/foo'
+
+$location.search() == {}
+$location.search({a: 'b', c: true});
+$location.absUrl() == 'http://host.com/base/index.html#!/foo?a=b&c'
+
+$location.path('/new').search('x=y');
+$location.absUrl() == 'http://host.com/base/index.html#!/new?x=y'
+</pre>
+
+### Crawling your app
+
+To allow indexing of your AJAX application, you have to add special meta tag in the head section of
+your document:
+<pre><meta name="fragment" content="!" /></pre>
+
+This will cause crawler bot to request links with `_escaped_fragment_` param so that your server
+can recognize the crawler and serve a HTML snapshots. For more information about this technique,
+see {@link http://code.google.com/web/ajaxcrawling/docs/specification.html Making AJAX Applications
+Crawlable}.
+
+## HTML5 mode
+
+In HTML5 mode, the `$location` service getters and setters interact with the browser URL address
+through the HTML5 history API, which allows for use of regular URL path and search segments,
+instead of their hashbang equivalents. If the HTML5 History API is not supported by a browser, the
+`$location` service will fall back to using the hashbang URLs automatically. This frees you from
+having to worry about whether the browser displaying your app supports the history API or not; the
+`$location` service transparently uses the best available option.
+
+- Opening a regular URL in a legacy browser -> redirects to a hashbang URL
+- Opening hashbang URL in a modern browser -> rewrites to a regular URL
+
+### Example
+
+<pre>
+angular.service('$config', function() {
+ return {
+ html5Mode: true,
+ hashPrefix: '!'
+ };
+});
+
+// in browser with html5 history support:
+// open http://host.com/#!/a -> rewrite to http://host.com/a
+// (replacing the http://host.com/#!/a history record)
+$location.path() == '/a'
+
+$location.path('/foo');
+$location.absUrl() == 'http://host.com/foo'
+
+$location.search() == {}
+$location.search({a: 'b', c: true});
+$location.absUrl() == 'http://host.com/foo?a=b&c'
+
+$location.path('/new').search('x=y');
+$location.url() == 'new?x=y'
+$location.absUrl() == 'http://host.com/new?x=y'
+
+// in browser without html5 history support:
+// open http://host.com/new?x=y -> redirect to http://host.com/#!/new?x=y
+// (again replacing the http://host.com/new?x=y history item)
+$location.path() == '/new'
+$location.search() == {x: 'y'}
+
+$location.path('/foo/bar');
+$location.path() == '/foo/bar'
+$location.url() == '/foo/bar?x=y'
+$location.absUrl() == 'http://host.com/#!/foo/bar?x=y'
+</pre>
+
+### Fallback for legacy browsers
+
+For browsers that support the HTML5 history API, `$location` uses the HTML5 history API to write
+path and search. If the history API is not supported by a browser, `$location` supplies a Hasbang
+URL. This frees you from having to worry about whether the browser viewing your app supports the
+history API or not; the `$location` service makes this transparent to you.
+
+### Html link rewriting
+
+When you use the history API mode, you will need different links in different browser, but all you
+have to do is specify regular URL links, such as: `&lt;a href="/some?foo=bar"&gt;link&lt;/a&gt;`
+
+When a user clicks on this link,
+
+- In a legacy browser, the URL changes to `/index.html#!/some?foo=bar`
+- In a modern browser, the URL changes to `/some?foo=bar`
+
+
+In cases like the following, links are not rewritten; instead, the browser will perform a full page
+reload to the original link.
+
+- Links with an `ng:ext-link` directive<br />
+ Example: `<a href="/ext/link?a=b" ng:ext-link>link</a>`
+- Links that contain `target="_blank"`<br />
+ Example: `<a href="/ext/link?a=b" target="_blank">link</a>`
+- Absolute links that go to a different domain<br />
+ Example: `<a href="http://angularjs.org/">link</a>`
+
+### Server side
+
+Using this mode requires URL rewriting on server side, basically you have to rewrite all your links
+to entry point of your application (e.g. index.html)
+
+### Crawling your app
+
+If you want your AJAX application to be indexed by web crawlers, you rill need to add the following
+meta tag to the HEAD section of your document:
+<pre><meta name="fragment" content="!" /></pre>
+
+This statement causes a crawler to request links with empty `_escaped_fragment_` parameter so that
+your server can recognize the crawler and serve it HTML snapshots. For more information about this
+technique, see {@link http://code.google.com/web/ajaxcrawling/docs/specification.html Making AJAX
+Applications Crawlable}.
+
+### Relative links
+
+Be sure to check all relative links, images, scripts etc. You must use an absolute path because the
+path is going to be rewritten. You can use `<base href="" />` tag as well.
+
+Running Angular apps with the History API enabled from document root is strongly encouraged as it
+takes care of all relative link issues. **Otherwise you have to specify &lt;base href="" /&gt; !**
+
+### Sending links among different browsers
+
+Because of rewriting capability in Html5 mode, your users will be able to open regular url links in
+legacy browsers and hashbang links in modern browser:
+
+- Modern browser will rewrite hashbang URLs to regular URLs.
+- Older browsers will redirect regular URLs to hashbang URLs.
+
+### Example
+
+Here you can see two `$location` instances, both in **Html5 mode**, but on different browsers, so
+that you can see the differences. These `$location` services are connected to a fake browsers. Each
+input represents address bar of the browser.
+
+Note that when you type hashbang url into first browser (or vice versa) it doesn't rewrite /
+redirect to regular / hashbang url, as this conversion happens only during parsing the initial URL
+= on page reload.
+
+In this examples we use `<base href="/base/index.html" />`
+
+<ul class="doc-example">
+<li ng:non-bindable class="html5-hashbang-example">
+ <div id="html5-mode" ng:controller="Html5Cntl">
+ <h3>Browser with History API</h3>
+ <ng:address-bar browser="html5"></ng:address-bar><br /><br />
+ $location.protocol() = {{$location.protocol()}}<br />
+ $location.host() = {{$location.host()}}<br />
+ $location.port() = {{$location.port()}}<br />
+ $location.path() = {{$location.path()}}<br />
+ $location.search() = {{$location.search()}}<br />
+ $location.hash() = {{$location.hash()}}<br />
+ <a href="/base/first?a=b">/base/first?a=b</a> | <a
+href="sec/ond?flag#hash">sec/ond?flag#hash</a> | <a href="/base/another?search"
+ng:ext-link>external</a>
+ </div>
+
+ <div id="hashbang-mode" ng:controller="HashbangCntl">
+ <h3>Browser without History API</h3>
+ <ng:address-bar browser="hashbang"></ng:address-bar><br /><br />
+ $location.protocol() = {{$location.protocol()}}<br />
+ $location.host() = {{$location.host()}}<br />
+ $location.port() = {{$location.port()}}<br />
+ $location.path() = {{$location.path()}}<br />
+ $location.search() = {{$location.search()}}<br />
+ $location.hash() = {{$location.hash()}}<br />
+ <a href="/base/first?a=b">/base/first?a=b</a> | <a
+href="sec/ond?flag#hash">sec/ond?flag#hash</a> | <a href="/base/another?search"
+ng:ext-link>external</a>
+ </div>
+</li>
+</ul>
+
+<script type="text/javascript">
+ function FakeBrowser(initUrl, baseHref) {
+ this.onUrlChange = function(fn) {
+ this.urlChange = fn;
+ };
+
+ this.url = function() {
+ return initUrl;
+ };
+
+ this.defer = function(fn, delay) {
+ setTimeout(function() { fn(); }, delay || 0);
+ };
+
+ this.baseHref = function() {
+ return baseHref;
+ };
+
+ this.hover = angular.noop;
+ this.notifyWhenOutstandingRequests = angular.noop;
+ }
+
+ var browsers = {
+ html5: new FakeBrowser('http://www.host.com/base/path?a=b#h', '/base/index.html'),
+ hashbang: new FakeBrowser('http://www.host.com/base/index.html#!/path?a=b#h',
+'/base/index.html')
+ };
+
+ function Html5Cntl($location) {
+ this.$location = $location;
+ }
+
+ function HashbangCntl($location) {
+ this.$location = $location;
+ }
+
+ angular.widget('ng:address-bar', function(tpl) {
+ return function(elm) {
+ var browser = browsers[elm.attr('browser')],
+ input = angular.element('<input type="text" />').val(browser.url()),
+ delay;
+
+ input.bind('keypress keyup keydown', function() {
+ if (!delay) {
+ delay = setTimeout(fireUrlChange, 250);
+ }
+ });
+
+ browser.url = function(url) {
+ return input.val(url);
+ };
+
+ elm.append('Address: ').append(input);
+
+ function fireUrlChange() {
+ delay = null;
+ browser.urlChange(input.val());
+ }
+ };
+ });
+
+ function initEnv(name) {
+ var root = angular.element(document.getElementById(name + '-mode'));
+ var scope = angular.scope(null, {
+ $config: {html5Mode: true, hashPrefix: '!'},
+ $browser: browsers[name],
+ $document: root,
+ $sniffer: {history: name == 'html5'}
+ });
+
+ angular.compile(root)(scope).$apply();
+ root.bind('click', function(e) {
+ e.stopPropagation();
+ });
+ }
+
+ initEnv('html5');
+ initEnv('hashbang');
+</script>
+
+
+# Caveats
+
+## Page reload navigation
+
+The `$location` service allows you to change only the URL; it does not allow you to reload the
+page. When you need to change the URL and reload the page or navigate to a different page, please
+use a lower level API, {@link api/angular.service.$window $window.location.href}.
+
+## Using $location outside of the scope life-cycle
+
+`$location` knows about Angular's {@link api/angular.scope scope} life-cycle. When a URL changes in
+the browser it updates the `$location` and calls `$apply` so that all $watchers / $observers are
+notified.
+When you change the `$location` inside the `$digest` phase everything is ok; `$location` will
+propagate this change into browser and will notify all the $watchers / $observers.
+When you want to change the `$location` from outside Angular (for example, through a DOM Event or
+during testing) - you must call `$apply` to propagate the changes.
+
+## $location.path() and ! or / prefixes
+
+A path should always begin with forward slash (`/`); the `$location.path()` setter will add the
+forward slash if it is missing.
+
+Note that the `!` prefix in the hashbang mode is not part of `$location.path()`; it is actually
+hashPrefix.
+
+
+# Testing with the $location service
+
+When using `$location` service during testing, you are outside of the angular's {@link
+api/angular.scope scope} life-cycle. This means it's your responsibility to call `scope.$apply()`.
+
+<pre>
+angular.service('$serviceUnderTest', function($location) {
+ // whatever it does...
+};
+
+describe('$serviceUnderTest', function() {
+ var scope, $location, $sut;
+
+ beforeEach(function() {
+ scope = angular.scope();
+ $location = scope.$service('$location');
+ $sut = scope.$service('$serviceUnderTest');
+ });
+
+ it('should...', function() {
+ $location.path('/new/path');
+ scope.$apply();
+
+ // test whatever the service should do...
+
+ });
+});
+</pre>
+
+
+# Migrating from earlier AngularJS releases
+
+In earlier releases of Angular, `$location` used `hashPath` or `hashSearch` to process path and
+search methods. With this release, the `$location` service processes path and search methods and
+then uses the information it obtains to compose hashbang URLs (such as
+`http://server.com/#!/path?search=a`), when necessary.
+
+## Changes to your code
+
+<table>
+ <tr class="head">
+ <td>Navigation inside the app</td>
+ <td>Change to</td>
+ </tr>
+
+ <tr>
+ <td>$location.href = value<br />$location.hash = value<br />$location.update(value)<br
+/>$location.updateHash(value)</td>
+ <td>$location.path(path).search(search)</td>
+ </tr>
+
+ <tr>
+ <td>$location.hashPath = path</td>
+ <td>$location.path(path)</td>
+ </tr>
+
+ <tr>
+ <td>$location.hashSearch = search</td>
+ <td>$location.search(search)</td>
+ </tr>
+
+ <tr class="head">
+ <td>Navigation outside the app</td>
+ <td>Use lower level API</td>
+ </tr>
+
+ <tr>
+ <td>$location.href = value<br />$location.update(value)</td>
+ <td>$window.location.href = value</td>
+ </tr>
+
+ <tr>
+ <td>$location[protocol | host | port | path | search]</td>
+ <td>$window.location[protocol | host | port | path | search]</td>
+ </tr>
+
+ <tr class="head">
+ <td>Read access</td>
+ <td>Change to</td>
+ </tr>
+
+ <tr>
+ <td>$location.hashPath</td>
+ <td>$location.path()</td>
+ </tr>
+
+ <tr>
+ <td>$location.hashSearch</td>
+ <td>$location.search()</td>
+ </tr>
+
+ <tr>
+ <td>$location.href<br />$location.protocol<br />$location.host<br />$location.port<br
+/>$location.hash</td>
+ <td>$location.absUrl()<br />$location.protocol()<br />$location.host()<br />$location.port()<br
+/>$location.path() + $location.search()</td>
+ </tr>
+
+ <tr>
+ <td>$location.path<br />$location.search</td>
+ <td>$window.location.path<br />$window.location.search</td>
+ </tr>
+</table>
+
+## Two-way binding to $location
+
+The Angular's compiler currently does not support two-way binding for methods (see {@link
+https://github.com/angular/angular.js/issues/404 issue}). If you should require two-way binding,
+you will need to specify an extra property that has two watchers. For example:
+<pre>
+<!-- html -->
+<input type="text" name="locationPath" />
+</pre>
+<pre>
+// js - controller
+this.$watch('locationPath', function(scope, path) {
+ $location.path(path);
+});
+
+this.$watch('$location.path()', function(scope, path) {
+ scope.locationPath = path;
+});
+</pre>
+
+
+# Related API
+
+* {@link api/angular.service.$location $location API}
+
+
+