AngularJS module.constant() : how to define a constant inside a module only? - javascript

On a page, I have several Angular modules.
For each module I define a constant which contains the version of the module.
var module1 = angular.module('module1').constant('version', '1.2.3');
var module2 = angular.module('module2').constant('version', '2.0.0');
...
I though a constant was defined inside a module. But when I use the constant inside module1, the value I get is '2.0.0'...
Is there a way to define a constant (or anything else) which is proper to a module ?
Edit: for alternative solutions, could you please explain how to use it, for example in a controller declaration ?
module2.controller('myCtrl', function( $scope, $http, $q, ..., version ){
// Here I can use the constant 'version'
}

A very good question. I could think of a quick fix for this:
angular.module('module1').constant('module1.version', '1.2.3');
angular.module('module2').constant('module2.version', '2.0.0');
I don't know how much it suits your needs. But I hope it helps.
The main problem is the naming in Angular. You cannot have the same name for services/constants/values etc. They will be overwritten. I solved this problem with "namespaces" like I showed you above.
Example of injecting namespace like names: http://codepaste.net/5kzfx3
Edit:
For your example, you could use it like so:
module2.controller('myCtrl', ['$scope', '$http', '$q', ... , 'module2.version'], function( $scope, $http, $q, ..., version ){
// Here I can use the constant 'version'
}

That happens because module2 is overriding module1 constant.
You can use moduleName.version as a name, but it's not possible to use the same name.

Related

Scope variable not visible within ng-click handler

I'm pretty new to Angular and am trying to figure out what's wrong here. There is a controller defined like this:
(function(){
function myController($scope, CommsFactory) {
$scope.doSomething = function() {
var x = $scope; // <- Doesn't work because $scope is not defined
}
}
angular
.module('aModule')
.controller('myController', myController);
})();
The doSomething() method is then called by a button click like:
<input type="button" ng-click="doSomething()" class="btn--link" value="do it"/>
This seems straightforward to me but the problem is that, when I break within the method, $scope is not defined. This is different from most of the examples I've seen, and I can't figure out why. Shouldn't it be visible here? Obviously a lot of code is missing - I've tried to show only the relevant bits - could I be missing something somewhere else?
You're declaring a module then you need to add [].
Something like this:
angular.module('aModule', [])
.controller('myController', myController);
Usage
angular.module(name, [requires], [configFn]);
Arguments
name.- The name of the module to create or retrieve.
requires (optional).- If specified then new module is being created. If unspecified then the module is being retrieved for further
configuration.
configFn (optional).- Optional configuration function for the module. Same as Module#config().
Please, I would to recommend to read this guide about Angular Module:
angular.module
(function() {
function myController($scope) {
$scope.doSomething = function() {
var x = $scope;
console.log(x);
}
}
angular
.module('aModule', [])
.controller('myController', myController);
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div data-ng-app="aModule">
<div data-ng-controller="myController">
<input type="button" ng-click="doSomething()" class="btn--link" value="do it" />
</div>
</div>
Your code is generally working fine as demonstrated in this fiddle.
Your main problem seems to be in the usage of $scope. $scope is an object containing all variables and methods which should be available in the corresponding template. For this reason, you would always reference a member of $scope, instead of the whole object.
Furthermore, John Papas AngularJS style guide recommends the usage of controllerAs in favor of $scope for multiple reasons as stated in Y030
By convention, you should also give your controllers uppercase names and use explicit Dependency Injection
A typical use case would rather look like:
(function(){
angular
.module('aModule', [])
.controller('myController', MyController);
MyController.$inject = ['$scope', 'CommsFactory'];
function MyController($scope, CommsFactory) {
var vm = this;
vm.doSomething = doSomething;
function doSomething() {
var $scope.x = "Did it!";
}
}
})();
SOLVED: It turns out that what I was experiencing had something to do with the way in which the Chrome debugger works. It appears to do some kind of lazy loading of variables defined outside of the function in which you break (or at least this as far as I've characterized it). What this means, at least in my case, is that if I break inside of the method, and $scope isn't actually used within that method (which, unfortunately, I was doing a lot because I was trying to verify that $scope was visible), then the debugger will report that $scope is unavailable.

Pass External Function into Angular Directive

I'm struggling to find an elegant way of passing an external/out-of-scope/non angular function into a directive. So far the only way I could see to do this was by passing the function name as a string into angular and then using eval which does not seem so nice. Here is an example of that on Plunker
http://plnkr.co/edit/L9GGDkxwh4IGNufXB8yg?p=preview
Using extFunc:'&' in the scope only work for functions in the scope so that does not work for me.
Is there a better way of doing this? I realise in my example one would just include the function inside the directive or controller but this is not always practical.
<script>
function nonAngularFunction( someText ) {
alert( someText );
}
<script>
<my-directive ext-func="nonAngularFunction('Hi there')" ></my-directive>
Sorry if this question has already been asked but I could not find a solution anywhere.
I'd import this function in Angular's world as a service:
/* global nonAngularFunction:false */
angular.module('aModuleName', [])
.value('nonAngularFunction', nonAngularFunction);
Then you can use injection and import the function to your scope:
// ... in your directive or controller
angular.module('maybeAnotherModule', ['aModuleName'])
.controller('YourController', ['$scope', 'nonAngularFunction',
function ($scope, nonAngularFunction) {
$scope.nonAngularFunction = nonAngularFunction;
}]);
Should work (I haven't tested it).
Edit: Alternative when there are dozens of external functions
eval is not a practical solution, since it doesn't have access to your scopes.
You could either import lots of your functions into Angular, e.g.
angular.module('aModuleName', [])
.value('myHorribleLib', {
'oneFunction': oneFunction,
'Constructor1': Constructor1,
...
});
Then you have to make them available by $scope.legacyLib = myHorribleLib and use it with a prefix: ng-click="legacyLib.oneFunction('hello')". A bit verbose, but you keep your eyes of what is legacy.
Or you just extend $rootScope (or a top-level scope) with the methods:
angular.module('aModuleName', [])
.run(['$rootScope', '$window', function ($rootScope, $window) {
'oneFunction Constructor1 anotherFunction ...'
.split(' ').forEach(function (f) {
$rootScope[f] = $window[f];
});
}]);
I'm not saying this is a super-clean solution, but this way you could expose the functions of your legacy library to Angular scope. They should now work in normal ng-click and the like and have access to your scope.

Angular.js, equivalent controller writing that fails

I'm starting with Angular.js and have a question; What's wrong with the second way to express the controller? Take a look at the jsfiddle below
http://jsfiddle.net/yDhv8/
function HelloCtrl($scope, testFactory, testFactory2)
{
$scope.fromFactory = testFactory.sayHello("World");
$scope.fromFactory2 = testFactory2.sayHello("World");
}
myApp.controller('GoodbyeCtrl', ['$scope', 'testFactory', 'testFactory2', function($scope, testFactory, testFactory2) {
$scope.fromFactory = testFactory.sayGoodbye("World");
$scope.fromFactory2 = testFactory2.sayGoodbye("World");
}]);
Any references that may be useful to understand what's going on will be appreciated,
Cheers,
Everything is fine. you just got confused. use app.controller
myApp is the module name, not a variable name.
app.controller(......)
If you run this in a javascript debugger, you'll find that the variable 'myApp' is not defined. You can either use the 'app' reference that you assigned to the original module call, or use the following syntax:
angular.module('myApp').controller(...)

AngularJs resolve/retrieve a module by its name

As suggested I wrap all my services into a module, So, I created files to organize them:
myServicesModule.js
var myServices = angular.module('myServices', []);
serviceA.js
myServices.service('serviceA', function(){...});
and serviceB.js
myServices.service('serviceB', function(){...});
etc etc.
As a good practice, we should try to avoid global variables, so is there any way to avoid declaring a global var myServices by something I guess module resolving by name? I understand by putting all services into a single file could solve this issue, but I don't want to create a huge file.
Second parameter of module function (dependencies array) is optional :
If you set the second parameter, it will create the module (and override any existing module with the same name) :
// Create the module
angular.module('foo', []).controller('FooCtrl', function($scope) {
// ...
});
If you do not set the second parameter, it will retrieve the module by its name and fail if module does not exist :
// Retrieve module by its name and fail if module does not exist.
angular.module('foo').controller('FooCtrl', function($scope) {
// ...
});
The angular.module is a global place for creating, registering and
retrieving Angular modules. All modules (angular core or 3rd party)
that should be available to an application must be registered using
this mechanism.
When passed two or more arguments, a new module is created. If passed
only one argument, an existing module (the name passed as the first
argument to module) is retrieved.
So this will work:
angular.module('myServices', []);
angular.module('myServices').service('serviceA', function(){...});
angular.module('myServices').service('serviceB', function(){...});
You don't need create variable for your service. It's possible tot do it that way`(in different files):
For example, file1.js:
angular.module('myServices', []);
file2.js
angular.module('myServices').service('serviceA', function(){...});

Understanding dependency injection in AngularJS controllers

Just learning dependency injection, and I think I'm starting to understand it.
Please tell me if I'm on the right track...
E.g.: Are these two equivalent?
/* injection method */
function <controller_name>($scope) {}
<controller_name>.$inject = ['$scope'];
/* other method */
var app = angular.module('myApp');
app.controller(<controller_name>, function($scope) {});
First a little clarification:
For dependency injection, it doesn't matter whether you declare a controller using a global function or as the argument of module.controller(...) method. Dependency injector is only concerned about the function itself. So what you're actually asking about is the equivalence of those two:
// First
function MyController($scope) {}
MyController.$inject = [ '$scope '];
// Second
function($scope) {}
And because whether the controller function is anonymous or not also doesn't matter for the injector, the above two can just as well be:
// First
function MyController($scope) {}
MyController.$inject = [ '$scope '];
// Second
function MyController($scope) {}
Now it's clear that the only difference between your two controllers is the presence of the $inject property in one of them.
And here's the actual answer to your question:
These two controllers are almost the same. Both will receive the $scope as the argument and will function the same. However, if you decide to minify your code later, only the version with $inject array set on it will work properly. This is because if you don't specify the $inject array nor use the inline annotation approach (http://docs.angularjs.org/guide/di#inlineannotation), the only way for the injector to find out which dependencies you were interested in is to check the names of your function arguments (treating them as service IDs). But minification would name those arguments randomly thus removing the chance to recognize dependencies this way.
So if you're going to minify your code, you have to specify the dependencies explicitly using $inject array or inline annotation, otherwise, any version will work just as good.
If you're going to use the module.controller method, the equivalent to your first example would be:
var app = angular.module('myApp');
app.controller(<controller_name>, ['$scope', function($scope) {}]);
Notice that this way we're passing the $inject string along with the function, so that if it later gets minimized it will still work.

Categories

Resources