103

I want to know is how to use multiple controllers for a single page application. I have tried to figure it out and I've found questions very similar to mine, but there is just a ton of different answers solving a specific problem where you end up not using multiple controllers for a single page app.

Is that because it would not be wise to use multiple controllers for a single page? Or is it just not possible?

Let's say I already have a kick-ass image carousel controller working the main page, but then I learn how to (let's say) use modals and I need a new controller for that as well (or any other thing I need a controller). What will I do then?

I have seen some answers to other questions where they ask about almost the same things as me and people answer "*OMG. Why would you even do that, just do this...".

What is the best way, or how do you do it?

Edit

Many of you are answering to just declare two controllers and then use ng-controller to call it. I use this bit of code below and then call MainCtrl with ng-controller.

app.config(function($routeProvider, $locationProvider) {                        
  $routeProvider                                                                
       .when('/', {                                            
         templateUrl: "templates/main.html",                                               
         controller:'MainCtrl',                                
        })                                                                      
        .otherwise({                      
            template: 'does not exists'   
        });      
});

Why do I even need to set a controller here if I can just use ng-controller without it? This is what confused me. (and you can't add two controllers this way, I think...)

TarkaDaal
  • 16,152
  • 7
  • 32
  • 50
  • I dont think I can declare 2 controllers for a single .html file? how is that done? `when: /home, controller: MainCtrl`. cannot add any more than that, or du you mean to just call it with the ng-controller? –  Jun 20 '14 at 05:53
  • 3
    @Mosho, you step 1, step 2, done, but don't explain how or why. If its that simple, then please explain how. That's like saying use AngularJS, Done. can you elaborate/explain? Or as its from June, they may not reply, can someone else explain? – redfox05 Dec 02 '15 at 10:57

7 Answers7

99

What is the problem? To use multiple controllers, just use multiple ngController directives:

<div class="widget" ng-controller="widgetController">
    <p>Stuff here</p>
</div>

<div class="menu" ng-controller="menuController">
    <p>Other stuff here</p>
</div>

You will need to have the controllers available in your application module, as usual.

The most basic way to do it could be as simple as declaring the controller functions like this:

function widgetController($scope) {
   // stuff here
}

function menuController($scope) {
   // stuff here
}
J. Bruni
  • 19,262
  • 11
  • 70
  • 90
  • 2
    Is this how this is done? Im using that `when /home, controller: MainCtrl` –  Jun 20 '14 at 05:55
  • 8
    If you're coming off examples that put the "ng-app" in each DIV next to your "ng-controller", try moving just one "ng-app" into the "body" tag (and delete the per-div "ng-app" tags) if only your first controller works. (I remember this from my Angular newbie days.) – ftexperts Oct 28 '14 at 04:11
  • 1
    The only issue I've had with using `ng-controller` in multiple DOM elements, is say in a scenario where I have MANY items printing to the DOM via `ng-repeat`. Let's say each of these makes a call to `ng-controller="myController`. From some of the console logs I have seen in my application, `myController` re-initializes with EACH element being rendered in the DOM that calls to it....Maybe I screwed something up in my particular usage, or maybe beyond the scope of the OP's question, but curious if others have experienced that at all either.... – twknab Aug 31 '17 at 07:04
91

I think you are missing the "single page app" meaning.

That doesn't mean you will physically have one .html, instead you will have one main index.html and several NESTED .html file. So why single page app? Because this way you do not load pages the standard way (i.e. browser call that completely refreshes the full page) but you just load the content part using Angular/Ajax. Since you do not see the flickering between page changes, you have the impression that you didn't move from the page. Thus, you feel like you are staying on a single page.

Now, I'm assuming that you want to have MULTIPLE contents for your SINGLE PAGE app: (e.g.) home, contacts, portfolio and store. Your single page/multiple content app (the angular way) will be organized this way:

  • index.html: contains header, <ng-view> and footer
  • contacts.html:contains the contact form (no header, no footer)
  • portfolio.html:contains the portfolio data (no header no footer)
  • store.html: contains the store, no header no footer.

You are in index, you click on the menu called "contacts" and what happens? Angular replaces the <ng-view> tag with the contacts.html code

How do you achieve that? with ngRoute, as you are doing, you will have something like:

app.config(function($routeProvider, $locationProvider) {                        
  $routeProvider                                                                
       .when('/', {                                            
         templateUrl: "templates/index.html",                                               
         controller:'MainCtrl',                                
        })
        .when('/contacts', {                                            
         templateUrl: "templates/contacts.html",                                               
         controller:'ContactsCtrl',                                
        })                                                                 
        .otherwise({                      
            template: 'does not exists'   
        });      
});

This will call the right html passing the right controller to it (please note: do not specify ng-controller directive in contacts.html if you are using routes)

Then, of course, you can declare as many ng-controller directives inside your contacts.html page. Those will be child controllers of ContactCtrl (thus inheriting from it). But for a single route, inside the routeProvider, you can declare a single Controller that will act as the "Father controller of the partial view".

EDIT Imagine the following templates/contacts.html

<div>
    <h3>Contacts</h3>
    <p>This is contacts page...</p>
</div>

the above routeProvider will inject a controller into your containing div. Basically the above html automaticaly becomes:

<div ng-controller="ContactsCtrl">
    <h3>Contacts</h3>
    <p>This is contacts page...</p>
</div>

When I say you can have others controllers, means that you can plug controllers to inner DOM elements, as the following:

<div>
    <h3>Contacts</h3>
    <p ng-controller="anotherCtrl">Hello {{name}}! This is contacts page...     
    </p>
</div>

I hope this clarify things a bit.

A

wally
  • 3,147
  • 21
  • 31
Adhara
  • 963
  • 6
  • 9
  • So if you declare a e.g. `
    ` inside a body tag that has a controller appied to it already e.g. `` this will work? im getting errors about `$apply already in progress` when i do this, but i think its related to dpd.js. Not to get into that ,i think its just loading it twice or something, not sure how, but my usage of the controller may be trying to reload that.
    – blamb Aug 08 '15 at 03:48
  • 1
    @BrianThomas Sure, that should work. Be careful: if you want to inject a controller in a view, use the $routeProvider, do not write ng-controller on the div tag. – Adhara Oct 28 '15 at 14:57
9

I'm currently in the process of building a single page application. Here is what I have thus far that I believe would be answering your question. I have a base template (base.html) that has a div with the ng-view directive in it. This directive tells angular where to put the new content in. Note that I'm new to angularjs myself so I by no means am saying this is the best way to do it.

app = angular.module('myApp', []);                                                                             

app.config(function($routeProvider, $locationProvider) {                        
  $routeProvider                                                                
       .when('/home/', {                                            
         templateUrl: "templates/home.html",                                               
         controller:'homeController',                                
        })                                                                      
        .when('/about/', {                                       
            templateUrl: "templates/about.html",     
            controller: 'aboutController',  
        }) 
        .otherwise({                      
            template: 'does not exists'   
        });      
});

app.controller('homeController', [              
    '$scope',                              
    function homeController($scope,) {        
        $scope.message = 'HOME PAGE';                  
    }                                                
]);                                                  

app.controller('aboutController', [                  
    '$scope',                               
    function aboutController($scope) {        
        $scope.about = 'WE LOVE CODE';                       
    }                                                
]); 

base.html

<html>
<body>

    <div id="sideMenu">
        <!-- MENU CONTENT -->
    </div>

    <div id="content" ng-view="">
        <!-- Angular view would show here -->
    </div>

<body>
</html>
Austin
  • 3,626
  • 4
  • 28
  • 48
  • indeed, I am also using ng-view so that I can use a main.html in my index.html. but you have /home and /about and the two controllers are for each one. I only have index.html with ng-view to my main.html. I cannet set two controllers for the main.html like you have done with `when /home` –  Jun 20 '14 at 05:50
  • Take a look at the first answer from this post: http://stackoverflow.com/questions/17354568/routing-in-angularjs-for-mutliple-controllers?answertab=votes#tab-top – Austin Jun 20 '14 at 12:36
  • well, I know how to set more `when`'s. I want two controllers for a single template. Or maby I misunderstood what you were trying to tell me? –  Jun 20 '14 at 13:55
  • Instead of using controllers try using directives. You can use many directives on a single template. If you still want to use your controllers here is a video on how directives can link up with controllers: https://egghead.io/lessons/angularjs-directives-talking-to-controllers – Austin Jun 20 '14 at 15:45
6
<div class="widget" ng-controller="widgetController">
    <p>Stuff here</p>
</div>

<div class="menu" ng-controller="menuController">
    <p>Other stuff here</p>
</div>
///////////////// OR ////////////


  <div class="widget" ng-controller="widgetController">
    <p>Stuff here</p>
    <div class="menu" ng-controller="menuController">
        <p>Other stuff here</p>
    </div>
</div>

menuController have access for menu div. And widgetController have access to both.

Muhammad Awais
  • 3,310
  • 1
  • 34
  • 32
  • This seems straightforward, but has no upvotes. Is this best practise, or bad? – redfox05 Dec 02 '15 at 10:54
  • 1
    what you will do, if you have many pages 100+ controllers!...ambiguous practice. – ArifMustafa Jun 30 '18 at 15:19
  • This seems odd if you want to do something within the div class="menu". That would basically mean you are using both widgetController and menuController. That's kind of using global variables in an application. It would be hard to debug and it would be easier to make a mistake. So I would go with the first option. – bestprogrammerintheworld Mar 10 '21 at 09:18
3

We can simply declare more than one Controller in the same module. Here's an example:

  <!DOCTYPE html>
    <html>

    <head>
       <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js">
       </script>
      <title> New Page </title>


    </head> 
    <body ng-app="mainApp"> <!-- if we remove ng-app the add book button [show/hide] will has no effect --> 
      <h2> Books </h2>

    <!-- <input type="checkbox" ng-model="hideShow" ng-init="hideShow = false"></input> -->
    <input type = "button" value = "Add Book"ng-click="hideShow=(hideShow ? false : true)"> </input>
     <div ng-app = "mainApp" ng-controller = "bookController" ng-if="hideShow">
             Enter book name: <input type = "text" ng-model = "book.name"><br>
             Enter book category: <input type = "text" ng-model = "book.category"><br>
             Enter book price: <input type = "text" ng-model = "book.price"><br>
             Enter book author: <input type = "text" ng-model = "book.author"><br>


             You are entering book: {{book.bookDetails()}}
     </div>

    <script>
             var mainApp = angular.module("mainApp", []);

             mainApp.controller('bookController', function($scope) {
                $scope.book = {
                   name: "",
                   category: "",
                   price:"",
                   author: "",


                   bookDetails: function() {
                      var bookObject;
                      bookObject = $scope.book;
                      return "Book name: " + bookObject.name +  '\n' + "Book category: " + bookObject.category + "  \n" + "Book price: " + bookObject.price + "  \n" + "Book Author: " + bookObject.author;
                   }

                };
             });
    </script>

    <h2> Albums </h2>
    <input type = "button" value = "Add Album"ng-click="hideShow2=(hideShow2 ? false : true)"> </input>
     <div ng-app = "mainApp" ng-controller = "albumController" ng-if="hideShow2">
             Enter Album name: <input type = "text" ng-model = "album.name"><br>
             Enter Album category: <input type = "text" ng-model = "album.category"><br>
             Enter Album price: <input type = "text" ng-model = "album.price"><br>
             Enter Album singer: <input type = "text" ng-model = "album.singer"><br>


             You are entering Album: {{album.albumDetails()}}
     </div>

    <script>
             //no need to declare this again ;)
             //var mainApp = angular.module("mainApp", []);

             mainApp.controller('albumController', function($scope) {
                $scope.album = {
                   name: "",
                   category: "",
                   price:"",
                   singer: "",

                   albumDetails: function() {
                      var albumObject;
                      albumObject = $scope.album;
                      return "Album name: " + albumObject.name +  '\n' + "album category: " + albumObject.category + "\n" + "Book price: " + albumObject.price + "\n" + "Album Singer: " + albumObject.singer;
                   }
                };
             });
    </script>

    </body>
    </html>
Abdallah Okasha
  • 1,227
  • 14
  • 18
2

I just put one simple declaration of the app

var app = angular.module("app", ["xeditable"]);

Then I built one service and two controllers

For each controller I had a line in the JS

app.controller('EditableRowCtrl', function ($scope, CRUD_OperService) {

And in the HTML I declared the app scope in a surrounding div

<div ng-app="app">

and each controller scope separately in their own surrounding div (within the app div)

<div ng-controller="EditableRowCtrl">

This worked fine

pat capozzi
  • 1,294
  • 1
  • 18
  • 16
0

You could also have embed all of your template views into your main html file. For Example:

<body ng-app="testApp">
  <h1>Test App</h1>
  <div ng-view></div>
  <script type = "text/ng-template" id = "index.html">
    <h1>Index Page</h1>
    <p>{{message}}</p>
  </script>
  <script type = "text/ng-template" id = "home.html">
    <h1>Home Page</h1>
    <p>{{message}}</p>
  </script>
</body>

This way if each template requires a different controller then you can still use the angular-router. See this plunk for a working example http://plnkr.co/edit/9X0fT0Q9MlXtHVVQLhgr?p=preview

This way once the application is sent from the server to your client, it is completely self contained assuming that it doesn't need to make any data requests, etc.