How do I stop a naming clash in angularJS? - javascript

We have a service...
angular.module('app.services').service('Booking', function () {})
In our controller we do this...
angular.module('app.controllers')
.controller('PassengersController',
function (Booking) {
Booking.load();
...
Which has been fine up until now. However the application is growing and we need to create another 'Booking' object. This object should live under a different namespace to avoid naming clashes as I can't see how angular will inject the right dependency. For example we now need...
app.services --> Booking
app.states --> Booking
Ideally we would like to refactor our modules to represent the file structure which is now built by feature but trying one thing at a time does anyone know how I can tell angular to access this new booking object explicitly in my controllers?
I was thinking it could be like this....
angular.module('app.controllers')
.controller('PassengersController',
function (Booking) {
Booking.load();
var bookingState = app.states.Booking;
bookingState.goToNextState();
...
Thats ths pseudocode anyway, if it was just pure javascript easy peasy I would just make a global variable and attach some modules but I am not sure how we will do it so we can leverage angularJs's dependency injection and stop the naming clash!

Related

Define a constant that can be used in module/controller/etc. definition in angular

I would like to put module name, controller name etc. to string constants, so that I can easily change them if the need be (compared to fixing it in each and every file).
So I have this code:
angular
.module("app.admin.home")
.controller("HomeController", HomeController);
And I would like it have this (or similar):
angular
.module(moduleConstants.admin)
.controller(controllerConstants.adminHome, HomeController);
Is there a proper way to define this type of constants in Angular (ordinary injection does not work in this case). I guess I can create just global javascript constants, but maybe there is a better way of achieving this, some common way to define constants in angular. I have a strong C# background and it sounds stupid to me that constants need to be injected, they are not abstract code that can have different implementation, so it sounds wrong to inject it rather than just reference.
To avoid polluting the global name space, you could use a closure:
(function (moduleConstants, controllerConstants) {
angular
.module(moduleConstants.admin)
.controller(controllerConstants.adminHome, HomeController);
})({admin: "app.admin.home"},
{adminHome: "HomeController"});
According with the documentation, the right way of defining constants using angular js is using: Constants
This can be a simple implementation:
angular.module('config', [])
.constant('CONSTANT_YOU_NEED', {
name: 'test',
valueOfConst: '3.14'
});
But i guess you can't use this for modules or Controllers, but just passing to them

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?
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!
Maybe something like this - create a fake module and wrap the existing provider under a new name. This will isolate the dependency.
var colorpicker = angular.module('my-colorpicker', ['colorpicker.module']);
colorpicker.factory('ColorPickerSlider', function() {
var injector = angular.injector(['colorpicker.module']);
var Slider = injector.get('Slider');
return Slider;
});
I know that this doesn't solve the fundamental problem of namespaces but it gives a way of hiding existing dependencies in a sandbox.
Yes, you can handle multiple factories with same name.
Here is the example,
var app = angular.module('firstApp', []);
app.factory('SameFact', [function () {
return { Name: "First" };
}]);
var app2 = angular.module('secondApp', ['firstApp']);
app2.factory('SameFact', [function () {
return { Name: "Second" };
}]);
app2.controller("testController", ["SameFact", "$scope", "$injector", function (SameFact, $scope, $injector) {
$scope.myName = {};
$scope.myName.Name1 = SameFact.Name; // This will be "Second", Last factory
var inj = angular.injector(['firstApp']);
$scope.my_inject = inj.get('SameFact').Name; // This will be "First", the first factory
}]);
Note
When you pass a factory as a dependency to a controller, it will register the last registered factory.
That is, in this example, I have registered two factory with same name SameFact but in different module.
When I refer the factory SameFact from my controller it will always point to the factory which is registered last, ie, factory in secondApp.
But, you can manually refer the factory which is in the module firstApp.
You can use angular.injector(['module_name']) to select injector of your required module and then use get() function inside it to get your factory or service.
Conclusion
So, you need declare a scope variable which point to your Slide factory of colorpicker module. And use this scope variable to get all required operations within colorpicker module. Find where the place where you are calling functions of colorpicker and replace it with this new variable.
Hope this will help you to survive your current situation.
Feel free to ask any doubts regarding this !!!
Place colorpicker.module preceeding your module that's containing Slider factory, at your module initialization :
angular.module('thomasApp', ['colorpicker.module','anotherModule'])
Here is an example on js fiddle

Angular JS, Add Global Array or Object to Module. Recommended or not?

When creating an Angular Module one could essentially add global arrays or objects to the module. Like so..
var myApp = angular.module('myApp', ['myModule']);
myApp.run(function()
{
});
angular.module('myModule', [])
.run(function()
{
// global to module
var thisModule = angular.module('myModule');
thisModule.globalArray = [];
thisModule.globalObject = {};
});
So here's the question(s). Would it be a bad idea to do something like this? Is there anything in the documentation that recommends not doing this? And if so, why would or wouldn't you recommend not doing this?
Demo:
http://jsfiddle.net/hrpvkmaj/8/
In general, Angular goes to great lengths to avoid global state. You can observe this in the dependency injection system that the framework is based on. To use a component, you must inject it as a parameter that is wired up behind the scenes. The framework also has a powerful scoping system that allows for nice and easy encapsulation. Relying on global variables works against these systems.
In particular, it would be a bad idea to do something exactly like your code example because it isn't how Angular was designed to be used. You shouldn't be adding your own properties to Angular's module object. At the very least, you should be injecting the $rootScope object and adding your global variables to that.
app.run(function($rootScope)
{
$rootScope.globalArray = [];
$rootScope.globalObject = {};
});
From the Angular documentation:
Every application has a single root scope. All other scopes are descendant scopes of the root scope.
If you went this route, you could inject $rootScope wherever you need to use those global values.
However, the Angular team discourages using $rootScope.
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested.
There is another way of defining global values in Angular that is even preferable over using $rootScope: defining a value provider.
A value provider is the simplest kind of provider. It defines a single value that can be injected throughout your app.
You can define one like this:
app.value("myValue", {
someProp: 'Hello World'
});
Then you can inject the value wherever you need it like this:
app.controller("myController", function ($scope, myValue) {
$scope.someValue = myValue.someProp;
});
Here's a fiddle using a value provider to inject a global value.
In the end, any answer you get, including this one, will include some level of subjectivity. There are many ways to handle global values, but Angular provides some really convenient ways of using the framework to accomplish this.

AngularJS DI for custom functions

I've been working a lot lately with AngularJS and I really like the dependency injection stuff. I'm using it all over the place, but just in Angular components, like controllers and the like.
This always calls on the angular object:
angular.module('app').controller(/**/);
Now I have this function:
var custom = function(MyService) {
// do stuff
};
I've declared the Service this way:
angular.module('app').factory('MyService', function($rootScope) {
return {
show: function() {
// do stuff
};
},
hide: function() {
// do stuff
}
};
});
I now want to use this service in my custom function. Is there a way of manually calling the angular DI container? (I couldn't find anything in the docs...)
I know that this works for controllers not defined with the angular.module()... thing:
function Controller(MyService) {
MyService.hide(); // works
}
But how to use it outside of AngularJS components, in completely independent functions?
Or do I have to take a completely different path to achieve my goal?
Cheers and thanks in advanced,
Christian
angularjs is pretty neat in the fact that it exposes its own internal services to the user. The service you're looking for is $injector
What you want to use to call your custom function is $injector.invoke(myCustomFunction,thisForCustomFunction,{named:locals});
If you are by chance wanting to invoke this function outside of angular you'll have to get the applications injector.
var injector = angular.element(elementWithNgApp).injector();
Note:
The invocation time may be reduced though. As the injector must annotate (find what services you need) using several regular expressions. The reason this is not an issue throughout angular is because it is done once. Because services,factories,providers, are all instantiated (newed) singletons that provide closure with the services and whatever uses the services inside your providers
To prevent this extra step you can provide a $inject property on your function.. Something like this:
myfunction.$inject = ['ServiceA','ServiceB'];
function myfunction(a,b){
//a is ServiceA
//b is ServiceB
};

how adding global function in backbone.js accessible from multiple templates?

I have created two templates one for table and another for form inputs. The function which is in one template is not accessible from the other template . I want one global function which is accessible from both the template . I am new to backbone world . So is there any provision to do this ? Or is there any way access the function on one template from the another ?
There are a few ways of achieving this.
1. Add to Backbone directly
The simplest, but perhaps least desirable from a maintenance point of view, would be to add the function as a property of the Backbone object:
Backbone.myFunction = function (...) { ... };
2. Register a templating helper
The second option, depending on your templating engine of choice, you might be able to register helpers. Example:
Handlebars.registerHelper("myHelper", function (...) { ... });
3. Use dependency injection
A third option, if you are using something like require, would be to define the functions in a common dependency, and add it as a dependency to both views.

Categories

Resources