I'd like to be able to define Angular constants in terms of other constants.
It's not possible to inject dependencies into Angular constant
services. When I want one of my constants to depend on another of my constants, I use a provider
service. This allows my constants to have dependencies (on other constants), but still be used in my .config
method call.
This sets off my code smell for two reasons.
I'm using a full-blown call to
provider
when I'm really just defining a constant. It's just that some constants are defined in terms of other constants, and I want to be able to use any of these constants in my.config
call.I'm injecting providers into my
.config
call and then manually calling their$get
methods. This seems to just be bypassing some of the tools Angular gives me. It seems to me if I'm calling$get
in my own code, it may be a sign that I'm fighting the framework.
Here's some sample code that shows the above in action. It defines a constant service that's an enumeration type, then it defines another constant service that depends on the enumeration type, and last it calls the .config
method, injecting the dependent constant service:
app.constant('authLevel', {
Public: "Public",
Admin: "Admin",
SuperAdmin: "SuperAdmin"
});
app.provider("routes", function (authLevel) {
var routes = [
{
path: "/",
controller: "mapsController",
templateUrl: "templates/mapsView.html",
navBarLabel: "LEFT_NAV_BAR.MAPS_NAV_LABEL",
navBarIconClass: "fa-map-marker",
authLevels: [authLevel.Public, authLevel.Admin, authLevel.SuperAdmin],
},
{
path: "/markers",
controller: "markersController",
templateUrl: "templates/markerListView.html",
navBarLabel: "LEFT_NAV_BAR.TABLE_NAV_LABEL",
navBarIconClass: "fa-table",
authLevels: [authLevel.SuperAdmin],
}
];
this.$get = function () {
return routes;
};
});
app.config(function ($routeProvider, routesProvider) {
var routes = routesProvider.$get();
for (var i = 0; i < routes.length; i++) {
var route = routes[i];
$routeProvider.when(route.path, {
controller: route.controller,
templateUrl: route.templateUrl
});
}
$routeProvider.otherwise({ redirectTo: "/" });
});
Am I right to have my code smell go off here?
Are there any sort of established patterns for defining Angular constants that use previously defined constants, and can still be used in the .config
method?
1 Answer 1
Using an IIFE seems to be the tidiest way I've found to do it. This allows you to define your constant as a function (that is immediately invoked) that returns your constant object so you can use it as normal, i.e: LABELS.appName
.constant('LABELS', (function () {
var self = {};
self.appName = 'MyApp';
self.companyName = 'MyCompany';
self.appHeader = self.appName + ' | ' + self.companyName;
return self;
})());//function is immediately invoked here
References:
-
1\$\begingroup\$ Welcome to Code Review! As far as I can tell, this is a pretty good first answer (it's even got a bibiography!) \$\endgroup\$anon– anon2015年12月24日 15:34:33 +00:00Commented Dec 24, 2015 at 15:34
-
\$\begingroup\$ IIFEs do let you control scope as you build complex objects. However, the constant I'm defining depends on a previously defined constant; I would still need a way to pass 'authLevel' into the anonymous function, something more like this:
.constant('routes', (function (authLevel) {...})(?));
One might think I could sayapp.factory('routes', ['authLevel', function (authLevel) { ... }])
, but dependencies defined using thefactory
method can't be used insideconfig
. \$\endgroup\$jcarpenter2– jcarpenter22015年12月25日 17:52:30 +00:00Commented Dec 25, 2015 at 17:52