AngularJS – Style Guide

Poniższy tekst jest tłumaczeniem Angular 1 Style Guide autorstwa @john_papa i zatwierdzonego przez zespół Angulara.

Źródło: https://github.com/johnpapa/angular-styleguide/tree/master/a1

Autor: https://twitter.com/john_papa

Angular 1 Style Guide

Zatwierdzone

Specjalne podziękowania dla Igora Minar, szefa zespołu Angulara, za poprawki, odzew, recenzje, opinie i powierzenie mi opieki nad tym przewodnikiem.

Cel

Jeśli szukasz przewodnika który pozwoli utrzymać Ci styl składni, konwencji i struktury twoich Angularowych aplikacji to dobrze trafiłeś. Te style są oparte na moim doświadczeniu jako programisty Angular, prezentacjach, kursach Pluralsight oraz pracy w zespołach.

Cel tego przewodnika jest taki aby pokazać Ci kilka porad i pokierować Tobą podczas tworzenia aplikacji w Angularze poprzez pokazywanie Ci konwencji których używam, oraz co ważniejsze wytłumaczenie dlaczego własnie je wybrałem.

Jeśli podoba Ci się ten poradnik, sprawdź mój kurs Angular Patterns: Clean Code na Pluralsight który może Ci towarzyszyć przy okazji przerabiania tego przewonika.

Super społeczność

Nigdy nie pracuj sam. Społeczność Angulara to niesamowita grupa ludzi, która pasjonuje się dzieleniem z innymi swoimi doświadczeniami. Ekspert Angulara Todd Motto i ja współpracowaliśmy nad wieloma stylami i konwencjami. Przy większości się zgadzaliśmy, przy niektórych jednak nie. Zachęcam Cię do sprawdzenia porad Todda żeby zrozumieć również jego podejście i porównać.

Wiele moich stylów pochodzi z sesji programowania jakie miałem z Wardem Bellem. Mój przyjaciel Ward z pewnością miał wpływ na rozwój tego przewodnika.

Odzwierciedlenie w prostej aplikacji

Przewodnik wyjaśnia co, jak i dlaczego ale myślę, że również przydatnym dodatkiem do niego jest zobaczenie tych wszystkich porad w praktyce. Towarzyszy mu więc przykładowa aplikacja która podąża za opisanymi w nim konwencjami i stylami. Możesz ją znaleźć tutaj w folderze modular. Nie krępuj się przed jej sklonowaniem czy ulepszaniem. Instrukcje jak sprawić, żeby działała znajdziesz tutaj.

Tłumaczenia

Tłumaczenia tego przewodnika są zamieszczane przez społeczność i możesz znaleźć je pod tym linkiem.

—————————————

Odpowiedzialność za pojedyncze funkcjonalności

Zasada nr 1

  • Twórz jeden komponent w jednym pliku, zalecane jest żeby nie miał więcej niż 400 linii kodu

Dlaczego? Jeden komponent na jeden plik ułatwia testy jednostkowe

Dlaczego? Jeden komponent na jeden plik sprawia, że staje się to łatwiejsze do czytania, utrzymania i uniknięcia kolizji z innymi

Dlaczego? Jeden komponent na jeden plik pozwala uniknąć bugów które często powstają na skutek łączenia się ze sobą komponentów w jednym pliku gdzie mogą dzielić zmienne, tworzyć niechciane zamknięcia lub sprzęgać się z zależnościami.

Poniższy przykład definiuje moduł app i jego zależności, kontroler i factory w jednym pliku.

Tutaj natomiast oddzielamy każdy z tych komponentów i umieszczamy w osobnym pliku.

Małe funkcje

  • Twórz małe funkcje, nie większe niż 75 linii kodu (mniej znaczy lepiej)

Dlaczego? Małe funkcje są łatwiejsze do testowania, zwłaszcza kiedy robią jedną rzecz w jakimś jasno określonym celu.

Dlaczego? Małe funkcje promują styl ponownego używania kodu.

Dlaczego? Małe funkcje są łatwiejsze do czytania.

Dlaczego? Małe funkcje są łatwiejsze do utrzymania.

Dlaczego? Małe funkcje pozwalają na uniknięcie bugów które mają miejsce w przypadku ogromnych funkcji które dzielą zmienne z zewnętrznych zakresów, tworzą niepotrzebne zakresy lub niepotrzebnie sprzęgają zależności.

IIFE

Zakresy

  • Otaczaj komponenty Angulara funkcjami natychmiastowymi

Dlaczego? IIFE (funkcje natychmiastowe) usuwają zmienne z przestrzeni globalnej. Pomaga to chronić deklaracje zmiennych i funkcji przed szerszym przeznaczeniem niż to było zaplanowane, co z kolei oznacza też ochronę przed kolidowaniem ich ze sobą nawzajem.

Dlaczego? Kiedy twój kod zostanie zminifikowany i wrzucony do jednego pliku w celu umieszczenia na serwerze produkcyjnym, może dojść do kolizji zmiennych lokalnych i globalnych. IIFE chroni Cię przed tym dostarczając odpowiedni zakres dla każdego pliku.

  • Zauważ: Pozostałe przykłady w tym przewodniku nie będą uwzględniać IIFE żeby treść była bardziej zwięzła ale nie znaczy to, że nie zostałyby one zastosowane w praktyce
  • Zauważ: IIFE zapobiega dostępowi do swoich prywatnych zasobów przez kod testowy. Mogłyby one być jednak dobrym materiałem do testów jednostkowych. Możemy jednak udostępnić je poprzez odpowiedni kod dostępowy lub wystawić na dostęp poprzez ich komponent.

Moduły

Unikanie kolizji nazw

  • Używaj unikalnego nazewnictwa z oddzieleniem pod-modułów

Dlaczego? Unikalne nazwy pomagają w uniknięciu kolizji w nazewnictwie. Ich odpowiednie oddzielenie pomaga również zachować hierarchie. Mamy dla przykładu moduł app który jest naszym głównym modułem podczas gdy app.dashboardapp.users mogą być modułami używanymi jako zależności modułu app.

Tworzenie modułów

  • Twórz moduły nie przypisując ich do zmiennych

Dlaczego? Z założeniem posiadania jednego komponentu w jednym pliku rzadko zachodzi potrzeba tworzenia zmiennej dla modułu.

Dostęp do modułów

  • Kiedy tworzysz moduły unikaj używania zmiennych, zamiast tego skorzystaj z łańcucha

Dlaczego? Tworzy to znacznie bardziej przejrzysty kod i pozwala na uniknięcie kolizji nazw.

Nazwane a anonimowe funkcje

  • Używaj funkcji z nazwami zamiast przekazywania anonimowych funkcji jako callback

Dlaczego? Poprawia czytelność kodu, jest znacznie łatwiejsze do debugowania oraz zmniejsza ilość zagnieżdżonych w sobie callback-ów.

Kontrolery

controllerAs po stronie widoku

  • Używaj stylu opartego na controllerAs zamiast używania $scope

Dlaczego? Jest to składnia bardziej zbliżona do konstruktora JS a w widoku jest bardziej kontekstowa, łatwiejsza do czytania i pomaga uniknąć problemów które bez niej mogłyby się pojawić.

Dlaczego? Pomaga uniknąć używania $parent w widoku.

controllerAs po stronie kontrolera

  • Używanie controllerAs zamiast korzystanie ze $scope
  • Składnia to korzysta z this w kontrolerze które i tak przypisane jest do $scope

Dlaczego? controllerAs to osłodzenie $scope. Dalej możesz podstawiać i uzyskiwać dostęp do metod $scope

Dlaczego? Pomaga uniknąć pokus użycia metod $scope w kontrolerze kiedy lepiej byłoby tego uniknąć lub przenieść metodę do Factory i jedynie mieć do niej dostęp w kontrolerze. Używaj $scope w kontrolerze tylko kiedy naprawdę zajdzie taka potrzeba. Przykładowo kiedy emitujesz i nasłuchujesz na jakieś zdarzenia dzięki $emit, $broadcast czy $on.

controllerAs z użyciem vm

  • Używaj jakiejś zmiennej żeby uzyskać dostęp do this kiedy korzystasz ze składni opartej na controllerAs. Korzystaj z jakiejś logicznej nazwy konsekwentnie, np vm – ViewModel

Dlaczego? Słowo this jest kontekstowe i używanie go w jakiejś funkcji w środku kontrolera może zmienić jego kontekst i to do czego się odnosi. Uchwycenie odpowiedniego kontekstu this do zmiennej pomaga uniknąć tego problemu.

Zauważ: Możesz uniknąć ostrzeżeń jshint poprzez wstawienie komentarza nad linią kodu. Jednakże nie jest to potrzebne jeśli funkcja jest nazwana w w użyciu UpperCasing jako konwencji nazewniczej, co będzie oznaczało, że ta funkcja jest konstruująca co rzeczywiście w przypadku kontrolerów w Angularze jest prawdą.

Zauważ: Kiedy tworzysz obserwatory w kontrolerze używając controllerAs możesz obserwować vm.*.

Zauważ: Praca z większymi projektami wymagać będzie używania bardziej opisowych nazw które ułatwią ich rozpoznawanie i wyszukiwanie. Unikaj zbyt rozwlekłych nazw które będą później uciążliwe w stosowaniu.

Umieszczanie podstawień na samej górze

  • Umieszczanie podstawień na samej górze kontrolera, w kolejności alfabetycznej zamiast rozrzuceniu ich po całym kontrolerze.

Dlaczego? Sprawia to, że całość jest łatwiejsza do czytania i pozwala natychmiast poznać które składniki kontrolera mogą być podstawione i użyte w widoku.

Dlaczego? Stosowanie anonimowych funkcji może być łatwe ale kiedy te funkcje mają dużo linii kodu może to znacznie wpływać na zmniejszenie czytelności kodu. Zdefiniowanie funkcji poniżej podstawień przesuwa szczegóły implementacji w dół zachowując podstawienia u góry i sprawiając, że całość jest łatwiejsza do czytania.

Dopuszczalnym jest jeszcze zdefiniowanie funkcji jedno liniowej w podstawieniu ale nie więcej, żeby nie pogarszać czytelności kodu.

Deklarowanie funkcji w celu ukrycia szczegółów implementacji

  • Na początku deklaruj funkcje a później przejdź do ich definicji i implementacji całej logiki kontrolera. Na samej górze powinny pojawić się podstawienia a dopiero później definicje zadeklarowanych tutaj funkcji. Jest to ściśle związane z treścią wcześniejszego podpunktu.

Dlaczego? Umieszczenie podstawień na samej górze zwiększa czytelność i pomaga natychmiastowo zorientować się co może zostać użyte w widoku.

Dlaczego? Umieszczenie implementacji szczegółów w dalszej części pliku przesuwa całą jego złożoność więc na samym początku możesz zwrócić uwagę na najważniejsze rzeczy,

Dlaczego? Przy deklarowaniu funkcji nie trzeba martwić się o kolejność (co ma miejsce w przypadku wyrażeń funkcyjnych)

Dlaczego? Nie musisz martwić się o to co stanie się jeśli przesuniesz var a przed var b bo a jest zależne od b.

Dlaczego? Bo kolejność jest kluczowa w przypadku wyrażeń funkcyjnych

Zauważ: W przykładzie powyżej te ważne rzeczy są rozsiane po całości kontrolera. Natomiast w przykładzie poniżej najważniejsze rzeczy są u samej góry. Przykładowo podstawienia takie jak vm.avengers czy vm.title. Sama implementacja natomiast przesunięta jest na dół. Jest to po prostu bardziej czytelne.

Przeniesienie logiki kontrolera do serwisów

  • Przeniesienie logiki z kontrolera do serwisów i fabryk (ang. factories)

Dlaczego? Logika może zostać ponownie użyta w wielu kontrolerach kiedy zostanie umieszczona w serwisie i wyeksponowana w postaci funkcji

Dlaczego? Logika w serwisie z łatwością może zostać wyizolowana w celu testów jednostkowych, podczas gdy wywołanie logiki w kontrolerze może być łatwo podstawione.

Dlaczego? Pozbywamy się zależności i przenosimy implementacje z kontrolera

Dlaczego? Utrzymujemy kontroler lekkim i zredukowanym do określonych funkcjonalności

Kontrolery napisane w jednym celu

  • Zdefiniowanie kontrolera dla widoku i staranie się nie używania go gdzie indziej, przykładowo dla innych widoków. Zamiast tego przesunięcie logiki do serwisów w celu utrzymania kontrolera prostego i skupionego na jego widoku

Dlaczego? Używanie kontrolerów z wieloma widokami nie jest najlepszym rozwiązaniem i wymaga napisania dobrych testów całościowych w celu upewnienia się, że aplikacja jest później stabilna.

Przypisywanie kontrolerów

  • Kiedy kontroler musi być powiązany z widokiem alet może być używany przez inny kontroler bądź widok, definiuj kontroler razem z ich ścieżkami

Zauważ: Jeśli widok jest ładowany przez inny widok, oprócz ładowania przez odpowiedni routing, używaj składni ng-controller=”Avengers as vm”

Dlaczego? Sparowanie kontrolera z widokiem podczas przypisywania odpowiednich ścieżek pozwala różnym ścieżkom na ładowanie różnych par widok-kontroler. Kiedy użyjemy ng-controller widok zawsze powiązany jest z określonym kontrolerem.

Serwisy

Singleton

  • Instancje serwisów tworzone są z wykorzystaniem słowa new, używają this dla publicznych metod i zmiennych. Od kiedy są tak podobne do fabryk, używaj fabryk.

Zauważ: Wszystkie serwisy w Angularze są singeltonami. Oznacza to, że mają jedną instancję przekazaną przez injector.

Fabryki

Pojedyncza odpowiedzialność

  • Fabryki powinny być odpowiedzialne za jedną określoną rzecz, zamkniętą w ich kontekście. Kiedy konkretna fabryka zaczyna znacznie rozszerzać tą funkcjonalność powinna zostać stworzona nowa.

Singleton

  • Fabryki są singletonami i zwracają obiekt który zawiera składowe serwisu

Deklaracja funkcjonalności na samej górze

  • Wyeksponuj funkcjonalności które można wywołać na samej górze.

Dlaczego? Umieszczenie funkcjonalności które możemy wywołać z zewnątrz fabryki tworzy ją łatwiejszą do czytania i od razu pozwala Ci poznać które funkcjonalności mogą zostać wywołane i potrzebują testów jednostkowych

Dlaczego? Jest to pomocne zwłaszcza kiedy pliki robią się coraz dłuższe. Nie musisz przewijać ich całych żeby zobaczyć co jest eksponowane.

Dlaczego? Tworzenie funkcji “jak leci” może być stosunkowo łatwiejsze ale gdy są dłuższe niż pojedyncza linijka znacznie pogarszają czytelność. Zdefiniowanie wywoływanego interfejsu jako zwracanego serwisu przesuwa implementacje na dół, utrzymuje samą deklarację intefejsu u góry i sprawia, że całość jest znacznie bardziej czytelna.

Deklarowanie funkcji w celu ukrycia szczegółów implementacji

  • Używaj deklaracji funkcji  w celu ukrycia szczegółów implementacji. Trzymaj rzeczy do których można mieć dostęp z zewnątrz u góry fabryki. Niech wskazują na funkcje których definicji pojawiają się dopiero w późniejszej części pliku.

Dlaczego? Utrzymywanie tych elementów do których mamy dostęp na samej górze poprawia czytelność i pomaga od razu zidentyfikować czym możemy się posługiwać na zewnątrz fabryki.

Dlaczego? Trzymanie deklaracji funkcji i szczegółów poniżej w pliku, sprawia, że im bardziej w dół tym fabryka staje się bardziej złożona ale najważniejsze rzeczy są wyeksponowane na samej górze.

Dlaczego? Nie musimy się martwić, że kolejność deklaracji funkcji spowoduje jakiś błąd tak jakby miało to miejsce w przypadku wyrażeń funkcyjnych

Dostęp do danych

Oddzielenie zapytań

  • Przebudowanie logiki w ten sposób, że wszelkie odwołanie się po dane i operacje na nich są przeniesione do fabryki. Obarczenie jej odpowiedzialnością za zapytania XHR, pamięć podręczną, zachowywanie danych w pamięci, czy jakiekolwiek inne operacji na danych

Dlaczego? Kontroler jest odpowiedzialny za zebranie danych i przedstawienie ich do widoku. Nie powinien przejmować się tym jak pozyskiwane są te dane, tylko wiedzieć kto się tym zajmuje. Odseparowanie logiki pozyskiwania tych danych do odpowiednich serwisów, pozwala kontrolerowi na spełnianie jedynie swojego zadania

Dlaczego? Sprawia, że całość staje się łatwiejsza do testowania.

Dlaczego? Serwisy które zajmują się pozyskiwaniem danych mogą mieć bardzo specyficzną implementacje żeby radzić sobie ze zbiorem danych. Może to zawierać odpowiednie nagłówki, określony sposób zapytania i inne serwisy takie jak $http. Oddelegowanie tej logiki do serwisu który zajmie się tym wszystkim sprawia, że logika ta jest znacznie łatwiejsza do zmiany w przypadku kiedy taka będzie konieczna.

Zwracanie obietnic

  • Kiedy wywoływany serwis, odpowiedzialny za pozyskanie danych, taki jak $http zwraca obietnicę, funkcja która go wywołuje również powinna to robić.

Dlaczego? Możesz powiązać obietnice ze sobą i określić kolejne działania dopiero po tym kiedy uzyskasz już dane.

Dyrektywy

Jedna na plik

  • Twórz jedną dyrektywę w jednym pliku. Nazwij plik odpowiednio do dyrektywy

Dlaczego? Łatwo jest pomieszać wszystkie dyrektywy w jednym pliku, ale ciężko później wyodrębnić tylko te które muszą być współdzielone w całej aplikacji, wokół określonych modułów lub tylko dla jednego modułu.

Dlaczego? Jedna dyrektywa na plik jest łatwiejsza do utrzymania.

Zauważ: “Najlepsza praktyka: Dyrektywy powinny po sobie sprzątać. Możesz użyć element.on(‘$destroy’, …) lub scope.$on(‘destroy’, …) żeby wywołać funkcje odpowiedzialną za posprzątanie po tym kiedy dyrektywa przestanie być używana. … Dokumentacja Angulara

Modyfikacje DOM w dyrektywie

  • Kiedy bezpośrednio manipulujesz drzewem DOM, używaj w tym celu dyrektywy. Jeśli można zrobić to inaczej, poprzez CSS, ngAnimate, określony widok czy ngShow bądź ngHide użyj ich zamiast robić to samemu. Jeśli przykładowo dyrektywa jedynie się chowa lub pokazuje użyj ngHide/ngShow.

Dlaczego? Modyfikacje drzewa DOM mogą być trudne do przetestowania, debugowanie i często istnieją lepsze rozwiązania (CSS, animacje, widoki)

Stosuj unikalne prefixy dla swoich dyrektyw

  • Używaj krótkich, unikalnych i opisowych prefixów dla swoich dyrektyw takich jak acmeSalesCustomerInfo które zostaną zadeklarowane w HTML jako amce-sales-customer-info

Dlaczego? Unikalny, krótki prefix pomaga zidentyfikować cel istnienia dyrektywy i to co robi. Przykładowo prefix cc- może oznaczać, że dyrektywa jest częścią aplikacji CodeCamper podczas gdy prefix acme- oznacza, że jest ona częścią aplikacji firmy Acme.

Zauważ: Unikaj stosowania prefixów ng- ponieważ zarezerwowane są dla natywnych dyrektyw Angulara. Zrób rozeznanie w popularnych i szeroko stosowanych dyrektywach, żeby uniknąć konfliktu nazw, takich jak ionic- dla Ionic Framework.

Używaj restrict

  • Kiedy tworzysz dyrektywę która istnieje samodzielnie jako element powinieneś użyć restryktu E, opcjonalnie A dla dyrektyw które rozszerzają jakiś istniejący element DOM a nie istnieją samodzielnie.

Dlaczego? Bo ma to sens.

Dlaczego? Nie powinniśmy pozwalać dyrektywie być używanej jako klasa skoro zachowuje się jak element, w ostateczności atrybut.

Zauważ: Restrykcja EA jest domyślna w Angularze 1.3+

Dyrektywy i controllerAs

  • Używaj składni opartej na controllerAs w dyrektywach jeśli już stosujesz ją w widokach i kontrolerach żeby zachować jednolitość w całej aplikacji

Dlaczego? Ma to sens i stanowi żadnego problemu

Zauważ: Dyrektywa pod spodem pokazuje pewne sposoby użycia scope w funkcji link() podczas gdy sam kontroler oparty jest raczej na controllerAs. Przykład podany jest w jednym pliku tylko w celu pokazania tego w jednym miejscu.

Jeśli chodzi o DI powiemy sobie o tym więcej później.

Zauważ: Kontroler dla tej dyrektywy jest poza jej domknięciem. Taki styl eliminuje przypadki gdzie wstrzyknięcie tworzy kod który staje się nieosiągalny po return.

Zauważ: Odniesienia do cyklu życia danej dyrektywy zostały wprowadzone w Angularze 1.5. Logika która jest zależna od wartości które mają zostać podstawione powinna zostać umieszczona w $onInit co gwarantuje, że wszystkie potrzebne dane zostaną już podstawione.

Zauważ: Możesz również nazwać kontroler kiedy wstrzykniesz go do funkcji link() i mieć dostęp do atrybutów dyrektywy jak do właściwości kontrolera.

 

  • Używaj bindToController = true kiedy korzystasz z controllerAs z dyrektywą kiedy chcesz podstawić zewnętrzne scope do scope kontrolera dyrektywy.

Dlaczego? Poprzez takie rozwiązanie jest po prostu łatwiej

Zauważ: bindToController zostało wprowadzone w Angular 1.3

Rozwiązywanie obietnic

Kontroler aktywuje obietnice

  • Umieszczenie “rozruchowej” logiki kontrolera w funkcji activate()

Dlaczego? Umieszczanie logiki która jest odpowiedzialna za procesy inicjalizacyjne w jednym miejscu ułatwia ich zlokalizowanie, testowanie i pomaga w uniknięciu rozrzucenia tego typu funkcjonalności po całym kontrolerze.

Dlaczego? Funkcja activate() ułatwia ponowne używanie tej logiki żeby odświeżać kontroler / widok, trzymanie tego razem, przeniesienie użytkownika szybciej do widoku, ułatwienie animacji na ngView lub uiView.

Zauważ: Jeśli warunkowo musisz anulować routing zanim zaczniesz korzystać z kontrolera, odpowiedź znajdziesz w kolejnym podpunkcie.

Router rozwiązuje obietnice

  • Kiedy kontroler zależy od obietnicy która musi być rozwiązana jeszcze zanim kontroler zacznie być używany, zajmij się tymi zależnościami w $routeProvider zanim logika kontrolera jest wykonywana. Możesz też warunkowo anulować routing.
  • Skorzystaj z route resolve jeśli chcesz zdecydować czy nie anulować go zanim przejdzie do widoku

Dlaczego? Kontroler może wymagać danych zanim jeszcze się one załadują. Dane mogą pochodzić z obietnicy dostarczonej przez stworzoną przez nas fabrykę bądź $http. Skorzystanie z funkcji resolve routera pozwala rozwiązać obietnice zanim załadowany zostanie kontroler, tak żeby mógł operować na tych danych z obietnicy.

Dlaczego? Kod wykonuje się po routingu i w funkcji activate kontrolera. Widok zaczyna się ładować. Kiedy obietnica się rozwiązuje dane zostają podstawione. Odpowiednia animacja może zostać wyświetlona podczas takiego przejścia widoków.

Zauważ: Kod wykonuje się przed routingiem dzięki obietnicy. Odrzucenie obietnicy anuluje routing. Rozwiązanie powoduje, że nowy widok czeka na załadowanie. Przed tym może zostać pokazana animacja świadcząca o tym, że coś jest ładowane. Jeśli chcesz otrzymać widok szybciej i nie musieć decydować czy możesz przejść do widoku rozważ użycie zamiast tego techniki opartej na stosowaniu funkcji activate() w widoku.

Przykład pod spodem pokazuje wskazanie na odpowiednią funkcję przy rozwiązaniu przejścia, co jest łatwiejsze to zdebugowania i radzenia sobie z DI.

Przykładowy kod nie jest niestety bezpieczny do minifikacji, o tym jak go takim uczynić powiemy później.

Obsługa wyjątków z obietnicami

  • Blok catch obietnicy musi zwracać odrzuconą obietnicę żeby zachować wyjątek w łańcuchu obietnicy
  • Zawsze obsługuj wyjątki w serwisach / fabrykach.

Dlaczego? Jeśli blok catch nie zwróci odrzuconej obietnicy, wywołujący obietnicy nie będzie wiedział, że wystąpił wyjątek. Wykona się blok then. Więc użytkownik może nigdy nie dowiedzieć się co się stało.

Dlaczego? W celu uniknięcia przepuszczania błędów i błędnego informowania użytkownika

Zauważ: Rozważ włożenie jakiejkolwiek obsługi wyjątku we współdzielony moduł i serwis.

Dependency Injection

Niebezpieczne do minifikacji

  • Unikaj stosowania skróconej składni deklarowania zależności bez stosowania bezpiecznego do minifikacji podejścia

Dlaczego? Parametry przekazywane do komponentu (kontrolera, fabryki, itd.) zostaną przekonwertowane na zniekształcone zmienne. Przykładowo nazwy common czy dataservice mogą stać się b i nie zostać znalezione przez Angulara.

Ten kod może wyprodukować zniekształcone nazwy zmiennych które później podczas działania aplikacji wyrzucą błędy.

Ręczne ustalenie zależności

  • Używaj $inject żeby samemu podstawić potrzebne Ci zależności

Dlaczego? Ta technika odzwierciedla technikę używaną przez ng-annotate, którą rekomendowałem do zautomatyzowania tworzenia bezpiecznych do minifikacji zależności. Jeśli ng-annotate wykryje że podstawienie zależności miało już miejsce nie powieli go.

Dlaczego? Zabezpieczy to zmianę nazw twoich zmiennych przy procesie minifikacji. Nie zostaną one zmienione w taki sposób, żeby nie zostać rozpoznanymi przez Angulara

Dlaczego? Unikaj tworzenia listy zależności w jednej linii, gdyż przy  długiej liście może stać się trudnym jej przeczytanie. Może to również być mylące że tablica jest serią ciągów znaków i ostatnim komponentem jako funkcją.

Zauważ: Kiedy twoja funkcja znajduje się pod return$inject może być nieosiągalne (może się to zdarzyć w dyrektywie). Możesz temu zapobiec poprzez przesunięcie kontrolera na zewnątrz dyrektywy.

Ręczne ustalanie zależności dla resolve w routerze

  • Używaj $inject żeby ręcznie określić zależności dla funkcji resolve w twoim routerze

Dlaczego? Ta technika pomaga pozbyć się anonimowej funkcji w resolve, sprawiając że staje się to bardziej czytelne

Dlaczego? $inject poprzedza resolve i zajmuje się zabezpieczeniem minifikacji wszelkich zależności.

Minifikacja i anotacje

ng-annotate

  • Używaj ng-annotate dla Gulp lub Grunt i komentuj funkcje które potrzebują automatycznego DI używając /* @ngInject */

Dlaczego? Zabezpieczy to twój kod przed użyciem jakichkolwiek praktyk które nie będą bezpieczne przy minifikacji

Dlaczego? ng-min jest przestarzałe

Wolę używać Gulp gdyż mam poczucie, że jest łatwiejszy do pisania, czytania i debuggowania

Poniższy kod nie używa technik bezpiecznych do minifikacji.

Kiedy powyższy kod przejdzie przez ng-annotate wyprodukowany zostanie następujący kod z użyciem $inject i będący bezpiecznym do minifikacji.

Zauważ: Jeśli ng-annotate wykryje, że podstawienie zostało już zrobione (@ngInject został wykryty) kod $inject nie zostanie powielony.

Zauważ: Kiedy używasz funkcji resolve w routerze możesz poprzedzić ja @ngInject i wyprodukowany zostanie odpowiedni kod, trzymający wszelkie zależności w bezpiecznym do minifikacji trybie.

Zauważ: Zaczynając od Angulara 1.3 używać parametru dyrektywy ngAppngStrictDi żeby wykryć wszelkie potencjalne braki w bezpieczeństwie minifikacji zależności. Kiedy injector zostanie stworzoni w trybie “strict-di” spowoduje to nieodpalenie funkcji które nie używają jawnych adnotacji funkcji (które nie mogą zostać bezpiecznie zminifikowane). Debugger wyświetli wszystkie informacje w konsoli żeby pomóc namierzyć niewłaściwy kod. Preferuje używanie ng-strict-di jedynie w celach debuggowania aplikacji. <body ng-app=”APP” ng-strict-di>.

Używaj Grunt lub Gulp dla ng-annotate

Dlaczego? ng-annotate wyłapie większość zależności, ale czasem wymaga użycia składni /* @ngInject */.

Poniższy kod jest przykładem zadania w gulp używającego ngAnnotate.

Obsługa wyjątków

Dekoratory

  • Używaj dekoratora w czasie konfiguracji stosując $provide na serwisie $exceptionHandler jeśli chcesz określić niestandardowe zachowanie kiedy wystąpi jakiś wyjątek

Dlaczego? Jest to spójny sposób na radzenie sobie z niewyłapywanymi przez Angulara wyjątkami w czasie rozwoju aplikacji i już podczas jej działania

Zauważ: Innymi sposobem jest nadpisanie serwisu zamiast używać dekoratora. Jest to dobre rozwiązanie ale jeżeli chcesz zachować domyślne zachowanie i tylko je rozszerzyć to jednak dekorator będzie tutaj bardziej na miejscu.

Wyłapywanie wyjątków

  • Stwórz fabrykę która wyeksponuje interfejs dzięki któremu będziesz mógł wyłapywać wyjątki i ładnie je obsługiwać.

Dlaczego? Tworzy to spójny sposób dla wyłapywania wyjątków które mogą zostać wyrzucone w twoim kodzie (np. podczas zapytania XHR czy odrzucenia obietnicy).

Zauważ: Funkcja wyłapująca wyjątki jest dobra do wyłapywania i reagowania na określone wyjątki dla wywołań które wiesz, że mogą je wyrzucić. Przykładowo, kiedy tworzysz zapytanie XHR w celu uzyskania jakichś zewnętrznych danych z zewnętrznego serwera i chcesz wyłapać każdy wyjątek z tego serwisu i odpowiednio na niego zareagować.

Błędy podczas routingu

Dlaczego? Prowadzi to do spójnego sposobu radzenia sobie ze wszystkimi błędami w routingu.

Dlaczego? Potencjalnie jest to lepsze rozwiązanie dla użytkownika jeśli pojawi się jakiś błąd podczas routingu i poprowadzi go do przyjaznego powiadomienia z większą ilością informacji i opcją powrotu.

Nazewnictwo

Poradnik nazewnictwa

  • Używaj spójnych nazw dla wszystkich komponentów trzymając się wzorca który opisuje funkcjonalności danego komponentu. Wzorcem który ja polecam jest feature.type.js . Dla większości zasobów są dwie nazwy:
    • Nazwa pliku (avengers.controller.js)
    • Rejestrowana nazwa komponentu w Angularze (AvengersController)

Dlaczego? Konwencje nazewnicze pomagają w dostarczeniu spójnego sposobu na rozpoznawanie funkcjonalności danego komponentu na pierwszy rzut oka. A spójność projektu jest bardzo ważna. Spójność w firmie oznacza ogromną wydajność.

Dlaczego? Konwencje nazewnicze zwyczajnie pomogą Ci znaleźć szybciej swój kod i ułatwią jego zrozumienie.

Nazwy funkcjonalności dla plików

  • Używaj spójnych nazw dla wszystkich komponentów trzymając się wzorca który opisuje funkcjonalności danego komponentu. Wzorcem który ja polecam jest feature.type.js .

Dlaczego? Spójny sposób na szybkie rozpoznanie komponentów

Dlaczego? Wzorzec dla wszystkich zautomatyzowanych zadań.

Zauważ: Inną popularną konwencją jest nazywanie plików kontrolerów bez słowa controller w nazwie pliku: avengers.js zamiast avengers.controller.js. Wszystkie inne konwencje trzymają się używania przyrostka określającego typ. Kontrolery są najczęściej używanym typem komponentów więc zwyczajnie zaoszczędzamy pisania ale nadal możemy łatwo zidentyfikować co zawiera ten plik. Zalecam Ci wybranie jednej konwencji i trzymanie się jej, zarówno przez Ciebie jak i resztę zespołu który pracuje nad aplikacją. Moim wyborem jest nazwanie pliku kontrolera avengers.controller.js dla AvengersController.

Nazywanie plików z testami

  • Pliki z testami nazywaj tak samo jak plik komponentu którego te testy się tyczą tylko z sufixem spec.

Dlaczego? Spójny sposób na identyfikację komponentu.

Dlaczego? Dostarcza wzorzec dla Karmy lub innych tego typu narzędzi.

Nazwy kontrolerów

  • Używaj spójnych nazw dla wszystkich kontrolerów które odzwierciedlają ich nazwy. Używaj UpperCamelCase dla kontrolerów które są konstruktorami.

Dlaczego? Pomaga to na szybkie zidentyfikowanie i odniesienie się do kontrolera

Dlaczego? UpperCamelCase jest przyjętą konwencją dla obiektów których instancje tworzone są z pomocą konstruktora.

Sufix w nazwie kontrolerów

  • Zakończ nazwę kontrolera sufixem Controller

Dlaczego? Sufix Controller jest najczęściej używanym i oczywiście opisuje daną funkcję

Nazwy fabryk i serwisów

  • Używaj spójnych nazw dla wszystkich fabryk i serwisów określających ich funkcjonalności. Używaj camelCase dla serwisów i fabryk. Unikaj poprzedzania ich nazw $. Dodawaj jedynie sufix Service kiedy nie jest do końca jasne czym są.

Dlaczego? Daje to spójny i szybki sposób na rozpoznawanie i odnoszenie się do fabryk

Dlaczego? Pozwala na uniknięcie kolizji nazw z wbudowanymi fabrykami i serwisami poprzedzonymi $.

Dlaczego? Proste nazwy takie jak logger nie wymagają sufixa

Dlaczego? Nazwy takie jak avengers które są rzeczownikami wymagają sufixa i powinny nazywać się avengersService.

Nazewnictwo dyrektyw

  • Używaj spójnego nazewnictwa dla wszystkich dyrektyw korzystając przy tym z camelCase. Używaj krótkie przedrostka żeby opisać obszar do którego należy dyrektywa (niektóre przykłady mają prefix firmy lub projketu)

Dlaczego? Daje sposób na spójne i szybkie rozpoznawanie i odnoszenie się do komponentów

Moduły

  • Kiedy w projekcie występuje więcej modułów, główny moduł nazywany jest app.module.js podczas gdy inne moduły, znajdujące się w jego zależnościach nazywane są po tym co reprezentują. Przykładowo, moduł który określa funkcjonalności administratora nazywa się admin.module.js .

Dlaczego? Wprowadza spójność do wielo-modułowych aplikacji i daje im możliwość do ogromnego rozrostu.

Dlaczego? Dostarcza prosty sposób na zautomatyzowanie zadań ładowania wszystkich definicji modułów a później plików z innymi komponentami.

Konfiguracja

  • Oddziel konfiguracje modułu do osobnego pliku nazwanego po nazwie modułu. Konfiguracja dla głównego modułu aplikacji powinna nazywać się app.config.js (lub po prostu config.js). Konfiguracja dla modułu admin.module.js powinna nazywać się admin.config.js.

Dlaczego? Oddzielenie konfiguracji od definicji modułu, komponentów i rzeczywistego kodu.

Dlaczego? Wprowadzenie jasno określonego miejsca w którym powinna mieć miejsce konfiguracja.

Routing

  • Oddzielenie routingu w aplikacji do osobnego pliku. Przykładem może być app.routes.js dla głównego modułu i admin.routes.js dla modułu admin. Nawet w mniejszych aplikacjach preferuję oddzielenie tego od reszty konfiguracji.

Strukturyzowanie naszej aplikacji wedle zasady LIFT

LIFT

  • Zbuduj swoją aplikację tak żeby mógł łatwo zlokalizować swój kod (Locate), rozpoznać go (Identity), utrzymać w jak najbardziej płaskiej strukturze (Flatest), i spróbuj (Try) pozostać DRY (Dont repeat yourself – Nie powtarzać się). Struktura powinna odpowiadać tym czterem zasadom.

Dlaczego LIFT? Tworzy spójna strukturę która jest skalowalna, modułowa i podnosi wydajność programisty poprzez umożliwienie znalezienia mu wszystkiego szybko i łatwo. Innym sposobem na sprawdzenie czy twoja aplikacja jest dobrze ustrukturyzowana jest spytanie samego siebie: Jak szybko jestem w stanie otworzyć i pracować ze wszystkimi plikami powiązanymi z daną funkcjonalnością?

Kiedy nie czuje się dobrze moją strukturą aplikacji wracam do tych czterech podstawowych zasad i sprawdzam czy się z nimi zgadza.

Locate

  • Spraw, żeby lokalizacja twojego kodu była intuicyjna, prosta i szybka.

Dlaczego? Odkryłem, że jest to bardzo ważne dla projektu. Jeśli zespół nie jest w stanie znaleźć plików na których muszą szybko popracować znacznie spada ich efektywność i struktura wymaga zmiany. Możesz nie wiedzieć jaka jest nazwa pliku czy gdzie znajdują się powiązane z nim pliki więc umieszczenie ich w najbardziej intuicyjnej lokalizacji, blisko siebie pozwala zaoszczędzić naprawdę sporo czasu. Opisowa struktura folderów w projekcie może znacznie nam w tym pomóc.

Indentify

  • Kiedy patrzysz na plik powinieneś od razu wiedzieć co zawiera i co reprezentuje.

Dlaczego? Spędzasz mniej czasu na wyszukiwaniu kodu i stajesz się bardziej wydajny. Jeśli to oznacza, że potrzebujesz dłuższych nazw plików to niech takie będą. Twórz opisowe nazwy i trzymaj tylko po jednym komponencie w jednym pliku. Unikaj tworzenia plików z wieloma kontrolerami, serwisami czy co gorsza mieszaniem ich. Są pewne odstępstwa od tej reguły kiedy w jednym pliku implementuje się bardzo małe funkcjonalności powiązane ze sobą ale tylko wtedy kiedy ich identyfikacja dalej pozostaje łatwa i intuicyjna.

Flat

  • Utrzymuj płaską strukturę folderów tak długo jak to możliwe. Kiedy masz powyżej 7 plików zastanów się nad rozdzieleniem.

Dlaczego? Nikt nie chce przeszukiwać ponad siedmio poziomiowej struktury folderów żeby znaleźć plik. Pomyśl o menu na stronie internetowej… wszystkie co zawiera się głębiej niż w drugim powinno ulec rozważeniu. W strukturze folderów nie ma jakichś twardo zdefiniowanych reguł ale jeśli w folderze masz około 7-10 plików może nadszedł już czas żeby utworzyć podfolder. Opiera się to na twoim poczuciu komfortu. Używaj płaskiej struktury folderów tak długo jak istnieje oczywista potrzeba i sens tworzenia podfoldera.

DRY

  • Nie powtarzaj się ale nie idź też w drugim kierunku i zachowaj czytelność kodu

Dlaczego? Nie powtarzanie się jest ważne, ale nie kluczowe jeśli ma to oznaczać, że będziesz musiał poświęcić czytelność swojego kodu.

Struktura aplikacji

Ogólne porady

  • Miej krótką i daleką wizję implementacji. Innymi słowy, zacznij mały projekt ale miej na uwadze, że może stać się on wielki. Cały kod aplikacji znajduje się w głównym folderze nazwanymi app . Cała jego treść trzyma się zasady jedna funkcjonalność na jeden plik. Każdy kontroler, serwis, moduł i widok ma swój własny plik. Wszystkie trzeciorzędne zależności trzymane są w osobnym głównym folderze ale nie w app. W zależności od projektu może to być (bower_components, scripts, lib).

Wygląd

  • Umieść komponenty które definiują ogólny wygląd aplikacji w folderze nazwanym layout. Może zawierać w sobie widok i kontroler które zachowują się jak pojemnik dla innych tego typu komponentów naszej aplikacji, nawigacje, menu, obszary z treścią i inne.

Dlaczego? Zorganizowanie całego widoku w jednym miejscu pozwala na ponowne używanie go w zakresie aplikacji.

Foldery oparte o funkcjonalności

  • Twórz foldery których nazwy będą odzwierciedlać funkcjonalności które są w nich umieszczone. Kiedy folder rośnie do takiego momentu kiedy zawiera w sobie więcej niż 7 plików, zacznij rozważać stworzenie nowego folderu dla nich. Twój próg może być inny więc odpowiednio go dostosuj.

Dlaczego? Programista może zlokalizować kod, z łatwością rozpoznać co reprezentuje każdy plik, struktura jest tak płaska jak tylko może być i nie ma powtarzających się czy nadmiernie opisowych nazw.

Dlaczego? Zasady LIFT są w pełni pokryte.

Dlaczego? Pomaga aplikacji nie być zagraconą poprzez zorganizowanie jej zawartości i utrzymywanie jej przypisanej dzięki zasadom LIFT.

Dlaczego? Kiedy znajduje się tam więcej plików (10+), zlokalizowanie ich staje łatwiejsze ze spójna strukturą folderów i trudniejsze z bardziej spłaszczoną.

 

 

 

 

 

 

 

 

Zauważ: Nie używaj struktury folderów opartej o typy. Wymaga to poruszania się po wielu folderach pracując nad jakąś konkretną funkcjonalnością i staje się szybko nieporęczne kiedy aplikacja się rozrasta.

Modułowość

Dużo małych, samowystarczalnych modułów

  • Twórz małe moduły odpowiedzialne za jedną funkcjonalność

Dlaczego? Modułowa aplikacja jest łatwiejsza do podłączenia i działania. Oznacza to, że nowe funkcjonalności mogą być uruchamiane od razu po napisaniu bez żadnych innych, zbędnych zmian i konfiguracji.

Stwórz główny moduł aplikacji

  • Stwórz główny moduł który będzie łączył wszystko razem. Nazwij go tak jak swoją aplikację.

Dlaczego? Angular wspiera modułowość i odseparowanie. Stworzenie głównego modułu którego zadaniem jest powiązanie wszystkich innych razem tworzy bezpośredni sposób na dodawanie i usuwanie modułów z twojej aplikacji.

Utrzymuj główny moduł zwięzłym

  • Umieszczaj w nim jedynie logikę odpowiedzialną za wiązanie wszystkiego razem. Pozostaw konkretne funkcjonalności w ich własnych modułach.

Dlaczego? Dodanie dodatkowych funkcji do głównego modułu aplikacji jak pobieranie danych, wyświetlanie widoków lub innej logiki nie związanej z utrzymywaniem aplikacji w całości czyni te funkcjonalności trudniejszymi do ponownego użycia i wyłączenia.

Dlaczego? Główny moduł jest manifestem który opisuje które moduły definiują aplikację.

Obszary funkcjonalności są modułami

  • Tworzenie modułów które reprezentują pewne obszary funkcjonalności takie jak layout, dzielone i przeznaczone do wielokrotnego użytku serwisy, panele i specyficzne funkcjonalności aplikacji.

Dlaczego? Samowystarczalny moduł może zostać dodany do aplikacji bez żadnych problemów

Dlaczego? Sprinty i iteracje (SCRUM) mogą skupić się na określonych obszarach funkcjonalności i włączyć je pod koniec takiego sprintu czy iteracji

Dlaczego? Odseparowanie obszarów funkcjonalności w modułu sprawia, że są łatwiejsze do testowania w odizolowanym kodzie wielokrotnego użytku.

Bloki kodu wielokrotnego użytku są modułami

  • Twórz moduły które reprezentują części aplikacji których można użyć wielokrotnie dla najczęściej stosowanych serwisów takich jak obsługa wyjątków, logowanie, diagnostyka, bezpieczeństwo czy przechowywanie lokalnie danych.

Dlaczego? Tego typu funkcjonalności są potrzebne w wielu aplikacjach, więc trzymanie ich oddzielnie w ich własnych modułach sprawia, że możemy wykorzystywać je w różnych aplikacjach.

Zależności modułów

  • Główny moduł aplikacji zależy od pewnych funkcjonalności określonych i udostępnianych przez inne moduły.

Dlaczego? Główny moduł aplikacji zawiera jasno określony manifest funkcjonalności tej aplikacji

Dlaczego? Każda funkcjonalność określa jakie ona sama ma zależności więc może zostać umieszczona w innej aplikacji i dalej działać

Dlaczego? Wewnętrzne serwisy aplikacji od chociażby dzielenia się zewnętrznymi danymi stają się łatwe do zlokalizowania i dzielenia w app.module

Zauważ: Ta strategia zapewnia spójność. jest tutaj wiele dobrych rozwiązań. Pozwala na zachowanie angularowych zasad odnośnie zależności, sprawia, że utrzymanie i rozwój aplikacji stają się znacznie łatwiejsze.

Moje struktury różnią się nieco zależnie od aplikacji ale podążają za zasadami strukturyzowania i modułowości. Implementacja zależy od funkcjonalności i zespołu. Innymi słowy nie przestawaj samodzielnie myśleć i tworzyć wykapanych struktur aplikacji a dostosuj swoje własne do projektów mając na uwadze spójność, utrzymanie i wydajność.

 

W małych aplikacjach możesz również rozważyć włożenie wszystkich wspólnie używanych zależności do głównego modułu gdzie moduły poszczególnych funkcjonalności nie mają żadnych bezpośrednich zależności. Sprawia, że staje się to łatwiejsze do zarządzania i zmniejsza samą aplikację, ale sprawia, że staje się trudniejsza do ponownego użytku tych modułów na zewnątrz.

Logika rozruchowa

Konfiguracja

  • Umieść kod w konfiguracji moduły który musi zostać skonfigurowany przed uruchomieniem aplikacji. Idealnymi kandydatami do tego są providery i stałe.

Dlaczego? Pozwala to na zajmowanie mniej miejsca przez konfigurację.

Bloki wykonawcze

  • Każdy kod który musi zostać odpalony kiedy aplikacja startuje powinien zostać umieszczony w fabryce, wyeksponowany przez funkcję i wstawiony do bloku wykonawczego.
  • Rozważ technikę ręcznego ładowania jako alternatywę dla logiki która musi wykonać się priorytetowo podczas uruchamiania aplikacji Angulara.

Dlaczego? Kod który znajduje się bezpośrednio w bloku wykonawczym może być trudnym do przetestowania. Wstawienie go do fabryki znacznie ułatwia sprawę.

Dlaczego? Pisanie bezpośrednio w bloku wykonawczym może tworzyć pewne warunki w logice rozruchowej naszej aplikacji bo nie ma jak przekazać informacji o tym, że asynchroniczne zapytanie się wykonało.

Wbudowane serwisy Angulara

$document i $window

  • Używaj $document$window zamiast documentwindow

Dlaczego? Te serwisy są dostarczone przez Angulara i znacznie łatwiejsze do przetestowania niż używanie ich natywnych odpowiedników.

$timeout i $interval

  • Używaj $timeout$interval zamiast setTimeoutsetInterval

Dlaczego? Te serwisy są dostarczone przez Angulara i znacznie łatwiejsze do przetestowania oraz obsługują cykl życia Angulara co zapewnia utrzymywanie podstawiania danych w odpowiedniej kolejności

Testowanie

Testy jednostkowe pomagają utrzymać i rozwijać czysty kod, pozwoliłem sobie wrzucić tutaj moje polecenia dla poszczególnych technologi

Stories

  • Pisanie zestawu testów dla każdego historii. Zacznij z pustym testem i wypełnij go w miarę pisania kodu dla historii.

Dlaczego? Pisanie opisów testów pozwala jasno stwierdzić jaka będzie historia i jak możemy ocenić czy się udała.

Biblioteki do testów

Dlaczego? Zarówno Jasmine jak i Mocha są szeroko stosowane w Angularowej społeczności. Obydwie są stabilne, dobrze utrzymane i dostarczają dobrych funkcjonalności do testowania.

Zauważ: Jeśli używasz Mocha, możesz rozważyć skorzystanie z biblioteki Chai, ja jednak preferuje Mocha.

Odpalanie testów

  • Używaj Karma do przeprowadzania testów.

Dlaczego? Karma jest łatwa w konfiguracji. Odpala się raz i automatycznie za każdym razem kiedy zmienisz swój kod.

Dlaczego? Karma obsługuje proces Ciągłej Integracji sama z siebie lub korzystając z Grunt lub Gulp.

Dlaczego? Niektóre IDE mogą zostać skonfigurowane z Karma, przykładowo WebStorm czy Visual Studio.

Dlaczego? Karma działa prawidłowo z narzędziami do automatyzowania zadań takimi jak Grunt (z grunt-karma) i Gulp. Kiedy korzystasz z Gulp, używaj Karma bezpośrednio.

Stubbing and spying

Dlaczego? Sinon działa dobrze z Jasmine i Mocha i rozszerza ich funkcjonalności odpowiedzialne za stubbing i spying

Dlaczego? Sinon ułatwia żąglowanie pomiędzy Jasmine i Mocha gdybyś chcial wypróbować obydwa.

Dlaczego? Sinon ma opisowane wiadomości kiedy test nie spełni założeń

Przeglądarka po stornie serwera

  • Uzywaj PhantomJS żeby uruchomić swoje testy na serwerze.

Dlaczego? PhantomJS jest przeglądarka która pozwala Ci na uruchomienie testów bez potrzeby posiadania wizualnej otoczki. Nie musisz więc instalować Chrome, Firefoxa, Safari czy IE na twoim serwerze

Zauważ: Dalej powinieneś testować kod we wszystkich przeglądarkach w swoim środowisku dla grupy docelowej dla której produkujesz aplikacje.

Analiza kodu

  • Używaj JSHint w swoich testach

Dlaczego? Testy to kod. JSHint pomaga określić jakość kodu i kwestie które mogą sprawić, że testy nie będą działały poprawnie

Spraw żeby testy JSHint były ładgodniejsze

  • Zmniejsz trochę restrykcyjność zasad żeby pozwolić popularnym globalnym takim jak describe czy expect działać. Zmniejsz ją trochę dla wyrażeń których używa Mocha.

Dlaczego? Twoje testy to kod i potrzebują takiej samej uwagi odnośnie swojej jakości co rzeczywisty kod twojej aplikacji. Jednakże globalne zmienne używane przez frameworki testujące mogą być potraktowane łagodniej poprzez dodanie tego do twojego testu:

Możesz również dodać to do swojego pliku z opcjami dla JSHint:

Organizowanie testów

  • Umieść pliki testów jednostkowych zaraz obok plików z rzeczywistym kodem. Testy które zajmują się integracją z serwerem, lub wieloma komponentami umieść w oddzielnym folderze tests.

Dlaczego? Testy jednostkowe są bezpośrednio powiązane z określonymi komponentami.

Dlaczego? Jest łatwiej zawsze utrzymywać je aktualnymi kiedy mamy je pod ręką. Zawsze masz je przed oczami więc o nich pamiętasz co może zapewnić im, że będą rozwijane i utrzymywane.

Dlaczego? Kiedy aktualizujesz kod, łatwiej jest od razu zaktualizować testy

Dlaczego? Umieszczanie ich zaraz obok plików z kodem który testują sprawia, że są łatwe do znalezienia i przeniesienia razem z kodem.

Dlaczego? Dla osoby która czyta twój kod łatwiejszym staje się sprawdzenie jak komponent powinien się zachowywać i odkrycie jego ograniczeń

Dlaczego? Oddzielenie testów tak żeby nie znalazły się w wersji produkcyjnej jest łatwe przy wykorzystaniu Grunt bądź Gulp

Animacje

Użycie

  • Używaj delikatnych animacji Angulara w celu przejścia między dwoma stanami dla widoku i jego głównych elementów. Załączaj moduł ngAnimate. Kluczem jest zachowanie ich subtelnymi, gładkimi i płynnymi.

Dlaczego? Subtelne animacje mogą poprawić doznania użytkownika jeśli zostaną odpowiednio użyte

Dlaczego? Animacje mogą poprawić wydajność strony z punktu widzenia użytkownika dzięki przejściom między widokami

Krótkie

  • Używaj krótkich animacji. Ja zazwyczaj zaczynam od 300ms i dostosowuje dopóki nie będzie prawidłowo.

Dlaczego? Długie animacje mogą mieć przeciwne działanie niż to które opisaliśmy w powyższym punkcie. Mogą dać użytkownikowi wrażenie, że nasza aplikacja jest powolna.

animate.css

  • Używaj animate.css dla standardowych animacji

Dlaczego? Animacje których dostarcza nam animate.css są szybkie, gładkie i łatwe do zaimplementowania w twojej aplikacji

Dlaczego? Wprowadza spójność w twoich animacjach

Dlaczego? animate.css jest szeroko używany i dobrze przetestowany

Zauważ: Zobacz ten świetny artykuł o animacjach w Angularze autorstwa Matiasa Niemela.

Komentarze

jsDoc

  • Jeżeli chcesz stworzyć dokumentację, używaj składni jsDoc do pisania komentarzy odnośnie funkcji, parametrów, opisów i tego co zwracają. Używaj @namespace@memberOf żeby utrzymać odpowiednią strukturę twojej aplikacji

Dlaczego? Możesz wygenerować dokumentację na podstawie swojego kodu zamiast pisać ją od samego początku

Dlaczego? Tworzysz spójność używając popularnego narzędzia w branży

JS Hint

Używaj pliku z opcjami

  • Używaj JS Hint w celu przepadania twojego kodu pod względem ew. błędów i upewnij się, że załącz plik z opcjami. Zobacz dokumentację JSHint żeby dowiedzieć się więcej o tych opcjach.

Dlaczego? Dostarcza pierwszego ostrzeżenia jeszcze sprzed scommitowaniem swojego kodu

Dlaczego? Dostarcza spójności w twoim zespole.

JSCS

Używaj pliku z opcjami

  • Używaj JSCS żeby sprawdzać styl pisanego przez Ciebie kodu i upewnij się, że załączyłeś plik z odpowiednio dostosowanymi opcjami. Więcej szczegółów o dostępnych opcjach możesz znaleźć w dokumentacji JSCS.

Dlaczego? Informuje nas jeśli coś jest nie tak jeszcze przed scomittowaniem

Dlaczego? Wprowadza spójność pisania kodu w twoim zespole

Stałe

Vendor

  • Stwórz Angularowe stałe będące odpowiednikami stałych z vendorowych bibliotek

Dlaczego? Jest to sposób na załączenie vendorowych bibliotek które w innym przypadku są globalne. Poprawia to możliwość testowania kodu dając Ci lepszy pogląd na to jakie są zależności twoich komponentów. Pozwala to również podstawić te zależności wtedy kiedy ma to sens.

  • Używaj stałych dla wartości które nie ulegają zmianie i nie pochodzą z innego serwisu. Kiedy stałe używane są tylko w module mogą być ponownie używane w wielu aplikacjach. Umieszczaj je w pliku nazwanym po module. Jeśli nie jest o wymagane to stałe trzymaj w głównym module i w pliku pod nazwą constants.js.

Dlaczego? Wartości które, chociaż nieczęsto, mogą się zmieniać powinny być pozyskiwane przez inne serwisy tak żeby nie musieć zmieniać kodu źródłowego tych serwisów. Przykładowo url dla serwisu który z tego urla pozyskuje jakieś dane mógłby być umieszczony jako stała ale lepszą praktyką byłoby załadowanie go osobno.

Dlaczego? Stałe mogą być używane w każdym komponencie aplikacji Angular, łącznie z providerami.

Dlaczego? Kiedy aplikacja jest podzielona na moduły mogą być wykorzystywane w innych aplikacjach. Każdy, osobny moduł powinien być stosunkowo niezależny wliczając w to stałe.

Szablony i fragmenty

Używaj szablonów lub fragmentów plików żeby pomóc sobie zachować spójny styl i wzorce. Podam Ci tutaj takie szablony i fragmenty do różnych IDE.

Sublime Text

  • Fragmenty napisane w Angularze które są zgodne z opisanymi tutaj stylami i zasadmi pisania kodu.
    • Pobierz fragmenty dla Sublime Text
    • Umieść je swoim folderze Package
    • Włącz ponownie Sublime
    • W plikach JavaScript wpisz te komendy i zakończ je TABem

Visual Studio

  • Szablony plików które są zgodne z opisanymi tutaj stylami i zasadami można pobrać z SideWaffle.
    • Pobierz rozszerzenie SideWaffle dla Visual Studio (plik vsix).
    • Uruchom ten plik
    • Odpal ponownie Visual Studio

WebStorm

  • Szablony plików na żywo zgodne z opisanymi tutaj stylami i zasadami
    • Pobierz webstorm-angular-live-templates.xml
    • Umieść go w swoim folderze z szablonami
    • Uruchom ponownie WebStorm
    • W plikach JavaScript wpisz następujące komendy i zakończ je TABem

       

Atom

  • Fragmenty napisane zgodnie z opisanymi tutaj stylami i zasadami

    lub

    • Otwórz Atom a następnie Package Manager (Packages -> Settings View -> Install Packages/Themes
    • Znajdź pakiet ‘angularjs-styleguide-snippets’
    • Kliknij ‘Install’ żeby zainstalować ten pakiet
  • W plikach JavaScript wpisz następujące komendy i zakończ je TABem.

Brackets

  • Fragmenty napisane zgodnie z opisanymi tutaj stylami i zasadami
    • Ściągnij Brackets Angular Snippets
    • Wejdź w Extension manager (File -> Extension manager)
    • Zainstaluj ‘Brackets Snippets (by edc)
    • Kliknij na żarówkę w prawym rogu
    • Kliknij Settings a następnie Import
    • Wybierz plik i zaznacz skip lub override
    • Kliknij Start Import
  • W plikach JavaScript wpisz te komendy i zakończ je TABem.

     

vim

  • Fragmenty dla vima które są napisane zgodnie z opisanymi tutaj stylami i zasadami
  • Fragmenty UltiSnips napisane zgodnie z opisanymi tutaj stylami i zasadami
    • Pobierz fragmenty UltiSnips dla vima
    • Ustaw UltiSnips
    • Skopiuj fragmenty do folderu UltiSnips

Visual Studio Code

  • Fragmenty dla Visual Studio Code napisane zgodnie z opisanymi tutaj zasadami i stylami.
    • Pobierz fragmenty dla VS
    • Skopiuj fragmenty do odpowiedniego folderu, lub skopiuj same fragmenty do pliku zawierającego twoje obecne

       

Emacs

  • Fragmenty dla Emacs napisane zgodnie z opisanymi tutaj zasadami i stylami.
    • Pobierz fragmenty dla Emacs , zauważ, że yasnippet kategoryzuje fragmenty po głównym trybie a istnieje wiele trybów w Emacsie do edytowania kodu JavaScript. Fragmenty znajdują się w js2-mode, a inne foldery zawierają jedynie pliki z kropkami żeby się do nich odnieść
    • Zainstaluj yasnippet (M-x package-install RET yasnippet RET)
    • skopiuj fragmenty do odpowiedniego folderu lub zmodyfikuj plik inicjujący Emacsa i dodaj do niego plik z fragmentami do yas-snippet-dirs.

Yeoman Generator

Możesz używać HotTowel yeoman generator żeby stworzyć aplikację która da Ci wyjściowy punkt do rozpoczęcia pracy nad swoim projektem. Oczywiście napisana będzie zgodnie z opisanymi tutaj zasadami i stylami.

  1. Zainstaluj generator-hottowel
  2. Stwórz folder i do niego przejdź
  3. Odpal generator

Routing

Routing po stronie klienta jest ważny żeby stworzyć nawigację i możliwość przechodzenia pomiędzy widokami oraz daje możliwość tworzenia widoków z wielu mniejszych szablonów i fragmentów.

Dlaczego? UI Router oferuje wszystkie funkcjonalności routingu w Angularze oraz dodatkowo funkcjonalności które oferują zagnieżdżony routing i stany

Dlaczego? Składnia jest podobna do angularowego routera i łatwo jest przerzucić się na UI Router

  • Zauważ: Możesz użyć providera takego jak routerHelperProvider pokazanego poniżej żeby pomóc sobie skonfigurować stany plików podczas fazy rozruchowej

  • Zdefiniuj ścieżki dla widoków w modułach w których istnieją. Każdy moduł powinien zawierać ścieżki do tych widoków które obsługuje.

Dlaczego? Każdy moduł powinien być samodzielny.

Dlaczego? Kiedy usuwamy lub dodajemy moduł, aplikacja powinna zawierać ścieżki tylko do istniejących widoków

Dlaczego? Sprawia, że łatwiej można włączyć lub wyłączyć określone obszary aplikacji

Automatyzacja zadań

Używaj Gulp lub Grunt to automatyzacji zadań. Gulp skłania do pisania kodu ponad konfigurację a Grunt ma odwrotnie. Ja osobiście wolę Gulp ponieważ moim zdaniem łatwiej się w nim pisze i czyta ale obydwa są świetne.

Dowiedz się więcej na temat Gulp i wzorców do automatyzacji zadań z kursu Gulp na Pluralsight

  • Używaj automatyzacji zadań do wylistowania wszystkich definicji modułów *.module.js przed innymi plikami JS naszej aplikacji

Dlaczego? Angular potrzebuje zarejestrować wszystkie pliki modułów zanim będzie mógł ich użyć

Dlaczego? Nazywanie modułów w konkretnym wzorcu jak chociażby *.module.js sprawia, że łatwiej je wszystkie zebrać i stworzyć z nich listę.

Filtry

  • Unikaj używania filtrów do przeszukiwania wszystkich właściwości obiektów. Używaj ich tylko do wybranych.

Dlaczego? Filtry mogą łatwo zostać nadużywane i negatywnie wpłynąć na wydajność całej aplikacji jeśli nie są mądrze używane. Przykładowo kiedy filtr zajmuje się dużą i rozbudowaną strukturą obiektu.

Dokumentacja

Jeśli chcesz dowiedzieć się czegoś jeszcze lub brakuje Ci jakiejś istotnej wiedzy zajrzyj do dokumentacji AngularJS.

 

Powyższy tekst jest tłumaczeniem Angular 1 Style Guide autorstwa @john_papa i zatwierdzonego przez zespół Angulara.

Źródło: https://github.com/johnpapa/angular-styleguide/tree/master/a1

Autor: https://twitter.com/john_papa