1

I have a Django web application serving an Angular JS client application.

My URL tree looks like this:

 > /
     > /admin/
     > /login/
     > /logout/
     > /static/
         > /admin/

My Angular application's base URL is /admin/, and its static content lives in /static/admin/(js|css|views).

Here's the configuration for the route provider:

app.config(['$routeProvider', function ($routeProvider) {
    $routeProvider.when('/admin/', {
        controller: 'IndexController',
        templateUrl: '/static/admin/views/index.html'
    }).otherwise({ redirectTo: '/admin/' });
});

app.config(['$locationProvider', function($locationProvider) {
    $locationProvider.html5Mode(true);
});

I have a few problems here. The first problem is URL flexibility. What if I decide to move my base URL to something like /angularadmin/? I'll have to rewrite a lot of JavaScript and a lot of <a> links. The second problem is that when I provide a link to /logout/, this link hits the otherwise clause and redirects back to /admin/.

How do I tell Angular to pass through links to /logout/ and how can I make configuration of the base URL here much more flexible?

Naftuli Kay
  • 87,710
  • 93
  • 269
  • 411
  • You might want to look at UI-Router which is more feature rich when compared to ng-route https://github.com/angular-ui/ui-router – Hattan Shobokshi Dec 26 '13 at 19:27
  • 2
    Base URL should be handled by HTML tag. All links should be relative. Angular will pass through `/logout` if you add `target="_self"` to the `a` tag. – Stewie Dec 26 '13 at 21:16

2 Answers2

1

You should always have your base path in configuration and then use that when you need to specify the URL.

Here is an example:

// Define the configuration
app.constant('config', {
    serviceRoot: '/api/',
    staticRoot: '/static/admin/',
    appRoot: '/admin/'
});

//Use the configuration
app.config(['$routeProvider', 'config', function ($routeProvider, config) {
    $routeProvider
        .when(config.appRoot, {
            controller: 'IndexController',
            templateUrl: config.staticRoot + 'views/index.html'
        })
        .otherwise({ redirectTo: config.appRoot });
}]);

UPDATE:

As stated in the comments in order to open an external link use target="_self".

If you need the configuration values in the views/templates you can inject the configuration object in the $rootScope and access it from the views:

// We add the configuration as part of the root scope
app.run(['$rootScope', 'config', function($rootScope, config) {
    $rootScope.appConfig = config;
}]);

Then you can use it in the view like this:

<a ng-href="{{appConfig.appRoot}}someroute/">...</a>
shizik
  • 910
  • 6
  • 16
  • Since he mentioned that the ngApp lives inside /admin/ and /logout/ is at the same level I think we have to assume that is an external link to the SPA. On the answers of the question look at the one by Noah Freitas http://stackoverflow.com/questions/11580004/angular-js-link-behaviour-disable-deep-linking-for-specific-urls for a way to implement it. – masimplo Dec 26 '13 at 20:12
  • With the `/logout/` page, it's an external page from the Angular JS application. Angular clobbers the URL in the route provider and doesn't allow me to leave the application. – Naftuli Kay Dec 26 '13 at 20:30
  • How do I reference these app level constants in my templates? – Naftuli Kay Dec 26 '13 at 20:30
1

Step 1. update your base tag in your angular application progmatically so that if you move your angular application, you don't have to change your code.

in index.html - <script src='setbase.js' />

var baseTag = document.createElement('base');
var base = document.location.href;

//console.log('Detecting Document Base: ',base);

// remove query parameters and anchors
base = base.replace(/\#.*/,'').replace(/\?.*/,'');
//console.log('    Removing Anchors and Query Parameters: ',base);

// remove documents, leaving only path
base = base.replace(/\/?[^\/]]*$/,'/');
//console.log('    Removing Documents: ',base);

baseTag.href = base;
document.head.appendChild(baseTag);

document.scripts[document.scripts.length-1].remove();

step 2: setup angular to use hash tags in html5 mode so that it rewrites anchor links. Include this module and then in your own module configire this module as a dependency.

(function(window,angular)
{
    'use strict';

    var module;

    module = angular.module('aLinkRewrite',[]);
    module.config
    (
        [
            '$provide','$locationProvider',
            function (p,lp)
            {                                       // (decorator is undocumented... *sigh*)
                p.decorator(                        // decorate the
                    '$sniffer',                     // sniffer service
                    [
                        '$delegate',                // obtain a delegate object to modify the sniffer
                        function(d)                 // which we will call 'd'
                        {
                            d.history = false;      // tell angular that we don't have html5 history capabilities
                            return d;
                        }
                    ]
                );

                lp.html5Mode(true);                 // turn on html5 mode link rewriting
            }
        ]
    ).run(['$location',function($location){}]);     // inject $location into the browser
})(window,window.angular);

From now on, all of your routes and links in angular do not include your base. If angular is located in '/admin/index.html', then your route for '/admin/' is now just '/'

All of your angular links in html can be referenced as <a href="whatever">, and they will directed to admin/whatever because of the base tag, and show up in the browser bar as admin/#/whatever

because your static content is outside of your base, you continue to link to it as /static/whatever. This is also true with your login and logout links.

Noishe
  • 1,411
  • 11
  • 14