Vários controladores com AngularJS em um aplicativo de página única

102

Eu quero saber é como usar vários controladores para um único aplicativo de página. Eu tentei descobrir e encontrei perguntas muito semelhantes às minhas, mas há apenas uma tonelada de respostas diferentes resolvendo um problema específico em que você acaba não usando vários controladores para um único aplicativo de página.

É porque não seria sensato usar vários controladores para uma única página? Ou simplesmente não é possível?

Digamos que eu já tenha um controlador de carrossel de imagens incrível trabalhando na página principal, mas depois aprendo a (digamos) usar modais e preciso de um novo controlador para isso também (ou qualquer outra coisa que precise de um controlador). O que vou fazer então?

Eu vi algumas respostas a outras perguntas onde eles perguntam quase as mesmas coisas que eu e as pessoas respondem "* OMG. Por que você faria isso, apenas faça isso ...".

Qual é a melhor maneira ou como você faz isso?

Editar

Muitos de vocês estão respondendo apenas para declarar dois controladores e usar o ng-controller para chamá-los. Eu uso este trecho de código abaixo e chamo MainCtrl com ng-controller.

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

Por que preciso definir um controlador aqui se posso apenas usar o ng-controlador sem ele? Isso é o que me confundiu. (e você não pode adicionar dois controladores desta forma, eu acho ...)

TarkaDaal
fonte
Eu não acho que posso declarar 2 controladores para um único arquivo .html? como isso é feito? when: /home, controller: MainCtrl. não pode adicionar mais do que isso, ou você quer apenas chamá-lo com o ng-controlador?
3
@Mosho, você passo 1, passo 2, pronto, mas não explica como ou por quê. Se for simples assim, explique como. É como dizer use AngularJS, Pronto. você pode elaborar / explicar? Ou como é de junho, eles podem não responder, alguém pode explicar?
redfox05

Respostas:

96

Qual é o problema? Para usar vários controladores, basta usar várias diretivas ngController:

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

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

Você precisará ter os controladores disponíveis em seu módulo de aplicação, como de costume.

A maneira mais básica de fazer isso pode ser tão simples quanto declarar as funções do controlador como esta:

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

function menuController($scope) {
   // stuff here
}
J. Bruni
fonte
2
É assim que isso é feito? Estou usando issowhen /home, controller: MainCtrl
8
Se você está saindo de exemplos que colocam o "ng-app" em cada DIV próximo ao "ng-controller", tente mover apenas um "ng-app" para a tag "body" (e exclua o per-div " tags ng-app ") se apenas o seu primeiro controlador funcionar. (Lembro-me disso dos meus dias de novato no Angular.)
ftexperts
1
O único problema que tive com o uso ng-controllerde vários elementos DOM é, digamos, em um cenário onde há MUITOS itens sendo impressos no DOM via ng-repeat. Digamos que cada um desses faça uma chamada para ng-controller="myController. De alguns dos logs de console que vi em meu aplicativo, myControllerreinicializa com CADA elemento sendo renderizado no DOM que o chama ... Talvez eu tenha estragado algo em meu uso específico, ou talvez além do escopo dos OPs questão, mas curioso se outros já experimentaram isso ...
twknab
91

Acho que está faltando o significado de "aplicativo de página única".

Isso não significa que você terá fisicamente um .html, em vez disso, terá um index.htmlarquivo principal e vários arquivos .html NESTED. Então, por que um aplicativo de página única? Porque desta forma você não carrega as páginas da maneira padrão (ou seja, uma chamada de navegador que atualiza completamente a página inteira), mas apenas carrega a parte do conteúdo usando Angular / Ajax. Como você não vê a oscilação entre as mudanças de página, tem a impressão de que não saiu da página. Assim, você sente que está permanecendo em uma única página.

Agora, estou assumindo que você deseja ter MÚLTIPLOS conteúdos para o seu aplicativo de ÚNICA PÁGINA: (por exemplo) casa, contatos, portfólio e loja. Seu aplicativo de página única / conteúdo múltiplo (a maneira angular) será organizado desta forma:

  • index.html: contém cabeçalho <ng-view>e rodapé
  • contacts.html: contém o formulário de contato (sem cabeçalho, sem rodapé)
  • portfolio.html: contém os dados do portfólio (sem cabeçalho e rodapé)
  • store.html: contém a loja, sem cabeçalho e rodapé.

Você está no índice, clica no menu chamado “contatos” e o que acontece? Angular substitui a <ng-view>tag pelo contacts.htmlcódigo

Como você consegue isso? com ngRoute, como você está fazendo, você terá 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'   
        });      
});

Isso irá chamar o html certo passando o controlador certo para ele (por favor, note: não especifique a ng-controllerdiretiva contacts.htmlse você estiver usando rotas )

Então, é claro, você pode declarar quantas diretivas ng-controller dentro de sua página contacts.html. Esses serão controladores filhos de ContactCtrl(herdando assim). Mas para uma única rota, dentro do routeProvider, você pode declarar um único Controlador que atuará como o "Controlador pai da visão parcial".

EDITAR Imagine os seguintes templates / contacts.html

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

o acima routeProviderirá injetar um controlador em seu div. Basicamente, o html acima torna-se automaticamente:

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

Quando digo que você pode ter outros controladores, significa que você pode conectar controladores a elementos DOM internos, da seguinte forma:

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

Espero que isso esclareça um pouco as coisas.

UMA

Adhara
fonte
Então, se você declarar um, por exemplo, <div ng-controller="FancyStuffController">dentro de uma tag de corpo que já tenha um controlador aplicado a ela, por exemplo, <body ng-controller="BodyController">isso funcionará? Estou recebendo erros sobre $apply already in progressquando faço isso, mas acho que está relacionado ao dpd.js. Para não entrar nisso, acho que é apenas carregá-lo duas vezes ou algo assim, não tenho certeza como, mas meu uso do controlador pode estar tentando recarregar isso.
blamb
1
@BrianThomas Claro, isso deve funcionar. Cuidado: se você quiser injetar um controlador em uma visualização, use o $ routeProvider, não escreva ng-controller no tag div.
Adhara,
9

Atualmente, estou no processo de construção de um aplicativo de página única. Aqui está o que eu tenho até agora que acredito que estaria respondendo à sua pergunta. Eu tenho um modelo base (base.html) que tem uma div com a ng-viewdiretiva nele. Essa diretiva informa ao angular onde colocar o novo conteúdo. Observe que também sou novo no angularjs, portanto, de forma alguma estou dizendo que essa é a melhor maneira de fazer isso.

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
fonte
na verdade, também estou usando o ng-view para poder usar um main.html no meu index.html. mas você tem / home e / about e os dois controladores são para cada um. Eu só tenho index.html com ng-view para meu main.html. Não consigo definir dois controladores para o main.html como você fez comwhen /home
Dê uma olhada na primeira resposta deste post: stackoverflow.com/questions/17354568/…
Austin
bem, eu sei definir mais when's. Quero dois controladores para um único modelo. Ou maby eu entendi mal o que você estava tentando me dizer?
Em vez de usar controladores, tente usar diretivas. Você pode usar muitas diretivas em um único modelo. Se você ainda quiser usar seus controladores, aqui está um vídeo sobre como as diretivas podem se vincular aos 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 tem acesso ao menu div. E widgetController tem acesso a ambos.

Muhammad Awais
fonte
Isso parece simples, mas não tem votos positivos. Essa é uma prática recomendada ou ruim?
redfox05
1
o que você fará, se tiver muitas páginas com mais de 100 controladores! ... prática ambígua.
ArifMustafa
3

Podemos simplesmente declarar mais de um controlador no mesmo módulo. Aqui está um exemplo:

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

Acabei de colocar uma declaração simples do aplicativo

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

Então criei um serviço e dois controladores

Para cada controlador eu tinha uma linha no JS

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

E no HTML eu declarei o escopo do aplicativo em um div circundante

<div ng-app="app">

e cada escopo do controlador separadamente em seu próprio div circundante (dentro do div do aplicativo)

<div ng-controller="EditableRowCtrl">

Isso funcionou bem

pat capozzi
fonte
0

Você também pode ter embutido todas as suas visualizações de modelo em seu arquivo html principal. Por exemplo:

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

Dessa forma, se cada modelo exigir um controlador diferente, você ainda poderá usar o roteador angular. Veja este plunk para um exemplo funcional http://plnkr.co/edit/9X0fT0Q9MlXtHVVQLhgr?p=preview

Dessa forma, uma vez que o aplicativo é enviado do servidor para o seu cliente, ele é completamente independente, supondo que não precise fazer nenhuma solicitação de dados, etc.

Daimonos
fonte