AngularJS
AngularJS gotchas och fällor
Sök…
Tvåvägs databindning slutar fungera
Man bör ha i åtanke att:
- Angular's databindning förlitar sig på Javascript prototyvliga arv, vilket är föremål för variabel skuggning .
- Ett barnomfång arv normalt prototypiskt från sitt föräldraområde. Ett undantag från denna regel är ett direktiv som har ett isolerat tillämpningsområde eftersom det inte ärotiskt prototypiskt.
- Det finns några direktiv som skapar ett nytt barnomfång:
ng-repeat
,ng-switch
,ng-view
,ng-if
,ng-controller
,ng-include
, etc.
Detta innebär att när du försöker binda två data till en primitiv som finns inuti ett barnomfång (eller tvärtom), kanske saker inte fungerar som förväntat. Här är ett exempel på hur lätt det är att "bryta" AngularJS.
Det här problemet kan lätt undvikas genom att följa dessa steg:
- Ha en "." i din HTML-mall när du binder till data
- Använd
controllerAs
syntax eftersom det främjar användningen av bindning till ett "prickat" objekt - $ förälder kan användas för att tillgång till moder
scope
variabler snarare än barn omfattning. som inuting-if
vi kan användang-model="$parent.foo"
..
Ett alternativ för ovanstående är att binda ngModel
till en getter / setter-funktion som kommer att uppdatera cache-versionen av modellen när den anropas med argument, eller returnera den när den anropas utan argument. För att kunna använda en getter / setter-funktion måste du lägga till ng-model-options="{ getterSetter: true }"
till elementet med attributet ngModal
och att ringa getter-funktionen om du vill visa dess värde i uttrycket ( Arbetande exempel ).
Exempel
Se:
<div ng-app="myApp" ng-controller="MainCtrl">
<input type="text" ng-model="foo" ng-model-options="{ getterSetter: true }">
<div ng-if="truthyValue">
<!-- I'm a child scope (inside ng-if), but i'm synced with changes from the outside scope -->
<input type="text" ng-model="foo">
</div>
<div>$scope.foo: {{ foo() }}</div>
</div>
Kontrollant:
angular.module('myApp', []).controller('MainCtrl', ['$scope', function($scope) {
$scope.truthyValue = true;
var _foo = 'hello'; // this will be used to cache/represent the value of the 'foo' model
$scope.foo = function(val) {
// the function return the the internal '_foo' varibale when called with zero arguments,
// and update the internal `_foo` when called with an argument
return arguments.length ? (_foo = val) : _foo;
};
}]);
Bästa praxis : Det är bäst att hålla getters snabbt eftersom Angular sannolikt kommer att ringa dem oftare än andra delar av din kod ( referens ).
Saker att göra när du använder html5Mode
När du använder html5Mode([mode])
är det nödvändigt att:
Du anger bas-URL för applikationen med en
<base href="">
i huvudet på dittindex.html
.Det är viktigt att
base
tag kommer innan taggar med URL-förfrågningar. Annars kan detta resultera i det här felet -"Resource interpreted as stylesheet but transferred with MIME type text/html"
. Till exempel:<head> <meta charset="utf-8"> <title>Job Seeker</title> <base href="/"> <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" /> <link rel="stylesheet" href="/styles/main.css"> </head>
Om du gör någon vill ange en
base
tag, configure$locationProvider
till inte kräva enbase
tag genom att en definition objekt medrequireBase:false
till$locationProvider.html5Mode()
så här:$locationProvider.html5Mode({ enabled: true, requireBase: false });
För att stödja direkt laddning av HTML5-URL: er måste du aktivera omskrivning av serversidan på URL-sidan. Från AngularJS / Developer Guide / Använda $ -plats
Att använda det här läget kräver omskrivning av URL på serversidan, i princip måste du skriva om alla dina länkar till startpunkten för din applikation (t.ex.
index.html
). Att kräva en<base>
-tagg är också viktigt för det här fallet, eftersom det gör att Angular kan skilja mellan den del av webbadressen som är applikationsbasen och sökvägen som ska hanteras av applikationen.En utmärkt resurs för exempel på omskrivning av förfrågningar för olika HTTP-serverimplementeringar finns i ui-routerns vanliga frågor - Hur man gör: Konfigurera din server så att den fungerar med html5Mode . Till exempel Apache
RewriteEngine on # Don't rewrite files or directories RewriteCond %{REQUEST_FILENAME} -f [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^ - [L] # Rewrite everything else to index.html to allow html5 state links RewriteRule ^ index.html [L]
nginx
server { server_name my-app; root /path/to/app; location / { try_files $uri $uri/ /index.html; } }
uttrycka
var express = require('express'); var app = express(); app.use('/js', express.static(__dirname + '/js')); app.use('/dist', express.static(__dirname + '/../dist')); app.use('/css', express.static(__dirname + '/css')); app.use('/partials', express.static(__dirname + '/partials')); app.all('/*', function(req, res, next) { // Just send the index.html for other files to support HTML5Mode res.sendFile('index.html', { root: __dirname }); }); app.listen(3006); //the port you want to use
7 Deadly Sins of AngularJS
Nedan är listan över några misstag som utvecklare ofta gör under användning av AngularJS-funktioner, några lärdomar och lösningar på dem.
1. Manipulera DOM genom regulatorn
Det är lagligt, men måste undvikas. Controllers är platserna där du definierar dina beroenden, binder dina data till vyn och gör ytterligare affärslogik. Du kan tekniskt manipulera DOM i en controller, men när du behöver samma eller liknande manipulation i en annan del av din app, behöver du en annan controller. Så den bästa praxisen med detta tillvägagångssätt är att skapa ett direktiv som inkluderar alla manipulationer och använder direktivet i hela din app. Därför lämnar styrenheten vyn intakt och gör sitt jobb. I ett direktiv är kopplingsfunktion det bästa stället att manipulera DOM. Den har full tillgång till omfattningen och elementet, så genom att använda ett direktiv kan du också dra nytta av återanvändbarhet.
link: function($scope, element, attrs) {
//The best place to manipulate DOM
}
Du kan komma åt DOM-element i bindningsfunktion genom flera sätt, såsom element
parametern, angular.element()
metoden, eller ren Java.
2. Databindande vid transkludering
AngularJS är berömd med sin tvåvägsdatabindning. Men du kan ibland stöta på att dina uppgifter endast är enkelriktade i direktiv. Stoppa där, AngularJS har inte fel men förmodligen du. Direktiv är lite farliga platser eftersom barnomfattning och isolerade omfattningar är inblandade. Antag att du har följande direktiv med en transkludering
<my-dir>
<my-transclusion>
</my-transclusion>
</my-dir>
Och inom my-transclusion har du några element som är bundna till uppgifterna i det yttre omfånget.
<my-dir>
<my-transclusion>
<input ng-model="name">
</my-transclusion>
</my-dir>
Ovanstående kod fungerar inte korrekt. Här skapar transkludering ett barnomfång och du kan få namnvariabeln, rätt, men oavsett ändring du gör till denna variabel kommer att stanna kvar. Så du kan verkligen ansluta denna variabel till $ parent.name . Men användningen kanske inte är den bästa praxis. Ett bättre tillvägagångssätt skulle vara att slå in variablerna i ett objekt. Till exempel i kontrollenheten kan du skapa:
$scope.data = {
name: 'someName'
}
Sedan i transkluderingen kan du komma åt denna variabel via 'data' -objektet och se att tvåvägsbindning fungerar perfekt!
<input ng-model="data.name">
Inte bara i slutsatser, utan i hela appen är det en bra idé att använda den prickade notationen.
3. Flera direktiv tillsammans
Det är faktiskt lagligt att använda två direktiv tillsammans inom samma element, så länge du följer regeln: två isolerade omfattningar kan inte existera på samma element. Generellt sett, när du skapar ett nytt anpassat direktiv, tilldelar du ett isolerat område för enkel parameteröverföring. Antagande att direktiven myDirA och myDirB har isolerade omfattningar och myDirC inte har, kommer följande element att vara giltigt:
<input my-dir-a my-dirc>
Följande element orsakar konsolfel:
<input my-dir-a my-dir-b>
Därför måste direktiv användas på ett klokt sätt med hänsyn till tillämpningsområdet.
4. Missbruk av $ emit
$ emit, $ broadcast och $ on, dessa fungerar i en avsändare-mottagare princip. Med andra ord är de ett kommunikationsmedel mellan kontrollerare. Till exempel avger följande rad "someEvent" från styrenhet A, som ska fångas av den berörda styrenheten B.
$scope.$emit('someEvent', args);
Och följande rad fångar "someEvent"
$scope.$on('someEvent', function(){});
Hittills verkar allt perfekt. Men kom ihåg att om styrenheten B inte åberopas ännu, kommer händelsen inte att fångas, vilket innebär att både emitter- och mottagarkontrollanter måste åberopas för att detta ska fungera. Så igen, om du inte är säker på att du definitivt måste använda $ emit, verkar bygga en tjänst ett bättre sätt.
5. Missbruk av $ räckvidd. $ Klocka
$ scope. $ watch används för att titta på en variabel förändring. När en variabel har ändrats åberopas denna metod. Ett vanligt misstag som gjorts är dock att ändra variabeln inom $ scope. $ Watch. Detta kommer att orsaka inkonsekvens och oändlig $ smältslinga vid någon tidpunkt.
$scope.$watch('myCtrl.myVariable', function(newVal) {
this.myVariable++;
});
Så i ovanstående funktion, se till att du inte har några funktioner på myVariable och newVal.
6. Bindningsmetoder till vyer
Detta är en av de dödligaste synderna. AngularJS har tvåvägsbindning, och när något ändras uppdateras vyerna många gånger. Så om du binder en metod till ett attribut i en vy kan den metoden potentiellt kallas hundra gånger, vilket också gör dig galen under felsökning. Det finns emellertid bara några attribut som är byggda för metodbindning, såsom ng-klick, ng-oskärpa, ng-on-Change, etc, som förväntar sig metoder som paremeter. Antag till exempel att du har följande vy i din markering:
<input ng-disabled="myCtrl.isDisabled()" ng-model="myCtrl.name">
Här kontrollerar du inaktiverad status för vyn via metoden isDisabled. I styrenheten myCtrl har du:
vm.isDisabled = function(){
if(someCondition)
return true;
else
return false;
}
I teorin kan det tyckas vara korrekt men tekniskt sett kommer detta att orsaka en överbelastning, eftersom metoden kommer att köras otaliga gånger. För att lösa detta bör du binda en variabel. I din controller måste följande variabel existera:
vm.isDisabled
Du kan initiera denna variabel igen när aktiveringen av regulatorn
if(someCondition)
vm.isDisabled = true
else
vm.isDisabled = false
Om villkoret inte är stabilt kan du binda detta till en annan händelse. Då ska du binda denna variabel till vyn:
<input ng-disabled="myCtrl.isDisabled" ng-model="myCtrl.name">
Nu har alla attribut i vyn vad de förväntar sig och metoderna kommer att köras endast när det behövs.
7. Använder inte Angular funktioner
AngularJS ger mycket bekvämlighet med några av dess funktioner, inte bara förenklar din kod utan också gör den mer effektiv. Några av dessa funktioner listas nedan:
- vinkelformigt. För alla slingor (varning, du kan inte "bryta;" det, du kan bara förhindra att du kommer in i kroppen, så tänk på prestanda här.)
- angular.element för DOM-väljare
- angular.copy : Använd detta när du inte ska ändra huvudobjektet
- Formvalideringar är redan fantastiska. Använd smutsiga, orörda, berörda, giltiga, obligatoriska och så vidare.
- Förutom Chrome-felsökare kan du också använda felsökning för mobilutveckling.
- Och se till att du använder Batarang . Det är en gratis Chrome-förlängning där du enkelt kan inspektera omfattningar .