Direktiver er en av de kraftigste komponentene i AngularJS, som hjelper deg med å utvide grunnleggende HTML-elementer / attributter og opprette gjenbruk og testbar kode. I denne veiledningen vil jeg vise deg hvordan du bruker AngularJS-direktiver med beste praksis i praksis.
Hva mener jeg her med direktiverer for det meste tilpassede retningslinjer under opplæringen. Jeg vil ikke prøve å lære deg hvordan du bruker innebygde direktiver som ng-repeat
, ng-vis
, etc. Jeg vil vise deg hvordan du bruker egendefinerte direktiver for å lage dine egne komponenter.
La oss si at du har en eCommerce-applikasjon om bøker, og du viser spesifikke detaljer om bøker på flere områder, for eksempel kommentarer, brukerprofilsider, artikler, etc. Din bokdetaljer widget kan være som nedenfor:
I denne widgeten er det et bokbilde, tittel, beskrivelse, kommentarer og vurdering. Å samle inn den informasjonen og sette inn et bestemt domelement kan være vanskelig å gjøre på alle steder du vil bruke det. La oss widgetize denne visningen ved hjelp av et AngularJS-direktiv.
angular.module ('masteringAngularJsDirectives', []) .directive ('bok', funksjon () retur begrense: 'E', omfang: data: '=', templateUrl: 'maler / bok-widget.html ')
En direktivfunksjon har blitt brukt i eksemplet ovenfor for å opprette et direktiv først. Navnet på direktivet er bok
. Dette direktivet returnerer et objekt, og la oss snakke litt om dette objektet. begrense
er for å definere direktiv type, og det kan være EN
(ENttribute),C
(Class), E
(Element), ogM
(koMmente). Du kan se bruken av hver henholdsvis nedenfor.
Type | bruk |
---|---|
EN | bok> |
C | |
E | <bok data = "book_data">bok> |
M |
omfang
er for å håndtere direktivets omfang. I det ovennevnte tilfellet overføres bokdata til direktivmalen ved å bruke "="
omfangstype. Jeg vil snakke om detaljer om omfanget i de følgende avsnittene. templateUrl
brukes til å ringe en visning for å gjengi spesifikt innhold ved å bruke data overført til direktivets omfang. Du kan også bruke mal
og gi HTML-kode direkte, slik:
… mal: 'Bok info'...
I vårt tilfelle har vi en komplisert HTML-struktur, og det er derfor jeg valgte templateUrl
alternativ.
Direktiver er definert i JavaScript-filen til AngularJS-prosjektet og brukes på HTML-siden. Det er mulig å bruke AngularJS-direktiver på HTML-sider som følger:
I denne bruken brukes direktivnavnet inne i standard HTML-elementer. La si at du har en rollebasert meny i eCommerce-applikasjonen. Denne menyen er dannet i henhold til din nåværende rolle. Du kan definere et direktiv for å bestemme om den aktuelle menyen skal vises eller ikke. HTML-menyen din kan være som nedenfor:
og direktivet som følger:
app.directive ("begrenset", funksjon () retur begrense: 'A', lenke: funksjon (omfang, element, attrs) // Noen authjekkingsfunksjon var erAuthorized = checkAuthorization (); hvis (! erAuthorized) element.css ('skjerm', 'ingen');)
Hvis du bruker begrenset
Direktivet i menyelementet som et attributt, kan du gjøre en tilgangsnivåkontroll for hver meny. Hvis den nåværende brukeren ikke er autorisert, vil den spesifikke menyen ikke bli vist.
Så, hva er det link
fungere der? Lenkfunksjonen er ganske enkelt den funksjonen du kan bruke til å utføre direktivspesifikke operasjoner. Direktivet gir ikke bare HTML-kode ved å gi noen innspill. Du kan også binde funksjoner til direktivelementet, ringe en tjeneste og oppdatere direktivverdien, få direktivattributter hvis det er en E
type direktiv osv.
Du kan bruke direktivnavnet inne i HTML-elementklasser. Forutsatt at du vil bruke ovennevnte direktiv som C
, Du kan oppdatere direktivet begrense
som C
og bruk den som følger:
Hvert element har allerede en klasse for styling, og som begrenset
klassen er lagt til det er faktisk et direktiv.
Du trenger ikke å bruke et direktiv i et HTML-element. Du kan lage ditt eget element ved å bruke et AngularJS-direktiv med en E
begrensning. La oss si at du har en bruker-widget i programmet ditt for å vise brukernavn
, avatar
, og rykte
på flere steder i søknaden din. Du vil kanskje bruke et slikt direktiv:
app.directive ("bruker", funksjon () retur begrense: 'E', lenke: funksjon (omfang, element, attrs) scope.username = attrs.username; scope.avatar = attrs.avatar; scope.reputation = attrs.reputation;, mal: 'Brukernavn: brukernavn, Avatar: avatar, Omdømme: reputation')
HTML-koden vil være:
I eksempelet ovenfor er et tilpasset element opprettet, og noen attributter er gitt som brukernavn
, avatar
, og rykte
. Jeg vil trekke oppmerksomhet til lenkefunksjonen. Elementattributter er tildelt direktivets omfang. Den første parameteren til lenkefunksjonen er omfanget av gjeldende direktiv. Den tredje parameteren i direktivet er attributtobjektet til direktivet, noe som betyr at du kan lese et hvilket som helst attributt fra det tilpassede direktivet ved å bruke attrs.attr_name
. Attributtverdier er tilordnet omfanget slik at de brukes i malen.
Faktisk kan du gjøre denne operasjonen på en kortere måte, og jeg vil snakke om det senere. Dette eksemplet er for å forstå hovedideen bak bruken.
Denne bruken er ikke så vanlig, men jeg vil vise hvordan jeg bruker den. La oss si at du trenger et kommentarskjema for at søknaden din skal brukes på mange steder. Du kan gjøre det ved å bruke følgende direktiv:
app.directive ("kommentar", funksjon () retur begrense: 'M', mal: '')
Og i HTML-elementet:
Hvert direktiv har sitt eget omfang, men du må være forsiktig med dataene som er bindende med direktivdeklarasjonen. La si si at du implementerer kurv
en del av eCommerce-applikasjonen din. På kurv siden har du allerede lagt til elementer her før. Hvert element har sitt feltfelt for å velge hvor mange elementer du vil kjøpe, som nedenfor:
Her er direktivdeklarasjonen:
app.directive ("element", funksjon () retur begrense: 'E', lenke: funksjon (omfang, element, attrs) scope.name = attrs.name;, mal: 'Navn: Navn Velg beløp: Valgt beløp: telle')
Og for å vise tre elementer i HTML:
Problemet her er at når du velger mengden av ønsket element, blir alle delene av elementene oppdatert. Hvorfor? Fordi det er toveisbinding med et navn telle
, men omfanget er ikke isolert. For å isolere omfang, bare legg til omfang:
til direktivattributtet i returavsnittet:
app.directive ("element", funksjon () retur begrense: 'E', omfang: , lenke: funksjon (omfang, element, attrs) scope.name = attrs.name;, mal: 'Navn: Navn Velg beløp: Valgt beløp: telle')
Dette fører til at ditt direktiv har sitt eget isolerte omfang, slik at toveisbinding vil skje i dette direktivet separat. Jeg vil også nevne om omfang
attributt senere.
Den største fordelen med direktivet er at det er en gjenbrukbar komponent som lett kan brukes - du kan til og med Gi noen ekstra attributter til det direktivet. Men hvordan er det mulig å overføre tilleggsverdi, bindende eller uttrykk for et direktiv for at data skal brukes i direktivet?
"@" Omfang: Denne typen omfang brukes til å overføre verdi til direktivets omfang. La oss si at du vil opprette en widget for en varslingsmelding:
app.controller ("MessageCtrl", funksjon () $ scope.message = "Produkt opprettet!";) app.directive ("varsel", funksjon () retur begrense: 'E' '@' , mal: 'budskap');
og du kan bruke:
I dette eksemplet blir meldingsverdien ganske enkelt tilordnet direktivets omfang. Det gjengitte HTML-innholdet vil være:
Produkt opprettet!
"=" Omfang: I denne omfangstypen blir variabler overført i stedet for verdiene, noe som betyr at vi ikke vil passere budskap
, vi vil passere budskap
i stedet. Årsaken bak denne funksjonen er å bygge toveisbinding mellom direktivet og sideelementene eller kontrollerne. La oss se det i aksjon.
.direktiv ("bookComment", funksjon () return restrict: 'E', scope: text: '=', mal: '')
I dette direktivet prøver vi å lage en widget for å vise kommentarinnlegg for å kommentere en bestemt bok. Som du kan se, krever dette direktivet en attributt tekst
å konstruere toveisbinding mellom andre elementer på sidene. Du kan bruke dette på siden:
Dette er tekstboksen på direktivet
Dette vil bare vise en tekstboks på siden, så la oss legge til noe mer for å samhandle med dette direktivet:
Dette er tekstboksen på siden
Dette er tekstboksen på direktivet
Når du skriver noe i den første tekstboksen, blir den også skrevet i den andre tekstboksen. Du kan gjøre det omvendt. I direktivet passerte vi variabelen commentText
i stedet for verdien, og denne variabelen er datobindende referanse til den første tekstboksen.
"&" Omfang: Vi er i stand til å overføre verdien, og referanse til direktiver. I denne omfangstypen vil vi se på hvordan vi sender uttrykk til direktivet. I virkelige tilfeller må du kanskje sende en bestemt funksjon (uttrykk) til direktiver for å hindre kopling. Noen ganger trenger direktiver ikke å vite mye om ideen bak uttrykkene. For eksempel vil et direktiv like boken for deg, men det vet ikke hvordan du gjør det. For å gjøre det kan du følge en slik struktur:
.direktiv ("likeBook", funksjon () return restrict: 'E', scope: like: '&', mal: '')
I dette direktivet vil et uttrykk bli sendt til direktivknappen via som
Egenskap. La oss definere en funksjon i kontrolleren og sende den til direktivet inne i HTML-en.
$ scope.likeFunction = function () alert ("Jeg liker boken!")
Dette vil være inne i kontrolleren, og malen vil være:
likeFunction ()
kommer fra kontrolleren og er sendt til direktivet. Hva om du vil sende en parameter til likeFunction ()
? For eksempel må du kanskje sende en ratingverdi til likeFunction ()
. Det er veldig enkelt: bare legg til et argument for funksjonen inne i kontrolleren, og legg til et inngangselement i direktivet for å kreve starttall fra brukeren. Du kan gjøre det som vist nedenfor:
.direktiv ("likeBook", funksjon () return restrict: 'E', scope: like: '&', mal: '
'+'')
$ scope.likeFunction = funksjon (stjerne) alert ("Jeg liker boken !, og ga" + stjerne + "stjerne.")
Som du ser, kommer tekstboksen fra direktivet. Tekstboksverdien er bundet til funksjonsargumentet som liker (stjerne: starCount)
. stjerne
er for kontrolleren funksjonen, og starCount
for tekstboksen verdi bindende.
Noen ganger kan du ha en funksjon som finnes i flere direktiver. De kan settes i foreldre direktiv slik at de er arvet av barnedirektiver.
La meg gi deg et virkelige eksempel. Du vil sende statistiske data når kunder flytter musemarkøren til toppen av en bestemt bok. Du kan implementere et museklikkhendelse for bokdirektivet, men hva om det vil bli brukt av et annet direktiv? I dette tilfellet kan du bruke arv av direktiver som nedenfor:
app.directive ('mouseClicked', funksjon () return restrict: 'E', scope: , controller: "MouseClickedCtrl som mouseClicked")
Dette er et foreldre direktiv som skal arves av barnedirektiver. Som du kan se er det en kontrollerattributt av direktivet ved hjelp av "as" -direktivet. La oss også definere denne kontrolleren:
app.controller ('MouseClickedCtrl', funksjon ($ element) var mouseClicked = dette; mouseClicked.bookType = null; mouseClicked.setBookType = funksjon (type) mouseClicked.bookType = type; $ element.bind ("klikk" funksjon () alert ("Type of book:" + mouseClicked.bookType + "sendt for statistisk analyse!");)
I denne kontrolleren setter vi ganske enkelt inn en regulator forekomst av variabelen boktype
ved å bruke barnedirektiver. Når du klikker på en bok eller et magasin, sendes elementelementet til back-end-tjenesten (jeg brukte en varslingsfunksjon bare for å vise dataene). Hvordan vil barnedirektiver kunne bruke dette direktivet?
app.directive ('eBok', funksjon () return krever: "mouseClicked", lenke: funksjon (omfang, element, attrs, mouseClickedCtrl) mouseClickedCtrl.setBookType ("EBOOK");. magasin ', funksjon () return krever: "mouseClicked", lenke: funksjon (omfang, element, attrs, mouseClickedCtrl) mouseClickedCtrl.setBookType ("MAGAZINE");)
Som du kan se, bruker barnedirektiver krever
søkeord for å bruke foreldre direktivet. Og enda et viktig poeng er det fjerde argumentet for lenkefunksjonen i barnedirektiver. Dette argumentet refererer til kontrollenhetsattributtet til foreldredirektivet som betyr at barnedirektivet kan bruke kontrollfunksjonen setBookType
inne i kontrolleren. Hvis det nåværende elementet er en eBok, kan du bruke det første direktivet, og hvis det er et magasin, kan du bruke den andre:
Spill av troner (klikk meg)
PC World (klikk meg)
Barnedirektiver er som en eiendom av foreldredirektivet. Vi har eliminert bruken av museklikkhendelsen for hvert barnedirektiv ved å sette den delen inn i foreldrerdirektivet.
Når du bruker direktiver i malen, er det du ser på siden, den kompilerte versjonen av direktivet. Noen ganger vil du se selve direktivbruken for feilsøking. For å se den ukompilerte versjonen av den nåværende delen, kan du bruke ng-non-bind
. For eksempel, la oss si at du har en widget som skriver ut de mest populære bøkene, og her er koden for det:
Bokens variabel variabel kommer fra kontrolleren, og utgangen av dette er som følger:
Hvis du vil vite direktivbruken bak denne kompilerte produksjonen, kan du bruke denne versjonen av koden:
Denne gangen vil utgangen bli som nedenfor:
Det er kult frem til nå, men hva om vi vil se både de ukompilerte og kompilerte versjonene av widgeten? Det er på tide å skrive et tilpasset direktiv som vil gjøre en avansert feilsøkingsoperasjon.
app.directive ('customDebug', funksjon ($ compile) return terminal: true, link: funksjon (omfang, element) var currentElement = element.clone (); currentElement.removeAttr newElement = $ compile (currentElement) (scope); element.attr ("style", "border: 1px solid red"); element.after (newElement);)
I dette direktivet kloner vi elementet som er i feilsøkingsmodus, slik at det ikke endres etter noen operasjoner. Etter kloning, fjern custom-debug
direktivet for ikke å fungere som feilsøkingsmodus, og deretter kompilere det med $ complile
, som allerede er injisert i direktivet. Vi har gitt en stil til feilsøkingsmoduselementet for å understreke den feilsøkte. Resultatet blir som følger:
Du kan lagre utviklingstiden din ved å bruke denne typen feilsøkingsdirektiv for å oppdage årsaken til feil i prosjektet.
Som du allerede vet, er enhetstesting en svært viktig del av utviklingen for å fullstendig kontrollere koden du har skrevet og forhindre potensielle feil. Jeg vil ikke dykke dypt inn i enhetstesting, men vil gi deg en anelse om hvordan du tester direktiver på noen få måter.
Jeg vil bruke Jasmine til enhetstesting og Karma for enhetstestløperen. For å kunne bruke Karma, bare installer den globalt ved å kjøre npm installere -g karma karma-cli
(du må ha Node.js og npm installert på datamaskinen). Etter installasjonen, åpne kommandolinjen, gå til prosjektmappen din og skriv inn karma init
. Det vil stille deg et par spørsmål som nedenfor for å sette opp testkravene dine.
Jeg bruker Webstorm for utvikling, og hvis du også bruker Webstorm, bare høyreklikk på karma.conf.js og velg Løpe karma.conf.js. Dette vil utføre alle tester som er konfigurert i karma conf. Du kan også kjøre tester med karma start
kommandolinje i prosjektmappen. Det handler om miljøoppsett, så la oss bytte til testdelen.
La oss si at vi vil teste bokedirektivet. Når vi overfører en tittel til direktivet, bør den bli samlet inn i en detaljvisning i boken. Så, la oss komme i gang.
beskrive ("Book Tests", funksjon () var element; var scope; beforeEach (modul ("masteringAngularJsDirectives")) førEk (injiser (funksjon ($ kompilere, $ rootScope) scope = $ rootScope; element = angular.element ""); $ compile (element) ($ rootScope) omfang. $ digest ())); det (" Direktivet skal vellykkes kompilert ", funksjon () Forvent (element.html) )));
I testen ovenfor tester vi et nytt direktiv som heter booktest
. Dette direktivet tar argumentet tittel
og oppretter en div ved å bruke denne tittelen. I testen, før hver testavdeling, ringer vi til modulen vår masteringAngularJsDirectives
først. Deretter genererer vi et direktiv som heter booktest
. I hvert testtrinn blir direktivets utgang testet. Denne testen er bare for en verdikontroll.
I denne delen skal vi teste omfanget av direktivet booktest
. Dette direktivet genererer en bokdetaljvisning på siden, og når du klikker denne detaljdelen, blir en variabel som heter Vist
vil bli satt som ekte
. I vår test vil vi sjekke om Vist
er satt til sant når klikkhendelsen utløses. Direktivet er:
.direktiv ('boktest', funksjon () retur begrense: 'E', omfang: title: '@', erstatt: sann, mal: 'tittel', lenke: funksjon (omfang, element, attrs) element.bind ("klikk", funksjon () console.log ("book viewed!"); scope.viewed = true;); )
For å sette en begivenhet til et element i AngularJS inne i direktivet, kan du bruke link
Egenskap. Innenfor dette attributtet har du det nåværende elementet, direkte bundet til et klikkhendelse. For å teste dette direktivet kan du bruke følgende:
beskrive ("Book Tests", funksjon () var element; var scope; beforeEach (modul ("masteringAngularJsDirectives")) førEk (injiser (funksjon ($ kompilere, $ rootScope) scope = $ rootScope; element = angular.element ""$; kompilere (element) ($ rootScope) omfang. $ digest ())); det (" omfanget lignet skal være sant når boken likte ", funksjon () element.triggerHandler (" klikk "); element.isolateScope (). viewed) .toBe (true);););
I testdelen utløses et klikkhendelse ved å bruke element.triggerHandler ( "klikk")
. Når en klikkhendelse utløses, må den viste variabelen settes som ekte
. Den verdien er hevdet ved å bruke forvente (element.isolateScope (). sett) .toBe (sann)
.
For å utvikle modulære og testbare webprosjekter, er AngularJS den beste til felles. Direktiver er en av de beste komponentene i AngularJS, og det betyr at jo mer du vet om AngularJS-direktiver, desto flere modulære og testbare prosjekter kan du utvikle..
I denne opplæringen har jeg forsøkt å vise deg de virkelige livsstilene om direktiver, og husk at du trenger å gjøre mye øvelse for å forstå logikken bak retningslinjene. Jeg håper denne artikkelen hjelper deg med å forstå AngularJS-direktiver godt.