AngularJS
Gotówki i pułapki AngularJS
Szukaj…
Dwukierunkowe wiązanie danych przestaje działać
Należy pamiętać, że:
- Powiązanie danych Angulara opiera się na prototypowym odziedziczeniu JavaScript, dlatego podlega zmiennemu cieniowaniu .
- Zakres potomny zwykle prototypowo dziedziczy po zakresie macierzystym. Jedynym wyjątkiem od tej reguły jest dyrektywa, która ma izolowany zakres, ponieważ nie dziedziczy prototypowo.
- Istnieje kilka dyrektyw, które tworzą nowy zakres potomny:
ng-repeat
ng-switch
ng-view
ng-if
,ng-if
,ng-controller
ng-include
,ng-include
itp.
Oznacza to, że podczas próby dwukierunkowego powiązania niektórych danych z operacją podstawową, która znajduje się w zasięgu potomnym (lub odwrotnie), rzeczy mogą nie działać zgodnie z oczekiwaniami. Oto przykład tego, jak łatwo „złamać” AngularJS.
Tego problemu można łatwo uniknąć, wykonując następujące kroki:
- Mieć "." w szablonie HTML za każdym razem, gdy wiążesz niektóre dane
- Użyj składni
controllerAs
ponieważ promuje ona użycie wiązania z obiektem „kropkowanym” - $ parent może być użyty do uzyskania dostępu do zmiennych
scope
nadrzędnego, a nie zakresu podrzędnego. jak wewnątrzng-if
możemy użyćng-model="$parent.foo"
..
Alternatywą dla powyższego jest powiązanie ngModel
z ngModel
getter / setter, która zaktualizuje buforowaną wersję modelu, gdy zostanie wywołana z argumentami, lub zwróci ją, gdy zostanie wywołana bez argumentów. Aby użyć funkcji getter / setter, musisz dodać ng-model-options="{ getterSetter: true }"
do elementu z atrybutem ngModal
i wywołać funkcję getter, jeśli chcesz wyświetlić jej wartość w wyrażeniu ( Przykład roboczy ).
Przykład
Widok:
<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>
Kontroler:
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;
};
}]);
Najlepsza praktyka : Najlepiej jest utrzymywać gettery szybko, ponieważ Angular może wywoływać je częściej niż inne części twojego kodu ( odniesienie ).
Co należy zrobić, używając html5Mode
Podczas korzystania z html5Mode([mode])
konieczne jest:
Podstawowy adres URL aplikacji podajesz za pomocą
<base href="">
w nagłówkuindex.html
.Ważne jest, aby tag
base
pojawiał się przed tagami z żądaniami adresu URL. W przeciwnym razie może to spowodować błąd:"Resource interpreted as stylesheet but transferred with MIME type text/html"
. Na przykład:<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>
Jeśli nie chcesz określać znacznika
base
, skonfiguruj$locationProvider
aby nie wymagał znacznikabase
, przekazując obiekt definicji za pomocąrequireBase:false
do$locationProvider.html5Mode()
następujący sposób:$locationProvider.html5Mode({ enabled: true, requireBase: false });
Aby wesprzeć bezpośrednie ładowanie adresów URL HTML5, musisz włączyć przepisywanie adresów URL po stronie serwera. Z AngularJS / Przewodnik dla programistów / Korzystanie z lokalizacji $
Korzystanie z tego trybu wymaga przepisywania adresów URL po stronie serwera, w zasadzie musisz przepisać wszystkie linki do punktu wejścia aplikacji (np.
index.html
). Wymaganie znacznika<base>
jest również ważne w tym przypadku, ponieważ pozwala Angularowi na rozróżnienie między częścią adresu URL, która jest bazą aplikacji, a ścieżką, którą powinna obsłużyć aplikacja.Doskonałe źródło przykładów przepisywania żądań dla różnych implementacji serwera HTTP można znaleźć w często zadawanych pytaniach dotyczących interfejsu użytkownika routera - Jak: Konfigurować serwer do pracy z html5Mode . Na przykład 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; } }
Wyrazić
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 grzechów głównych AngularJS
Poniżej znajduje się lista niektórych błędów, które często popełniają programiści podczas korzystania z funkcjonalności AngularJS, niektóre wyciągnięte z nich wnioski i rozwiązania.
1. Manipulowanie DOM za pomocą kontrolera
Jest to legalne, ale należy go unikać. Kontrolery to miejsca, w których definiujesz swoje zależności, łączysz dane z widokiem i tworzysz logikę biznesową. Możesz technicznie manipulować DOM w kontrolerze, ale ilekroć potrzebujesz takiej samej lub podobnej manipulacji w innej części aplikacji, potrzebny będzie inny kontroler. Zatem najlepszą praktyką tego podejścia jest stworzenie dyrektywy, która obejmuje wszystkie manipulacje i stosowanie tej dyrektywy w całej aplikacji. W związku z tym kontroler pozostawia widok nienaruszony i wykonuje swoją pracę. W dyrektywie funkcja łączenia jest najlepszym miejscem do manipulacji DOM. Ma pełny dostęp do zakresu i elementu, więc korzystając z dyrektywy, możesz również skorzystać z możliwości ponownego użycia.
link: function($scope, element, attrs) {
//The best place to manipulate DOM
}
Dostęp do elementów DOM w funkcji łączenia można uzyskać na kilka sposobów, takich jak parametr element
, angular.element()
lub czysty Javascript.
2. Powiązanie danych w transkluzji
AngularJS słynie z dwukierunkowego wiązania danych. Czasami jednak może się zdarzyć, że dane są ograniczone tylko w jedną stronę do dyrektyw. Zatrzymaj się, AngularJS nie jest zły, ale prawdopodobnie ty. Dyrektywy to trochę niebezpieczne miejsca, ponieważ w grę wchodzą lunety dziecięce i lunety izolowane. Załóżmy, że masz następującą dyrektywę z jednym transkluzją
<my-dir>
<my-transclusion>
</my-transclusion>
</my-dir>
A wewnątrz mojej transkluzji masz pewne elementy, które są powiązane z danymi w zewnętrznym zakresie.
<my-dir>
<my-transclusion>
<input ng-model="name">
</my-transclusion>
</my-dir>
Powyższy kod nie będzie działał poprawnie. Tutaj transkluzja tworzy zakres potomny i możesz uzyskać zmienną nazwy, prawda, ale każda zmiana, którą wprowadzisz w tej zmiennej, pozostanie. Tak więc naprawdę możesz uzyskać dostęp do tej zmiennej jako $ parent.name . Jednak takie użycie może nie być najlepszą praktyką. Lepszym podejściem byłoby zawijanie zmiennych wewnątrz obiektu. Na przykład w kontrolerze możesz utworzyć:
$scope.data = {
name: 'someName'
}
Następnie w transkluzji możesz uzyskać dostęp do tej zmiennej poprzez obiekt „data” i przekonać się, że dwukierunkowe wiązanie działa idealnie!
<input ng-model="data.name">
Nie tylko w tłumaczeniach, ale w całej aplikacji, warto użyć kropkowanej notacji.
3. Wiele dyrektyw razem
Dopuszczalne jest stosowanie dwóch dyrektyw razem w tym samym elemencie, pod warunkiem przestrzegania reguły: dwa izolowane zakresy nie mogą istnieć w tym samym elemencie. Mówiąc ogólnie, podczas tworzenia nowej dyrektywy niestandardowej przydzielany jest izolowany zakres w celu łatwego przekazywania parametrów. Zakładając, że dyrektywy myDirA i myDirB mają izolowane zakresy, a myDirC nie, następujący element będzie ważny:
<input my-dir-a my-dirc>
podczas gdy następujący element spowoduje błąd konsoli:
<input my-dir-a my-dir-b>
Dlatego dyrektywy należy stosować mądrze, biorąc pod uwagę zakresy.
4. Niewłaściwe użycie $ emit
$ emituj, $ broadcast i $ on, działają one na zasadzie nadawca-odbiorca. Innymi słowy, są one środkiem komunikacji między kontrolerami. Na przykład następujący wiersz emituje komunikat „someEvent” z kontrolera A, który ma zostać przechwycony przez kontroler B.
$scope.$emit('someEvent', args);
A następująca linia łapie „someEvent”
$scope.$on('someEvent', function(){});
Jak dotąd wszystko wydaje się idealne. Pamiętaj jednak, że jeśli kontroler B nie zostanie jeszcze wywołany, zdarzenie nie zostanie wychwycone, co oznacza, że zarówno emiter, jak i odbiornik muszą zostać wywołane, aby to działało. Więc ponownie, jeśli nie jesteś pewien, czy zdecydowanie musisz użyć $ emit, zbudowanie usługi wydaje się lepszym sposobem.
5. Niewłaściwe użycie $ scope. $ Watch
$ scope. $ watch służy do obserwowania zmiany zmiennej. Za każdym razem, gdy zmienia się zmienna, wywoływana jest ta metoda. Jednak często popełnianym błędem jest zmiana zmiennej wewnątrz $ scope. $ Watch. W pewnym momencie spowoduje to niespójność i nieskończoną pętlę $ digest.
$scope.$watch('myCtrl.myVariable', function(newVal) {
this.myVariable++;
});
Dlatego w powyższej funkcji upewnij się, że nie masz żadnych operacji na myVariable i newVal.
6. Metody wiązania widoków
To jeden z najgroźniejszych grzechów. AngularJS ma dwukierunkowe wiązanie i za każdym razem, gdy coś się zmienia, widoki są aktualizowane wiele razy. Jeśli więc powiążesz metodę z atrybutem widoku, metodę tę można potencjalnie wywołać sto razy, co również doprowadza cię do szaleństwa podczas debugowania. Jednak istnieją tylko niektóre atrybuty, które są zbudowane do wiązania metod, takie jak kliknięcie ng, rozmycie ng, zamiana przy zmianie itp., Które oczekują metod jako paremeter. Załóżmy na przykład, że masz znacznik w następującym widoku:
<input ng-disabled="myCtrl.isDisabled()" ng-model="myCtrl.name">
Tutaj możesz sprawdzić status wyłączenia widoku za pomocą metody IsDisabled. W kontrolerze myCtrl masz:
vm.isDisabled = function(){
if(someCondition)
return true;
else
return false;
}
Teoretycznie może się to wydawać poprawne, ale technicznie spowoduje to przeciążenie, ponieważ metoda będzie działać niezliczoną ilość razy. Aby rozwiązać ten problem, powiąż zmienną. W kontrolerze musi istnieć następująca zmienna:
vm.isDisabled
Możesz ponownie zainicjować tę zmienną podczas aktywacji kontrolera
if(someCondition)
vm.isDisabled = true
else
vm.isDisabled = false
Jeśli warunek nie jest stabilny, możesz powiązać to z innym zdarzeniem. Następnie powiąż tę zmienną z widokiem:
<input ng-disabled="myCtrl.isDisabled" ng-model="myCtrl.name">
Teraz wszystkie atrybuty widoku mają to, czego oczekują, a metody będą działać tylko w razie potrzeby.
7. Nieużywanie funkcjonalności Angulara
AngularJS zapewnia dużą wygodę dzięki niektórym funkcjom, nie tylko upraszczając kod, ale także zwiększając jego wydajność. Niektóre z tych funkcji są wymienione poniżej:
- angular.forEach for loop (Uwaga, nie możesz „złamać”; możesz tylko zapobiec dostaniu się do ciała, więc rozważ wydajność tutaj.)
- element kątowy dla selektorów DOM
- angular.copy : Użyj tego, gdy nie powinieneś modyfikować głównego obiektu
- Sprawdzanie poprawności formularzy jest już niesamowite. Używaj brudnych, nieskazitelnych, dotkniętych, ważnych, wymaganych i tak dalej.
- Oprócz debugera Chrome używaj również zdalnego debugowania do programowania urządzeń mobilnych.
- I upewnij się, że używasz Bataranga . To bezpłatne rozszerzenie do Chrome, w którym możesz łatwo sprawdzić zakresy .