Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent Third Party Factory Interfering with My App's Factory with the Same Name

I am using angular-bootstrap-colorpicker in my app and am having a weird issue. The colorpicker module has a factory named Slider. That's causing the colorpicker not to work because my app also has a factory called Slider. Refactoring every occurrence of this in the app isn't possible, and it seems like a sloppy workaround anyway. The error being thrown is

Uncaught TypeError: Slider.setSaturation is not a function

which I've concluded is because my app's factory has no method setSaturation and Angular is "confused". I don't really know enough about factories and how Angular organizes them, but it seems very odd that they would be available across modules like that. eg

angular.module('thomasApp', [])
...
.factory('Slider', ...

is affected by

angular.module('colorpicker.module', [])
...
.factory('Slider', ...

or vice versa.

Is there someway I can compartmentalize this colorpicker so that it does not interfere with my Slider factory?

Edit:

I agree with the linked answer that using a prefix as a namespace is a smart idea. However that would require an unrealistic amount of refactoring. I appreciate the answer below but it isn't fleshed out enough for me to be able to put into action.

1) Is this really the best possible solution (apart from prefixing from the project's beginning)? - If I make a change like this, will it be erased the next time I do a bower update, or someone pulls down my project and does a bower install?

2) Is there a better way? - If not, can the current answer be expanded and have explanations of what's happening added?

like image 674
1252748 Avatar asked Oct 14 '15 15:10

1252748


1 Answers

The problem that you have is general already known issue in Angular. @fracz was right, it's connected with Modules and namespace / name collision in AngularJS. The issue is that Angular has only one $injector instance per module instantiatation and all defined injectable objects go into it. By injectable objects I mean constants, values, services, controllers, directives.. This is bad in cases as this one because even if we modularize our application by defining multiple modules and dependencies between them at the end all the defined injectable objects end up in a same context/namespace/injector.

Angular makes this even worse by not using fail-fast technique in such cases and because the application continues working you may end up noticing the issue late which often can lead to expensive refactorings. And there is still a question how we can improve this, IMO failing-fast at least will help in avoiding this issue at it's beginning.

However in your case you are lucky that the library is really small and what you need is only the color picker directive. I have made a workaround example here by defining the color picker directive in your module while taking it's definition from the instantiated library module $injector. Like this you are free to change even it's name :).

Code example:

// NOTE: redefine the directive
app.directive('colorpicker', function () {
  var injector = angular.injector(['ng', 'colorpicker.module']);
  return injector.get('colorpickerDirective')[0];
});

For clarification purposes there is also an example that shows how Angular behaves when you define two service with the same name. The application successfully continues working with the last service definition.

I hope that this answer makes the things clear and successfully solves your problem. If you have more questions feel free to ask. Cheers!

like image 114
S.Klechkovski Avatar answered Oct 20 '22 02:10

S.Klechkovski