Plusieurs contrôleurs avec AngularJS dans une application de page unique

102

Je veux savoir comment utiliser plusieurs contrôleurs pour une application d'une seule page. J'ai essayé de le comprendre et j'ai trouvé des questions très similaires aux miennes, mais il y a juste une tonne de réponses différentes pour résoudre un problème spécifique où vous finissez par ne pas utiliser plusieurs contrôleurs pour une seule application de page.

Est-ce parce qu'il ne serait pas sage d'utiliser plusieurs contrôleurs pour une seule page? Ou n'est-ce pas possible?

Disons que j'ai déjà un contrôleur de carrousel d'images qui fonctionne sur la page principale, mais j'apprends ensuite à (disons) utiliser des modaux et j'ai besoin d'un nouveau contrôleur pour cela également (ou de toute autre chose dont j'ai besoin d'un contrôleur). Que vais-je faire alors?

J'ai vu des réponses à d'autres questions où ils posent à peu près les mêmes choses que moi et les gens répondent "* OMG. Pourquoi feriez-vous même cela, faites-le simplement ...".

Quelle est la meilleure façon de procéder ou comment procédez-vous?

Éditer

Beaucoup d'entre vous répondent simplement pour déclarer deux contrôleurs, puis utiliser ng-controller pour l'appeler. J'utilise ce morceau de code ci-dessous, puis j'appelle MainCtrl avec ng-controller.

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

Pourquoi ai-je même besoin de définir un contrôleur ici si je peux simplement utiliser ng-controller sans lui? C'est ce qui m'a dérouté. (et vous ne pouvez pas ajouter deux contrôleurs de cette façon, je pense ...)

TarkaDaal
la source
Je ne pense pas que je peux déclarer 2 contrôleurs pour un seul fichier .html? comment cela se fait-il? when: /home, controller: MainCtrl. vous ne pouvez pas ajouter plus que cela, ou vous voulez simplement l'appeler avec le ng-controller?
3
@Mosho, vous faites l'étape 1, l'étape 2, mais n'expliquez pas comment ni pourquoi. Si c'est aussi simple que cela, veuillez expliquer comment. C'est comme dire utiliser AngularJS, Terminé. pouvez-vous élaborer / expliquer? Ou comme c'est à partir de juin, ils peuvent ne pas répondre, quelqu'un d'autre peut-il expliquer?
redfox05

Réponses:

96

Quel est le problème? Pour utiliser plusieurs contrôleurs, utilisez simplement plusieurs directives ngController:

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

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

Vous devrez avoir les contrôleurs disponibles dans votre module d'application, comme d'habitude.

La façon la plus simple de le faire pourrait être aussi simple que de déclarer les fonctions du contrôleur comme ceci:

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

function menuController($scope) {
   // stuff here
}
J. Bruni
la source
2
Est-ce ainsi que cela se fait? when /home, controller: MainCtrl
8
Si vous sortez d'exemples qui placent le "ng-app" dans chaque DIV à côté de votre "ng-controller", essayez de déplacer un seul "ng-app" dans la balise "body" (et supprimez le par-div " ng-app ") si seulement votre premier contrôleur fonctionne. (Je me souviens de cela de mes jours de débutant Angular.)
ftexperts
1
Le seul problème que j'ai eu avec l'utilisation ng-controllerdans plusieurs éléments DOM, c'est par exemple dans un scénario où de nombreux éléments sont imprimés sur le DOM via ng-repeat. Disons que chacun d'entre eux appelle à ng-controller="myController. À partir de certains des journaux de console que j'ai vus dans mon application, myControllerse réinitialise avec CHAQUE élément rendu dans le DOM qui l'appelle .... Peut-être que j'ai foiré quelque chose dans mon utilisation particulière, ou peut-être au-delà de la portée des OP question, mais curieux de savoir si d'autres en ont fait l'expérience non plus ....
twknab
91

Je pense que vous manquez le sens "application d'une seule page".

Cela ne signifie pas que vous aurez physiquement un fichier .html, mais plutôt un index.htmlfichier principal et plusieurs fichiers .html NESTED. Alors pourquoi une seule page? Parce que de cette façon, vous ne chargez pas les pages de la manière standard (c'est-à-dire un appel de navigateur qui rafraîchit complètement la page entière) mais vous chargez simplement la partie de contenu en utilisant Angular / Ajax. Comme vous ne voyez pas le scintillement entre les changements de page, vous avez l'impression que vous n'avez pas bougé de la page. Ainsi, vous avez l'impression de rester sur une seule page.

Maintenant, je suppose que vous voulez avoir PLUSIEURS contenus pour votre application SEULE PAGE: (par exemple) accueil, contacts, portfolio et magasin. Votre application à page unique / à contenu multiple (de manière angulaire) sera organisée de cette façon:

  • index.html: contient l'en-tête <ng-view>et le pied de page
  • contacts.html: contient le formulaire de contact (pas d'en-tête, pas de pied de page)
  • portfolio.html: contient les données du portefeuille (pas d'en-tête ni de pied de page)
  • store.html: contient le magasin, pas d'en-tête, pas de pied de page.

Vous êtes dans l'index, vous cliquez sur le menu appelé "contacts" et que se passe-t-il? Angular remplace la <ng-view>balise par le contacts.htmlcode

Comment y parvenir? avec ngRoute, comme vous le faites, vous aurez quelque chose comme:

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'   
        });      
});

Cela appellera le bon html en lui passant le bon contrôleur (veuillez noter: ne spécifiez pas de ng-controllerdirective contacts.htmlsi vous utilisez des routes )

Ensuite, bien sûr, vous pouvez déclarer autant de directives ng-controller dans votre page contacts.html. Ce seront les contrôleurs enfants de ContactCtrl(héritant ainsi de lui). Mais pour une seule route, à l'intérieur du routeProvider, vous pouvez déclarer un seul contrôleur qui agira comme "contrôleur père de la vue partielle".

EDIT Imaginez les templates / contacts.html suivants

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

ce qui précède routeProviderinjectera un contrôleur dans votre div contenant. Fondamentalement, le html ci-dessus devient automatiquement:

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

Quand je dis que vous pouvez avoir d'autres contrôleurs, cela signifie que vous pouvez brancher des contrôleurs sur des éléments DOM internes, comme suit:

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

J'espère que cela clarifiera un peu les choses.

UNE

Adhara
la source
Donc, si vous déclarez un exemple à l' <div ng-controller="FancyStuffController">intérieur d'une balise de corps qui a déjà un contrôleur associé, <body ng-controller="BodyController">cela fonctionnera? $apply already in progressJe reçois des erreurs quand je fais cela, mais je pense que c'est lié à dpd.js. Pour ne pas entrer dans cela, je pense que c'est juste le charger deux fois ou quelque chose du genre, je ne sais pas comment, mais mon utilisation du contrôleur tente peut-être de recharger cela.
blamb
1
@BrianThomas Bien sûr, cela devrait fonctionner. Attention: si vous voulez injecter un contrôleur dans une vue, utilisez le $ routeProvider, n'écrivez pas ng-controller sur la balise div.
Adhara
9

Je suis actuellement en train de créer une application d'une seule page. Voici ce que j'ai jusqu'ici qui, je crois, répondrait à votre question. J'ai un modèle de base (base.html) qui contient un div avec la ng-viewdirective. Cette directive indique à angular où placer le nouveau contenu. Notez que je suis moi-même nouveau dans angularjs, donc je ne dis pas que c'est la meilleure façon de le faire.

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
la source
en effet, j'utilise également ng-view pour pouvoir utiliser un main.html dans mon index.html. mais vous avez / home et / about et les deux contrôleurs sont pour chacun. Je n'ai que index.html avec ng-view dans mon main.html. Je peux configurer deux contrôleurs pour le main.html comme vous l'avez fait avecwhen /home
Jetez un œil à la première réponse de cet article: stackoverflow.com/questions/17354568/…
Austin
eh bien, je sais comment définir plus whende. Je veux deux contrôleurs pour un seul modèle. Ou maman j'ai mal compris ce que tu essayais de me dire?
Au lieu d'utiliser des contrôleurs, essayez d'utiliser des directives. Vous pouvez utiliser plusieurs directives sur un seul modèle. Si vous souhaitez toujours utiliser vos contrôleurs, voici une vidéo sur la façon dont les directives peuvent être reliées aux contrôleurs: egghead.io/lessons/angularjs-directives-talking-to-controllers
Austin
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 a accès au menu div. Et widgetController a accès aux deux.

Muhammad Awais
la source
Cela semble simple, mais n'a pas de votes positifs. Est-ce une bonne pratique ou une mauvaise?
redfox05
1
ce que vous allez faire, si vous avez plusieurs pages 100+ contrôleurs! ... pratique ambiguë.
ArifMustafa
3

Nous pouvons simplement déclarer plus d'un contrôleur dans le même module. Voici un exemple:

  <!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
la source
2

Je viens de mettre une simple déclaration de l'application

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

Ensuite, j'ai construit un service et deux contrôleurs

Pour chaque contrôleur j'avais une ligne dans le JS

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

Et dans le HTML, j'ai déclaré la portée de l'application dans un div environnant

<div ng-app="app">

et chaque champ d'application de contrôleur séparément dans leur propre div environnant (dans le div de l'application)

<div ng-controller="EditableRowCtrl">

Cela a bien fonctionné

Pat Capozzi
la source
0

Vous pourriez également avoir intégré toutes vos vues de modèle dans votre fichier html principal. Par exemple:

<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>

De cette façon, si chaque modèle nécessite un contrôleur différent, vous pouvez toujours utiliser le routeur angulaire. Voir ce plunk pour un exemple de travail http://plnkr.co/edit/9X0fT0Q9MlXtHVVQLhgr?p=preview

De cette façon, une fois que l'application est envoyée du serveur à votre client, elle est complètement autonome en supposant qu'elle n'a pas besoin de faire de demandes de données, etc.

Daimonos
la source