As you AngularJS application gets more complex you will likely come to discover that the ngApp directive has two fairly big limitations:
- You can only have one
ng-app
per page.
- You can only associate a single module with a single HTML element
Combine modules into a single module
One way to work around this is to “combine” multiple modules into a single module by referencing them in another module. Here is an example of what I mean:
var moduleA = angular.module("MyModuleA", []);
moduleA.controller("MyControllerA", function($scope) {
$scope.name = "Bob A";
});
var moduleB = angular.module("MyModuleB", []);
moduleB.controller("MyControllerB", function($scope) {
$scope.name = "Steve B";
});
angular.module("CombineModule", ["MyModuleA", "MyModuleB"]);
ng-app="CombineModule">
myDiv1
ng-controller="MyControllerA">
{{name}}
myDiv2
ng-controller="MyControllerB">
{{name}}
That works, but it has it’s problems. One it’s less clear the second div myDiv2
only needs the controller from MyModuleB
why combine everything. You can imagine it would become less clear as your app and therefore the number of modules grows.
Also this example works because the controllers have different names, what if they had the same names. Being able to associate different modules with different HTML elements would give us better control of our namespace.
Do it programmatically using angular.bootstrap()
You might be surprised to find out that the limitations of the ngApp
are not limitations of Angular itself. Angular allows you to associate more than one module per HTML element. Angular also allows you to have multiple HTML elements on a page associate with modules. You just have to do it grammatically.
Below is an example of how to do this. In this example we have two modules each with one controller. myDiv1
is associated with both modules. WhilemyDiv2
is associated with just with a single module.
var moduleA = angular.module("MyModuleA", []);
moduleA.controller("MyControllerA", function($scope) {
$scope.name = "Bob A";
});
var moduleB = angular.module("MyModuleB", []);
moduleB.controller("MyControllerB", function($scope) {
$scope.name = "Steve B";
});
angular.element(document).ready(function() {
var myDiv1 = document.getElementById("myDiv1");
angular.bootstrap(myDiv1, ["MyModuleA", "MyModuleB"]);
var myDiv2 = document.getElementById("myDiv2");
angular.bootstrap(myDiv2, ["MyModuleB"]);
});
id="myDiv1">
myDiv1
ng-controller="MyControllerA">
{{name}}
ng-controller="MyControllerB">
{{name}}
id="myDiv2">
myDiv2
ng-controller="MyControllerB">
{{name}}
The output of that code is:
myDiv1
Bob A
Steve B
myDiv2
Steve B
You can see for yourself and play around with that code at:
This gives us a lot of flexibility, the problem is it’s pretty ugly. You have to reference the element itself in your code which means you are coupling your code with the HTML. That goes against one of the main goals of Angular.
Ideal solution: A more robust ngApp (enter ngModule)
The ideal solution would be for ngApp
to allow you to do everything angular.bootstrap()
allows you to do. Allow you to use it on multiple HTML elements. Allow you to specify more than one module.
Normally the solution to this would be to create your own Angular directive. The problem is, how would you define that directive? You would need a module to define it in which would defeat the purpose.
To implement a directive like
ngApp
you would need to implement it the way
ngApp
is implemented. I’ll spare you the trouble, I’ve already implemented it. You can download it from this link:
angular.ng-modules.js
This JavaScript file introduces the ngModule
directive. Here is an example of how you would rewrite the code above to use ngModule
:
var moduleA = angular.module("MyModuleA", []);
moduleA.controller("MyControllerA", function($scope) {
$scope.name = "Bob A";
});
var moduleB = angular.module("MyModuleB", []);
moduleB.controller("MyControllerB", function($scope) {
$scope.name = "Steve B";
});
ng-modules="MyModuleA, MyModuleB">
Module A, B
ng-controller="MyControllerA">
{{name}}
ng-controller="MyControllerB">
{{name}}
ng-module="MyModuleB">
Just Module B
ng-controller="MyControllerB">
{{name}}
Notes about ngModule
You might have noticed that in one case I use the plural version of the directive ng-modules
with the “s” and in another case I use the singular version without the “s” ng-module
.
This was just a preference of mine. I allow for both spellings interchangeably regardless of whether you reference a single or multiple modules. I just liked how it read better.
In keeping with Angular’s convention I allow you to use any variation of the directive name. For example, the following are all valid: ng:module
, x-ng-modules
, data-ng-modules
, etc.
Finally you should know that while you can now associate multiple HTML elements on a single page with modules by using this directive. Those HTML elements cannot be nested. If they are nested they will not behave properly. Said another way if you associate an HTML element with a module, you can associate a child of that element with another module.
Why the ngApp limitations?
One questions that I’ve seen asked in several forums is why did the Angular team place those limitations on ngApp
to begin with. After all as I mentioned above the Angular framework itself does not have those limitations. I haven’t seen an answer from the Angular team but I can speculate.
Earlier versions of angular have the “view” and “route” functionality built in. In fact, that seemed the be the expected or recommended way to use Angular. Since you only have one URL, you can’t have multiple multiple views and routes on the same page. Therefore there might have been little need to be able to associate multiple modules.
They might have felt that having a single module associated with a page made it easier to understand and use. After all you often hear Angular often being mentioned as a technology for single page applications.
They might have simply not gotten around to it since two approaches already exists for doing it. I covered them above.
They might have felt that that kind of information belongs in your code, not your markup. While all these reasons are debatable, I think this one would be the most debatable since it’s possible for a module to only contain directives for front end controls.
I mentioned above that AngularJS does not allow nesting. Meaning you can’t have a element and a child element associated with different modules. The Angular team might have felt that because of that limitation it was better to avoid the situation by only allowing one module in your HTML.
refer : http://www.simplygoodcode.com/2014/04/angularjs-getting-around-ngapp-limitations-with-ngmodule/