Mam aplikację o złożonym układzie, w której użytkownik może umieścić (przeciągnij / upuść) widżety (wybierając z predefiniowanego zestawu ponad 100 widżetów), gdzie każdy widżet jest niestandardową implementacją, która wyświetla zestaw danych (pobieranych za pomocą wywołania REST) w określony sposób. Przeczytałem mnóstwo postów na blogu, pytań dotyczących stackoverflow i oficjalnej dokumentacji AngularJS, ale nie mogę dowiedzieć się, jak zaprojektować moją aplikację, aby sprostać tym wymaganiom. Patrząc na aplikacje demonstracyjne, jest pojedynczy moduł (ng-app) i podczas konstruowania go w pliku .js zależne moduły są zadeklarowane jako jego zależności, jednak mam duży zestaw widżetów i jakoś nie zaleca się ich wszystkich opisywać tam. Potrzebuję odpowiedzi na następujące pytania:
- Jak zaprojektować swoją aplikację i widżety - czy powinienem mieć osobny moduł AngularJS, czy każdy widżet powinien być dyrektywą do modułu głównego?
- Jeśli zaprojektuję widget jako dyrektywy, czy istnieje sposób na zdefiniowanie zależności w ramach dyrektywy. To znaczy, że moja dyrektywa używa ng-calender w swojej implementacji?
- Jeśli zaprojektuję każdy widget jako oddzielny moduł, czy istnieje sposób na dynamiczne dodawanie modułu widgetu jako zależności do modułu głównego?
- Jak zaprojektować kontrolery - prawdopodobnie jeden kontroler na widget?
- Jak oddzielić stan (zakres), jeśli w widoku mam wiele widżetów tego samego typu?
- Czy istnieją najlepsze praktyki projektowania widżetów wielokrotnego użytku w AngularJS?
EDYTOWAĆ
Przydatne referencje:
- ocLazyLoad - świetna leniwa biblioteka ładująca dla AngularJS
- Projekt seed - moduły + leniwe ładowanie przy zmianie trasy (ES6, systemjs, ocLazyLoad)
- Leniwe ładowanie w AngularJS
- Dynamiczne ładowanie kontrolerów i widoków za pomocą AngularJS i RequireJS
- Ładowanie komponentów AngularJS z RequireJS po bootstrapie aplikacji
- Projekt demonstracyjny dotyczący leniwego ładowania zasobów AngularJS na GitHub
- Załaduj projekt na żądanie
- Wstaw moduł dynamicznie tylko w razie potrzeby
- Kolejne leniwe ładowanie w artykule Angular
- Organizacja kodu w dużych aplikacjach AngularJS i JavaScript
źródło
Odpowiedzi:
To tylko ogólne rady.
Mówisz o setkach widżetów, wydaje się naturalne, że podzielisz je na kilka modułów. Niektóre widżety mogą mieć więcej wspólnego niż inne. Niektóre mogą być bardzo ogólne i pasować do innych projektów, inne są bardziej szczegółowe.
Zależności od innych modułów są wykonywane na poziomie modułu, ale nie ma problemu, jeśli moduł
A
zależy od modułuB
i zarówno od modułu, jakA
iB
od niegoC
. Dyrektywy są naturalnym wyborem przy tworzeniu widżetów w Angular. Jeśli dyrektywa zależy od innej dyrektywy, definiujesz je w tym samym module lub tworzysz zależność na poziomie modułowym.Nie jestem pewien, dlaczego chcesz to zrobić, i nie wiem, jak to zrobić. Dyrektywy i usługi nie są inicjowane, zanim zostaną użyte w Angular. Jeśli masz ogromną bibliotekę dyrektyw (widżetów) i wiesz, że prawdopodobnie użyjesz niektórych z nich, ale nie wszystkich - ale nie wiesz, które z nich zostaną użyte, gdy aplikacja zostanie zainicjowana, możesz w rzeczywistości "leniwy załaduj ”swoje dyrektywy po załadowaniu modułu. Stworzyłem przykład tutaj
Zaletą jest to, że możesz szybko ładować aplikację, nawet jeśli masz dużo kodu, ponieważ nie musisz ładować skryptów, zanim będziesz ich potrzebować. Wadą jest to, że przy pierwszym ładowaniu nowej dyrektywy może wystąpić znaczne opóźnienie.
Widżet będzie prawdopodobnie potrzebował własnego kontrolera. Kontrolery powinny być generalnie małe, jeśli są duże, możesz rozważyć, czy istnieje jakakolwiek funkcjonalność, która lepiej pasowałaby do usługi.
Widżety, które wymagają zmiennych zakresu, powinny bez wątpienia mieć własne izolowane zakresy (
scope:{ ... }
w konfiguracji dyrektywy).Wyizoluj zakres, ogranicz zależności do niezbędnego minimum. Zobacz film Misko o najlepszych praktykach w Angular
Brian Ford napisał również artykuł o napisaniu ogromnej aplikacji w Angular
źródło
To pytanie jest również dla mnie bardzo ważne. Strona główna AngularJS zawiera kilka przykładów (można by je nazwać widżetami), więc przejrzałem ich kod źródłowy, aby sprawdzić, jak rozdzielili widżety.
Po pierwsze, nigdy nie deklarują atrybutu „ng-app”. Oni używają
function bootstrap() { if (window.prettyPrint && window.$ && $.fn.popover && angular.bootstrap && hasModule('ngLocal.sk') && hasModule('ngLocal.us') && hasModule('homepage') && hasModule('ngResource')) { $(function(){ angular.bootstrap(document, ['homepage', 'ngLocal.us']); }); } }
aby upewnić się, że wszystko jest poprawnie załadowane. Niezły pomysł, ale to dziwne, że tak bardzo naciskają na atrybut ng-app, a nawet sami go nie używają. W każdym razie tutaj jest moduł strony głównej, którą ładują z aplikacją - http://angularjs.org/js/homepage.js
Jest tam dyrektywa o nazwie appRun
.directive('appRun', function(fetchCode, $templateCache, $browser) { return { terminal: true, link: function(scope, element, attrs) { var modules = []; modules.push(function($provide, $locationProvider) { $provide.value('$templateCache', { get: function(key) { var value = $templateCache.get(key); if (value) { value = value.replace(/\#\//mg, '/'); } return value; } }); $provide.value('$anchorScroll', angular.noop); $provide.value('$browser', $browser); $locationProvider.html5Mode(true); $locationProvider.hashPrefix('!'); }); if (attrs.module) { modules.push(attrs.module); } element.html(fetchCode(attrs.appRun)); element.bind('click', function(event) { if (event.target.attributes.getNamedItem('ng-click')) { event.preventDefault(); } }); angular.bootstrap(element, modules); } }; })
Jako przykładu użyję listy rzeczy do zrobienia. W przypadku html mają
<div app-run="todo.html" class="well"></div>
a następnie na dole strony, którą mają
<script type="text/ng-template" id="todo.html"> <h2>Todo</h2> <div ng-controller="TodoCtrl"> <span>{{remaining()}} of {{todos.length}} remaining</span> [ <a href="" ng-click="archive()">archive</a> ] <ul class="unstyled"> <li ng-repeat="todo in todos"> <input type="checkbox" ng-model="todo.done"> <span class="done-{{todo.done}}">{{todo.text}}</span> </li> </ul> <form ng-submit="addTodo()"> <input type="text" ng-model="todoText" size="30" placeholder="add new todo here"> <input class="btn-primary" type="submit" value="add"> </form> </div> </script>
Oni też mają
<style type="text/css" id="todo.css"> //style stuff here </style> <script id="todo.js"> //controller stuff here </script>
Kod jest używany, ale atrybuty id w tych skryptach nie są ważne dla uruchamiania aplikacji. Dotyczy to tylko wyświetlania kodu źródłowego po lewej stronie aplikacji.
Zasadniczo mają dyrektywę o nazwie appRun, która używa funkcji fetchCode
.factory('fetchCode', function(indent) { return function get(id, spaces) { return indent(angular.element(document.getElementById(id)).html(), spaces); } })
aby pobrać kod. Następnie używają angular.bootstrap () do tworzenia nowej aplikacji. Mogą również ładować moduły poprzez uruchamianie aplikacji. Przykład projektu JavaScript jest inicjowany jak
<div app-run="project.html" module="project" class="well"></div>
Mam nadzieję, że to pomoże. Nadal nie jestem pewien, jaka jest „najlepsza” technika, ale wygląda na to, że strona główna AngularJS po prostu używa całkowicie oddzielnej aplikacji kątowej (ng-app) dla każdego przykładu / widżetu. Myślę, że zrobię to samo, z wyjątkiem zmiany funkcji fetchCode, aby uzyskać rzeczy z AJAX.
źródło
function hasModule(name) { try { angular.module(name); } catch(err) { return false; } return true; }