Jak pisząc dyrektywę w AngularJS, jak zdecydować, czy nie potrzebuję nowego zakresu, nowego zakresu potomnego ani nowego zakresu izolowanego?


265

Szukam wskazówek, które można wykorzystać, aby określić, jakiego rodzaju zakresu należy użyć podczas pisania nowej dyrektywy. Idealnie chciałbym coś podobnego do schematu blokowego, który poprowadzi mnie przez kilka pytań i wyskoczy prawidłową odpowiedź - bez nowego zakresu, nowego zakresu potomnego lub nowego zakresu izolowania - ale to prawdopodobnie wymaga zbyt wiele. Oto mój obecny marny zestaw wskazówek:

Wiem, że użycie dyrektywy z izolowanym zasięgiem na elemencie zmusza wszystkie inne dyrektywy dotyczące tego samego elementu do korzystania z tego samego (jednego) zakresu izolowania, więc czy nie ogranicza to poważnie, kiedy można zastosować zakres izolowania?

Mam nadzieję, że niektórzy z zespołu Angular-UI (lub inni, którzy napisali wiele dyrektyw) mogą podzielić się swoimi doświadczeniami.

Nie dodawaj odpowiedzi, która po prostu mówi „użyj izolowanego zakresu dla komponentów wielokrotnego użytku”.


„zakres potomny” oznacza utworzenie zakresu w funkcji łącza przez „zakres. $ new ()”? Ponieważ, jak wiem, dyrektywa może mieć zakres izolowany lub go nie mieć (więc będzie używać zakresu, w którym został użyty)
Valentyn Shybanov

3
@ValentynShybanov Ustawienie scope: trueutworzy zakres potomny przy użyciu $scope.new()automatycznie.
Josh David Miller

2
@ Valentyn, co powiedział Josh: więc trzy możliwości to scope: false(domyślny, brak nowego zakresu), scope: true(nowy zakres dziedziczy prototypowo) i scope: { ... }(nowy zakres izolowania).
Mark Rajcok

1
Tak, dziękuję. Brakuje mi różnicy między „prawdziwym” a „{}”. Dobrze wiedzieć.
Valentyn Shybanov

Jest czwarty przypadek, który ludzie zwykle ignorują ... to jest „kontroler dyrektywy” .. Myślę, że pytanie powinno zostać rozszerzone, aby obejmowało również ich ... +1 do pytania ..
ganaraj

Odpowiedzi:


291

Cóż za świetne pytanie! Ja uwielbiam słuchać, co inni mają do powiedzenia, ale tutaj są wytyczne używam.

Przesłanka: zakres wysokościowy jest używana jako „klej”, którego używamy do komunikacji między kontrolerem nadrzędnym, dyrektywą i szablonem dyrektywy.

Zakres nadrzędny: scope: false więc w ogóle nie ma nowego zakresu

Nie używam tego zbyt często, ale jak powiedział @MarkRajcok, jeśli dyrektywa nie ma dostępu do żadnych zmiennych zakresu (i oczywiście nie ustawia żadnych!), To jest to dla mnie w porządku. Jest to również pomocne w przypadku dyrektyw podrzędnych, które są używane tylko w kontekście dyrektywy nadrzędnej (choć zawsze są wyjątki od tej zasady) i które nie mają szablonu. Zasadniczo cokolwiek z szablonem nie należy do dzielenia zakresu, ponieważ z natury ujawniasz ten zakres dostępu i manipulacji (ale jestem pewien, że istnieją wyjątki od tej reguły).

Jako przykład niedawno stworzyłem dyrektywę, która rysuje (statyczną) grafikę wektorową przy użyciu biblioteki SVG, którą właśnie piszę. Jest $observeto dwa atrybuty ( widthi height) i wykorzystuje je w swoich obliczeniach, ale nie ustawia ani nie odczytuje żadnych zmiennych zakresu i nie ma szablonu. Jest to dobry przypadek użycia, aby nie tworzyć innego zakresu; nie potrzebujemy jednego, więc po co zawracać sobie głowę?

Jednak w innej dyrektywie SVG wymagałem zestawu danych do użycia i dodatkowo musiałem przechowywać odrobinę stanu. W takim przypadku użycie zakresu nadrzędnego byłoby nieodpowiedzialne (ponownie, ogólnie rzecz biorąc). Więc zamiast...

Zakres dziecka: scope: true

Dyrektywy z zakresem potomnym są kontekstowe i mają na celu interakcję z bieżącym zakresem.

Oczywiście kluczową zaletą tego w stosunku do zakresu izolowanego jest to, że użytkownik może swobodnie korzystać z interpolacji dowolnych atrybutów; np. użycie class="item-type-{{item.type}}"dyrektywy z zasięgiem izolowanym nie będzie domyślnie działać, ale działa dobrze na jednym z zasięgiem potomnym, ponieważ wszystko, co jest interpolowane, nadal domyślnie znajduje się w zakresie nadrzędnym. Również sama dyrektywa może bezpiecznie oceniać atrybuty i wyrażenia w kontekście własnego zakresu, nie martwiąc się o zanieczyszczenie lub uszkodzenie rodzica.

Na przykład podpowiedź to coś, co dopiero się dodaje; zakres izolowany nie działałby (domyślnie, patrz poniżej), ponieważ oczekuje się, że użyjemy tutaj innych dyrektyw lub interpolowanych atrybutów. Etykietka to tylko ulepszenie. Jednak podpowiedź musi również określać pewne elementy zakresu, aby używać go z pod-dyrektywą i / lub szablonem, i oczywiście zarządzać własnym stanem, więc byłoby naprawdę źle używać zakresu nadrzędnego. Zanieczyszczamy to lub uszkadzamy, i nie bueno.

Często używam lunet dziecięcych niż lunet izolacyjnych lub nadrzędnych.

Wyizoluj zakres: scope: {}

Dotyczy to komponentów wielokrotnego użytku. :-)

Ale poważnie myślę o „komponentach wielokrotnego użytku” jak o „komponentach samowystarczalnych”. Chodzi o to, aby miały być używane do określonego celu, więc łączenie ich z innymi dyrektywami lub dodawanie innych interpolowanych atrybutów do węzła DOM z natury nie ma sensu.

Mówiąc ściślej, wszystko, co jest potrzebne do tej samodzielnej funkcjonalności, jest dostarczane poprzez określone atrybuty oceniane w kontekście zakresu nadrzędnego; są to albo ciągi jednokierunkowe („@”), wyrażenia jednokierunkowe („&”), albo dwukierunkowe powiązania zmiennych („=”).

W przypadku komponentów niezależnych nie ma sensu stosowanie innych dyrektyw lub atrybutów, ponieważ istnieje ona sama. Jego stylem rządzi własny szablon (w razie potrzeby) i można przenieść odpowiednią treść (w razie potrzeby). Jest samodzielny, więc umieściliśmy go w izolowanym zakresie, aby powiedzieć: „Nie zadzieraj z tym. Daję ci zdefiniowane API poprzez te kilka atrybutów”.

Dobrą najlepszą praktyką jest wykluczenie jak największej liczby elementów opartych na szablonie z funkcji łącza dyrektywy i kontrolera, jak to możliwe. Zapewnia to kolejny punkt konfiguracji „podobny do API”: użytkownik dyrektywy może po prostu zastąpić szablon! Funkcjonalność pozostała taka sama, a jej wewnętrzny interfejs API nigdy nie został zmieniony, ale możemy popsuć stylem i implementacją DOM tyle, ile potrzebujemy. Interfejs użytkownika / bootstrap jest doskonałym przykładem tego, jak to zrobić dobrze, ponieważ Peter i Paweł są niesamowici.

Lunety z izolatami świetnie nadają się również do stosowania z transkluzją. Weź karty; stanowią one nie tylko całą funkcjonalność, ale wszystko, co jest w środku , można swobodnie oceniać z zakresu nadrzędnego, pozostawiając zakładki (i panele), aby zrobić, co chcą. Karty mają oczywiście swój własny stan , który należy do zakresu (do interakcji z szablonem), ale ten stan nie ma nic wspólnego z kontekstem, w którym został użyty - jest całkowicie wewnętrzny z tego, co czyni dyrektywę tabu dyrektywą tab. Ponadto nie ma sensu używać żadnych innych dyrektyw z kartami. To są zakładki - a my już mamy tę funkcjonalność!

Otocz go większą funkcjonalnością lub włącz więcej funkcjonalności, ale dyrektywa jest już taka, jak jest.

To powiedziawszy, należy zauważyć, że istnieją pewne sposoby obejścia niektórych ograniczeń (tj. Cech) izolowanego zakresu, jak wskazał @ProLoser w swojej odpowiedzi. Na przykład w sekcji zakresu potomnego wspomniałem o interpolacji w przypadku łamania atrybutów niebędących dyrektywami podczas korzystania z zakresu izolowanego (domyślnie). Ale użytkownik może na przykład po prostu użyć class="item-type-{{$parent.item.type}}"i to po raz kolejny zadziała. Jeśli więc istnieje ważny powód, aby używać lunety izolacyjnej nad lunetą potomną, ale martwisz się niektórymi z tych ograniczeń, wiedz, że możesz obejść je wszystkie, jeśli zajdzie taka potrzeba.

Podsumowanie

Dyrektywy bez nowego zakresu są tylko do odczytu; są całkowicie zaufani (tj. wewnętrzni dla aplikacji) i nie dotykają wtyczki jack. Dyrektywy z zakresem potomnym dodają funkcjonalność, ale nie są jedyną funkcjonalnością. Wreszcie, izolowane zakresy dotyczą dyrektyw, które są całym celem; są samodzielne, więc jest w porządku (i większość „poprawnych”), aby pozwolić im przejść na nieuczciwość.

Chciałem poznać moje początkowe przemyślenia, ale kiedy wymyślę więcej rzeczy, zaktualizuję to. Ale cholera - to długo czeka na SO odpowiedź ...


PS: Całkowicie styczna, ale skoro mówimy o zakresach, wolę powiedzieć „prototypowy”, podczas gdy inni wolą „prototypowy”, który wydaje się być bardziej dokładny, ale po prostu nie zsuwa się z języka. :-)


Dzięki Josh, świetna odpowiedź. Chciałem / oczekiwałem na to długich odpowiedzi. Dwie rzeczy, których nie przestrzegałem: 1) zakres potomny: „użytkownik może dowolnie używać interpolacji dla dowolnych atrybutów”. 2) wyodrębnij zakres: „czy nie wszystkie, w przypadku„? ”” Czy możesz je trochę rozwinąć? (Jeśli to jest łatwiejsze, możesz edytować swój post zamiast pisać komentarze).
Mark Rajcok

@MarkRajcok Dla (1) zmieniłem go, aby był trochę mniej mglisty - daj mi znać, jeśli mi się nie uda. Dla (2) było to połączenie literówki i złego sformułowania; Przepisałem ten akapit, aby był jaśniejszy. Dodałem także dodatkowy przykład lub dwa, wyjaśniłem kilka innych rzeczy i poprawiłem kilka literówek.
Josh David Miller,

Jak wspomniano w odpowiedzi - bootstrap for angular jest doskonałym przykładem ich połączenia. Uważam, że przykład akordeonu jest szczególnie przydatny - GitHub - Accordion
CalM 10.09.13

Wspomniałeś, że najczęściej korzystasz z lunet dziecięcych, myślałem, że wzór dyrektyw wielokrotnego użytku jest najczęstszy i unikałem pisania dyrektyw, które miały być używane tylko raz. Czy to nie jest potrzebne? Czasami, gdy mój HTML staje się zbyt duży, mam ochotę przenieść tę sekcję do dyrektywy, ale zostanie ona użyta tylko raz, więc zostawiam ją w pliku HTML.
user2483724,

2
@ user2483724 Bardzo częstym nieporozumieniem jest to, że dyrektywy „wielokrotnego użytku” to takie, które używają zakresu izolowanego; skąd. Jeśli spojrzysz na fabrycznie zapakowane dyrektywy, prawie żadna z nich nie używa lunet izolujących - niektóre nawet nie mają zasięgu potomnego - ale zapewniam cię, że można je ponownie wykorzystać! Zasada powinna dotyczyć sposobu stosowania zakresu dyrektywy. Jeśli chodzi tylko o oszczędność miejsca w pliku, nie jestem pewien, czy dyrektywa jest najlepszym rozwiązaniem. Zwiększa czas przetwarzania ze względu na programistę. Ale jeśli musisz, to idź. Lub użyj ngInclude. Lub zrób to jako część swojej kompilacji. Dużo opcji!
Josh David Miller

52

Moja osobista polityka i doświadczenie:

Izolowany: prywatny piaskownica

Chcę utworzyć wiele metod zakresu i zmiennych, które są WYŁĄCZNIE używane w mojej dyrektywie i nigdy nie są widoczne ani dostępne dla użytkownika. Chcę dodać do białej listy, jaki zakres danych jest dla mnie dostępny. Mogę użyć opcji integracji, aby umożliwić użytkownikowi powrót do zakresu nadrzędnego (bez zmian) . Ja nie chce moje zmienne i metody dostępne w dołączany dzieci.

Dziecko: podsekcja treści

Chcę utworzyć metody zakres i zmienne, które MOGĄ być dostępne przez użytkownika, ale nie odnoszą się do otaczającego zakresów (rodzeństwo i rodzice) poza kontekstem moim dyrektywy. Chciałbym również pozwolić, aby WSZYSTKIE dane zakresu nadrzędnego przesuwały się w przejrzysty sposób.

Brak: proste dyrektywy tylko do odczytu

Naprawdę nie muszę zadzierać z metodami lub zmiennymi zakresu. Prawdopodobnie robię coś, co nie ma związku z zasięgami (takie jak wyświetlanie prostych wtyczek jQuery, sprawdzanie poprawności itp.).

Notatki

  • Nie należy pozwolić, aby ngModel lub inne rzeczy bezpośrednio wpływały na twoją decyzję. Możesz obejść dziwne zachowanie, wykonując czynności takie jak ng-model=$parent.myVal(dziecko) lub ngModel: '='(izoluj).
  • Isolate + transclude przywróci wszystkie normalne zachowanie do dyrektyw rodzeństwa i powróci do zakresu nadrzędnego, więc nie pozwól, aby miało to również wpływ na twoją ocenę.
  • Nie zadzieraj z lunetą na none, ponieważ jest to jak umieszczanie danych w lunecie dla dolnej połowy DOM, ale nie dla górnej połowy, co ma 0 sensu.
  • Zwróć uwagę na priorytety dyrektywy (nie ma konkretnych przykładów, jak to może wpłynąć na rzeczy)
  • Wprowadzaj usługi lub używaj kontrolerów do komunikacji między dyrektywami dowolnego typu zakresu. Możesz także require: '^ngModel'zajrzeć do elementów nadrzędnych.

1
Być może źle zrozumiałem tę część: „Izolowanie + transkluzja przywróci wszystkie normalne zachowanie dyrektywom rodzeństwa”. Zobacz ten plunker . Musisz zajrzeć do konsoli.
Josh David Miller

1
Dzięki ProLoser za wgląd / odpowiedź. Jesteś jedną z osób, które miałem nadzieję zobaczyć ten post, jeśli dodam tag angularjs-ui.
Mark Rajcok

@JoshDavidMiller, kiedy mówimy o dyrektywach dotyczących tego samego elementu DOM, sprawy stają się bardziej skomplikowane i zamiast tego powinieneś rzucić okiem na właściwość pierwszeństwa. Zamknięcie jest bardziej odpowiednie dla treści dla dzieci.
ProLoser

1
@ProLoser Racja, ale nie jestem pewien, co miałeś na myśli przez to stwierdzenie. Oczywiście wpływają one na dzieci, ale w jaki sposób zakres dyrektyw w ogóle wpływa na ich dyrektywy dotyczące rodzeństwa?
Josh David Miller

18

Po napisaniu wielu dyrektyw postanowiłem użyć mniejszego isolatedzakresu. Mimo że jest to fajne i hermetyzujesz dane i pamiętasz, aby nie przeciekać danych do zakresu nadrzędnego, znacznie ogranicza to liczbę dyrektyw, których możesz używać razem. Więc,

Jeśli dyrektywa, którą napiszesz, zachowa się całkowicie sama i nie będziesz dzielić się nią z innymi dyrektywami, wybierz izolowany zakres . (np. komponent, który można po prostu podłączyć, bez zbytniego dostosowywania dla końcowego programisty) (staje się to trudniejsze, gdy próbujesz pisać podelementy, które zawierają dyrektywy)

Jeśli dyrektywa, którą napiszesz, będzie po prostu dokonywała manipulacji domem, które nie wymagają wewnętrznego stanu zakresu, lub wyraźnych zmian zakresu (głównie bardzo prostych rzeczy); iść do żadnego nowego zakresu . (takie jak ngShow, ngMouseHover, ngClick, ngRepeat)

Jeśli dyrektywa, którą zamierzasz napisać, musi zmienić niektóre elementy w zakresie nadrzędnym, ale musi także obsługiwać stan wewnętrzny, wybierz nowy zakres podrzędny . (takie jak ngController)

Koniecznie sprawdź kod źródłowy dla dyrektyw: https://github.com/angular/angular.js/tree/master/src/ng/directive
Bardzo pomaga w tym, jak o nich myśleć


Jeśli kilka komponentów musi się ze sobą komunikować, mogą mieć izolowany zakres i zastosowanie require, dzięki czemu dyrektywy pozostaną oddzielone. Jak więc ogranicza możliwości? To jeszcze bardziej uszczegóławia dyrektywy (więc zadeklaruj, na czym polegasz). Pozostawiłbym więc tylko jedną zasadę: jeśli twoja dyrektywa ma stan lub potrzebuje danych z zakresu, w którym jest używana - użyj zakresu izolowanego. W przeciwnym razie nie używaj zakresu. I o „dziecięcych zakresach” - napisałem też sporo dyrektyw i nigdy nie potrzebowałem tej funkcji. Jeśli „trzeba zmienić niektóre elementy w zakresie nadrzędnym” - użyj powiązań.
Valentyn Shybanov

A także o „potrzebie zmiany niektórych elementów w zakresie nadrzędnym” - jeśli zmodyfikujesz coś w zakresie podrzędnym, zmiany nie zostaną wprowadzone do zakresu nadrzędnego (chyba że użyjesz brudnego $parentwłamania). Więc tak naprawdę „zakresy potomne” dla dyrektyw to coś, co wygląda na to, że powinno być używane całkiem z tyłu - tak, ngRepeatże tworzy nowe zakresy potomne dla każdego elementu do powtórzenia (ale także tworzy go przy użyciu scope.$new();i nie scope: true.
Valentyn Shybanov

1
Nie można poprosić o wiele izolowanych zakresów w tym samym elemencie, nie można uzyskać dostępu do funkcji w zakresie nadrzędnym, chyba że zostaną one jednoznacznie powiązane. (Powodzenia przy użyciu ngClickitp.) Wymaganie tworzy rodzaj rozdzielenia Zgadzam się, ale nadal musisz znać dyrektywę nadrzędną. O ile nie jest to element , jestem przeciwny izolacji. Dyrektywy (przynajmniej większość z nich) mają być w wysokim stopniu wielokrotnego użytku, a izolacja to psuje.
Umur Kontacı

Nie używam również zakresu podrzędnego w dyrektywach, ale ponieważ zakres podrzędny dziedziczy prototypowo z zakresu nadrzędnego, jeśli dostęp do właściwości w obrębie właściwości w zakresie nadrzędnym, zmiany zostaną zapełnione. Autorzy
Angulara

5
Po pierwsze, myślę, że jesteś trochę zbyt surowy w zakresie izolowanych zakresów. Myślę, że mają one szersze zastosowanie niż przyznajesz im za to, i że istnieją sposoby na uniknięcie wielu wyzwań, które (prawidłowo) wskazałeś, przed którymi stoimy podczas ich używania. Nie zgadzam się również z „niewielkim dostosowaniem dla końcowego programisty” - szczegóły znajdują się w mojej odpowiedzi. To powiedziawszy, twoja odpowiedź nie była ani zła, ani zła i dotyczyła pytania, więc nie jestem pewien, dlaczego został odrzucony. Więc +1.
Josh David Miller

9

Pomyślałem, że dodam moje obecne rozumienie i to, jak odnosi się ono do innych koncepcji JS.

Domyślne (np. Niezadeklarowane lub zakres: fałsz)

Jest to filozoficznie równoważne z użyciem zmiennych globalnych. Twoja dyrektywa może uzyskać dostęp do wszystkiego w kontrolerze nadrzędnym, ale wpływa również na nie i jest jednocześnie dotknięta.

zakres:{}

To jest jak moduł, wszystko, czego chce użyć, musi zostać przekazane jawnie. Jeśli KAŻDA dyrektywa, której używasz, jest zakresem izolowanym, może to być odpowiednik tworzenia KAŻDEGO pliku JS, który piszesz swój własny moduł z dużym nakładem pracy przy wprowadzaniu wszystkich zależności.

zakres: dziecko

Jest to środek pomiędzy zmiennymi globalnymi a jawnym przekazywaniem. Jest podobny do łańcucha prototypów javascript i po prostu rozszerza kopię zakresu nadrzędnego. Jeśli utworzysz zakres izolowany i przekażesz każdy atrybut i funkcję zakresu nadrzędnego, będzie to funkcjonalnie równoważne z tym.


Kluczem jest to, że KAŻDĄ dyrektywę można napisać W DOWOLNY sposób. Różne deklaracje zakresu są tylko po to, aby pomóc Ci zorganizować. Możesz uczynić wszystko modułem lub możesz po prostu użyć wszystkich zmiennych globalnych i być bardzo ostrożnym. Jednak dla ułatwienia konserwacji lepiej jest modulować logikę na logicznie spójne części. Istnieje równowaga między otwartą łąką a zamkniętym więzieniem. Uważam, że powodem, dla którego jest to trudne, jest fakt, że kiedy ludzie się o tym dowiadują, myślą, że uczą się, jak działają dyrektywy, ale w rzeczywistości uczą się o organizacji kodu / logiki.

Kolejną rzeczą, która pomogła mi zrozumieć, jak działają dyrektywy, jest poznanie ngInclude. ngInclude pomaga dołączyć częściowe HTML. Kiedy po raz pierwszy zacząłem używać dyrektyw, odkryłem, że możesz użyć opcji szablonu, aby zmniejszyć swój kod, ale tak naprawdę nie przywiązywałem żadnej logiki.

Oczywiście między dyrektywami angular a pracą zespołu angular-ui nie musiałem jeszcze tworzyć własnej dyrektywy, która robi cokolwiek istotnego, więc moje zdanie na ten temat może być całkowicie błędne.


2

Zgadzam się z Umurem. Teoretycznie izolowane zakresy brzmią wspaniale i „przenośnie”, ale budując moją aplikację tak, aby obejmowała nietrywialne funkcje, natknąłem się na potrzebę włączenia kilku dyrektyw (niektóre zagnieżdżone w innych lub dodanie do nich atrybutów), aby w pełni napisać w mojej własny HTML, który jest celem języka specyficznego dla domeny.

W końcu jest zbyt dziwne, aby przekazywać każdą globalną lub wspólną wartość w dół łańcucha z wieloma atrybutami przy każdym wywołaniu DOM dyrektywy (zgodnie z wymaganiami w zakresie izolowania). To po prostu głupie, aby powtarzać to wszystko w DOM i wydaje się nieefektywne, nawet jeśli są to wspólne obiekty. Niepotrzebnie komplikuje to również deklaracje dyrektywy. Obejście polegające na używaniu $ parent do „sięgania” i pobierania wartości z HTML-u dyrektywy wydaje się bardzo złym formularzem.

Ja też skończyłem na zmianie mojej aplikacji, aby mieć głównie dyrektywy o zasięgu dziecięcym z bardzo małą liczbą izolatów - tylko te, które nie muszą uzyskiwać dostępu do ŻADNEGO elementu nadrzędnego poza tym, co można przekazać za pomocą prostych, niepowtarzalnych atrybutów.

Przez dziesięciolecia marzyłem o marzeniach związanych z domenami, zanim coś takiego się wydarzyło. Jestem podekscytowany, że AngularJS zapewnia tę opcję i wiem, że w miarę, jak więcej programistów pracuje w tym obszarze, zobaczymy kilka bardzo fajnych aplikacji, które ich architekci mogą również łatwo pisać, rozszerzać i debugować.

- D

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.