Varios controladores con AngularJS en la aplicación de una sola página

102

Lo que quiero saber es cómo usar varios controladores para una aplicación de una sola página. Intenté resolverlo y encontré preguntas muy similares a las mías, pero solo hay un montón de respuestas diferentes que resuelven un problema específico en el que terminas sin usar varios controladores para una aplicación de una sola página.

¿Se debe a que no sería prudente utilizar varios controladores para una sola página? ¿O simplemente no es posible?

Digamos que ya tengo un controlador de carrusel de imágenes increíble que funciona en la página principal, pero luego aprendo cómo (digamos) usar modales y también necesito un nuevo controlador para eso (o cualquier otra cosa que necesite un controlador). ¿Qué haré entonces?

He visto algunas respuestas a otras preguntas en las que preguntan sobre casi las mismas cosas que yo y la gente responde "* Dios mío. ¿Por qué harías eso, solo haz esto ...".

¿Cuál es la mejor forma o cómo se hace?

Editar

Muchos de ustedes están respondiendo simplemente declarar dos controladores y luego usar ng-controller para llamarlo. Utilizo este código a continuación y luego llamo a MainCtrl con ng-controller.

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

¿Por qué necesito configurar un controlador aquí si puedo usar ng-controller sin él? Esto es lo que me confundió. (y no se pueden agregar dos controladores de esta manera, creo ...)

TarkaDaal
fuente
¿No creo que pueda declarar 2 controladores para un solo archivo .html? como se hace eso when: /home, controller: MainCtrl. no puede agregar más que eso, o du, ¿quiere decir simplemente llamarlo con el controlador ng?
3
@Mosho, paso 1, paso 2, hecho, pero no explique cómo ni por qué. Si es así de simple, explique cómo. Eso es como decir use AngularJS, Listo. ¿Puedes elaborar / explicar? O como es de junio, es posible que no respondan, ¿alguien más puede explicarlo?
redfox05

Respuestas:

96

¿Cuál es el problema? Para usar varios controladores, simplemente use varias directivas ngController:

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

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

Deberá tener los controladores disponibles en su módulo de aplicación, como de costumbre.

La forma más básica de hacerlo podría ser tan simple como declarar las funciones del controlador de esta manera:

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

function menuController($scope) {
   // stuff here
}
J. Bruni
fuente
2
¿Es así como se hace esto? Estoy usando esowhen /home, controller: MainCtrl
8
Si está saliendo de ejemplos que colocan la "ng-app" en cada DIV junto a su "ng-controller", intente mover solo una "ng-app" a la etiqueta "body" (y elimine el per-div " ng-app ") si solo funciona su primer controlador. (Recuerdo esto de mis días de novato en Angular.)
ftexperts
1
El único problema que he tenido con el uso ng-controllerde varios elementos DOM, es decir, en un escenario en el que tengo MUCHOS elementos imprimiendo en el DOM a través de ng-repeat. Digamos que cada uno de estos hace una llamada a ng-controller="myController. De algunos de los registros de la consola que he visto en mi aplicación, se myControllerreinicializa con CADA elemento que se representa en el DOM que lo llama ... Tal vez arruiné algo en mi uso particular, o tal vez más allá del alcance de los OP pregunta, pero curioso si otros han experimentado eso en absoluto ....
twknab
91

Creo que le falta el significado de "aplicación de una sola página".

Eso no significa que tendrá físicamente un .html, sino que tendrá un index.htmlarchivo .html principal y varios NESTED. Entonces, ¿por qué una aplicación de una sola página? Porque de esta manera no carga las páginas de la forma estándar (es decir, una llamada del navegador que actualiza completamente la página completa) sino que simplemente carga la parte de contenido usando Angular / Ajax. Como no ve el parpadeo entre los cambios de página, tiene la impresión de que no se ha movido de la página. Por lo tanto, se siente como si estuviera en una sola página.

Ahora, supongo que desea tener MÚLTIPLES contenidos para su aplicación de UNA PÁGINA: (por ejemplo) casa, contactos, cartera y tienda. Su aplicación de una sola página / contenido múltiple (la forma angular) se organizará de esta manera:

  • index.html: contiene encabezado <ng-view>y pie de página
  • contacts.html: contiene el formulario de contacto (sin encabezado, sin pie de página)
  • portfolio.html: contiene los datos de la cartera (sin encabezado ni pie de página)
  • store.html: contiene la tienda, sin encabezado ni pie de página.

Estás en el índice, haces clic en el menú llamado "contactos" y ¿qué pasa? Angular reemplaza la <ng-view>etiqueta con el contacts.htmlcódigo

¿Cómo logras eso? con ngRoute, como lo estás haciendo, tendrás algo como:

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

Esto llamará al html correcto y le pasará el controlador correcto (tenga en cuenta: no especifique la ng-controllerdirectiva contacts.htmlsi está utilizando rutas )

Luego, por supuesto, puede declarar tantas directivas ng-controller dentro de su página Contacts.html. Aquellos serán controladores secundarios de ContactCtrl(heredando así). Pero para una sola ruta, dentro de routeProvider, puede declarar un solo controlador que actuará como el "controlador padre de la vista parcial".

EDITAR Imagine las siguientes plantillas / contactos.html

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

lo anterior routeProviderinyectará un controlador en su div contenedor. Básicamente, el html anterior se convierte automáticamente en:

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

Cuando digo que puede tener otros controladores, significa que puede conectar controladores a elementos DOM internos, como se muestra a continuación:

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

Espero que esto aclare un poco las cosas.

UNA

Adhara
fuente
Entonces, si declaras un por ejemplo <div ng-controller="FancyStuffController">dentro de una etiqueta de cuerpo que ya tiene un controlador aplicado, por ejemplo, ¿ <body ng-controller="BodyController">esto funcionará? Recibo errores sobre $apply already in progresscuándo hago esto, pero creo que está relacionado con dpd.js. Para no entrar en eso, creo que es solo cargarlo dos veces o algo así, no estoy seguro de cómo, pero mi uso del controlador puede estar intentando recargar eso.
culpa
1
@BrianThomas Seguro, eso debería funcionar. Tenga cuidado: si desea inyectar un controlador en una vista, use $ routeProvider, no escriba ng-controller en la etiqueta div.
Adhara
9

Actualmente estoy en el proceso de crear una aplicación de una sola página. Esto es lo que tengo hasta ahora y creo que respondería a su pregunta. Tengo una plantilla base (base.html) que tiene un div con la ng-viewdirectiva en él. Esta directiva le dice a angular dónde colocar el nuevo contenido. Tenga en cuenta que yo mismo soy nuevo en angularjs, así que de ninguna manera estoy diciendo que esta sea la mejor manera de hacerlo.

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
fuente
de hecho, también estoy usando ng-view para poder usar un main.html en mi index.html. pero tienes / home y / about y los dos controladores son para cada uno. Solo tengo index.html con ng-view en mi main.html. Puedo configurar dos controladores para main.html como lo ha hecho conwhen /home
Eche un vistazo a la primera respuesta de esta publicación: stackoverflow.com/questions/17354568/…
Austin
bueno, sé cómo configurar más when. Quiero dos controladores para una sola plantilla. ¿O maby entendí mal lo que estabas tratando de decirme?
En lugar de usar controladores, intente usar directivas. Puede utilizar muchas directivas en una sola plantilla. Si aún desea usar sus controladores, aquí hay un video sobre cómo las directivas pueden vincularse con los controladores: 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 tiene acceso al menú div. Y widgetController tiene acceso a ambos.

Muhammad Awais
fuente
Esto parece sencillo, pero no tiene votos a favor. ¿Es esta la mejor práctica o es mala?
redfox05
1
¡Qué vas a hacer, si tienes muchas páginas de más de 100 controladores! ... práctica ambigua.
ArifMustafa
3

Simplemente podemos declarar más de un controlador en el mismo módulo. He aquí un ejemplo:

  <!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
fuente
2

Solo puse una simple declaración de la aplicación.

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

Luego construí un servicio y dos controladores

Para cada controlador tenía una línea en el JS

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

Y en el HTML declaré el alcance de la aplicación en un div circundante

<div ng-app="app">

y cada alcance del controlador por separado en su propio div circundante (dentro del div de la aplicación)

<div ng-controller="EditableRowCtrl">

Esto funcionó bien

Pat Capozzi
fuente
0

También podría haber incrustado todas las vistas de su plantilla en su archivo html principal. Por ejemplo:

<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 esta manera, si cada plantilla requiere un controlador diferente, aún puede usar el enrutador angular. Vea este plunk para un ejemplo funcional http://plnkr.co/edit/9X0fT0Q9MlXtHVVQLhgr?p=preview

De esta manera, una vez que la aplicación se envía desde el servidor a su cliente, es completamente autónoma asumiendo que no necesita realizar ninguna solicitud de datos, etc.

Daimonos
fuente