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:
<!DOCTYPE html> <html> <head> <script src="angular.js"></script> <script> 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"]); </script> </head> <body ng-app="CombineModule"> <div> <h1>myDiv1</h1> <div ng-controller="MyControllerA"> {{name}} </div> </div> <div> <h1>myDiv2</h1> <div ng-controller="MyControllerB"> {{name}} </div> </div> </body> </html>
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. While myDiv2
is associated with just with a single module.
<!DOCTYPE html> <html> <head> <script src="angular.js"></script> <script> 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"]); }); </script> </head> <body> <div id="myDiv1"> <h1>myDiv1</h1> <div ng-controller="MyControllerA"> {{name}} </div> <div ng-controller="MyControllerB"> {{name}} </div> </div> <div id="myDiv2"> <h1>myDiv2</h1> <div ng-controller="MyControllerB"> {{name}} </div> </div> </body> </html>
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:
http://jsfiddle.net/luisperezphd/QX3fQ/
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 find it on GitHub at:
https://github.com/luisperezphd/ngModule
This JavaScript file introduces the ngModule
directive. Here is an example of how you would rewrite the code above to use ngModule
:
<!DOCTYPE html> <html> <head> <script src="angular.js"></script> <script src="angular.ng-modules.js"></script> <script> 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"; }); </script> </head> <body> <div ng-modules="MyModuleA, MyModuleB"> <h1>Module A, B</h1> <div ng-controller="MyControllerA"> {{name}} </div> <div ng-controller="MyControllerB"> {{name}} </div> </div> <div ng-module="MyModuleB"> <h1>Just Module B</h1> <div ng-controller="MyControllerB"> {{name}} </div> </div> </body> </html>
You see in action here: http://jsfiddle.net/luisperezphd/j5jzsppv/
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’t 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.
Learning AngularJS?
I created a free 5 day mini email course to walk you through creating an app for scratch. Sign up here
Bryan says
For Firefox had to use this altered code instead, otherwise a module that was still just being declared via
ng-app
would fail to load:Andy says
+1
I sent a pull request, we’ll see..
Luis Perez says
Hey Andy, I haven’t gotten around to make this update to the actual codebase on GitHub. For now you would have to pull the code and make the changes Bryan mentioned.
Luis Perez says
I fixed the issue in the GitHub source code that was causing issues in Firefox. While Bryan’s solution will work the core of the issue was a bug in my code. An error I made while adapting Angular’s ngApp code.
Saiteja says
I think, in “Notes about ngModule” section, while talking about singular and plural versions of ng-module, it was entered “ng-model” for singular version instead of “ng-module”.
Luis Perez says
Thanks! I went ahead and made the fix.
Joe says
This looks really nice, but I’m having a fair bit of trouble getting it to work. I can use the ng-module directive with as many “modules” as I like (Ex. ng-module=”mod1, mod2, mod3″) without error the first time it appears in the HTML, but after that it doesn’t seem to work. Any ideas what I might be doing wrong? Probably just a stupid mistake I’m missing?
Javascript: (pastebin)
HTML: (pastebin also)
Luis Perez says
Hey Joe, I put your code into JS Fiddle and it works fine: http://jsfiddle.net/luisperezphd/1h60gLnd/
joe says
Hmmm… Ok. That jsfiddle works fine for me in google chrome, but not in firefox. I guess it’s just a firefox problem?
Luis Perez says
Have you tried Bryan’s solution? It’s the first comment on this post.
joe says
Oh wow. Now I feel like an idiot. I assumed that that solution only applied to the use of normal “ng-app”s in html that was using the ng-modules directive.
Works great in firefox now; thanks for putting up with my silly questions, and thanks for writing this awesome code.
Luis Perez says
Andy I fixed the issue and updated GitHub. The real core of the issue turned out to be something other than Bryan’s solution.
Iftikhar Ali says
Hi Luis. Very information article. I have one question though. In a large where each group/or development is building there own modules with potentially the same name. For this example, say Developer 1 has a module named “ModuleA” in Module1.js and Developer 2 has also developed a module with the same name “ModuleA” in Module2.js.
When I reference Module1.js and Module2.js, both modules will end up registering with angular.module(…) with the same name. Any suggestions to control that?
Luis Perez says
Hey Iftikhar. Unfortunately modules need unique names.
The key is how do you come up with a process so that developers always create unique names.
One approach is to look at how other languages do it. For example in Java the “full name” of the class is based on the name of the file and the folder it’s in. For example if you had a Java file called Bitmap.java in the folder MyArtStuff, the full name of the class would be MyArtStuff.Bitmap
Turns out AngularJS allows you to have dots (.) as part of your module name so you could essentially use the name convention.
For example if a developer create a module called “ModuleA” in the script “MainPage\Module1.js” they should name their module “MainPage.Module1.ModuleA”. Because each path and filename is unique in your app then your module name will be unique.
You would just have to get your developers to follow this convention.
Here’s a JSFiddle to illustrate what I mean:
http://jsfiddle.net/luisperezphd/bn2apxc0/1/
Hope that helps.
Iftikhar Ali says
Thanks. I was also thinking about some sort of namespace convention for module names. The problem is Angular doesn’t raise any explicit errors for duplicate module names – and combined with dynamic nature of JS, it just makes it harder to debug these kind of issues.
Pankaj says
I have tried you module script it works fine. but the problem arise when the I defined the module as
var app = angular.module(‘app’, [‘ngRoute’]);
With this, I got the error
Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.2.26/$injector/modulerr?p0=app&p1=Error%3A%20…%5D%20(http%3A%2F%2Fangular.local%2Fapp%2Flibs%2Fangular.min.js%3A18%3A387)
Do you have any idea why??
Luis Perez says
Hey Pankaj, I created a JSFiddle with two modules. Included on in the other and referenced a service with no problem. See: http://jsfiddle.net/luisperezphd/zg9oLnx3/ – are you sure you included the necessary ngRoute JavaScript files? If it’s still a problem can you create a JSFiddle so I can take a look at the exact code.
Fred Lima says
How can i use this ngModule with $sce to bind views on my html? Without the $sce my code is working fine, but if I use this, the script not work.
Luis Perez says
Hey Fred, it’s hard to determine the issue without more information. The best way is to create a JSFiddle with the minimal amount of code necessary to recreate the problem. If you do that I can take a further look. Thanks, Luis.
Tony says
Hi Luis, I’m a bit of a newbie to AngularJS so maybe this is a problem that only applies if you don’t code the way angularjs is supposed to be coded, but have you already noticed that ng-module will only work if the tag has no other strings, like i.e. id=’idname’ , class=’classname’ etc., in it?
Thanks a lot for ng-module anyways. It’s one of these small things the make such a big matter.
Greetings
Suzie Doe says
If it helps, the Uncaught Error: [$injector:modulerr] error you are getting might just be the Web Developer Tools in Google Chrome. If you uncheck the Enable JavaScript source maps in the Web Developer Settings or put the .map file in the same directory as your angularJS file it should stop howling and just work. Ran into this a couple of times before I found the answer googling.
Amr Hussien says
Hello Luis,
I tried to nest two ng-modules but the inner module is working fine and the outer’s functions is not defined, any idea ?
Francisco Javier Estrella Rodriguez says
Genial tu solución, no sabía de la limitación que tenía angujar con ng-app hasta que me encontré el error Error: [ng:areq] from angular controller. Lo curioso es que teniendo ese error la aplicación se ejecuta correctamente, digo visualmente no se ve afectada solo que en la consola del navegador se puede ver el error. Salvo eso lo demás funciona bien. Pero con tu solución creo que se resuelve el dichoso error.
Kiran says
Hi Luis,
one quick question.. can i use a child ng-module inside a parent ng-module? because i’m planning to use ng-view inside the parent ng-module so that i can load templates based on route. those templates may have their own ng-module. so will this template’s ng-module conflict with the parent one?
Luis Perez says
Unfortunately you cannot, you get some strange behaviour if you try. This is currently a limitation of AngularJS. You can though have nested controllers that can communicate with each other, I don’t know though if that accomplishes what you want.
chitgoks says
hi luis, how can i call bootstrap ng-module programmatically?
Luis Perez says
ng-module is a directive that serves as a convenient shortcut for calling angular.bootstrap() so you wouldn’t call the directive programmatically instead you would angular.bootstrap(). As long as you get can the element in some way you can “bootstrap” it. You could jQuery to get the element. There is an example in this article in the section titled “Do it programmatically using angular.bootstrap()”.
Sarbasish Mishra says
Sorry for the inconvenience but we can have multiple ng-app in a page. I do have example for it.
Luis Perez says
Hey Sarbasish, are you saying it is possible to have multiple ng-app’s on a single page? Also are you saying you have an example of that? Can you include the example? According to the documentation “Only one AngularJS application can be auto-bootstrapped per HTML document” https://docs.angularjs.org/api/ng/directive/ngApp
Saqib Nisar says
I have been trying to include multiple ng-Apps in a page but it only activates the first it gets in html page and ignores the rest. So yes “Only one AngularJS application can be auto-bootstrapped per HTML document”.
Luis Perez says
Correct AngularJS only allows one ngApp directive, to get around that I created ngModule, it works just like ngApp except that it allows you to have multiple ngModule per page.
Saqib Nisar says
Hi Luis,
i have found this tutorial so useful, you not only solved my problem also showed how we could use multiple Modules under a single html page.
Thanks a lot.
adam says
Hello, when i load in the file it says that angular is not defined on line 62 in your file. What am I doing wrong?
Luis Perez says
Adam, more than likely either the AngularJS script wasn’t included or it was included after ngModule was included. It’s hard to say without more detail. Can you recreate the scenario in Plunkr or JS Fiddle? I can help you further from there.
Dudi says
Nice article!
Tnx,
Shruthy says
You have stated finally that nesting is not applicable to parent – child – each referring to different modules, but the statement it sounds otherway.I hope it is to be corrected as “can’t” instead of “can”.
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”(‘can’t’) associate a child of that element with another module.
Luis Perez says
Great catch Shruthy, thanks! I went ahead and made that fix.
Shruthy says
Thanks :-). Happy to know.
Rafael says
I was loking for this, thank you!