AngularJS
Trucchi e trappole per AngularJS
Ricerca…
Il binding dei dati a due vie smette di funzionare
Uno dovrebbe avere in mente che:
- L'associazione dei dati di Angular si basa sull'eredità prototipale di JavaScript, quindi è soggetta a ombreggiamenti variabili .
- Un ambito figlio normalmente eredita prototipicamente dall'ambito principale. Un'eccezione a questa regola è una direttiva che ha un ambito isolato in quanto non eredita prototipicamente.
- Ci sono alcune direttive che creano un nuovo ambito figlio:
ng-repeat
,ng-switch
,ng-view
,ng-if
,ng-controller
,ng-include
, ecc.
Ciò significa che quando si tenta di associare in modo bidirezionale alcuni dati a una primitiva che si trova all'interno di un ambito figlio (o viceversa), le cose potrebbero non funzionare come previsto. Ecco un esempio di quanto sia facile "rompere" AngularJS.
Questo problema può essere facilmente evitato seguendo questi passaggi:
- Avere un "." all'interno del tuo modello HTML ogni volta che leghi dei dati
- Utilizzare la sintassi
controllerAs
in quanto promuove l'uso del binding a un oggetto "tratteggiato" - $ genitore può essere utilizzato per accedere alle variabili
scope
genitore piuttosto che all'ambito secondario. come dentrong-if
possiamo usareng-model="$parent.foo"
..
Un'alternativa per quanto sopra è di associare ngModel
a una funzione getter / setter che aggiornerà la versione cache del modello quando viene chiamata con argomenti, o la restituisce quando viene chiamata senza argomenti. Per utilizzare una funzione getter / setter, è necessario aggiungere ng-model-options="{ getterSetter: true }"
all'elemento con l'attributo ngModal
e chiamare la funzione getter se si desidera visualizzare il suo valore nell'espressione ( Esempio di lavoro ).
Esempio
Vista:
<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>
controller:
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;
};
}]);
Best Practice : è meglio mantenere i getter veloci perché è probabile che Angular li chiami più frequentemente rispetto ad altre parti del codice ( riferimento ).
Cose da fare quando si utilizza html5Mode
Quando si utilizza html5Mode([mode])
è necessario che:
Specifica l'URL di base per l'applicazione con un
<base href="">
nella testa del tuoindex.html
.È importante che il tag
base
venga prima di qualsiasi tag con richieste di URL. Altrimenti, questo potrebbe causare questo errore -"Resource interpreted as stylesheet but transferred with MIME type text/html"
. Per esempio:<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>
Se non si desidera specificare un tag
base
, configurare$locationProvider
per non richiedere un tagbase
passando un oggetto definizione conrequireBase:false
a$locationProvider.html5Mode()
questo modo:$locationProvider.html5Mode({ enabled: true, requireBase: false });
Per supportare il caricamento diretto degli URL HTML5, è necessario abilitare la riscrittura degli URL lato server. Da AngularJS / Guida per gli sviluppatori / Utilizzo $ posizione
L'utilizzo di questa modalità richiede la riscrittura degli URL sul lato server, in pratica è necessario riscrivere tutti i collegamenti al punto di accesso dell'applicazione (ad esempio
index.html
). Anche richiedere un tag<base>
è importante in questo caso, poiché consente a Angular di distinguere tra la parte dell'URL che è la base dell'applicazione e il percorso che deve essere gestito dall'applicazione.Un'eccellente risorsa per gli esempi di riscrittura delle richieste per varie implementazioni del server HTTP può essere trovata nelle domande frequenti sull'i-router - Come: Configurare il server per lavorare con html5Mode . Ad esempio, 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; } }
Esprimere
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
Di seguito l'elenco di alcuni errori che gli sviluppatori fanno spesso durante l'uso delle funzionalità di AngularJS, alcune lezioni apprese e soluzioni a loro.
1. Manipolazione del DOM attraverso il controller
È legale, ma deve essere evitato. I controller sono i luoghi in cui si definiscono le dipendenze, si vincolano i dati alla vista e si sviluppa ulteriormente la logica aziendale. Puoi tecnicamente manipolare il DOM in un controller, ma ogni volta che hai bisogno di manipolazioni uguali o simili in un'altra parte della tua app, sarà necessario un altro controller. Pertanto, la migliore pratica di questo approccio è la creazione di una direttiva che includa tutte le manipolazioni e utilizzi la direttiva in tutta l'app. Quindi, il controller lascia intatta la vista e fa il suo lavoro. In una direttiva, la funzione di collegamento è il posto migliore per manipolare il DOM. Ha pieno accesso all'ambito e all'elemento, quindi utilizzando una direttiva, puoi anche trarre vantaggio dalla riusabilità.
link: function($scope, element, attrs) {
//The best place to manipulate DOM
}
È possibile accedere agli elementi DOM nella funzione di collegamento in diversi modi, ad esempio il parametro element
, il metodo angular.element()
o puro Javascript.
2. Dati vincolanti in fase di transizione
AngularJS è famoso per il suo binding di dati bidirezionale. Tuttavia, a volte potresti riscontrare che i tuoi dati sono limitati a senso unico all'interno delle direttive. Fermati lì, AngularJS non è sbagliato ma probabilmente tu. Le direttive sono un po 'pericolose perché sono coinvolti gli ambiti figlio e gli ambiti isolati. Supponiamo che tu abbia la seguente direttiva con una transclusione
<my-dir>
<my-transclusion>
</my-transclusion>
</my-dir>
E all'interno della mia transclusione, hai alcuni elementi che sono legati ai dati nello scope esterno.
<my-dir>
<my-transclusion>
<input ng-model="name">
</my-transclusion>
</my-dir>
Il codice sopra non funzionerà correttamente. Qui, la transclusione crea un ambito figlio e puoi ottenere la variabile nome, giusto, ma qualsiasi modifica apportata a questa variabile rimarrà lì. Quindi, puoi veramente accedere a questa variabile come $ parent.name . Tuttavia, questo utilizzo potrebbe non essere la migliore pratica. Un approccio migliore sarebbe il wrapping delle variabili all'interno di un oggetto. Ad esempio, nel controller è possibile creare:
$scope.data = {
name: 'someName'
}
Quindi nella transclusione, puoi accedere a questa variabile tramite l'oggetto "dati" e vedere che il bind a due vie funziona perfettamente!
<input ng-model="data.name">
Non solo nelle transizioni, ma in tutta l'app, è una buona idea usare la notazione puntata.
3. Più direttive insieme
In realtà è legale utilizzare due direttive insieme all'interno dello stesso elemento, purché obbedisca alla regola: non possono esistere due ambiti isolati sullo stesso elemento. In generale, quando si crea una nuova direttiva personalizzata, si assegna un ambito isolato per facilitare il passaggio dei parametri. Supponendo che le direttive myDirA e myDirB abbiano ambiti isoleted e myDirC no, il seguente elemento sarà valido:
<input my-dir-a my-dirc>
mentre il seguente elemento causerà l'errore della console:
<input my-dir-a my-dir-b>
Pertanto, le direttive devono essere utilizzate con saggezza, prendendo in considerazione gli scopi.
4. Uso improprio di $ emit
$ emettono, $ broadcast e $ on, funzionano in un principio di ricevitore-ricevitore. In altre parole, sono un mezzo di comunicazione tra i controllori. Ad esempio, la riga seguente emette "someEvent" dal controller A, per essere intercettata dal controller interessato B.
$scope.$emit('someEvent', args);
E la seguente riga cattura il "someEvent"
$scope.$on('someEvent', function(){});
Finora tutto sembra perfetto. Ma ricorda che, se il controller B non è ancora stato richiamato, l'evento non verrà catturato, il che significa che sia i controller emettitore che quelli riceventi devono essere richiamati per farlo funzionare. Quindi, ancora una volta, se non sei sicuro di dover usare $ emit, costruire un servizio sembra un modo migliore.
5. Uso improprio di $ scope. $ Watch
$ scope. $ watch è usato per guardare un cambiamento variabile. Ogni volta che una variabile è cambiata, questo metodo viene invocato. Tuttavia, un errore comune è cambiare la variabile all'interno di $ scope. $ Watch. Ciò causerà incoerenza e un infinito ciclo $ digest a un certo punto.
$scope.$watch('myCtrl.myVariable', function(newVal) {
this.myVariable++;
});
Quindi, nella funzione sopra, assicurati di non avere operazioni su myVariable e newVal.
6. Metodi vincolanti per le viste
Questo è uno dei peccati più mortali. AngularJS ha un'associazione a due vie e ogni volta che qualcosa cambia, le viste vengono aggiornate molte volte. Quindi, se si associa un metodo a un attributo di una vista, quel metodo potrebbe essere potenzialmente chiamato cento volte, cosa che fa impazzire anche durante il debug. Tuttavia, ci sono solo alcuni attributi che sono costruiti per il binding dei metodi, come ng-click, ng-blur, ng-on-change, ecc, che si aspettano metodi come paremeter. Ad esempio, supponi di avere la seguente vista nel tuo markup:
<input ng-disabled="myCtrl.isDisabled()" ng-model="myCtrl.name">
Qui si controlla lo stato disabilitato della vista tramite il metodo isDisabled. Nel controller myCtrl, hai:
vm.isDisabled = function(){
if(someCondition)
return true;
else
return false;
}
In teoria, può sembrare corretto ma tecnicamente ciò causerà un sovraccarico, poiché il metodo verrà eseguito innumerevoli volte. Per risolvere questo problema, dovresti associare una variabile. Nel controller, deve essere presente la seguente variabile:
vm.isDisabled
È possibile avviare nuovamente questa variabile nell'attivazione del controller
if(someCondition)
vm.isDisabled = true
else
vm.isDisabled = false
Se la condizione non è stabile, puoi associarla a un altro evento. Quindi dovresti associare questa variabile alla vista:
<input ng-disabled="myCtrl.isDisabled" ng-model="myCtrl.name">
Ora, tutti gli attributi della vista hanno ciò che si aspettano e i metodi verranno eseguiti solo quando necessario.
7. Non usando le funzionalità di Angular
AngularJS offre grande praticità con alcune delle sue funzionalità, non solo per semplificare il codice ma anche per renderlo più efficiente. Alcune di queste funzionalità sono elencate di seguito:
- angular.forOgni per i loop (Attenzione, non puoi "spezzare", puoi solo evitare di entrare nel corpo, quindi considera le prestazioni qui).
- angular.element per selettori DOM
- angular.copy : utilizzare questo quando non si deve modificare l'oggetto principale
- Le convalide dei form sono già fantastiche. Usa sporco, incontaminato, toccato, valido, richiesto e così via.
- Oltre al debugger di Chrome, usa il debug remoto anche per lo sviluppo mobile.
- E assicurati di usare Batarang . È un'estensione gratuita di Chrome in cui è possibile ispezionare facilmente gli ambiti .