15

We have AngularJS embedded into our Django application, with URL routing handled by AngularJS ui-router. All is working fine navigating between partials using ui-sref and clicking around within the application.

return $stateProvider.state('root.dashboard', {
        abstract: true,
        url: 'dashboard/'
      }).state('root.dashboard.profile', {
        url: 'profile/',
        views: {
          '@': {
            templateUrl: Urls['dashboard:profile'](),
            controller: 'ProfileController'
          }
        }
      }).state('root.dashboard.home', {
        url: '',
        views: {
          '@': {
            templateUrl: Urls['dashboard:dashboard_home'](),
            controller: 'DashboardController'
          }
        }
...

The problem is when the user has navigated to a non-root page (say for example http://example.com/dashboard/profile/), and the user refreshes the browser, re-loads the browser's URL or simply pastes in the non-root URL directly into the browser. Instead of loading the page retaining the same URL in the browser, the user is getting redirected to the root page (http://example.com/dashboard/) in this case.

Since routing is handled by Angular, on the server side we don't have any url routes defined for those non-root URLs; instead we have middleware that redirects 404s to the root page:

class Redirect404(object):
    def process_response(self, request, response):
        if response.status_code != 404 or request.method != 'GET':
            return response
        return HttpResponsePermanentRedirect('/dashboard')

We expect that the router would be able to maintain the original URL and bring the user back to the original page (i.e. 'dashboard/profile'). Note we have set HTML5Mode in Angular as follows:

$locationProvider.html5Mode = true;

There is some mistake in our understanding and/or setup, and would appreciate clarification.

1 Answer 1

9
+50

We expect that the router would be able to maintain the original URL and bring the user back to the original page.

That is the misunderstanding.

Here is the sequence of events:

  1. The user types http://example.com/dashboard/profile/ into the location bar.
  2. The browser sends a GET request to the server for that URL.
  3. Your server responds with a 301 redirect response.
  4. The browser sees that response and sends a new GET request to http://example.com/dashboard/.
  5. The server responds with your Angular page.
  6. The Angular application starts up and looks at window.href to see what the current route is. It sees the root route and responds appropriately.

In other words, when you redirect you lose the original URL.

The solution is simple: instead of redirecting, simply return your page in response to any (valid) URL. That way the requested URL is maintained, and when Angular starts up it will be able to figure out the right route. (This assumes that routing is set up properly in Angular, but it sounds like you have that working.)

The implementation is also simple. Just change your Django urls.py from something like this:

urlpatterns = [
    url(r'^dashboard/$', my_view),
]

to something like this:

urlpatterns = [
    url(r'^dashboard/.*$', my_view),
]
Sign up to request clarification or add additional context in comments.

5 Comments

Great. What if I want to allow only valid URLs and not all, without hard-coding every valid URL?
@Neil: You mean that you want Django to only return the page for valid URLs exposed by the Angular application, without having to type them all in urls.py? Good question. I don't know of an easy way to do that, though. A quick Google search turns up some apps for integrating Django urls and Angular routes, but they seem geared towards the REST backend, not this. You could right your own code to process the Angular files and construct urls.py programmatically, but that seems like overkill.
@Neil: Consider letting your Angular app deal with bad URLs. It should already have some mechanism for dealing with errors, including URL errors. (For example, /user/42/ might be a syntactically valid URL, but if that user doesn't exist your Angular app will have to have some way of handling the error.) So it might be easier just to do all the error handling in one place instead of getting the server to generate 404s.
makes sense. There is the otherwise rule in ui-router which will work well.
Thank you very much. I loved this solution. Congrats!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.