10

I have a development and production environment in which my URL's differ:

production:

www.exmaple.com/page

development:

dev.environment/project/page

I know that I can set the base URL in AngularJS with the

<base href='/project/' />

but that doesn't help me out here. Before I load my AngularJS application I fetch a config file (in app.js, with the .run statement, which reads a variable that has the environment:

]).run([
  '$rootScope',
  '$http',
  function (
    $rootScope,
    $http
  ) {
    var configDeferred = $q.defer();

    // fetch config and set the API    
    $http.get('config.json').then(function(response) {
      $rootScope.config = response.data;
      configDeferred.resolve();
    });

    // Wait for the config to be loaded, then go to state
    $rootScope.$on('$stateChangeStart', function (event, next, current) {
      event.preventDefault();
      $q.all([configDeferred.promise]).then(function() {
        $state.transitionTo(next.name);
        return;
      });
    });

Is there a way to dynamically set the base URL, based on a fetched config file in AngularJS (maybe with a .htaccess)?

Attempt 1: Try to get the config via .run and set the base url via ng-href:

Edit the following line of code in my app.js:

// fetch config and set the API    
$http.get('config.json').then(function(response) {
  $rootScope.config = response.data;
  $rootScope.baseUrl = response.data.baseUrl; // '/project/'
  configDeferred.resolve();
});

and in my index.html:

<base ng-href="{{baseUrl}}" />

It looks like this is not working: when I change the href attribute of tag to ng-href, it loads the content correctly, but changes my URL to dev.environment/page instead of dev.environment/project/page

UPDATE: The config file:

{
  "mode": "development",
  "baseUrl": "/project/"
}
10
  • 1
    really not sure what implication is or what you need help with Commented Jan 7, 2014 at 12:54
  • 1
    Stated the question differently Commented Jan 7, 2014 at 15:47
  • 1
    still not clear what the problem is with url...can parse location.href to determine existing url if that helps Commented Jan 7, 2014 at 21:26
  • 1
    According to AngularJS docs, I need to set the base url in my index.html file (in the <base> tag), which is loaded at first and then app.js is loaded. In app.js I have a $http request to a config file, which has the right environment and thus the right base URL that needs to be set. So after retrieving the config file, I need some way to set the base url Commented Jan 7, 2014 at 21:29
  • 1
    can't you have server code just set a variable in a script tag? Or parse the location.href ? Commented Jan 7, 2014 at 21:31

4 Answers 4

6
+50

I personnaly do this kind of stuff with grunt.

When I run my angular-app I have multiple tasks :

> grunt run --target=dev
> grunt run --target=prod
> grunt build --target=dev
> grunt build --target=prod
> etc...

Then grunt do strings replacement with the help of the grunt-preprocess module :

my constants.tpl.js file gets parsed :

[...]
baseUrl:           '/* @echo ENV_WS_URL */',
[...]

and the url is populated.

There are endless possibilities (string replacements, file copy, etc).

Doing it with grunt ensure that dev config files do not go in production for example..

I can put more details if you're interested but I only wanted to show you a different approach.

edit gruntFile example :

'use strict';

module.exports = function(grunt) {

    /**
     * Retrieving current target
     */
    var target = grunt.option('target') || 'dev';
    var availableTargets = [
        'dev',
        'prod'
    ];

    /**
     * Load environment-specific variables
     */
    var envConfig = grunt.file.readJSON('conf.' + target + '.json');

    /**
     * This is the configuration object Grunt uses to give each plugin its
     * instructions.
     */
    grunt.initConfig({
        env: envConfig,       

        /*****************************************/
        /* Build files to a specific env or mode */
        /*****************************************/
        preprocess: {
            options: {
                context: {
                    ENV_WS_URL: '<%= env.wsUrl %>'
                }
            },
            constants: {
                src: 'constants.tpl.js',
                dest: 'constants.js'
            }
        },

        karma: {
            unit: {
                configFile: '<%= src.karma %>',
                autoWatch: false,
                singleRun: true
            },
            watch: {
                configFile: '<%= src.karma %>',
                autoWatch: true,
                singleRun: false
            }
        }

    });


    /****************/
    /* Plugins load */
    /****************/
    grunt.loadNpmTasks('grunt-preprocess');

    /*******************/
    /* Available tasks */
    /*******************/
    grunt.registerTask('run', 'Run task, launch web server for dev part', ['preprocess:constants']);

};

Now, the command :

> grunt run --target=dev

will create a new file with an url

Sign up to request clarification or add additional context in comments.

3 Comments

Very interesting, can you give me a small example of what extra code I would need to make this work? I prefer not to work with the base tag as @Rishul Matta explained
Do you know grunt ? If not, the scope of this answer will be way too large and I suggest you to read grunt docs ;) I updated my answer to give you a better example (not tested, very simplified code)
Seems there is no answer that is integrated with AngularJS, that's why this is the most clean solution imo
3

asumming you created a service that gets configuration object from server

app.run(function($rootScope, yourService){   
    yourService.fetchConfig(function(config){
        $rootScope.baseUrl = config.baseUrl;
   })
});

markup

<html ng-app="yourApp">
<head>
   <base ng-href="{{baseUrl}}">
   ......

note

  • If your service uses $http or $resource you should be fine
  • If instead you use jQuery ajax calls you should run $rootScope.$apply() after setting variables to the scope.

4 Comments

It looks like this is not working: when I change the href attribute of <base> tag to ng-href, it loads the content correctly, but changes my URL to dev.environment/page instead of dev.environment/project/page.
I also tried to change the <base> tag to <base href="{{baseUrl}}">, but this gives me the following error: Error: A history state object with URL 'http://0.0.1.148/' cannot be created in a document with origin 'http://dev.environment'.. Maybe good to say that I also use angular-ui-router
Show me the environment variables that you try to get into base href.
This answer will not work + using the <base href="" /> can be used for the Angular framework but will not be respected by normal anchor tags
1

using base urls might not be the best thing because it might also screw up the functioning of the anchor tags and might lead to un wanted navigation. The approach that you have to read the configuration from an XML file is perfect and this is what i did:

1.Before initializing your angular app read a variable from your config file:

if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else {// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("GET", "config.xml", false);
xmlhttp.send();
xmlDoc = xmlhttp.responseXML;

let your config.xml have such a field

<?xml version="1.0" encoding="utf-8" ?>
<config>  
<url>http://dev/ur/</url>
</config>

2.now inject a constant in your app for this i was guided by Eddiec and Cd

var myApp= angular.module("App", ["ui.router"]).constant('BASE_URL',    
  xmlDoc.getElementsByTagName("url")[0].childNodes[0].nodeValue);

3.you can use BASE_URL anywhere across your app you need to inject it this is how i used it to avoid hard coded paths in ui.router.js

var routeResolver = function ($stateProvider, $urlRouterProvider,BASE_URL){
  $stateProvider
    .state('home', {
        url: "/Home",
        views: {
            "main-view": {
                templateUrl: BASE_URL+"/views/Home.htm",
                controller: "homeCtrl",
                resolve: {

                }
            }
        }
    })
}
myApp.config(routeResolver);

hope it helps cheers!

2 Comments

I agree, using the base tag is bad and screws up my anchors. But I prefer (because I work in a JS environment) a JSON file in your example and I don't like the fact that I have to run a file before initializing my Angular App (but maybe that is the only way).
hmm well what ever you do... to read the configuration you will have to run the file before your start the application and more over json is an improvement over xml i have coded for xml you can also get the code for json :)
0

I have tried to solve this problem and I really don't want to put my base path in some config file. We are working with many developers on our app and they each have different working environment. Finally I found out a solution which I am not really proud of, but it works like a charm. It requires only one thing: PHP.

So instead of writing base href with your JS in head, try this:

<?php
    $path = dirname($_SERVER['SCRIPT_NAME']);
    $uri = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['SERVER_NAME'].$path.'/';
    echo '<base href="' . $uri . '" />';
?>

If you already have PHP on your server, then this is only matter of renaming your index.html to index.php (also dont forget about .htaccess if you have one).

I know that this solution isn't good for everybody, it is not supposed to, it's just my solution of problem for those, who have PHP already on server. I hope this helps for at least some of you :)

Comments

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.