I have been working with AngularJs for a while and I now feel comfortable enough with it that I started to review my earlier work.
So I started out with following the basic tutorials and got used to setting up the application, controllers and services as following:
Module injections
// application.js
angular.module('appname',
[ // dependencies
'ui.router'
, 'someController'
]);
// somecontroller.js
angular.module('someController', [])
.controller('someCtrl',
['$scope', function ($scope) {
// Some stuff
}]);
And of course including js files in my index.html e.g.:
<script src="path/angular.js"></script>
<script src="path/angular-ui-router.min.js"></script>
<script src="path/application.js"></script>
<script src="path/somecontroller.js"></script>
Since I tend to separate all logic by feature (separate directory, own MV* structure per directory), I found that the list of dependencies can grow rather large. And also I think adding each controller, service etc. to the app dependencies is kind of a drag.
So I started to do it this way instead:
Single application
// application.js
var app = angular.module('appname',
[ // dependencies
'ui.router'
]);
// somecontroller.js
app.controller('someCtrl',
['$scope', function ($scope) {
// Some stuff
}]);
The obvious win for doing it like this is that I do not longer have to add the controller reference to the app dependencies.
So now my question(s) are:
Besides the obvious, what is the difference between the 2 methods?
From the Angular perspective which of the methods is considered
"good/bad practice"? And why?
I don't think the second method is good. You add all controllers to your app main's module. If you want to truly group your code by feature, I recommend a third approach :
// application.js
angular.module('appname',
[ 'ui.router'
, 'appname.booking'
]);
//booking.mod.js
angular.module('appname.booking', []);
//booking.ctrl.js
angular.module('appname.booking')
.controller('BookingCtrl', function ($scope) {
// Some booking logic
}]);
This way, you create a module per feature and obtain a project architecture easy to understand and navigate.
Looks like you need to read a bit more about modules.
The difference between your two approaches is that in the first one you create a different module for your controller and by injecting that module into the app dependencies you tell angular "by the way, I want the features that come with that module, too".
In your second approach, you only have a single huge module for the entire application, which means all the controllers, service, directives, .. will be automatically considered as part of the application.
Both approaches work, however having different modules for different functionality could help you structure you application better, as well as provide more reusability & easier testing.
In your first method you are creating different modules for your controller. That's why you have to inject your controller name at the time of creating Angular JS app.
angular.module('appName', ['controllerName']);
angular.module('controllerName', []);
In the second approach you are only creating a single app and you are attaching the controller to this app. In this case you don't have to inject the controller name at the time of creating the Angular JS app.
var app = angular.module('appName', []);
app.controller('controllerName', function(){});
About your second question, it depends on the type of application you are developing. For a small single page application a single app module will do fine
When working on large applications everything might not be contained on a single page, and by having features contained within modules it's much simpler to reuse modules across apps. So in this case your first approach is better.
You can create different modules like shown below and can inject to your app;
var sharedServicesModule = angular.module('sharedServices',[]);
sharedServices.service('NetworkService', function($http){});
var loginModule = angular.module('login',['sharedServices']);
loginModule.service('loginService', function(NetworkService){});
loginModule.controller('loginCtrl', function($scope, loginService){});
var app = angular.module('app', ['sharedServices', 'login']);
Related
My app is following John Papa's styleguide for AngularJS applications:
The styleguide emphasizes using a strongly modular approach to the design of the app. My question is about multiple configurations and their effect on shared services.
Suppose I have a main module like this:
angular.module("app", ["app.feature1"])
.config(function() {
// do some configuration here
console.log("app configured");
});
And a feature module that configures a shared angular service, let's say the $http service:
angular.module("app.feature1", [])
.config(function($http) {
// configure the $http service
console.log("feature1 configured");
});
Is my understanding correct, that the configuration by "feature1" will carry over to the main module, since the $http service is a singleton and therefore shared across modules? Or do I have to configure the $http service in the main module instead, because each module has it's own $http service instance?
Edit: I can confirm that dependency configs are carried over and are executed first. See David's jsfiddle example.
As a matter of best practice, you should configure services as early as possible, which is typically your main module (the app root), and preferably only once to avoid overlapping changes.
Since $http is a singleton (as you mentioned), any changes via configuration will be propagated throughout the application every time you inject $http.
It's also worth mentioning that configuration to services is First In First Out, meaning that if you have two configuration changes, the last-accessed configuration will be the one that is persisted to the service since the previous configuration will be overwritten if they are changing identical components of the service.
In your case, yes, the change to $http in your module will be extended to your main application and any other modules using $http.
Finally, in light of the comments, child dependency configs are resolved before parent configs, as demonstrated by this simple fiddle:
http://jsfiddle.net/maqzo6fv/
HTML:
<div ng-app="app"></div>
JS:
angular.module("app", ["app.feature1"])
.config(function() {
alert('main config');
});
angular.module("app.feature1", [])
.config(function() {
alert('child config');
});
The child config will call alert before main on every load.
I've started testing my Angular app and have question that bother me a lot. For example I have controller (mainController) which is injecting 2 services: authService, configService.
Before testing I should prepare something like that:
describe('controller: testController with testService mock', function() {
var controller, authService, configService;
beforeEach(module('app'));
beforeEach(inject(function($controller, _authService_, _configService_) {
authService = _authService_;
configService = _configService_;
controller = $controller('mainController');
}));
it('should be registered with all dependencies', function() {
expect(controller).to.be.defined;
expect(authService).to.be.defined;
expect(configService).to.be.defined;
});
}
And that's totally clear for me. What if one of services or both have their own dependencies (services) ? Of course I'm gonna add it by passing through the inject function. In small apps that's no big problem. I'm adding as much services as I need. But the question is what if that services are injecting other services and others injecting others and there is a huge hierarchy ? What if we must add 30 services and we can't make a mistake because otherwise it's not gonna work ?
To be honest I've search a lot but there are many testing examples and tutorials but every single one is based on totally basic apps with few controllers and services.
Is there a painless way to handle this ? Maybe there is a way to skip some dependencies or force to automatically inject services with it's dependencies ?
What if one of services or both have their own dependencies (services) ? Of course I'm gonna add it by passing through the inject function
No, you won't. Angular will instantiate these services and inject them where they need to be injected, provided they're in the 'app' module or in one of its dependencies.
There's no reason to inject any service into your test, unless you need to do something with them, like calling one of their methods, or spying them.
1) What are your considerations when planning the architecture of an AngularJS application?
What is your mental process?
2) Which criteria do you take in consideration to decide if you will:
2.a) Make each part of your application it's own module and inject each module to the main module as a dependency?
angular.module('WebsitePart1', []);
angular.module('WebsitePart2', []);
angular.module('Main', ['WebsitePart1','WebsitePart2']);
2.b) Create only one module, and attach everything else (directives, controllers, services, etc) to the main module?
var mynamespace = angular.module('', ['ngRoute']);
And then attach a controller to each router setup, attach the directives to the mynamespace module, etc.
Thank you for sharing your take on it!
I have an angular application that looks like such.
file1.js
var app = angular.module('app', ['third-party-stuff-A']
file2.js
var app = angular.module('app', ['third-party-stuff-B']
so essentially file1 and file2 represent two separate pages in my application.
I've registered all my controllers, services, and other injectables into the module app, so for example.
app.factory('thing', function ($scope) { ... });
the thing factory is available to both app in file1 and app in file2.
I've read that it's better practice to break things into modules. app in file1 and file2 should probably be their own separate module.
And to go even a step further, perhaps thing factory can go into a module MyFactories. and MyFactories would be injected into each of my apps.
But I'm not sure what the immediate benefit is of doing this.
Most applications have one main method and links to different parts of the application.
Angular apps don't have a main method. Instead modules specify how an application should be bootstrapped. advantages for creating modules are
You can package code as reusable modules. The modules can be loaded
in any order (or even in parallel) because modules delay execution.
see more at https://docs.angularjs.org/guide/module
In one of our angular project, we defined some modules, like:
angular.module("services", [])....
angular.module("controllers", ['services'])....
angular.module("factories", [])....
Then make a main module depending on them:
angular.module("app", ['services', 'controllers', 'factories'])
Some colleague thinks this way is not good, he prefers to group modules by business features, like:
angular.module("login", [])....
angular.module("user-admin", [])....
angular.module("post-admin", [])....
Then combine them together:
angular.module("app", ['login', 'user-admin', 'post-admin'])
I think his approach is making sense, but I'm not sure the best practice to define modules.
What rules we should follow when defining modules?
Sometimes when we start to play with AngularJS we think is a good idea to create different modules, for example, for our controllers, services, directives, etc…but this may not be the best option. For example: let’s say we create a controller inside a module named ‘myapp.controllers’ and this component depends on a service that is inside the module ‘myapp.services’. When we want to use that controller in another application we will have to not only require the controller module but the service one too and any other module that is as a dependency. However, if we have a login module and we create controllers, services, directives, etc under the module ‘myapp.login’, then later on when we want to use that module we will have everything we need to use to it without needing other dependencies.
source
another way
Another method we can use to break up our app is to divide our modules by route. This breakdown
allows us to write isolated tests that focus on the functionality per route. Modularizing by route can
make more sense, depending upon the project; it allows us to divide our functionality efficiently
when we’re dealing with a lot of independent routes.
angular.module('myApp.home', []);
angular.module('myApp.login', []);
angular.module('myApp.account', []);
angular.module('myApp', [
'myApp.home',
'myApp.login',
'myApp.account'
]);
This modularization makes sense specifically when we’re dealing with large numbers of routes
and/or when we don’t have too much cross-over between routes.