AngularJS - module dependencies, naming clash
Asked Answered
E

2

35

I have two third-party modules, both defining a factory with the same name. Obviously, I don't have any control over the naming of those modules without resorting to a kludge.

Additionally, I have two further, internal modules, each using a different one of the two third-party modules as a dependency (as below). I was sure that I was unable to access components from a module not listed in current module's dependencies, but it turned out I was wrong.

Here even if own1 depends on thirdParty1 (which has hello defined as hello world) it's getting hi there (from thirdParty2) in controller. The same is for the other modules' pair.

Is there any way to "isolate" modules so I can only use stuff that I explicitly depend on? If not, what's the point of having modules if I can reach anything at any time (assuming main app module has it as its dependency)? Also if I have two modules with components named hello how can I tell which is gonna be used?

Here is jsbin for that http://jsbin.com/vapuye/3/edit?html,js,output

angular.module('app', ['own1', 'own2']);

//third-party modules
angular.module('thirdParty1', []).factory('hello', function () {
  return 'hello world';
});

angular.module('thirdParty2', []).factory('hello', function () {
  return 'hi there';
});

// "own" modules
angular.module('own1', ['thirdParty1']).controller('Own1Ctrl', function(hello) {
  this.greet = hello;
});

angular.module('own2', ['thirdParty2']).controller('Own2Ctrl', function(hello) {
  this.greet = hello;
});

And the result of:

<body ng-app="app">
  <div ng-controller="Own1Ctrl as own1">
    Own1: {{ own1.greet }}
  </div>
  <div ng-controller="Own2Ctrl as own2">
    Own2: {{ own2.greet }}
  </div>
</body>

Is :

Own1: hi there
Own2: hi there
Earreach answered 21/5, 2015 at 13:18 Comment(6)
Do you actually have two libraries that define services with the same name? Modules are used to encapsulate specific behaviors and help with testing your applications.Ivon
Modules serve more structural and organisational purpose rather then isolation. At the time of bootstrapping all modules are packed into one global module so they can access each other.Reeher
If the two libraries you're trying to use have an internal structure, often you can just include the part you're using. For example, if you want parts of both AngularUI-Bootstrap and AngularStrap you can pick and choose the modules you include. (AngularStrap has mgcrea.ngStrap.modal, AngularUI-Bootstrap has ui.bootstrap.modal). By just including the pieces you need, you can make sure you're getting the right one from each.Unmannerly
@NicholasThomson this is exactly the case! I'm about to get rid of one of these but this is where I was bitten hard by this thing. But... even if I cherry-pick submodules from third-parties, if they still define the same component, I'll have naming clash anyway, do I?Earreach
I believe angular will prefer the second module. So if you're only using $modal from AngularStrap then try something like: angular.module('app', ['ui-bootstrap', 'mcrea.ngStrap.modal']);Unmannerly
@MichalOstruszka I've got a similar question along the lines of your blog post. Quite disappointed right now. https://mcmap.net/q/190221/-angularjs-module-sub-dependencies-available-to-sibling-modules/1444541Examinant
W
18

You can request a factory of a certain module explicitly (without dependency injection):

var injector = angular.injector(['thirdParty1']);
var hello1 = injector.get('hello');

var injector = angular.injector(['thirdParty2']);
var hello2 = injector.get('hello');

You can also use this, to wrap the third party factories into own factories:

angular.module('own1', ['thirdParty1']).factory('hello1', function () {
  var injector = angular.injector(['thirdParty1']);
  var hello = injector.get('hello');
  return hello;
});

angular.module('own2', ['thirdParty2']).factory('hello2', function () {
  var injector = angular.injector(['thirdParty2']);
  var hello = injector.get('hello');
  return hello;
});

This allows you to use hello1 and hello2 in all other parts of your application.

Wilonah answered 21/5, 2015 at 14:4 Comment(3)
Yeah, but this is hacky as hell :)Earreach
What is hacky about it? You just define the two wrappers once inside of your application and then you're able to use your two factories everywhere else without the need to worry about conflicting names. For me, it would be quite an elegant solution. But I guess people have different standards ;)Wilonah
Ah, I misunderatood you a bit (was in hurry). This is fine as long as you wrap things for your app to use. By hacky I meant if you had to call $injector every time you need given dep. This would be completely against DI stuff.Earreach
I
2

Since there is no built-in name spacing of modules (or components of modules) the best way to achieve your goal is to use a unique naming convention for your modules. Most libraries for angular do this, and then you should be good to go.

Besides encapsulating your applications behavior, modules help testing and mocking your application.

I dont think it is possible for angular to differentiate between two components that are named the same (I think this changes with angular 2). And I might argue that two components that are named the same might do the same and you should look why you need both?

Ivon answered 21/5, 2015 at 13:24 Comment(5)
So I don't really get what are modules and all this thing with dependencies for :) Currently it happened to me that I had two modules (third party) that both define $modal and even if only specific one is defined as a dependency I'm getting $modal from the other one.Earreach
Also using say "dot notation" for components e.g. mymodule.myservice makes it hard to use as function argument (because of the dot)Earreach
For function-arguments you should really be not using the original component-name. Else javascript minification could hit you there. Take a look into Array-Initialization (e.g. .controller(['componentID', function(comp1){ ... }]);)Ivon
Yeah, know this syntax but I prefer handling minification with ngAnnotate instead of writing tons of boilerplate and repeating the same stuff most of the times. BTW, do you name your components with "dot notation" or do you use any other convention?Earreach
I tend to use camelCase, <companyPrefix>Component.Ivon

© 2022 - 2024 — McMap. All rights reserved.