Несколько контроллеров с AngularJS в одностраничном приложении

102

Я хочу знать, как использовать несколько контроллеров для одностраничного приложения. Я попытался разобраться в этом и нашел вопросы, очень похожие на мои, но есть всего множество разных ответов, решающих конкретную проблему, когда в конечном итоге вы не используете несколько контроллеров для одностраничного приложения.

Это потому, что было бы неразумно использовать несколько контроллеров для одной страницы? Или это просто невозможно?

Скажем, у меня уже есть потрясающий контроллер карусели изображений, работающий на главной странице, но затем я узнаю, как (скажем) использовать модальные окна, и мне также нужен новый контроллер для этого (или что-то еще, что мне нужно в контроллере). Что мне тогда делать?

Я видел некоторые ответы на другие вопросы, где они спрашивают почти о тех же вещах, что и я, и люди отвечают: «* Боже, зачем тебе вообще это делать, просто сделай это ...».

Какой способ лучше или как ты это делаешь?

редактировать

Многие из вас просто объявляют два контроллера, а затем используют ng-controller для их вызова. Я использую этот фрагмент кода ниже, а затем вызываю MainCtrl с помощью ng-controller.

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

Зачем мне вообще нужно устанавливать здесь контроллер, если я могу использовать ng-controller без него? Вот что меня смутило. (и вы не можете добавить два контроллера таким образом, я думаю ...)

ТаркаДаал
источник
Я не думаю, что могу объявить 2 контроллера для одного файла .html? как это делается? when: /home, controller: MainCtrl. не могу добавить больше, или вы хотите просто вызвать это с помощью ng-controller?
3
@Mosho, вы сделали шаг 1, шаг 2, но не объясняете, как и почему. Если все так просто, объясните, пожалуйста, как это сделать. Это все равно, что сказать: используйте AngularJS, Готово. вы можете уточнить / объяснить? Или, поскольку это с июня, они могут не отвечать, может кто-нибудь объяснить?
redfox05 02

Ответы:

96

В чем проблема? Чтобы использовать несколько контроллеров, просто используйте несколько директив ngController:

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

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

Как обычно, вам необходимо, чтобы контроллеры были доступны в вашем модуле приложения.

Самый простой способ сделать это - объявить функции контроллера следующим образом:

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

function menuController($scope) {
   // stuff here
}
Дж. Бруни
источник
2
Это как это делается? Я использую этоwhen /home, controller: MainCtrl
8
Если вы отказываетесь от примеров, которые помещают "ng-app" в каждый DIV рядом с вашим "ng-controller", попробуйте переместить только одно "ng-app" в тег "body" (и удалите per-div " ng-app "), если работает только ваш первый контроллер. (Я помню это с тех времен, когда я был
новичком в
1
Единственная проблема, с которой я столкнулся при использовании ng-controllerв нескольких элементах DOM, - это, скажем, сценарий, в котором МНОГИЕ элементы печатаются в DOM через ng-repeat. Допустим, каждый из них обращается к ng-controller="myController. Из некоторых журналов консоли, которые я видел в своем приложении, myControllerповторная инициализация с КАЖДЫМ элементом, отображаемым в DOM, который его вызывает .... Может быть, я что-то напортачил в моем конкретном использовании, или, может быть, за пределами OP вопрос, но любопытно, испытывали ли другие это вообще ....
twknab
91

Я думаю, вам не хватает значения "одностраничное приложение".

Это не означает, что у вас физически будет один .html, вместо этого у вас будет один основной index.htmlи несколько NESTED .html файлов. Так почему же одностраничное приложение? Потому что таким образом вы не загружаете страницы стандартным способом (т.е. вызов браузера, который полностью обновляет всю страницу), а просто загружаете часть содержимого с помощью Angular / Ajax. Поскольку вы не видите мерцания между изменениями страницы, создается впечатление, что вы не двигались со страницы. Таким образом, вы чувствуете, что остаетесь на одной странице.

Теперь я предполагаю, что вы хотите иметь НЕСКОЛЬКО содержимого для вашего приложения на ОДНОЙ СТРАНИЦЕ: (например) дом, контакты, портфолио и магазин. Ваше одностраничное / многостраничное приложение (угловой способ) будет организовано следующим образом:

  • index.html: содержит верхний <ng-view>и нижний колонтитулы
  • contacts.html: содержит контактную форму (без заголовка, без нижнего колонтитула)
  • portfolio.html: содержит данные портфолио (без верхнего и нижнего колонтитула)
  • store.html: содержит магазин, без верхнего колонтитула.

Вы находитесь в индексе, нажимаете на меню «Контакты» и что происходит? Angular заменяет <ng-view>тег на contacts.htmlкод

Как этого добиться? with ngRoute, как вы это делаете, у вас будет что-то вроде:

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

Это вызовет правильный html, передающий ему правильный контроллер (обратите внимание: не указывайте ng-controllerдирективу, contacts.htmlесли вы используете маршруты )

Затем, конечно, вы можете объявить столько директив ng-controller на своей странице contacts.html. Это будут дочерние контроллеры ContactCtrl(таким образом, унаследованные от него). Но для одного маршрута внутри routeProviderвы можете объявить один контроллер, который будет действовать как «родительский контроллер частичного представления».

ИЗМЕНИТЬ Представьте следующие шаблоны / contacts.html

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

приведенное выше routeProviderвведет контроллер в ваш содержащий div. По сути, приведенный выше html автоматически становится:

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

Когда я говорю, что у вас могут быть другие контроллеры, это означает, что вы можете подключать контроллеры к внутренним элементам DOM, как показано ниже:

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

Надеюсь, это немного прояснит ситуацию.

А

Адхара
источник
Итак, если вы объявите, например, <div ng-controller="FancyStuffController">внутри тега body, к которому уже привязан контроллер, например, <body ng-controller="BodyController">это сработает? $apply already in progressЯ получаю ошибки о том, когда я это делаю, но я думаю, что это связано с dpd.js. Чтобы не вдаваться в это, я думаю, что это просто загрузка его дважды или что-то в этом роде, не знаю, как, но мое использование контроллера может пытаться перезагрузить его.
blamb
1
@BrianThomas Конечно, это должно сработать. Будьте осторожны: если вы хотите внедрить контроллер в представление, используйте $ routeProvider, не пишите ng-controller в теге div.
Адхара
9

В настоящее время я создаю одностраничное приложение. Вот то, что у меня есть до сих пор, что, я думаю, могло бы ответить на ваш вопрос. У меня есть базовый шаблон (base.html), в котором есть div с ng-viewдирективой в нем. Эта директива сообщает angular, куда поместить новый контент. Обратите внимание, что я сам новичок в angularjs, поэтому я ни в коем случае не говорю, что это лучший способ сделать это.

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>
Остин
источник
действительно, я также использую ng-view, так что могу использовать main.html в своем index.html. но у вас есть / home и / about, и два контроллера предназначены для каждого. У меня есть только index.html с ng-view для моего main.html. Я могу установить два контроллера для main.html, как вы это сделалиwhen /home
Взгляните на первый ответ из этого сообщения: stackoverflow.com/questions/17354568/…
Остин
ну я знаю как установить еще whenх. Мне нужны два контроллера для одного шаблона. Или, детка, я неправильно понял, что ты пытался мне сказать?
Вместо использования контроллеров попробуйте использовать директивы. Вы можете использовать несколько директив в одном шаблоне. Если вы все еще хотите использовать свои контроллеры, вот видео о том, как директивы могут быть связаны с контроллерами: egghead.io/lessons/angularjs-directives-talking-to-controllers
Остин
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 имеет доступ к меню div. И у widgetController есть доступ к обоим.

Мухаммад Авайс
источник
Это кажется простым, но без голосов. Это лучшая практика или плохая?
redfox05 02
1
что будешь делать, если у тебя много страниц 100+ контроллеров! ... практика неоднозначная.
ArifMustafa
3

Мы можем просто объявить более одного контроллера в одном модуле. Вот пример:

  <!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>
Абдалла Окаша
источник
2

Я просто поместил одно простое объявление приложения

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

Затем я построил одну службу и два контроллера.

Для каждого контроллера у меня была строчка в JS

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

И в HTML я объявил область приложения в окружающем div

<div ng-app="app">

и каждая область контроллера отдельно в собственном окружающем div (внутри приложения div)

<div ng-controller="EditableRowCtrl">

Это сработало нормально

Пэт Капоцци
источник
0

Вы также могли бы встроить все представления вашего шаблона в ваш основной файл HTML. Например:

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

Таким образом, если для каждого шаблона требуется другой контроллер, вы все равно можете использовать angular-router. См. Этот лист для рабочего примера http://plnkr.co/edit/9X0fT0Q9MlXtHVVQLhgr?p=preview

Таким образом, как только приложение отправлено с сервера вашему клиенту, оно полностью автономно, при условии, что ему не нужно делать какие-либо запросы данных и т. Д.

Даймоно
источник