Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend or isolate a constant value in AngularJS when using modules?

To keep my code clean I'm in the habit of defining url's as constants in my AngularJs applications, looking like:

var myApp = angular.module('myApp', ['myModule']);
myApp.constant('URL', {
    google: 'http://www.google.com',
    facebook: 'http://www.facebook.com'
});

Now, that works fine but I get into problems when I also define (and use) this constant in my module:

var myModule= angular.module('myModule', []);
myModule.constant('URL', {
    twitter: 'http://www.twitter.com'
});

Because, now when I want to use the URL.twitter constant in a directive (inside my module) it is no longer available because the definition of the URL constant in 'myApp' has overriden the value. For example when I create a directive:

myModule.directive('myDirective', function(){
    return {
        template: '<div></div>',
        controller: function(URL){
            console.log(URL.twitter); // logs undefined when used within 'myApp'
        }
    };
});

See a live example: http://plnkr.co/edit/xiOGVWfJ4GupSvkTs0Ic?p=preview

Think of 'myModule' being a third-party module which I include in my project. If I accidentally use the same names for my constants, the whole thing is broken.

How to overcome this use case without having to use different names for the same purpose?

like image 926
Bas Slagter Avatar asked Dec 11 '25 19:12

Bas Slagter


2 Answers

I think the question boils down to:

A third party module (that I didn't write) defines 'URL' as a constant.

I include this module as dependency in my app, and define URL too (without knowing that URL was already defined):

var app = angular.module('myApp', ['thirdPartyModule']);
app.constant('URL', { twitter: 'www.twitter.com'});

All of a sudden, the app breaks and stops working.

How does one avoid this pitfall?

Answer:

You can use angular's injector to determine if the constant is already defined:

angular.module('myApp', ['thirdPartyModule']);
var injector = angular.injector(),
    url = injector.has('URL') ? injector.get('URL') : {};

The above code will guarantee that an object will be returned - either the URL defined in a third party module, or an empty object literal, if it is not defined.

Then, you can extend the 'URL' as follows:

angular.extend(url, { twitter: 'www.twitter.com' });

Word of Caution:

This will work if URL was previously undefined or defined in a third-party module as an object literal. This will not work if URL was previously defined as a primitive (ie. string).

Unfortunately, I can't think of a way to guard against this scenario in a clean way. I would recommend not overriding previously defined constants at all, and relying on naming convention to make your injectables unique. For example, for your constants, services, providers and factories, use a unique prefix that reduces the likelihood of naming collisions:

var app = angular.module('tgApp', ['thirdPartyModule']);
app.constant('tgURL', { ... });
like image 104
pixelbits Avatar answered Dec 14 '25 18:12

pixelbits


I Realize this is an old question but I didn't see this answer provided. I just spent a few hours banging my head against the desk on the very same issue the OP described. I have quite a few jQuery widgets I am converting to angular. These are essentially third-party modules that will be used across several sites. I solved this issue by using a provider for each module.

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

myModule.provider('myModuleProvider', function() {
    this.$get = [function() {
        return {
            twitter: 'http://www.twitter.com',
        };
    }]
})

myModule.directive('myDirective', function() {
    return {
        template: '<div></div>',
        controller: function(myModuleProvider) {
            var URL = myModuleProvider;
            console.log(URL.twitter);
        }
    }
})

My example does make full use of providers but the nice thing is they can be configurable. So if I have "options" I want to expose they can be configured via the angular.config() function on the module that myModule is being injected in to.

like image 22
jsternadel Avatar answered Dec 14 '25 19:12

jsternadel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!