Direktiv är en av de mest kraftfulla komponenterna i AngularJS, vilket hjälper dig att utöka grundläggande HTML-element / attribut och skapa återanvändbar och testbar koda. I denna handledning ska jag visa dig hur du använder AngularJS-direktiv med bästa praxis i verkligheten.
Vad jag menar här med direktivenär mestadels anpassade riktlinjer under handledningen. Jag kommer inte att försöka lära dig hur man använder inbyggda direktiv som ng-repeat
, ng-show
, etc. Jag ska visa dig hur man använder egna riktlinjer för att skapa egna komponenter.
Låt säga att du har en e-handelsapplikation om böcker och du visar specifik bokinformation på flera områden, t.ex. kommentarer, användarprofilsidor, artiklar etc. Din bokdetalj widget kan vara som nedan:
I den här widgeten finns en bokbild, titel, beskrivning, kommentarer och betyg. Att samla in informationen och lägga in ett specifikt domelement kan vara svårt att göra på alla ställen du vill använda den. Låt oss widgetisera denna vy med hjälp av ett AngularJS-direktiv.
angular.module ('masteringAngularJsDirectives', []) .directive ('bok', funktion () return restrict: 'E', scope: data: '=', templateUrl: 'mallar / bok-widget.html ')
En direktivfunktion har använts i ovanstående exempel för att skapa ett direktiv först. Direktivets namn är bok
. Detta direktiv returnerar ett objekt, och låt oss prata lite om det här objektet. begränsa
är för att definiera direktivstypen, och det kan vara en
(enttribute),C
(Ctös), E
(Element), ochM
(coMmenade). Du kan se användningen av respektive respektive nedan.
Typ | Användande |
---|---|
en | bok> |
C | |
E | <bok data = "book_data">bok> |
M |
omfattning
är för att hantera direktivets räckvidd. I ovanstående fall överförs bokdata till direktivmallen genom att använda "="
omfattningstyp. Jag kommer att prata om detaljer om omfattning i följande avsnitt. templateUrl
används för att ringa en vy för att göra specifikt innehåll genom att använda data som överförs till direktivets räckvidd. Du kan också använda mall
och ge HTML-kod direkt, så här:
… mall: 'Bok info'...
I vårt fall har vi en komplicerad HTML-struktur, och det är därför jag valde templateUrl
alternativ.
Direktiv definieras i JavaScript-filen i ditt AngularJS-projekt och används på HTML-sidan. Det är möjligt att använda AngularJS-direktiv på HTML-sidor enligt följande:
I denna användning används direktivnamnet inom standard HTML-element. Låt säga att du har en rollbaserad meny i din eCommerce-applikation. Den här menyn är utformad enligt din nuvarande roll. Du kan definiera ett direktiv för att bestämma om den aktuella menyn ska visas eller inte. Din HTML-meny kan vara som nedan:
och direktivet enligt följande:
app.directive ("begränsad", funktion () return limit: 'A', länk: funktion (räckvidd, element, attrs) // En del auth checkfunktion var isAuthorized = checkAuthorization element.css ("display", "none");)
Om du använder begränsad
direktivet i menyelementet som ett attribut kan du göra en åtkomstnivåkontroll för varje meny. Om den nuvarande användaren inte är behörig visas inte den specifika menyn.
Så, vad är det länk
funktion där? Länkfunktionen är helt enkelt den funktion som du kan använda för att utföra direktivspecifika operationer. Direktivet ger inte bara HTML-kod genom att ge några inmatningar. Du kan också binda funktioner till direktivets element, ringa en tjänst och uppdatera direktivvärdet, få direktivattribut om det är ett E
typ direktiv etc..
Du kan använda direktivnamnet i HTML-elementsklasser. Förutsatt att du kommer att använda ovanstående direktiv som C
, Du kan uppdatera direktivet begränsa
som C
och använd det enligt följande:
Varje element har redan en klass för styling, och som begränsad
klassen läggs till är det faktiskt ett direktiv.
Du behöver inte använda ett direktiv i ett HTML-element. Du kan skapa ditt eget element genom att använda ett AngularJS-direktiv med en E
restriktion. Låt oss säga att du har en användar-widget i din ansökan att visa Användarnamn
, avatar
, och rykte
på flera ställen i din ansökan. Du kanske vill använda ett sådant direktiv:
app.directive ("användare", funktion () return limit: 'E', länk: funktion (räckvidd, element, attrs) scope.username = attrs.username; scope.avatar = attrs.avatar; scope.reputation = attrs.reputation;, mall: 'Användarnamn: username, Avatar: avatar, Rykte: reputation')
HTML-koden kommer att vara:
I ovanstående exempel skapas ett anpassat element och vissa attribut tillhandahålls som Användarnamn
, avatar
, och rykte
. Jag vill uppmärksamma länkfunktionsorganet. Elementattributen tilldelas direktivets räckvidd. Den första parametern för länkfunktionen är omfattningen av det nuvarande direktivet. Direktivets tredje parameter är direktivets attributobjekt, vilket innebär att du kan läsa något attribut från det anpassade direktivet genom att använda attrs.attr_name
. Attributvärden tilldelas räckvidden så att de används inom mallen.
Egentligen kan du göra denna operation på kortare sätt, och jag kommer att prata om det senare. Detta exempel är för att förstå huvudidén bakom användningen.
Denna användning är inte så vanlig, men jag kommer visa hur man använder den. Låt oss säga att du behöver ett formulär för att din ansökan ska användas på många ställen. Du kan göra det genom att använda följande direktiv:
app.directive ("kommentar", funktion () return restrict: 'M', mall: '')
Och i HTML-elementet:
Varje direktiv har sitt eget räckvidd, men du måste vara försiktig med de uppgifter som är bindande för direktivdeklarationen. Låt säga att du genomför den korg
en del av din eCommerce-applikation. På korgsidan har du redan lagt till föremål här tidigare. Varje objekt har sitt antal fält för att välja hur många objekt du vill köpa, som nedan:
Här är direktivdeklarationen:
app.directive ("objekt", funktion () return limit: 'E', länk: funktion (scope, element, attrs) scope.name = attrs.name;, mall: 'Namn: namn Välj antal: Valt belopp: räkna')
Och för att visa tre objekt i HTML:
Problemet här är att när du väljer mängden av det önskade objektet uppdateras alla delar av objekten. Varför? Eftersom det finns tvåvägsdatabindning med ett namn räkna
, men omfattningen är inte isolerad. För att isolera omfattningen, lägg bara till omfattning:
till direktivets attribut i returavsnittet:
app.directive ("objekt", funktion () return restrict: 'E', scope: , länk: funktion (scope, element, attrs) scope.name = attrs.name;, mall: 'Namn: namn Välj antal: Valt belopp: räkna')
Detta leder till att ditt direktiv har sitt eget isolerade räckvidd så att tvåvägsdatabindning kommer att ske i detta direktiv separat. Jag kommer också att nämna om omfattning
attribut senare.
Huvuddelen av direktivet är att det är en återanvändbar komponent som lätt kan användas - du kan till och med ge några ytterligare attribut till det direktivet. Men hur är det möjligt att skicka ytterligare värde, bindande eller uttryck till ett direktiv för att data ska kunna användas inom direktivet?
"@" Omfattning: Denna typ av omfattning används för att överföra värde till direktivets räckvidd. Låt oss säga att du vill skapa en widget för ett meddelande:
app.controller ("MessageCtrl", funktion () $ scope.message = "Produkt skapad!";) app.directive ("meddelande", funktion () return limit: 'E' '@' , mall: 'meddelande');
och du kan använda:
I det här exemplet är meddelandevärdet helt enkelt tilldelat direktivets räckvidd. Det renderade HTML-innehållet kommer att vara:
Produkt skapad!
"=" Omfattning: I denna räckviddstyp passeras variabla variabler istället för värdena, vilket innebär att vi inte kommer att klara oss meddelande
, vi kommer att passera meddelande
istället. Anledningen till denna funktion är att konstruera dubbelriktad databindning mellan direktivet och sidelementen eller kontrollerna. Låt oss se det i aktion.
.direktiv ("bookComment", funktion () return restrict: 'E', scope: text: '=', mall: '')
I det här direktivet försöker vi skapa en widget för att visa kommentarinmatning för att kommentera en viss bok. Som du kan se kräver detta direktiv ett attribut text
att konstruera dubbelriktad databindning mellan andra element på sidorna. Du kan använda detta på sidan:
Det här är textrutan på direktivet
Detta kommer helt enkelt visa en textruta på sidan, så låt oss lägga till något mer att interagera med detta direktiv:
Det här är textrutan på sidan
Det här är textrutan på direktivet
När du skriver något i den första textrutan skrivs den också i den andra textrutan. Du kan göra det tvärtom. I direktivet passerade vi variabeln commentText
istället för värdet, och denna variabel är den databindande referensen till den första textrutan.
"&" Omfattning: Vi kan överföra värdet och hänvisa till direktiv. I denna omfattningstyp kommer vi att titta på hur man skickar uttryck till direktivet. I verkliga fall kan du behöva skicka en specifik funktion (uttryck) till direktiv för att förhindra koppling. Ibland behöver direktiv inte veta mycket om tanken bakom uttrycken. Till exempel, ett direktiv kommer att gilla boken för dig, men det vet inte hur man gör det. För att göra det kan du följa en struktur som denna:
.direktiv ("likeBook", funktion () return restrict: 'E', scope: like: '&', mall: '')
I detta direktiv skickas ett uttryck till direktivknappen via tycka om
attribut. Låt oss definiera en funktion i regulatorn och skicka den till direktivet inne i HTML.
$ scope.likeFunction = function () alert ("Jag gillar boken!")
Detta kommer att ligga inne i kontrollenheten och mallen kommer att vara:
likeFunction ()
kommer från regulatorn och skickas till direktivet. Vad händer om du vill skicka en parameter till likeFunction ()
? Du kan till exempel behöva överföra ett värderingsvärde till likeFunction ()
. Det är mycket enkelt: lägg bara till ett argument för funktionen i kontrollenheten och lägg till ett inmatningselement i direktivet för att kräva starträkning från användaren. Du kan göra det som visas nedan:
.direktiv ("likeBook", funktion () return restrict: 'E', scope: like: '&', mall: '
"+"')
$ scope.likeFunction = funktion (stjärna) alert ("Jag gillar boken !, och gav" + stjärna + "stjärna.")
Som du kan se kommer textrutan från direktivet. Textrutans värde är bundet till funktionsargumentet som gillar (stjärna: starCount)
. stjärna
är för styrfunktionen, och starCount
för textboxens värde bindande.
Ibland kan du ha en funktion som finns i flera direktiv. De kan läggas i ett föräldrerdirektiv så att de är föremål för barndirektiven.
Låt mig ge dig ett verkligt exempel. Du vill skicka statistiska data när kunder flyttar sin muspekare till toppen av en viss bok. Du kan genomföra en muskollshändelse för bokdirektivet, men vad händer om det kommer att användas av ett annat direktiv? I det här fallet kan du använda arv av direktiven enligt nedan:
app.directive ('mouseClicked', funktion () return limit: 'E', scope: , controller: "MouseClickedCtrl som mouseClicked")
Detta är ett föräldrerdirektiv som ska ärftas av barndirektiv. Som du kan se finns det en kontrollant attribut av direktivet med "as" -direktivet. Låt oss definiera denna kontroller också:
app.controller ('MouseClickedCtrl', funktion ($ element) var mouseClicked = this; mouseClicked.bookType = null; mouseClicked.setBookType = funktion (typ) mouseClicked.bookType = type; $ element.bind ("click" funktion () alert ("Typ av bok:" + mouseClicked.bookType + "skickad för statistisk analys!");))
I den här kontrollenheten ställer vi helt enkelt in en kontroller instans av variabeln bookType
genom att använda barnriktlinjer. När du klickar på en bok eller tidskrift skickas elementets typ till back-end-tjänsten (jag använde en varningsfunktion för att bara visa data). Hur kommer barndirektiv att kunna använda detta direktiv?
app.directive ("ebook", funktion () return kräver: "mouseClicked", länk: funktion (scope, element, attrs, mouseClickedCtrl) mouseClickedCtrl.setBookType ("EBOOK");. magasin ", funktion () return kräver:" mouseClicked ", länk: funktion (scope, element, attrs, mouseClickedCtrl) mouseClickedCtrl.setBookType (" MAGAZINE ");)
Som du kan se använder barnriktlinjerna fordra
sökord för att använda överordningsdirektivet. Och en viktigare punkt är det fjärde argumentet för länkfunktionen i barndirektiven. Detta argument hänvisar till moderatirektivets attribut som betyder att barndirektivet kan använda kontrollfunktionen setBookType
inuti kontrollenheten. Om det aktuella elementet är en eBook kan du använda det första direktivet, och om det är en tidning kan du använda den andra:
Spel av troner (klicka mig)
PC World (klicka mig)
Barndirektiv är som en förmögenhet för moderdirektivet. Vi har eliminerat användningen av musklicksevenemanget för varje barndirektiv genom att sätta det avsnittet i moderdirektivet.
När du använder direktiv inom mallen, är det som du ser på sidan den sammanställda versionen av direktivet. Ibland vill du se den faktiska användningen av direktivet för felsökningsändamål. För att se den komprimerade versionen av det aktuella avsnittet kan du använda ng-icke-bindbar
. Låt oss till exempel säga att du har en widget som skriver ut de mest populära böckerna och här är koden för det:
Bokens variabelvariabel kommer från regulatorn, och resultatet av detta är följande:
Om du vill veta användningen av direktivet bakom den sammanställda produktionen kan du använda den här versionen av koden:
Den här gången kommer utsignalen att vara som nedan:
Det är coolt hittills, men vad händer om vi vill se både de kompilerade och kompilerade versionerna av widgeten? Det är dags att skriva ett anpassat direktiv som kommer att göra en avancerad debugging operation.
app.directive ("customDebug", funktion ($ compile) return terminal: true, link: funktion (scope, element) var currentElement = element.clone (); currentElement.removeAttr newElement = $ compile (currentElement) (scope); element.attr ("style", "border: 1px solid red"); element.after (newElement);)
I det här direktivet klonar vi elementet som är i debug-läge så att det inte ändras efter några uppsättningar av operationer. Efter kloning, ta bort custom-debug
direktivet för att inte fungera som debug mode och sedan kompilera det med $ complile
, som redan injiceras i direktivet. Vi har gett en stil till debug mode-elementet för att betona den debugged. Slutresultatet kommer att vara som nedan:
Du kan spara din utvecklingstid genom att använda den här typen av felsökningsdirektiv för att upptäcka orsaken till eventuella fel i ditt projekt.
Som du redan vet är enhetstestning en mycket viktig del av utvecklingen för att helt kontrollera koden du har skrivit och förhindra potentiella fel. Jag kommer inte dyka djupt i testning av enheten, men kommer ge dig en aning om hur man testar direktiv på ett par sätt.
Jag kommer att använda Jasmine för enhetstestning och Karma för enhetstestlöparen. För att kunna använda Karma, installerar du det globalt genom att springa npm installera -g karma karma-cli
(du måste ha Node.js och npm installerad på din dator). Efter installationen, öppna kommandoraden, gå till din projektmapp och skriv karma init
. Det kommer att ställa dig ett par frågor som nedan för att ställa in dina testkrav.
Jag använder Webstorm för utveckling, och om du använder Webstorm, högerklickar du bara på karma.conf.js och välj Springa karma.conf.js. Detta kommer att utföra alla tester som konfigureras i karma conf. Du kan också köra test med karma start
kommandoraden i projektmappen. Det handlar om miljöinstallation, så låt oss byta till testdelen.
Låt oss säga att vi vill testa bokdirektivet. När vi skickar en titel till direktivet bör den sammanställas i en detaljvy i boken. Så, låt oss börja.
beskriva ("Book Tests", funktion () var element; var scope; beforeEach (modul ("masteringAngularJsDirectives")) föreEach (injicera (funktion ($ kompilera, $ rootScope) scope = $ rootScope; element = angular.element ""); $ compile (element) ($ rootScope) omfattning. $ digest ())), det (" direktivet ska kompileras framgångsrikt ", funktion () förvänta (element.html) )));
I det ovanstående testet testar vi ett nytt direktiv som heter booktest
. Detta direktiv tar argumentet titel
och skapar en div genom att använda denna titel. I testet, före varje testavsnitt, ringer vi vår modul masteringAngularJsDirectives
först. Då genererar vi ett direktiv som heter booktest
. I varje teststeg kommer direktivets utmatning att testas. Detta test är bara för en värdekontroll.
I det här avsnittet kommer vi att testa direktivets räckvidd booktest
. Detta direktiv genererar en detaljvy på boken på sidan, och när du klickar på den här detaljdelen beskrivs en räckvidd variabel tittade
kommer att ställas in som Sann
. I vårt test ska vi kolla om tittade
är satt till true när klickhändelsen utlöses. Direktivet är:
.direktiv ("boktest", funktion () return restrict: 'E', scope: title: '@', ersätt: true, template: 'titel', länk: funktion (räckvidd, element, attrs) element.bind ("click", function () console.log ("book viewed!"); scope.viewed = true;); )
För att ställa in en händelse till ett element i AngularJS i direktivet, kan du använda länk
attribut. Inne i det här attributet har du det aktuella elementet, direkt kopplat till ett klickhändelse. För att testa detta direktiv kan du använda följande:
beskriva ("Book Tests", funktion () var element; var scope; beforeEach (modul ("masteringAngularJsDirectives")) föreEach (injicera (funktion ($ kompilera, $ rootScope) scope = $ rootScope; element = angular.element ""), $ compile (element) ($ rootScope) Omfattning. $ digest ())), det (" Omfattningen gillade borde vara sant när boken liknade ", Funktion () element.triggerHandler element.isolateScope (). viewed) .toBe (true);););
I testdelen utlöses en klickhändelse med hjälp av element.triggerHandler ( "klick")
. När en klickhändelse utlöses måste den visade variabeln ställas in som Sann
. Det värdet hävdas genom att använda förväntar (element.isolateScope (). visas) .toBe (true)
.
För att utveckla modulära och testbara webbprojekt är AngularJS den bästa gemensamma. Direktiv är en av de bästa komponenterna i AngularJS, vilket innebär att ju mer du vet om AngularJS-direktiv, desto mer modulära och testbara projekt kan du utveckla.
I denna handledning har jag försökt visa dig de bästa metoderna i riktlinjerna om riktlinjer, och kom ihåg att du behöver göra mycket träning för att förstå logiken bakom direktiven. Jag hoppas att den här artikeln hjälper dig att förstå AngularJS-direktiven bra.