Using a Relative Path for a Service Call in AngularJS

JavascriptAngularjs

Javascript Problem Overview


I have the following code, which was working fine until I deployed to a test server:

$scope.getUserList = function (userName) {
    $http({
        method: "get",
        url: "GetUserList",
        params: { userName: userName }
    }).
        success(function (data) {
            $scope.users = data;
        }).
        error(function () {
            alert("Error getting users.");
          

The problem is that I deployed to a virtual directory, and the call below is attempting to hit GetUserList from the server root. This makes sense, and I know a number of ways to fix it.

What I would like to know is the right way to reference the service URL in a way that is portable and maintainable in Angular.

Javascript Solutions


Solution 1 - Javascript

I'd suggest using an HTML base tag in the head, and coding all paths relative to this. In ASP.NET, for example, you can get a reference to the base of the application, which may or may not be the root path of the site, so using a base tag helps. Bonus: it works for every other asset too.

You can have a base path like this:

<base href="/application_root/" />

...and then links like "foo/bar.html" will actually be /application_root/foo/bar.html.

Another approach I like to use is to put named links in the header. I will often have an API root in one location and a directive template root somewhere else. In the head, I'll then add some tags like this:

<link id="linkApiRoot" href="/application_root/api/"/>
<link id="linkTemplateRoot" href="/application_root/Content/Templates/"/>

... and then use $provide in the module to get the link href and expose it to services and directives like so:

angular.module("app.services", [])
	.config(["$provide", function ($provide) {
		$provide.value("apiRoot", $("#linkApiRoot").attr("href"));
	}]);

... and then inject it to a service like this:

angular.module("app.services").factory("myAdminSvc", ["apiRoot", function (apiRoot) {
    var apiAdminRoot = apiRoot + "admin/";
    ...

Just my opinion though. Do the least complex thing for your application.

Solution 2 - Javascript

I would suggest defining a module that contains a global config that you can then pass around your application:

// Module specific configuration
angular.module('app.config')
  .value('app.config', {
    basePath: '/' // Set your base path here
  });

Then you can access this from anywhere within your application thanks to AngularJS dependency injection:

// Make sure your config is included in your module
angular.module('app', ['app.config']);

// Access your config e.g. in a controller
angular.module('app')
  .controller('TestCtrl', ['$scope','app.config', function($scope, config){

    // Use config base path to assemble url
    $scope.url = config.basePath + 'GetUserList';
    ...
  }]);

Whenever the base path changes (e.g. when you change to another server or host), you just need to change it in your global config and you're done.

Solution 3 - Javascript

I wasn't able to use <base> tag since my application was created in a popup dynamically from another origion. I was considering the others option in this thread to use something like a basePath variable and use it in every $http, ng-src, templateUrl etc

But that was a bit overhead, built a interceptor that change every url before a xhr is made

var app = angular.module("myApp", []);

app.config(["$httpProvider", function($httpProvider) {
    $httpProvider.interceptors.push('middleware');
}]);

app.factory('middleware', function() {
    return {
        request: function(config) {
            // need more controlling when there is more than 1 domain involved
            config.url = "//example.com/api/" + config.url
            return config;
        }
    };
});

app.controller("Ctrl", ["$http", function($http) {
    $http.get("books"); // actually requestUrl = http://example.com/api/books
}])

And html aswell

<div ng-include src="'view'">
    <!-- actually src = http://example.com/api/view -->
</div>

But i do recommend to use <base> tag instead unless you are using window.popup()

Solution 4 - Javascript

Use the $location service - it will return your path, the hash, the server address.. Everything you need! Your call would be to $location.path()+"/GetUserList" or something similar.

See here: http://docs.angularjs.org/guide/dev_guide.services.$location

Solution 5 - Javascript

I had a similar problem I solve it with the just one line of code in my MVC page

<base href="~/" />

Solution 6 - Javascript

The accepted answer helped me. I'm using Angular served up my an MVC app. I took one extra step so that my baseUrl could be used within my angular controllers for web api calls or for accessing templates.

Using ng-init to set the baseUrl (from a server side populated value)

<html ng-app="app" ng-controller="AppController">
<head>
    <base href="{{baseUrl}}" ng-init="baseUrl = '@Model.BaseUrl'" />
</head>

Angular controller

    $scope.manageCustomerGroups = function () {
        openDialog($scope.baseUrl + 'content/templates/customerGroups.html');
    }

Solution 7 - Javascript

A simple way, I'm using ASP.NET Razor (Web MVC), so I get the Application path and make it as the base of app.

<head>

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Title</title>

<meta name="description" content="">
@{ var appPath = Request.ApplicationPath.ToString();
    if (!appPath.EndsWith("/")) {
        appPath = appPath + "/";
    }
}
<base href="@appPath" />

Solution 8 - Javascript

I would just make all URL relative.

url: "../GetUserList",

instead

url: "GetUserList",

Hard coding for the, <base href="/application_root/" /> dose not sound good idea to me as you might need to change this environment to environment, and has dependency over virtual directory name.

Solution 9 - Javascript

In the ng-init function pass a parameter that contains the value the virtual directory (int ASP.NET stored in Request.ApplicationPath).

<div ng-controller="ControllerName" ng-init="init('@Request.ApplicationPath')">

Inside the angular controller, use this value as prefix of URL in every http call. You can use this function to combine the paths

function combinePath(path1, path2) {
    if (path1 == null) {
        return path2;
    }
    var last = path1.slice(-1);
    var first = path2.charAt(0);

    if (last == '/' && first == '/') {
        path1 = path1.substring(0, path1.length - 1);
    }
    return path1 + path2;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPhil SandlerView Question on Stackoverflow
Solution 1 - JavascriptBarnabas KendallView Answer on Stackoverflow
Solution 2 - JavascriptjvandemoView Answer on Stackoverflow
Solution 3 - JavascriptEndlessView Answer on Stackoverflow
Solution 4 - JavascriptTiago RoldãoView Answer on Stackoverflow
Solution 5 - JavascriptJosé GomesView Answer on Stackoverflow
Solution 6 - JavascriptWhat-About-BobView Answer on Stackoverflow
Solution 7 - JavascriptMi.HTRView Answer on Stackoverflow
Solution 8 - JavascriptSubodh WasankarView Answer on Stackoverflow
Solution 9 - JavascriptMartin StaufcikView Answer on Stackoverflow