Quando scrivo una direttiva in AngularJS, come faccio a decidere se non ho bisogno di un nuovo ambito, di un nuovo ambito figlio o di un nuovo ambito isolato?


265

Sto cercando alcune linee guida che è possibile utilizzare per determinare quale tipo di ambito utilizzare quando si scrive una nuova direttiva. Idealmente, mi piacerebbe qualcosa di simile a un diagramma di flusso che mi guidi attraverso una serie di domande e faccia apparire la risposta corretta - nessun nuovo nuovo ambito, nuovo ambito figlio o nuovo ambito isolato - ma ciò probabilmente richiede troppo. Ecco la mia attuale serie di linee guida irrisorie:

Sono consapevole del fatto che l'uso di una direttiva con un ambito isolato su un elemento impone a tutte le altre direttive sullo stesso elemento di utilizzare lo stesso (un) ambito di isolamento, quindi ciò non limita gravemente quando è possibile utilizzare un ambito di isolamento?

Spero che alcuni membri del team Angular-UI (o altri che hanno scritto molte direttive) possano condividere le loro esperienze.

Non aggiungere una risposta che dice semplicemente "usa un ambito isolato per componenti riutilizzabili".


per "ambito figlio" intendi creare l'ambito nella funzione di collegamento con "ambito. $ new ()"? Perché, come so, la direttiva può avere un ambito isolato o non averlo (quindi utilizzerà l'ambito dove è stato utilizzato)
Valentyn Shybanov

3
@ValentynShybanov Setting scope: truecreerà automaticamente un ambito figlio usando $scope.new().
Josh David Miller,

2
@Valentyn, quello che ha detto Josh: quindi, le tre possibilità sono scope: false(impostazione predefinita, nessun nuovo ambito), scope: true(nuovo ambito che eredita prototipicamente) e scope: { ... }(nuovo ambito isolato).
Mark Rajcok,

1
Sì, grazie. Ho perso quella differenza tra "vero" e "{}". Buono a sapersi.
Valentyn Shybanov,

C'è un quarto caso che la gente tende generalmente a ignorare ... quello è il "responsabile della direttiva" .. Penso che la domanda dovrebbe essere ampliata per includervi anche ... +1 alla domanda ..
Ganaraj,

Risposte:


291

Che bella domanda! Mi piacerebbe piace sentire ciò che gli altri hanno da dire, ma qui ci sono le linee guida che uso.

La premessa per l'alta quota: l'ambito è usato come "colla" che usiamo per comunicare tra il controllore principale, la direttiva e il modello di direttiva.

Ambito genitore: scope: false quindi nessun nuovo scopo

Non lo uso molto spesso, ma come ha detto @MarkRajcok, se la direttiva non accede a nessuna variabile dell'ambito (e ovviamente non ne imposta alcuna!), Per quanto mi riguarda va bene. Questo è utile anche per le direttive sui minori che sono solo utilizzate nel contesto della direttiva padre (anche se ci sono sempre delle eccezioni) e che non hanno un modello. Fondamentalmente qualsiasi cosa con un modello non appartiene alla condivisione di un ambito, perché stai esponendo intrinsecamente tale ambito per l'accesso e la manipolazione (ma sono sicuro che ci sono eccezioni a questa regola).

Ad esempio, ho recentemente creato una direttiva che disegna un'immagine vettoriale (statica) usando una libreria SVG che sto scrivendo. Ha $observedue attributi ( widthe height) e li usa nei suoi calcoli, ma non imposta né legge alcuna variabile di ambito e non ha un modello. Questo è un buon caso d'uso per non creare un altro ambito; non ne abbiamo bisogno, quindi perché preoccuparsi?

Ma in un'altra direttiva SVG, tuttavia, avevo bisogno di un set di dati da utilizzare e dovevo anche memorizzare un po 'di stato. In questo caso, l'utilizzo dell'ambito padre sarebbe irresponsabile (di nuovo, in generale). Quindi invece...

Ambito del bambino: scope: true

Le direttive con ambito figlio sono sensibili al contesto e intendono interagire con l'ambito corrente.

Ovviamente, un vantaggio chiave di questo rispetto a un ambito isolato è che l'utente è libero di usare l'interpolazione su tutti gli attributi che desidera; ad es. l'uso di class="item-type-{{item.type}}"una direttiva con un ambito isolato non funzionerà di default, ma funziona bene su uno con un ambito figlio perché tutto ciò che è interpolato può ancora trovarsi di default nell'ambito genitore. Inoltre, la direttiva stessa può valutare in modo sicuro attributi ed espressioni nel contesto del proprio ambito di applicazione senza preoccuparsi dell'inquinamento o del danno al genitore.

Ad esempio, una descrizione comandi è qualcosa che viene appena aggiunto; un ambito isolato non funzionerebbe (per impostazione predefinita, vedi sotto) perché si prevede che qui utilizzeremo altre direttive o attributi interpolati. La descrizione comandi è solo un miglioramento. Ma il tooltip deve anche impostare alcune cose sull'ambito da usare con una sub-direttiva e / o modello e ovviamente per gestire il proprio stato, quindi sarebbe davvero un male usare l'ambito genitore. O lo stiamo inquinando o danneggiandolo, e nemmeno lo è bueno.

Mi ritrovo a utilizzare gli ambiti figlio più spesso di quelli isolati o parent.

Isolare l'ambito: scope: {}

Questo è per componenti riutilizzabili. :-)

Ma seriamente, penso ai "componenti riutilizzabili" come ai "componenti autonomi". L'intento è che devono essere usati per uno scopo specifico, quindi combinarli con altre direttive o aggiungere altri attributi interpolati al nodo DOM intrinsecamente non ha senso.

Per essere più specifici, tutto ciò che è necessario per questa funzionalità autonoma viene fornito attraverso attributi specifici valutati nel contesto dell'ambito padre; sono stringhe unidirezionali ('@'), espressioni unidirezionali ('&') o associazioni di variabili bidirezionali ('=').

Sui componenti autonomi, non ha senso applicare altre direttive o attributi perché esiste da solo. Il suo stile è regolato dal proprio modello (se necessario) e può essere escluso il contenuto appropriato (se necessario). È autonomo, quindi lo inseriamo in un ambito isolato anche per dire: "Non scherzare con questo. Ti sto dando un'API definita attraverso questi pochi attributi".

Una buona pratica è quella di escludere il maggior numero possibile di elementi basati su template dal collegamento direttiva e dalle funzioni del controller. Ciò fornisce un altro punto di configurazione "simile ad API": l'utente della direttiva può semplicemente sostituire il modello! Le funzionalità sono rimaste tutte uguali e la sua API interna non è mai stata toccata, ma possiamo fare confusione con lo stile e l'implementazione del DOM di cui abbiamo bisogno. ui / bootstrap è un ottimo esempio di come farlo bene perché Peter e Pawel sono fantastici.

Gli ambiti di isolamento sono ottimi anche per la trasclusione. Prendi schede; non sono solo l'intera funzionalità, ma tutto ciò che è al suo interno può essere valutato liberamente dall'ambito padre lasciando le schede (e i riquadri) a fare ciò che vogliono. Le schede hanno chiaramente il loro stato , che appartiene all'ambito (per interagire con il modello), ma quello stato non ha nulla a che fare con il contesto in cui è stato usato - è interamente interno a ciò che rende una direttiva tab una direttiva tab. Inoltre, non ha molto senso usare altre direttive con le schede. Sono schede - e abbiamo già questa funzionalità!

Circondalo con più funzionalità o escludi più funzionalità, ma la direttiva è quella che è già.

Detto questo, dovrei notare che ci sono modi per aggirare alcune delle limitazioni (ovvero le caratteristiche) di un ambito isolato, come ha suggerito @ProLoser nella sua risposta. Ad esempio, nella sezione ambito figlio, ho menzionato l'interpolazione sugli attributi non direttivi che si rompono quando si utilizza un ambito isolato (per impostazione predefinita). Ma l'utente potrebbe, ad esempio, semplicemente utilizzare class="item-type-{{$parent.item.type}}"e funzionerebbe ancora una volta. Quindi, se c'è una ragione convincente per usare un ambito isolato su un ambito figlio ma sei preoccupato per alcune di queste limitazioni, sappi che puoi aggirarle virtualmente tutte, se necessario.

Sommario

Le direttive senza un nuovo ambito sono di sola lettura; sono completamente fidati (cioè interni all'app) e non toccano jack. Le direttive con un ambito figlio aggiungono funzionalità, ma non sono le uniche funzionalità. Infine, gli scopi di isolamento sono per le direttive che rappresentano l'intero obiettivo; sono autonomi, quindi va bene (e la maggior parte "corretta") lasciarli andare canaglia.

Volevo far emergere i miei pensieri iniziali, ma mentre penso a più cose, aggiornerò questo. Ma santa merda - questo è lungo per una risposta SO ...


PS: Totalmente tangenziale, ma dal momento che stiamo parlando di scopi, preferisco dire "prototipico" mentre altri preferiscono "prototipo", che sembra essere più accurato ma non si adatta affatto alla lingua. :-)


Grazie Josh, ottima risposta. Volevo / aspettavo lunghe risposte per questo. Due cose che non ho seguito: 1) ambito figlio: "l'utente è libero di usare l'interpolazione su tutti gli attributi che desidera". 2) isolare scope: "o non tutti, nel caso di '?'" Puoi approfondire un po 'quelli? (Sentiti libero di modificare il tuo post invece di scrivere commenti se è più facile.)
Mark Rajcok,

@MarkRajcok Per (1), l'ho cambiato per renderlo un po 'meno nebuloso - fammi sapere se non ho avuto successo. Per (2), quella era una combinazione di un refuso e una formulazione scadente; Ho riscritto quel paragrafo per essere più chiaro. Ho anche aggiunto un altro esempio o due, chiarito alcune altre cose e risolto alcuni errori di battitura.
Josh David Miller,

Come menzionato nella risposta, bootstrap per angular è un ottimo esempio di combinazione di questi. Ho trovato particolarmente utile l'esempio della fisarmonica - GitHub - Accordion
CalM,

Hai detto che usi maggiormente gli ambiti secondari, ho pensato che il modello riutilizzabile delle direttive fosse il più comune e ho evitato di scrivere direttive che avrebbero dovuto essere usate solo una volta. Non è necessario? A volte, quando il mio HTML diventa troppo grande, ho voglia di spostare quella sezione in una direttiva, ma verrà utilizzata solo una volta, quindi la lascio nel codice HTML.
user2483724

2
@ user2483724 Un malinteso molto comune è che le direttive "riutilizzabili" sono quelle che usano un ambito isolato; non così. Se si guardano le direttive preconfezionate, quasi nessuna di esse usa ambiti isolati - alcuni nemmeno un ambito figlio - ma ti assicuro che sono riutilizzabili! La regola dovrebbe essere nel modo in cui viene utilizzato l'ambito di applicazione della direttiva. Se si tratta solo di risparmiare spazio in un file, non sono sicuro che una direttiva sia l'approccio migliore. Aumenta i tempi di elaborazione per il bene dello sviluppatore. Ma se è necessario, allora provaci. Oppure usa un ngInclude. Oppure fallo come parte della tua build. Molte opzioni!
Josh David Miller,

52

La mia politica personale ed esperienza:

Isolato: un sandbox privato

Voglio creare molti metodi e variabili di ambito che vengono utilizzati SOLO dalla mia direttiva e che l'utente non può mai vedere o accedere direttamente. Voglio autorizzare quali dati dell'ambito sono disponibili per me. Posso utilizzare la inclusione per consentire all'utente di tornare indietro nell'ambito genitore (non interessato) . Faccio NON voglio che i miei variabili e metodi accessibili nei bambini transclusa.

Bambino: una sottosezione di contenuto

Voglio creare metodi e variabili di ambito che PU CAN essere accessibili da parte dell'utente, ma non sono rilevanti per la circonda Ambito di applicazione (fratelli e genitori) al di fuori del contesto della mia direttiva. Vorrei anche consentire a TUTTI i dati dell'ambito padre di scorrere in modo trasparente.

Nessuna: direttive semplici di sola lettura

Non ho davvero bisogno di pasticciare con metodi o variabili dell'ambito. Probabilmente sto facendo qualcosa che non ha a che fare con gli ambiti (come la visualizzazione di plugin jQuery semplici, la convalida, ecc.).

Appunti

  • Non dovresti lasciare che ngModel o altre cose influenzino direttamente la tua decisione. Puoi eludere il comportamento strano facendo cose come ng-model=$parent.myVal(bambino) o ngModel: '='(isolato).
  • Isolate + transclude ripristinerà tutti i comportamenti normali alle direttive dei fratelli e tornerà all'ambito genitore, quindi non lasciare che ciò influisca anche sul tuo giudizio.
  • Non scherzare con l'ambito su nessuno perché è come mettere i dati sull'ambito per la metà inferiore del DOM ma non per la metà superiore che ha senso 0.
  • Prestare attenzione alle priorità della direttiva (non avere esempi concreti di come ciò può influire sulle cose)
  • Iniettare servizi o utilizzare i controller per comunicare attraverso le direttive con qualsiasi tipo di ambito. Puoi anche fare require: '^ngModel'una ricerca negli elementi principali.

1
Potrei aver frainteso questa parte: "Isolare + trasludere ripristinerà tutti i comportamenti normali alle direttive dei fratelli". Vedi questo plunker . Dovrai guardare nella console.
Josh David Miller,

1
Grazie ProLoser per le tue opinioni / risposte. Sei una delle persone che speravo potesse vedere questo post se avessi aggiunto il tag angularjs-ui.
Mark Rajcok,

@JoshDavidMiller quando si parla di direttive sullo stesso elemento DOM le cose diventano più complicate e si dovrebbe invece dare un'occhiata alla proprietà prioritaria. La inclusione è più rilevante per i contenuti dei minori.
ProLoser,

1
@ProLoser Giusto, ma non sono sicuro di cosa intendevi con quella frase. Ovviamente colpiscono i bambini, ma in che modo gli scopi delle direttive influiscono sulle loro direttive di pari livello?
Josh David Miller,

18

Dopo aver scritto molte direttive, ho deciso di usarne meno isolated portata. Anche se è interessante e incapsuli i dati e assicurati di non perdere i dati nell'ambito padre, limita fortemente la quantità di direttive che puoi usare insieme. Così,

Se la direttiva avete intenzione di scrivere si comporterà tutto da solo e non si ha intenzione di condividerlo con altre direttive, andare per ambito isolato . (come un componente puoi semplicemente collegarlo, con poca personalizzazione per lo sviluppatore finale) (diventa molto più complicato quando provi a scrivere sotto-elementi che hanno direttive all'interno)

Se la direttiva che stai per scrivere eseguirà solo manipolazioni dom che non hanno bisogno di uno stato interno dell'ambito o alterazioni esplicite dell'ambito (per lo più cose molto semplici); andare per nessun nuovo ambito . (ad esempio ngShow, ngMouseHover, ngClick, ngRepeat)

Se la direttiva che stai per scrivere deve modificare alcuni elementi nell'ambito genitore, ma deve anche gestire un certo stato interno, scegli un nuovo ambito figlio . (come ngController)

Assicurati di controllare il codice sorgente delle direttive: https://github.com/angular/angular.js/tree/master/src/ng/directive
Aiuta molto su come pensarci


Se diversi componenti devono comunicare tra loro, possono avere portata e utilizzo isolati require, mantenendo quindi le direttive ancora disaccoppiate. Quindi, come limita le possibilità? Rende ancora più specifiche le direttive (quindi dichiarate da cosa dipendete). Quindi lascerei solo una regola: se la tua direttiva ha stato o necessita di alcuni dati dall'ambito in cui viene utilizzata - usa un ambito isolato. Altrimenti non utilizzare l'ambito. E su "ambiti secondari" - ho anche scritto molte direttive e non ho mai avuto bisogno di questa funzione. Se "deve modificare alcuni elementi nell'ambito genitore", utilizzare i collegamenti.
Valentyn Shybanov,

E anche su "deve cambiare alcuni elementi nell'ambito genitore" - se modifichi qualcosa nei cambiamenti dell'ambito figlio non sono popolati nell'ambito genitore (a meno che tu non usi un $parenthack sporco ). Quindi in realtà "ambiti figlio" per le direttive è qualcosa che sembra dovrebbe essere usato piuttosto indietro - come ngRepeatquello crea nuovi ambiti figlio per ogni elemento da ripetere (ma lo crea anche usando scope.$new();e non scope: true.
Valentyn Shybanov

1
Non è possibile richiedere più ambiti isolati all'interno dello stesso elemento, non è possibile accedere alle funzioni nell'ambito genitore, a meno che non vengano associate esplicitamente. (Buona fortuna usando ngClickecc.) Il requisito crea una sorta di disaccoppiamento Sono d'accordo, ma è ancora necessario conoscere la direttiva principale. A meno che non sia come un componente , sono contrario all'isolamento. Le direttive (almeno, la maggior parte di esse) sono pensate per essere altamente riutilizzabili e l'isolamento rompe questo.
Umur Kontacı,

Inoltre, non utilizzo l'ambito figlio nelle direttive ma poiché un ambito figlio eredita prototipicamente dall'ambito padre, se l'accesso a una proprietà all'interno di una proprietà nell'ambito padre, le modifiche vengono popolate. Gli autori di Angular ne hanno parlato all'incontro con MTV, è "bello avere un punto da qualche parte" youtube.com/watch?v=ZhfUv0spHCY
Umur Kontacı

5
In primo luogo, penso che tu sia un po 'troppo duro con gli oscilloscopi isolati. Penso che abbiano un'applicabilità più ampia di quella che attribuisci loro credito e che ci sono modi per evitare molte delle sfide che (correttamente) hai sottolineato che affrontiamo quando le utilizzi. Sono anche in disaccordo con "poca personalizzazione per lo sviluppatore finale" - vedere la mia risposta per i dettagli. Detto questo, la tua risposta non è stata né cattiva né sbagliata e ha affrontato la domanda, quindi non sono sicuro del motivo per cui è stata votata in negativo. Quindi, +1.
Josh David Miller,

9

Ho pensato di aggiungere la mia attuale comprensione e come si collega ad altri concetti di JS.

Predefinito (ad es. Non dichiarato o ambito: falso)

Ciò è filosoficamente equivalente all'utilizzo di variabili globali. La tua direttiva può accedere a tutto nel controller principale, ma influisce anche su di essi e allo stesso tempo ne risente.

scopo:{}

Questo è come un modulo, tutto ciò che vuole usare deve essere passato esplicitamente. Se OGNI direttiva che usi è un ambito isolato, può essere l'equivalente di fare OGNI file JS per scrivere il proprio modulo con un sacco di sovraccarico nell'iniezione di tutte le dipendenze.

ambito: bambino

Questa è la via di mezzo tra variabili globali e passthrough esplicito. È simile alla catena di prototipi di JavaScript e ti estende solo una copia dell'ambito padre. Se si crea un ambito isolato e si passa in ogni attributo e funzione dell'ambito padre, è funzionalmente equivalente a questo.


La chiave è che QUALSIASI direttiva può essere scritta IN QUALSIASI modo. Le diverse dichiarazioni di ambito sono lì per aiutarti ad organizzare. Puoi trasformare tutto in un modulo, oppure puoi semplicemente usare tutte le variabili globali ed essere molto attento. Per facilitare la manutenzione è preferibile modulare la logica in parti logicamente coerenti. C'è un equilibrio tra un prato aperto e una prigione chiusa. La ragione per cui questo è complicato, credo, è che quando le persone apprendono ciò pensano che stanno imparando come funzionano le direttive, ma in realtà stanno imparando l'organizzazione del codice / logica.

Un'altra cosa che mi ha aiutato a capire come funzionano le direttive è imparare su ngInclude. ngInclude ti aiuta a includere i parziali html. Quando ho iniziato a usare le direttive ho scoperto che potresti usare l'opzione template per ridurre il tuo codice, ma non stavo davvero collegando alcuna logica.

Naturalmente tra le direttive di Angular e il lavoro del team angular-ui non ho ancora dovuto creare la mia direttiva che fa qualcosa di sostanziale, quindi la mia opinione su questo potrebbe essere completamente sbagliata.


2

Concordo con Umur. In teoria, gli ambiti isolati sembrano meravigliosi e "portatili", ma nella costruzione della mia app per coinvolgere funzionalità non banali mi sono imbattuto nella necessità di incorporare diverse direttive (alcune nidificate all'interno di altre o l'aggiunta di attributi ad esse) al fine di scrivere completamente nel mio proprio HTML, che è lo scopo di un linguaggio specifico di dominio.

Alla fine, è troppo strano dover passare tutti i valori globali o condivisi lungo la catena con più attributi su ogni invocazione DOM di una direttiva (come richiesto con l'ambito isolato). Sembra stupido scrivere ripetutamente tutto ciò nel DOM e sembra inefficiente, anche se si tratta di oggetti condivisi. Inoltre complica inutilmente le dichiarazioni della direttiva. La soluzione alternativa all'utilizzo di $ parent per "raggiungere" e acquisire valori dalla direttiva HTML sembra Very Bad Form.

Anch'io ho finito per cambiare la mia app per avere direttive per lo più figlio con pochissimi isolati - solo quelli che non hanno bisogno di accedere a NESSUNO dal genitore diverso da quello che possono essere passati attraverso attributi semplici e non ripetitivi.

Avendo sognato il sogno delle lingue specifiche del dominio per decenni prima che ci fosse una cosa del genere, sono euforico che AngularJS fornisca questa opzione e so che, mentre più sviluppatori lavorano in quest'area, vedremo alcune app molto interessanti che sono anche facili da scrivere, espandere e eseguire il debug per i loro architetti.

- D

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.