Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

$anchorScroll breaks anchor links with html5mode enabled #14315

Closed
lookfirst opened this issue Mar 24, 2016 · 10 comments
Closed

$anchorScroll breaks anchor links with html5mode enabled #14315

lookfirst opened this issue Mar 24, 2016 · 10 comments

Comments

@lookfirst
Copy link

I boiled this problem down to a very simple test case included below. Apologies if this has been reported before, but I did search around first for this.

Just by simply injecting $anchorScroll and not even calling it anywhere in the code, all anchor href's are broken. The value in the location bar changes, but navigation to the new link does not work.

Disabling html5Mode works.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>boo</title>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.2/angular.min.js"></script>
        <base href="/">
    </head>

    <body ng-controller="MyCtrl">

        <a href="/nogo">GOOOOOOO</a>

        <script type="text/javascript">
            angular.element(document).ready(function() {    
                var appModule = angular.module('appModule', []);

                appModule.config(['$locationProvider', function($locationProvider) {
                    $locationProvider.html5Mode({
                        enabled: true
                    });
                }]);

                appModule.controller('MyCtrl', ['$anchorScroll', function($anchorScroll) {}]);
/*
                appModule.controller('MyCtrl', function() {});
*/

                return angular.bootstrap(document.body, [appModule.name], {
                    strictDi: true
                });
            });
        </script>
    </body>
</html>
@gkalpak
Copy link
Member

gkalpak commented Mar 24, 2016

It's not clear to me what you mean by "broken" and "navigation does not work" ?
What should I see using your example (and how is it different from the expected behavior) ?

@lookfirst
Copy link
Author

@gkalpak I would expect clicking on a link to navigate to a new page. This does not happen. All that happens is that the location bar changes.

@gkalpak
Copy link
Member

gkalpak commented Mar 25, 2016

@lookfirst, I was able to reproduce the behavior here (I think).
Basically, the behavior you are seeing with $anchorScroll is the expected behavior; it's HTML 5 mode deep linking at work.

Why does it happen with $anchorScroll ?

When Angular instantiates your controller (which depends on $anchorScroll), the injector needs to instantiate $anchorScroll (even if you don't use it in your controller). $anchorScroll, in turn, depends on $location, so $location has to be instantiated first. Instantiating $location makes the deep-linking take effect.

As you can see in the updated plnkr, the same "issue" happens without $anchorScroll, just by injecting $location into your controller.

What can I do ?

If you don't want $location to intercept your <a> elements, you can set rewriteLinks to false:

$location.html5Mode({
  enabled: true,
  rewriteLinks: false
});

Demo

All that said, the fact that $location is not instantiated unless the user injects it (either directly or indirectly) doesn't feel right to me (I would go as far as calling it a bug 😛).
On the other hand, changing this behavior now, would probably break a few apps and a lot of tests.

@lookfirst
Copy link
Author

I'd just be happy with some documentation around this. =) I had to spend quite a few hours figuring out the problem and hopefully anyone else who comes across this will find this issue as I couldn't find anything else like it. Thanks for investigating it further and providing the plunkrs, you definitely reproduced it.

@lookfirst
Copy link
Author

On a related note, I just found out that $location.search() returns no data if I pass a url in with a query string and html5Mode isn't enabled. So I used enabled: true and rewriteLinks: false and things are working for me now.

@gkalpak
Copy link
Member

gkalpak commented Apr 7, 2016

@lookfirst, that is expected behavior. In hashbang mode, the part after the first # is the URL, so the search params are retrieved from there. E.g.:

HTML5 mode Hashbang mode
localhost/some/path?key=value#target localhost#/some/path?key=value#target

@lookfirst
Copy link
Author

I think your example in hashbang is wrong. You've included two #'s....

Even still, the expected behavior seems wrong. I expect $location.search() to give me the data within the query string (the part after the ?) regardless of whether it is hashbang mode or html5 mode. Short of parsing the entire url myself, there is no other way of getting this data and I think that the location service should do that.

@gkalpak
Copy link
Member

gkalpak commented Apr 8, 2016

@lookfirst, my example is corrent. $location is not a general URL parsing utility. It's specifically designed for Angular's client-side routing and deep-linking needs. So the values it's concerned with are only those that are part of the "client-side" URL (I just made that term up).

In the default hashbang mode, the part after the first # (which is ignored in server requests) is what Angular uses as the "client-side" URL (and yes, it can contain a hash fragment itself, hence the second #).

The $location section of the Developer Guide is quite illustrative.

@lookfirst
Copy link
Author

What a mess. 😢

@gkalpak
Copy link
Member

gkalpak commented Apr 9, 2016

Closing this as you were able to make things work for you (by setting html5Mode to {enabled: true, rewriteLinks: false}). I think the Developer Guide does a good job explaining the concept, but if you disagree, PRs improving the docs are always welcome 😃

@gkalpak gkalpak closed this as completed Apr 9, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants