According to AngularJS documentation, it states that a hash bang URL is in this format:
http://foo.com/#!/bar?baz=23
This indicates that the hash fragment is between the path and the query parameters of the URL. It's DOMAIN - HASH FRAGMENT - QUERY PARAMETER
However on Google AJAX Crawling Specification, when it converts a hash bang URL into the _escaped_fragment_ format for the purposes of indexing and then converts it back for search results, it instead places the hash fragment after the query parameter. (See the Search Results section)
http://foo.com/?baz=23#!/bar
This indicates DOMAIN - QUERY PARAMETER - HASH FRAGMENT.
It seems that both are a valid URL structure (try it on a browser with any non SPA application). However if you are using this URL format on any AngularJS site, the site correctly ascertains the path, however it "forgets" the query parameters.
So if you go to http://docs.angularjs.org?key=value#!/guide, it turns into http://docs.angularjs.org/guide. As you can see it forgets the query parameters. If I'm using the Google AJAX specification, then this would result in search results having URLs that don't work properly as their query parameters gets dropped.
I did some testing with both urls which has illuminated the problem:
Case 1: Using DOMAIN - HASH - QUERY PARAMETER (AngularJS format) (http://example.com/#!/path?key=value), the query parameters never gets received by the server, but it is remembered on the client side. It's also available in $location.search().
Case 2: Using DOMAIN - QUERY PARAMETER - HASH (Google's format) (http://example.com/?key=value#!/path), the query parameter gets received by the server (as it is not after the #), but it is forgotten on the client side. It's also not available in $location.search().
Perhaps a solution can be made for case 2. So that when there is query parameter and then hash fragment, AngularJS can correctly remember the query parameter and shift it to the end of the URL (if converting to HTML5 urls) and also make it accessible with $location.search(). Case 1 cannot be solved due to the URL standards as everything after the # is not sent to the server.
Angular does not remove the query before the hash bang. For angularjs.org this is done on the server with a redirect. See this jsfiddle: http://jsfiddle.net/dVNPz/show/?a=b#!/test?c=d
However you are right in that Angular does not include the query parameter before the hash into $location when in hashbang mode. The reason for this is that Angular can't change anything before the hash without the browser reloading the page.
How did you get the value for the _escaped_fragment_?
With regards to the value of _escaped_fragment_, that's just based off the Google AJAX Crawling Specification.
So the server is the one that removes the query before the hash bang? If one could make the server keep the query, can AngularJS extract those values and place it in $location? It doesn't need to change the values before the hash to read the values before the hash right?
Right, the server behind angularjs.org does remove the query before the hash bang. Your server might not, depending on how you configured it.
Angular could provide this extra information, but the other parts of the url before the # are not present on $location either (e.g. the path). The best thing would be to have a new service that encapsulates window.location only for reading purposes.
For your app, you can just do this (e.g. directly access window.location) and read out the query value.
Before we work on this, we need verify that Google really moves the query part from behind the hash to the front of it, and does not escape it as well.
The part where it mentions it in the Google AJAX specification is here:
Search result
The search engine agrees to display in the search results the corresponding pretty URLs:
domain[:port]/path#!hashfragment
domain[:port]/path?queryparams#!hashfragment
domain[:port]/path
domain[:port]/path?queryparams
Specifically:
domain[:port]/path?queryparams#!hashfragment
Now Google will put everything after the hash fragment into the _escaped_fragment_. This will result in an AngularJS compatible URL. This is not the problem.
The problem is that DOMAIN HASH QS and DOMAIN QS HASH are both valid URL structures. And while we cant fix DOMAIN HASH QS, surely AngularJS should be able to extract the QS from DOMAIN QS HASH.
Query parameters should come first according to the URI syntax spec: http://tools.ietf.org/html/std66#section-3
Otherwise they are just part of the fragment, I believe. Angular is free to do what it wants within the fragment, but I also would like Angular to read the query parameters when the URI is in the form of DOMAIN - QUERY PARAMETER - HASH FRAGMENT
Any updates on this issue. Any idea whether the DOMAIN QS HASH will be supported by Angular? I.e:$location.search() returning the values of the query string in the case of DOMAIN QS HASH.
Thank you
Would it be possible to change the routing to support DOMAIN QS HASH when NOT in hashbang mode? Here's the situation my team is in:
This is also breaking google analytics campaign tracking when linking to hashbang URLs.
Let's take a URL like domain.com/#!/path?utm_campaign=blah. If you go to this URL and print out window.location.search, there is nothing there.
I'm assuming that google analytics is going to use whatever it finds in window.location.search for its campaign tracking.
If you use the DOMAIN QS HASH version like domain.com?utm_campaign=blah#!/path, the query string is in window.location.search. The problem is that DQS breaks the routing when in html5 mode.
Is there any update on this? I am having the same problem as what "timomall" has reported on 9 Dec 2014.
There are too different concepts here:
$location.search()).In order to avoid confusing crawlers, one should probably encode that second "query-like" part of the _hash fragment_, when using an _escaped_fragment_ URL.
In any case, the Google AJAX Crawling Specification has been deprecated since October 2015.
Closing as I don't believe there is anything we can fix on the Angular side of things.
My problem is not with the crawler at all. My code was using the non-HTML5 mode and we have URLs given to external customers which are of the form https://abcd.efgh?utm_campaign=abcd#/abcd/12. Now, if I turn on HTML5 mode and the external customer tries to use the URL already with them, the URL changes in the browser to just https://abcd.efgh/abcd/12. The query parameter of utm_campaign is completely lost.
How can I get angularjs to preserve the query parameter so that the URL becomes https://abcd.efgh/abcd/12?utm_campaign=abcd when somebody tries to use the old URL?
@nmandya, I suspect it is a server configuration issue. But you can't really mix URL styles (hashbang and html5 mode) and have things work as expected, I'm afraid.
In https://abcd.efgh?utm_campaign=abcd#/abcd/12, Angular is only concerned with the part after #, namely /abcd/12. It sees it as:
$location.path() === '/abcd/12'
$location.search() === {}
$location.hash() === ''
The equivalent of the above in html5 mode is indeed https://abcd.efgh/abcd/12.
The "problem" is that in hashbang mode, there is the _query_ part of the URL (before the #), which Angular is not concerned with and the _query_ part of the client-side routing (after the #) which Angular "sees" and interacts with. In html5 mode there is only one _query_ path.
So you can't really use the hashbang mode URLs in html5 mode (unless you do some server-side transformation maybe, converting ?utm_campaign=abcd#/abcd/12 to #/abcd/12?utm_campaign=abcd).
My problem is that IE10 auto-rewrites "https://host/#hash?search=..." to "https://host/?hash&search=..." so our Angular2 routing is all broken.
If it's a Angular2 issue, you should raise it on https://github.com/angular/angular.
Although it sounds more like a IE10 issue...
Hi there, hate to comment on a closed, old issue, but the fact is that the URI RFC (RFC3986) is pretty clear that the fragment identifier comes _after_ the query string. The way the non-html5 mode of Angular's location service works is to lose the query portion of the URL if it appears prior to the fragment. I have a small patch to the location service that fixes this issue. I'm in the process of submitting a pull request for it. Hopefully it will help someone.
Why AngularJS supports hashbang ? Can it be solved in future ? It just does not work with server side php ,especially post methods .
For support questions, please, use one of the appropriate support channels.
Thx!
Most helpful comment
My problem is that IE10 auto-rewrites "https://host/#hash?search=..." to "https://host/?hash&search=..." so our Angular2 routing is all broken.