Is there a way in AngularJS to define constants with other constants?
Asked Answered
P

6

77

I'm trying to define constants with other constants, but it seems that it can't be done, because the initial constant isn't ready when the required constant depending require it. I want to be sure if this isn't possible at all.

Currently I have constants in this way:

angular.module('mainApp.config', [])
    .constant('RESOURCE_USERS_DOMAIN', 'http://127.0.0.1:8008')
    .constant('RESOURCE_USERS_API', 'http://127.0.0.1:8008/users')
    // Specific routes for API
    .constant('API_BASIC_INFORMATION', RESOURCE_USERS_API + '/api/info')
    .constant('API_SOCIAL_NETWORKS', RESOURCE_USERS_API + '/api/social')
    ;

The second two constants are what I want to accomplish.

Perspire answered 28/8, 2013 at 17:5 Comment(0)
B
45

The AngularJS way to define dependencies between Controllers, Services and others is by dependency injection (DI). So if you have a controller A that depends on a service B you would have to create it like this:

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

myApp.controller("aCtrl", function(serviceB){
    // Controller functionally here
});

See, AngularJS will check the serviceB dependency and look for the service you created with that name. If you don't create one you will get an error.

So, if you want to create a constant A that depends on constant B, you would need to tell angular that A depends on B. But a constant can't have a dependency. A constant can return a function, but the DI won't work for the constant. Check this Fiddle so you can see for which methods DI work for.

So answering your question, you can't define a constant with other constants.

But you can do this:

angular.module('projectApp', [])
  .constant('domain', 'http://somedomain.com')
  .constant('api', '/some/api/info')
  .service('urls', function(domain, api) {this.apiUrl = domain + api;})

  .controller('mainCtrl',function($scope,urls) {

      $scope.url = urls.apiUrl;

  });

Check this fiddle to see it working:

If you want to understand more about DI, check out this post.

Basic answered 8/5, 2014 at 14:18 Comment(0)
T
148

An easy way to do this is like this:

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

myApp.constant('RESOURCES', (function() {
  // Define your variable
  var resource = 'http://127.0.0.1:8008';
  // Use the variable in your constants
  return {
    USERS_DOMAIN: resource,
    USERS_API: resource + '/users',
    BASIC_INFO: resource + '/api/info'
  }
})());

And use the constants like this:

myApp.controller("ExampleCtrl", function(RESOURCES){
  $scope.domain = RESOURCES.USERS_DOMAIN;
});

Credits: link

Tower answered 24/10, 2014 at 13:49 Comment(6)
@Helzgate That's not what @gabn88 was saying. The function being passed to myApp.constant is an Immediately-Invoked Function Expression, or IIFE. It calls itself so you don't have to! It looks like this: (function (params) {//do stuff})(). Note the parentheses around the function expression and the extra set at the end.Lautrec
@Helzgate The constant with the name RESOURCES will not be a function expression, therefore, but the object returned by that function.Lautrec
Best answer available.Chaco
Shouldn't there be a ; semicolon after the returned object, eg: return { ..... }; I know that technically you don't need a semi-colon though based on javascript's auto semicolon insertion, like this link - But what is best practice?Floatstone
For other's clarity, if you end up having to do RESOURCES().USERS_DOMAIN then read the comments above about Immediately-Invoked Function Expression (IIFE). I assume this was what Helzgate's comment was about which has been deleted?Floatstone
if I am using these constants in config then getting error : [$injector:modulerr] Failed to instantiate moduleManes
B
45

The AngularJS way to define dependencies between Controllers, Services and others is by dependency injection (DI). So if you have a controller A that depends on a service B you would have to create it like this:

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

myApp.controller("aCtrl", function(serviceB){
    // Controller functionally here
});

See, AngularJS will check the serviceB dependency and look for the service you created with that name. If you don't create one you will get an error.

So, if you want to create a constant A that depends on constant B, you would need to tell angular that A depends on B. But a constant can't have a dependency. A constant can return a function, but the DI won't work for the constant. Check this Fiddle so you can see for which methods DI work for.

So answering your question, you can't define a constant with other constants.

But you can do this:

angular.module('projectApp', [])
  .constant('domain', 'http://somedomain.com')
  .constant('api', '/some/api/info')
  .service('urls', function(domain, api) {this.apiUrl = domain + api;})

  .controller('mainCtrl',function($scope,urls) {

      $scope.url = urls.apiUrl;

  });

Check this fiddle to see it working:

If you want to understand more about DI, check out this post.

Basic answered 8/5, 2014 at 14:18 Comment(0)
H
13

I do that this way:

var constants = angular.module('constants', []);

constants.factory("Independent", [function() {
   return {
      C1: 42
   }
}]);

constants.factory('Constants', ["Independent", function(I) {
   return {
      ANSWER_TO_LIFE: I.C1
   }
}]);
Hygienic answered 28/8, 2013 at 17:53 Comment(2)
@Beterraba. Could you elaborate on this? Where would this code go? Thanks. MarkPetronilapetronilla
@Petronilapetronilla This code can go wherever you want. I like to put this on my services file. You must remember to include the constants module on your main module.Hygienic
F
8

As long as you don't need access to your constant in providers, this should work fine:

.constant('HOST', 'localhost')
.factory('URL', function(HOST) { return "http://" + HOST })

If you need access to you constants in providers, then I guess you have to do some more work:

.constants('HOST', 'localhost')
.provider('DOMAIN', function(HOST) {
    var domain = "http://" + HOST;
    this.value = function() { return domain };
    this.$get = this.value;
 })
 .provider("anyOtherProvider", function(DOMAINPovider) {
     var domain = DOMAINProvider.value();
 };
 .factory("anyOtherService", function(DOMAIN) {
 })
Fairy answered 29/5, 2015 at 8:48 Comment(0)
D
4

I can't tell for sure if that's (im)possible. But a workaround would be to define the base constants as regular constants, and the higher-order ones as services using closures to make sure they cannot be altered.

Rough example:

angular.module('myApp').constant('BASE_CONSTS',{
    'FIRST_CONST': '10',
    'SECOND_CONST': '20'
});

angular.module('myServices').factory('MyServiceName', ['BASE_CONSTS', function ('BASE_CONSTS') {
    var SECOND_ORDER_CONST = BASE_CONSTS.FIRST_CONST * 100;
    return {
        GET_SECOND_ORDER_CONST: function() {
            return SECOND_ORDER_CONST;
        }
    }
}]);

And use it after injecting the service:

MyServiceName.GET_SECOND_ORDER_CONST();

It is not very elegant, but it should get the job done.

Drandell answered 28/8, 2013 at 17:47 Comment(0)
J
3

The solution provided by @Linkmichiel is good, but if you desperately want to use one constant inside another, you can combine them in the config block:

angular.module("exampleApp", [])

.constant('BASE_URL', 'http://127.0.0.1:8008')

.constant('RESOURCES', {
  USERS_DOMAIN: '',
  USERS_API: '/users',
  BASIC_INFO: '/api/info'
})

.config(function(BASE_URL, RESOURCES) {
  for (prop in RESOURCES) {
    RESOURCES[prop] = BASE_URL + RESOURCES[prop];
  }
})

.controller('WhatIsInResourcesController', function($scope, RESOURCES) {
  $scope.RESOURCES = RESOURCES;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="exampleApp">
  <div ng-controller="WhatIsInResourcesController">
    <pre>{{ RESOURCES | json }}</pre>
  </div>
</div>

After the config phase, all constants will be setup correctly (try out the snippet).

The moral of the story is: Angular is so cool that you can even change the constants.

Jimenez answered 12/7, 2016 at 14:13 Comment(1)
As complement. You need to declara for (prop in RESOURCES) { using var or let, like for (let prop in RESOURCES) {.Illhumored

© 2022 - 2024 — McMap. All rights reserved.