Removing the fragment identifier from AngularJS urls (# symbol)
Asked Answered
L

14

258

Is it possible to remove the # symbol from angular.js URLs?

I still want to be able to use the browser's back button, etc, when I change the view and will update the URL with params, but I don't want the # symbol.

The tutorial routeProvider is declared as follows:

angular.module('phonecat', []).
  config(['$routeProvider', function($routeProvider) {
  $routeProvider.
  when('/phones', {templateUrl: 'partials/phone-list.html',   controller: PhoneListCtrl}).
  when('/phones/:phoneId', {templateUrl: 'partials/phone-detail.html', controller: PhoneDetailCtrl}).
  otherwise({redirectTo: '/phones'});
}]);

Can I edit this to have the same functionality without the #?

Liebowitz answered 8/2, 2013 at 11:1 Comment(4)
hashtag nice oneSigne
#14320467Kurtz
For me all the answer here are wrong. We can "rewrite" the url and remove the #, but never access directly to the webpage without #. Any real solution here ???Shulem
Here is the solution if you are using Angular 1.6.Kovno
O
254

Yes, you should configure $locationProvider and set html5Mode to true:

angular.module('phonecat', []).
  config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {

    $routeProvider.
      when('/phones', {templateUrl: 'partials/phone-list.html',   controller: PhoneListCtrl}).
      when('/phones/:phoneId', {templateUrl: 'partials/phone-detail.html', controller: PhoneDetailCtrl}).
      otherwise({redirectTo: '/phones'});

    $locationProvider.html5Mode(true);

  }]);
Overmodest answered 8/2, 2013 at 11:17 Comment(24)
Doing this seems to cause issue in IE, I get all sorts of digest errors in the console and it still ends up redirecting to #/phones Error: 10 $digest() iterations reached. Aborting! I don't use any custom tags in my App eitherLiebowitz
I've read through that article, although it refers to IE7 & 8, I have the same issues in 9. I'm able to get my app working in IE7 using my module definition above. But that has the hash in the url. If I use your definition I get errors in IE9. :(Liebowitz
Because IE lt 10 doesn't support html5 history API which were enabled by setting up html5Mode(true). In IE you have to use # in routes.Overmodest
Ok, thanks. I opted to have a different config for ie not setting html5mode.Liebowitz
@MaximGrach can you please make it more clear what IE lt 10 means?Insinuate
@Insinuate IE lt 10 means Internet Explorer less then version 10Overmodest
Can you set it so this fallsback to using # in IE? Is it as simple as just wrapping $locationProvider.html5Mode(true); in if !(IE lt 10) ?Transpacific
After doing this, is it possible to access localhost/phones directly? not passing by the index.html?Dark
So this solution does solve removing the hashtag in the url given the url has a hashtag, however it doesn't solve how to respond to requests that doesn't want the hashtag to be included in the first place. Aka, it solves blah/#/phones -> blah/phones but it doesn't handle blah/phones directly.Footrace
@Footrace is very correct... does anybody have a solution for a way to directly handle URLs given to the browser without the # to begin with? (as in blah/phones resolving to a page in our app). That's the problem I want to solve...Hunan
@Mik Cox, I was actually able to solve it. Have your backend send back index.html for all URLs, that way you won't be seeing 404s.Footrace
Sorry, I don't get what you mean @Luke, could you add a bit more detail?Hunan
@MikCox, when a URL is directly inputted in the browser, your server may return a 404 because it can't find that resource. What you want to do is for the browser to hand back index.html (since most SPAs have just a single html template) and have Angular handle the URL to correctly manipulate the template.Footrace
@Luke, how do you handle something like emailing a url to someone that looks like "mywebsite.com/user/123131/settings" instead of "mywebsite.com/#/user/123131/settings"?Damper
@BogdanVarlamov can you clarify? Handling the emailing? Or handling the link? If you are talking about handling the link, the answer is already provided above.Footrace
@Luke, I see where you say that "it doesn't handle blah/phones" directly. I'm wondering if there is a different solution that can handle that type of URL as a request (i.e. i paste that URL in the browser and wan't the app to resolve the route without the #)Damper
You may find this useful - github.com/angular-ui/ui-router/wiki/…Arnettearney
I also had to add requireBase: false to get it to work under the $locationProvider.html5Mode blockManage
@Footrace The .htaccess solution by Thomas Knápek fixed this for me.Acantho
I wish this answer had the explanation in the answer rather than in the comments. I find myself unsure of how to actually implement this @MaximGrachJahdai
I needed to put <base href="/" /> in my index.html <head> section.Reifel
It works fine but it doesn't work when we refresh / reload the page. please help if you have any suggestionsOrnithischian
when I tried this, it works but /actionname is removed from the url so /actionname changes to /Evangelize
This solutions just removes the "#" from the URL visually. I cannot access the Page directly like example.com/about from the Browser URL. If I want to access the Page from Browser URL, I still need to include "#" like example.com/#/about. Is that correct or I am making some mistake? I am using AngularJS 1.6.Marginal
P
56

Be sure to check browser support for the html5 history API:

  if(window.history && window.history.pushState){
    $locationProvider.html5Mode(true);
  }
Potpourri answered 7/12, 2013 at 7:30 Comment(3)
This might be better suited as a comment as it doesn't directly answer the question.Lallygag
According to the developer guide, this detection is done automatically: If the HTML5 History API is not supported by a browser, the $location service will fall back to using the hashbang URLs automaticallyClaudeclaudel
Worked like a charm, and I appreciate method checking rather than browser version checking--future proof.Lumenhour
T
47

To remove the Hash tag for a pretty URL and also for your code to work after minification you need to structure your code like the example below:

jobApp.config(['$routeProvider','$locationProvider',
    function($routeProvider, $locationProvider) {
        $routeProvider.
            when('/', {
                templateUrl: 'views/job-list.html',
                controller: 'JobListController'
            }).
            when('/menus', {
                templateUrl: 'views/job-list.html',
                controller: 'JobListController'
            }).
            when('/menus/:id', {
                templateUrl: 'views/job-detail.html',
                controller: 'JobDetailController'
            });

         //you can include a fallback by  including .otherwise({
          //redirectTo: '/jobs'
        //});


        //check browser support
        if(window.history && window.history.pushState){
            //$locationProvider.html5Mode(true); will cause an error $location in HTML5 mode requires a  tag to be present! Unless you set baseUrl tag after head tag like so: <head> <base href="/">

         // to know more about setting base URL visit: https://docs.angularjs.org/error/$location/nobase

         // if you don't wish to set base URL then use this
         $locationProvider.html5Mode({
                 enabled: true,
                 requireBase: false
          });
        }
    }]);
Triton answered 8/1, 2015 at 9:41 Comment(2)
For the requireBase: false +1Cyndy
Hello @digitlimit, After adding $locationProvider.html5Mode code my code $urlRouterProvider.otherwise('/home'); stopped working.Crazyweed
J
18

I write out a rule in web.config after $locationProvider.html5Mode(true) is set in app.js.

Hope, helps someone out.

  <system.webServer>
    <rewrite>
      <rules>
        <rule name="AngularJS" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
            <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>

In my index.html I added this to <head>

<base href="/">

Don't forget to install url rewriter for iis on server.

Also if you use Web Api and IIS, this match url will not work out, as it will change your api calls. So add third input(third line of condition) and give out a pattern that will exclude calls from www.yourdomain.com/api

Judd answered 19/11, 2014 at 7:15 Comment(1)
How do you install url rewriter for IIS? I'm hosting on Azure.Hesitation
C
11

If you are in .NET stack with MVC with AngularJS, this is what you have to do to remove the '#' from url:

  1. Set up your base href in your _Layout page: <head> <base href="/"> </head>

  2. Then, add following in your angular app config : $locationProvider.html5Mode(true)

  3. Above will remove '#' from url but page refresh won't work e.g. if you are in "yoursite.com/about" page refreash will give you a 404. This is because MVC does not know about angular routing and by MVC pattern it will look for a MVC page for 'about' which does not exists in MVC routing path. Workaround for this is to send all MVC page request to a single MVC view and you can do that by adding a route that catches all

url:

routes.MapRoute(
    name: "App",
    url: "{*url}",
    defaults: new {
        controller = "Home", action = "Index"
    }
);
Cain answered 16/9, 2015 at 23:28 Comment(2)
Which file should I add the routes.MapRoute?Sense
RouteConfig.cs its under App_Start folderCain
D
6

You can tweak the html5mode but that is only functional for links included in html anchors of your page and how the url looks like in the browser address bar. Attempting to request a subpage without the hashtag (with or without html5mode) from anywhere outside the page will result in a 404 error. For example, the following CURL request will result in a page not found error, irrespective of html5mode:

$ curl http://foo.bar/phones

although the following will return the root/home page:

$ curl http://foo.bar/#/phones

The reason for this is that anything after the hashtag is stripped off before the request arrives at the server. So a request for http://foo.bar/#/portfolio arrives at the server as a request for http://foo.bar. The server will respond with a 200 OK response (presumably) for http://foo.bar and the agent/client will process the rest.

So in cases that you want to share a url with others, you have no option but to include the hashtag.

Degauss answered 5/6, 2015 at 14:48 Comment(1)
Is there no way around this? This is a pretty big issue.Dionysius
A
5

Follow 2 steps-
1. First set the $locationProvider.html5Mode(true) in your app config file.
For eg -
angular.module('test', ['ui.router']) .config(function($stateProvider, $urlRouterProvider, $locationProvider) { $locationProvider.html5Mode(true); $urlRouterProvider.otherwise('/'); });

2.Second set the <base> inside your main page.
For eg ->
<base href="/">

The $location service will automatically fallback to the hash-part method for browsers that do not support the HTML5 History API.

Armenian answered 14/4, 2016 at 7:28 Comment(0)
G
3

My solution is create .htaccess and use #Sorian code.. without .htaccess I failed to remove #

RewriteEngine   On
RewriteBase     /
RewriteCond     %{REQUEST_URI} !^(/index\.php|/img|/js|/css|/robots\.txt|/favicon\.ico)
RewriteCond     %{REQUEST_FILENAME} !-f
RewriteCond     %{REQUEST_FILENAME} !-d
RewriteRule     ./index.html [L]
Gilgamesh answered 19/1, 2014 at 21:9 Comment(0)
H
1

According to the documentation. You can use:

$locationProvider.html5Mode(true).hashPrefix('!');

NB: If your browser does not support to HTML 5. Dont worry :D it have fallback to hashbang mode. So, you don't need to check with if(window.history && window.history.pushState){ ... } manually

For example: If you click: <a href="/other">Some URL</a>

In HTML5 Browser: angular will automatically redirect to example.com/other

In Not HTML5 Browser: angular will automatically redirect to example.com/#!/other

Huerta answered 6/1, 2016 at 6:11 Comment(0)
P
1

This answer assumes that you are using nginx as reverse proxy and you already did set $locationProvider.html5mode to true.

->For the people who might still be struggling with all cool stuff above.

Surely, @maxim grach solution works fine, but for the solution for how to respond to requests that doesn't want the hashtag to be included in the first place what can be done is check if php is sending 404 and then rewrite the url. Following is the code for nginx,

In the php location, detect 404 php error and redirect to another location,

location ~ \.php${
  ...
  fastcgi_intercept_errors on;
  error_page 404 = /redirect/$request_uri ;
}

Then rewrite url in redirect location and proxy_pass the data, offcourse, put your website url at the place of my blog's url. (Request : Question this only after you tried it)

location /redirect {
  rewrite ^/redirect/(.*) /$1;
  proxy_pass http://www.techromance.com;
}

And see the magic.

And it definitely works, atleast for me.

Plonk answered 10/2, 2016 at 18:10 Comment(2)
Where to add this?Mechanotherapy
Where to add above code? Put them in main Nginx config file.Plonk
G
1

Start from index.html remove all # from <a href="#/aboutus">About Us</a> so it must look like <a href="/aboutus">About Us</a>.Now in head tag of index.html write <base href="/"> just after last meta tag.

Now in your routing js inject $locationProvider and write $locatonProvider.html5Mode(true); Something Like This:-

app.config(function ($routeProvider, $locationProvider) {
    $routeProvider
        .when("/home", {
            templateUrl: "Templates/home.html",
            controller: "homeController"
        })
            .when("/aboutus",{templateUrl:"Templates/aboutus.html"})
            .when("/courses", {
                templateUrl: "Templates/courses.html",
                controller: "coursesController"
            })
            .when("/students", {
                templateUrl: "Templates/students.html",
                controller: "studentsController"
            })
        $locationProvider.html5Mode(true);
    });

For more Details watch this video https://www.youtube.com/watch?v=XsRugDQaGOo

Grater answered 25/2, 2018 at 6:14 Comment(1)
Removing all # Url helped because i was testing it without removing #urls.Underfeed
A
1

Guess this is reallllly late for this. But adding the below config to the app.module imports does the job:

RouterModule.forRoot(routes, { useHash: false })
Apulia answered 28/12, 2018 at 11:14 Comment(1)
Question is asked for AnguarJS!Picture
S
0

Step 1: Inject the $locationProvider service into the app config's constructor

Step 2: Add code line $locationProvider.html5Mode(true) to the app config's constructor.

Step 3: in the container (landing, master, or layout) page, add html tag such as <base href="/"> inside the tag.

Step 4: remove all '#" for routing config from all anchor tags. For examples, href="#home" becomes href="home"; href="#about" becomes herf="about"; href="#contact" becomes href="contact"

 <ul class="nav navbar-nav">
     <li><a href="home">Home</a></li>
     <li><a href="about">About us</a></li>
     <li><a href="contact">Contact us</a></li>
</ul>
Scruple answered 17/8, 2016 at 20:23 Comment(0)
V
-1

Just add $locationProvider In

.config(function ($routeProvider,$locationProvider)

and then add $locationProvider.hashPrefix(''); after

.otherwise({
        redirectTo: '/'
      });

That's it.

Val answered 21/4, 2017 at 6:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.