4

OK, I know this is an open ended question, but -

I'm running an application using AngularJS 1.4.x and ui-router. For the most part everything works fine and as expected. My various pages use ui-sref's to navigate, pages show up as expected, and the URL that's showing looks correct. However, if I refresh the page (F5) on any page at all, it causes a 404 error to occur. And typing in the URL no longer works.

Things that might be involved:

  • I turned on the $locationProvider.html5Mode(true) flag
  • I use

    $rootScope.$on('$stateChangeStart', function (event, toState, toParams)

    to manage page access (authentication)

  • <base href="/"> is in my index.html because html5Mode() won't work without it
Todd Davis
  • 5,855
  • 10
  • 53
  • 89

3 Answers3

7

Q: When you refresh the page, what is actually happening?

A: You're sending an HTTP request via your browser to a server. GET /whatever. So the server will need to have a route set up for this request that responds with index.html. Currently, you're getting a 404 because your server doesn't have a route that matches the request to GET /whatever.

Q: Why?

A: When you make a request via your browser (rather than through AJAX), your browser will try to display the response to you. Ex. if the response is JSON, it'll show you JSON. If it's a string, it'll show you a string. If it's an html page, it'll show you that. You can't just send back a template for whatever route, because if you do, it'll just render that template with no other context. You need index.html to load angular, run your controller, place the template in it's ui-view container, etc.

(What'll happen is index.html will be sent back, UI Router will check the route, make a request for the templateUrl, run the controller, and then render it.)

Q: How?

A: That depends on a) what type of server you're using and b) your file structure. You'll probably want some sort of catch all route at the end of your route file that serves index.html. This is how I do it with Node/Express:

app.get('/*', function(req, res) {
  var url = path.resolve(__dirname + '/../client/index.html');
  res.sendFile(url, null, function(err) {
    if (err) res.status(500).send(err);
    else res.status(200).end();
  });
});
Adam Zerner
  • 17,797
  • 15
  • 90
  • 156
  • What you are saying makes sense. Before adding the html5Mode() flag, my URL would look like this (http://localhost:50086/index.html#/dashboard) whereas once I added the flag, it looks like this (http://localhost:50086/dashboard). The latter is what I want, but for some reason, since Index.html is no longer part of the URL, the VS IIS server is not savvy enough to understand that index.html is my default document and to just run from there. It looks for a literal path called dashboard, and lacking an index.html page there, just 404's. – Todd Davis Aug 05 '15 at 18:13
  • Sorry to hear that. It may be a good idea to separate this into two questions: 1) Why doesn't UI router work on refresh (this one) and 2) How do I add a catch all route to a VS IIS server that sends back `index.html`. I don't know the answer to question 2, and given that you used angular tags for this question, I don't think you'll find it here. It shouldn't be difficult to add the catch all route though. – Adam Zerner Aug 05 '15 at 18:18
2

@Adam Zerner described it quite nicely. I also managed to find these nice posts:

AngularJS HTML5 mode reloading the page gives wrong GET request

Page reload fails when using Angular Ui Router with Html5 mode enabled

So, given the problem lies in you server's routing config, you could obviously:

  1. check your .htaccess setup
  2. set some catch all route config in your server's routing configuration

or simply:

  1. serve your templates (through some simple generic controller function)

This might sound like it's going against the dynamic routing you seek, but since no other option offers a better solution to this problem, this just might be the simple and most elegant one.

Think about it, in any of the other solutions offered, you still need to pass through the server, be it a routing rule or simply serving your request - the cost stays roughly the same, even if there is a difference - it should be negligible.

Community
  • 1
  • 1
TrampGuy
  • 1,737
  • 1
  • 13
  • 9
0

This will be due to your server routes not being set up correctly and Angular is not being loaded in time to handle the routing. You need to make sure you have a catch-all route set up on your server that sends your index page for all non-api url calls so that Angular will then have chance to load and manage the routing.

You haven't specified your server language but in node/express it would be something like:

app.use('/*', express.static('./path/to/index.html'));
Matt Herbstritt
  • 4,754
  • 4
  • 25
  • 31
  • I'm not sure I understand what you are saying. I'm creating the website in Visual Studio, although I'm otherwise not using any Microsoft technology in the website, I'm just using it as my editor. So all the pages are pure HTML5 and Angular, and the server is IIS Express or whatever MS calls it these days. It's configured to run index.html by default as far as I know. – Todd Davis Aug 04 '15 at 22:42
  • I've no experience with that set-up so I'm not sure how to advise you. All I know is that I have had the problem you have described and it has always been due to the server taking over the routing before Angular's ui-router has had chance to take over, i.e Angular is not loaded into the browser in time. Sorry I can't be of more help. – Matt Herbstritt Aug 04 '15 at 23:05