Suppose that I have an SPA written via AngularJS 1.x.
It has a single app module defined like this:
var app = angular.module('app', ['ngAlertify', 'ngRoute', 'ui.bootstrap'])
I also have several controllers which are defined in separate *Ctrl.js-files. What is the appropriate way to define them?
I see two options here. The first one is
app.controller('LoginCtrl', function($scope) { /* ... */ });
and the second one is
angular.module('app').controller('LoginCtrl', function($scope) { /* ... */ });
Which one is better and most common-used practice? Is there any downsides of using either of them?
If I understand your question correctly then you wan to know different between
app.controller('LoginCtrl', function($scope) { /* ... */ });
vs
angular.module('app').controller('LoginCtrl', function($scope) { /* ... */ });
In above two in first method app is a global object which you declared somewhere i.e. in app.js like
var app = angular.module('app',[]);
In this case app is a global variable which will be accessible throughout your entire application. which I believe is not a good thing to use global variable
in our application.
In second method we are using global angular object to create a controller so that in this we will not be using global variable. In this case app.js will look like
(function(){
'use strict';
var app = angular.module('app', []);
....
....
....
....
})
In this case app variable will not be available anywhere apart from this file.
So I belive second method is better than first one.
My personal preference is to use app.controller('LoginCtrl', function($scope) { /* ... */ }); as this makes it easier to reuse the controller in another project with little to no changes, without those annoying module not found errors because you forgot to rename the module when reusing the file
I think that depends a bit on the personal style of writing. One thing is that while working with AngularJS 1.x.x you can have different styles of writing code, method stacking etc.
Personally, I prefer app.controller('LoginCtrl', function($scope) { /* ... */ }); mainly because you can easily preview your controller and distinguish it from thge module. Another bonus I see of having a clearly defined separate module is that you can easily check what includes you have ('ngAlertify', 'ngRoute', 'ui.bootstrap').
Most commonly used as far I have seen, even here on SO, is the method that I previously mentioned. Yet again this is something that is more reflective of personal style rather than strong pre-requirements of writing code. I hope that helps to some extend.
None of the above. The purpose of modules is to keep the application modular and not pollute global scope.
You can have var app = ... but this should be done inside IIFE once per file.
Another issue with modules is the precedence. If the application uses angular.module('app') module getter, the files should be loaded in specific order, in order for the module to be defined when it is retrieved in other files. This creates problems if they aren't, for example when they are concatenated in alphabetic order.
The solution is to use one module per file. This makes the application truly modular, independent of file loading order, also benefits testability. See also this answer for how this pattern supposed to work.
You can use module setter and getter methods for implementation of controllers in different file.
Suppose myApp.module.js
angular.module('myApp', []); //Setter method, registring module
In myApp.homeCtrl.js
var myApp = angular.module('myApp'); // getter method, getting the module already registered.
myApp.controller('homeCtrl', [function()]{ })
For more info check this https://toddmotto.com/angular-modules-setters-getters/
The second approach your are taking about is better because it uses the already created module and doesn't create the new module but with the first approach you are using global variable that is not recommended
Related
I have an angular 1.5 project with many modules and each module may depend on other modules. Trying to unit test say a controller which is part of a module I would do import the module like this:
angular.mock.module('SaidModule');
...then provide and inject its services where needed.
The problem is that SaidModule depends on AnotherModule1, AnotherModule2, AnotherModule3....
angular.module('SaidModule', ['AnotherModule1', 'AnotherModule2', 'AnotherModule3']);
So naturally when I call SaidModule the other modules are also invoked which is out of scope in terms of Unit testing
In the unit test I have tried the following solution
angular.module('AnotherModule1',[]);
angular.module('AnotherModule2',[]);
angular.module('AnotherModule3',[]);
angular.mock.module('SaidModule');
and although for the current unit test I have successfully decoupled the dependencies I have also destroyed
the actual AnotherModule1, AnotherModule2, AnotherModule3 so when its there turn to be unit tested they are
not even visible in the angular project which seems correct to me. as I am using angular.module to define a
new module which just happens to override the actual module.
This solution though is also suggested here mocking module dependencies
In the angular docs it states see angular docs mock module
If an object literal is passed each key-value pair will be registered on the module via $provide.value,
the key being the string name (or token) to associate with the value on the injector.
So it seems to me that the solution is using somehow angular.mock.module somehow to override the dependent
modules but so far I have not found a solution.
Any help much appreciated
By calling angular.module('AnotherModule1',[]) you are redefining the AnotherModule1, which I think is causing your downstream problems. Instead, use $provide for each dependent service. There's no need to mock the dependent modules.
Let's say your controller definition looks like this:
angular
.module('SaidModule', ['AnotherModule1', 'AnotherModule2'])
.controller('SaidController', [
'$scope',
'AnotherService',
function($scope, AnotherService) {
this.anotherService = AnotherService.helper();
}
);
Then your test might look like:
describe('SaidController', function() {
var controller, scope, AnotherService;
beforeEach(module('SaidModule'));
beforeEach(module(function($provide) {
AnotherService = { helper: function() { return 0; } };
$provide.value('AnotherService', AnotherService);
}));
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
controller = $controller('SaidController', {
$scope: scope
});
}));
it('creates controller', function() {
expect(controller).not.toBeNull();
});
});
There's no need to mock the dependent modules, just the dependent services.
I have a constant value that is common throughout my project, among 2 of my modules- I want to have a design that I would have to only define it in one module and pass on the value to another module. How can I ?-
Module1 is like
var myApp = angular.module('myApp', ['ui.router']);
myApp.constant('constVar', 'http://45.26365.23/mydata/');
and Module2 is like
var Dashboardapp =angular.module('Dashboardapp', ['ui.router']);
Dashboardapp.constant('constVar', 'http://45.26365.23/mydata/');
For the record, the Google internal style guide says "do not define variables on the scope." (It actually explicitly says that). Note that means in particular, but not only, $rootScope (your code would be guaranteed to get kicked back in review if you did that, with very ugly "THIS IS BAD" comments).
Just define it at a higher level then both modules, typically at the app level (Angular 1.x defines a configurable place for constants in the app config API, you may want to look that up, this is useful because it actually enforces the constant). You can also just use a class defined in no module at all (just a top level class that you import and use statically), or you can define another module that you inject (which might be overkill for a few simple constants).
A naive approach would be to define the constant outside the modules:
var myConstant = 'http://45.26365.23/mydata/';
or maybe even better if you have more constants or other general data:
var myConfig = {
myConstant : 'http://45.26365.23/mydata/'
// other data
}
and then call it in the modules:
var myApp = angular.module('myApp', ['ui.router']);
myApp.constant('constVar', myConfig.myConstant);
var Dashboardapp =angular.module('Dashboardapp', ['ui.router']);
Dashboardapp.constant('constVar', myConfig.myConstant);
Other possibility would be to define it in only one module, which would be your main module, and inject it in the other modules:
var myApp = angular.module('myApp', ['ui.router']);
myApp.constant('constVar', 'http://45.26365.23/mydata/');
var Dashboardapp =angular.module('Dashboardapp', ['ui.router', 'myApp']);
Dashboardapp.constant('constVar', myApp.constant);
Instead of the last line you could just call the constant where ever it is needed.
You could always use the 'constant' that Angular comes with ...
import * as angular from 'angular';
export default angular
.module('app.constants.foo', [])
.constant('fooAction', Object.freeze({
name: value
}))
.name;
Then you inject your named constant. Thus it is accessible anywhere in the app (and more importantly immutable (mostly)).
I am creating small application called puzometr. It is for educational needs only. I want to create this application using AngularJS. Also, I want to use RequireJS as module system.
I have strange problem. I created my test controller and I got problem: controller initialization fires two times.
Firstly, full code available here on GitHub (wait, don't click me, I will explain everything below).
So, problem is in myCtrl.js file. Here is code of this file:
define(['angular'], function (angular) {
var module = angular.module('main.myModule', []);
module.controller('main.myCtrl', function ($scope) {
console.log($scope.$id);
$scope.bob = function () {
}
})
});
It is included in main/controllers/controllers.js by this:
define(['app', 'main/controllers/myCtrl'], function (app) {
var module = angular.module('main.controllers', ['main.myModule']);
});
This file included in main.js by this code:
angular.module('main', ['ngRoute', 'main.services', 'main.controllers', 'main.directives']);
And main.js is included into app.js:
var app = angular.module('myApp', ['ngRoute', 'main', 'common']);
So, I incidentally noticed, that function definition in myCtrl controller fired two times. I put console.log there and saw this:
Can you please explain me why is this happens? Why controller is being initialised two times?
Also, I have this in ng-inspector:
So one scope is created as child for another scope. Notice, that scope with id 3 has correct controller name.
If you use ng-route to register controllers and bind them with views, then don't add them again using attributes in your html files.
I would like to know what is the best way to reuse the same directive for multiple modules. In my mind i think directives are like DOM-modules, so the architecture of angular for this is a bit annoying of my point of view.
The way I'm doing it now is:
var mainapp = angular.module('MainApp');
mainapp.controller( ... );
var otherapp = angular.module('OtherApp');
otherapp.controller( ... );
And in other file i have my directive i want to use in this two controllers/modules
mainapp.directive('autoselect', function(){ ... });
But as you can see, I have to specify in what app I have to use it. If I want to use in both, do i have to copy twice the same code?
You can maintain a module that contains common (services, directives, values, constants etc.) and inject into each module.
angular.module('CommonApp',['OtherCommonModule1','OtherCommonModule2']).directive(..);
var mainapp = angular.module('MainApp', ['CommonApp']);
mainapp.controller( ... );
var otherapp = angular.module('OtherApp', ['CommonApp']);
otherapp.controller( ... );
No, you don't have to specify which app/module you need to use a directive in.
Let me explain:
In angularJS, everything is a module. For example, MainApp, OtherApp and even the autoselect directive is a module.
Each module can 'depend' on one or more modules.
So, here's how I would design the solution:
angular.module('com.example.mydirective', [])
.directive('autoselect', function() { ... });
angular.module('MainApp', ['com.example.mydirective']);
.controller(...);
angular.module('OtherApp', ['com.example.mydirective']);
.controller(...);
One thing I'd like to add: the square brackets that denote the dependency have a special significance.
angular.module('module-name', []); // this line tells angularJS to create a new module
angular.module('module-name'); // no brackets -- this asks angularjs to return a pre-existing module
Hope this helps
What is the behaviour of calling angular.module('myModule') multiple times?
For example, I wish to define my routes and my directives in separate .js files.
Is this safe?
eg:
//routes.js
angular.module('app',['$strap'])
.config(function($routeProvider, $locationProvider) {
...
});
//directives.js
angular.module('app')
.directive('formInput', function() {
...
Also, what is the impact of defining the dependencies multiple times? Is this additive, or last-in-wins?
eg:
angular.module(name[, requires], configFn);
...
requires(optional) – {Array.=} – If specified then new module is being created. If unspecified then the the module is being retrieved for further configuration.
-- angular.module docs
I would interpret that as follows: you can only define the dependencies once -- the first time you call angular.module for a particular module. You can call angular.module() multiple times after that, but the requires option must not be specified.
You should only create your module once. According to the docs, if you create a module with a name that already exists, it will overwrite the previous one. (So last-in-wins.)
angular.module('app', []);
You can retrieve your module as many times as you like and in separate files if you wish. You will typically retrieve your module multiple times to declare services, controllers, directives, etc.
angular.module('app').service('myService', ...);
angular.module('app').controller('myController', ...);
angular.module('app').directive('myDirective', ...);
In the AngularJS docs on Modules, see the section called Creation versus Retrieval.
I'm new to angular, but this is my understanding: you create one module in each file with a namespaced module name, and in your main module you require those modules.
// in main app.js file
var app = angular.module('myapp',
['myapp.routers', 'myapp.directives', 'myapp.filters']);
// in filters.js
angular.module('myapp.filters', []).filter(....)
// in routers.js
angular.module('myapp.routers', []).router(....)
// in directives.js
angular.module('myapp.directives', []).directive(....)