Ziarno AngularJS: umieszczanie JavaScript w oddzielnych plikach (app.js, controllers.js, directives.js, filters.js, services.js)

93

Używam szablonu kątowego do tworzenia struktury mojej aplikacji. Początkowo umieścić wszystkie moje kodu JavaScript w jednym pliku main.js. Ten plik zawierał moją deklarację modułu, kontrolery, dyrektywy, filtry i usługi. Aplikacja działa dobrze w ten sposób, ale martwię się o skalowalność i łatwość konserwacji, ponieważ moja aplikacja staje się bardziej złożona. Zauważyłem, że szablon angular-seed ma osobne pliki dla każdego z nich, więc próbowałem rozprowadzić mój kod z jednego main.jspliku do każdego z innych plików wymienionych w tytule tego pytania i znalazłem w app/jskatalogu angular szablon nasion .

Moje pytanie brzmi: jak zarządzać zależnościami, aby aplikacja działała? Istniejąca dokumentacja tutaj nie jest zbyt jasna w tym względzie, ponieważ każdy z podanych przykładów pokazuje pojedynczy plik źródłowy JavaScript.

Oto przykład tego, co mam:

app.js

angular.module('myApp', 
    ['myApp.filters',
     'myApp.services',
     'myApp.controllers']);

controllers.js

angular.module('myApp.controllers', []).
    controller('AppCtrl', [function ($scope, $http, $filter, MyService) {

        $scope.myService = MyService; // found in services.js

        // other functions...
    }
]);

filtry.js

angular.module('myApp.filters', []).
    filter('myFilter', [function (MyService) {
        return function(value) {
            if (MyService.data) { // test to ensure service is loaded
                for (var i = 0; i < MyService.data.length; i++) {
                    // code to return appropriate value from MyService
                }
            }
        }
    }]
);

services.js

angular.module('myApp.services', []).
    factory('MyService', function($http) {
        var MyService = {};
        $http.get('resources/data.json').success(function(response) {
            MyService.data = response;
        });
        return MyService;
    }
);

main.js

/* This is the single file I want to separate into the others */
var myApp = angular.module('myApp'), []);

myApp.factory('MyService', function($http) {
    // same code as in services.js
}

myApp.filter('myFilter', function(MyService) {
    // same code as in filters.js
}

function AppCtrl ($scope, $http, $filter, MyService) {
    // same code as in app.js
}

Jak zarządzać zależnościami?

Z góry dziękuję.

ChrisDevo
źródło
2
jest kilka artykułów na ten temat. na przykład briantford.com/blog/huuuuuge-angular-apps.html i niektóre projekty, takie jak yeoman. można to osiągnąć na dwa sposoby: oddzielając komponenty warstwami (jak ziarno kątowe) lub według funkcji, jak sugerują niektóre artykuły.
Eduard Gamonal
Kiedy deklarujesz moduł, np. Angular.module ('myApp.service1', []) myślę, że musisz dodać zależności do [] np. Angular.module ('myApp.service1', ['myApp.service2', 'myApp .service2 ']). . Jestem całkiem nowy w AngularJS, więc mogę się mylić. Jeśli to się uda, daj mi znać, a opublikuję odpowiedź.
Danny Varod
Gdzie należy wstawić tę linię? Umieściłem angular.module('myApp.services', ['myApp.services']);w app.js bez powodzenia.
ChrisDevo
@ChrisDevo 1. Odpowiadając na komentarze, zawsze zaczynaj komentarz od „@” i nazwy użytkownika (bez spacji), aby użytkownik otrzymał powiadomienie. 2. myApp.services powinny być zależne od innych modułów, a nie od siebie, np angular.module('myApp.services', ['myApp.server', 'myApp.somethingElse']).
Danny Varod
@DannyVarod, czy edytowałeś swój poprzedni komentarz? Przeczytałem to jak angular.module('myApp.service1', ['myApp.service1', 'myApp.service2'])oryginalnie. W przeciwnym razie nie uwzględniłbym myApp.services jako własnej zależności.
ChrisDevo

Odpowiedzi:

108

Problem jest spowodowany ponownym deklarowaniem modułu aplikacji we wszystkich oddzielnych plikach.

Tak wygląda deklaracja modułu aplikacji (brak pewności, czy deklaracja jest właściwym terminem):

angular.module('myApp', []).controller( //...

Oto jak wygląda przypisanie (nie jestem pewien, czy przypisanie jest właściwym terminem) do modułu aplikacji:

angular.module('myApp').controller( //...

Zwróć uwagę na brak nawiasów kwadratowych.

Tak więc poprzednia wersja, ta z nawiasami kwadratowymi, powinna być używana tylko raz, zwykle w twoim app.jslub main.js. Wszystkie inne powiązane pliki - kontrolery, dyrektywy, filtry … - powinny używać tej ostatniej wersji, bez nawiasów kwadratowych.

Mam nadzieję, że to ma sens. Twoje zdrowie!

Justin L.
źródło
9
Znalazłem rozwiązanie tego problemu. Wydaje się, że potrzebny jest tylko jeden moduł myApp. W moim pliku app.js utworzyłem dla niego var var myApp = angular.module('myApp', []);We wszystkich innych plikach po prostu odniosłem się do tego var (np. myApp.filter('myFilter', function(MyService) { /* filter code */ });O ile dodałem nazwy plików w tagach skryptu w moim html, nie musiałem deklarować żadnych innych zależności. Szablon projektu
angular
1
Teraz jestem naprawdę zdezorientowany. Poszedłem z powrotem i usunięty globalnego var myApp, tak że jest teraz anonimowy moduł w app.js: angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'myApp.controllers']);. We wszystkich innych plikach zadeklarowałem również takie anonimowe moduły angular.module('myApp.filter', []).filter('myFilter', function(MyService) { /* filter code */ });. Moja aplikacja też działa teraz w ten sposób. Przejrzałem historię mojego kodu i nie widzę, gdzie to jest inne niż to, co ja, kiedy nie działało.
ChrisDevo
4
Obiecuję, że to działa. Używam go każdego dnia, rozwijając Angular Apps. Znów mam jedną deklarację angular.module('myApp', []);(zwróć uwagę na nawiasy kwadratowe) w moim głównym pliku js. Następnie wszystkie inne pliki odwołują się do modułu przy użyciu składni angular.module('myApp').controllerlub .directive.
Justin L.
5
Błędy są spowodowane tym, że Angular szuka modułów o nazwach myApp.controller, myApp.filters… ale to nie są moduły, tylko komponenty rozszerzające jeden moduł o nazwie myApp. Wszystko powinno działać, jeśli usuniesz wszystkie swoje myApp. niezależnie od zależności modułów. Wystarczy zachować nawiasy kwadratowe pusty (chyba, że są w tym inne prawdziwe modułów kątowych, np ngCookies, ngResource) kiedy deklarując swój główny moduł aplikacji:angular.module('myApp', []);
Justin L.
1
Ah, dobrze. Umieszczenie ich jako zależności dla modułu aplikacji spowoduje błąd, ponieważ musi je znaleźć. Jeśli wyjmiesz je z nawiasów kwadratowych, możesz je załadować do woli, a aplikacji nie będzie to obchodzić. ngResourcejest zdecydowanie jedną z tych, które umieszczasz w nawiasach deklaracji modułu. Gdyby moja odpowiedź pomogła, głosowanie i zatwierdzenie jej byłoby bardzo wdzięczne.
Justin L.
12

Jeśli chcesz umieścić różne części aplikacji ( filters, services, controllers) w różnych plikach fizycznych, musisz zrobić dwie rzeczy:

  1. Zadeklaruj te przestrzenie nazw (z braku lepszego terminu) w swoim app.jslub w każdym pliku;
  2. Odwołaj się do tych przestrzeni nazw w każdym z plików.

Więc twój app.jswyglądałby tak:

angular.module('myApp', ['external-dependency-1', 'myApp.services', 'myApp.controllers'])
.run(function() {
   //...

})
.config(function() {
  //...
});

A w każdym pliku:

services.js

angular.module('myApp.services', []); //instantiates
angular.module('myApp.services') //gets
.factory('MyService', function() { 
  return {};
});

controllers.js

angular.module('myApp.controllers', []); //instantiates
angular.module('myApp.controllers')      //gets
.controller('MyCtrl', function($scope) {
  //snip...
})
.controller('AccountCtrl', function($scope) {
  //snip...
});

Wszystko to można połączyć w jedno połączenie:

controllers.js
angular.module('myApp.controllers', []) 
.controller('MyCtrl', function($scope) {
 //snip...
});    

Ważną częścią jest to, że nie powinieneś redefiniować angular.module('myApp'); spowodowałoby to nadpisanie go podczas tworzenia wystąpienia kontrolerów, prawdopodobnie nie to, czego chcesz.

George Stocker
źródło
1
Podoba mi się to podejście, możesz go użyć do stworzenia warstwowej struktury (powiedzmy, że twój kontroler potrzebuje usługi lub filtra). Uwaga, odkryłem również, że po wstrzyknięciu pod-zależności (takiej jak ten filtr dla kontrolera), jest ona również dostępna we wszystkich powyższych rodzicach (jeśli wizualizujesz jako drzewo) bez ponownego zadeklarowania wstrzyknięcia, po prostu nie rób tego zapomnij o tym, jeśli zmienisz dziecko i używasz go u rodzica. Zalecałbym również, aby nigdy nie umieszczać więcej niż jednego modułu (przestrzeni nazw) w pliku i nigdy nie próbować używać tej samej przestrzeni nazw w wielu plikach.
Gary
Co się stanie, jeśli mam dwa pliki kontrolera lub dwa pliki usług? Czy instancja będzie miała miejsce dla każdego pojedynczego pliku?
Avinash Raj
3

Otrzymujesz błąd, ponieważ jeszcze nie zdefiniowałeś myApp.services. Do tej pory umieściłem wszystkie początkowe definicje w jednym pliku, a następnie użyłem ich w innym. Jak na przykład, włożyłbym:

app.js

angular.module('myApp.services', []);

angular.module('myApp', 
    ['myApp.services',
      ...]);

To powinno wyeliminować błąd, chociaż myślę, że powinieneś przeczytać artykuł Eduarda Gamonal, o którym mowa w jednym z komentarzy.

Torsten Engelbrecht
źródło
Dzięki, Torsten, ale przeczytałem ten artykuł i dodałem wiersz angular.module('myApp.services', []);do mojego pliku app.js. Żadne tak naprawdę nie rozwiązuje mojego problemu.
ChrisDevo