Skip to content

Latest commit

 

History

History
561 lines (469 loc) · 22.6 KB

README-it-it.md

File metadata and controls

561 lines (469 loc) · 22.6 KB

Introduzione

Questo documento è finalizzato al presentare una serie di buone pratiche e linee guida per lo stile e l'applicazione di AngularJS. Queste pratiche derivano da:

  1. Codice AngularJS
  2. Codice e articoli che ho letto
  3. Esperienza

Nota 1: Questo è ancora un abbozzo della guida stilistica: il suo obiettivo è di essere guidato dalla community, quindi eventuali migliorie sarebbero molto apprezzate da parte di tutti.

In questa guida non si accennerà a comuni linee guida sulla programmazione JavaScript. Queste possono essere trovate nei seguenti link:

  1. Google's JavaScript style guide
  2. Mozilla's JavaScript style guide
  3. Douglas Crockford's JavaScript style guide
  4. Airbnb JavaScript style guide

Per la programmazione AngularJS è raccomandato: Google's JavaScript style guide.

Nel repository AngularJS su GitHub c'è una sezione simile curata da ProLoser. Potete visionarla quì.

Contenuti

Generale

Struttura delle directory

Dal momento che una grande applicazione di AngularJS implica tante componenti, sarebbe consigliabile strutturare i file e le directory in maniera gerarchica. Ci sono due possibili approcci:

  • Creare una divisione ad alto livello in base al tipo di componenti ed una a basso livello in base alle funzionalità

In questo modo le directory avranno questa struttura:

.
├── app
│   ├── app.js
│   ├── controllers
│   │   ├── home
│   │   │   ├── FirstCtrl.js
│   │   │   └── SecondCtrl.js
│   │   └── about
│   │       └── ThirdCtrl.js
│   ├── directives
│   │   ├── home
│   │   │   └── directive1.js
│   │   └── about
│   │       ├── directive2.js
│   │       └── directive3.js
│   ├── filters
│   │   ├── home
│   │   └── about
│   └── services
│       ├── CommonService.js
│       ├── cache
│       │   ├── Cache1.js
│       │   └── Cache2.js
│       └── models
│           ├── Model1.js
│           └── Model2.js
├── partials
├── lib
└── test
  • Creare una divisione ad alto livello in base alle funzionalità ed una a basso livello in base al tipo di componenti

Questa divisione avrà invece questo tipo di struttura:

.
├── app
│   ├── app.js
│   ├── common
│   │   ├── controllers
│   │   ├── directives
│   │   ├── filters
│   │   └── services
│   ├── home
│   │   ├── controllers
│   │   │   ├── FirstCtrl.js
│   │   │   └── SecondCtrl.js
│   │   ├── directives
│   │   │   └── directive1.js
│   │   ├── filters
│   │   │   ├── filter1.js
│   │   │   └── filter2.js
│   │   └── services
│   │       ├── service1.js
│   │       └── service2.js
│   └── about
│       ├── controllers
│       │   └── ThirdCtrl.js
│       ├── directives
│       │   ├── directive2.js
│       │   └── directive3.js
│       ├── filters
│       │   └── filter3.js
│       └── services
│           └── service3.js
├── partials
├── lib
└── test
  • Quando si creano le directive sarebbe utile mettere tutti i file associati ad una data directive (es: template, CSS/SASS file, JavaScript) in una singola cartella. Se scegliete di usare questo stile, siate coerenti e usatelo in ogni occasione.
app
└── directives
    ├── directive1
    │   ├── directive1.html
    │   ├── directive1.js
    │   └── directive1.sass
    └── directive2
        ├── directive2.html
        ├── directive2.js
        └── directive2.sass

Questo approccio può essere combinato con entrambe le strutture di directory trattate in precedenza

  • Un'ulteriore leggera variazione di entrambe le strutture è quella usata in ng-boilerplate. In questa, le unit tests per un determinato componente sono poste nella stessa cartella del componente stesso. In questo modo quando vengono fatti cambiamenti ad un componente è più semplice trovare il relativo test. Il test, in questo modo, fa anche da documentazione e mostra i casi d'uso.
services
├── cache
│   ├── cache1.js
│   └── cache1.spec.js
└── models
    ├── model1.js
    └── model1.spec.js
  • Il file app-js contiene la definizione dei route, impostazioni e/o bootstrap manuali (se richiesti).
  • Ogni file JavaScript dovrebbe contenere solo un singolo componente. Il file dovrebbe essere chiamato con il nome del componente.
  • Per il progetto AngularJS, usate una struttura di template simile alla seguente:
  • The app.js file contains route definitions, configuration and/or manual bootstrap (if required). ng-boilerplate.

Personalmente preferisco la prima struttura perché rende più facili da trovare i componenti più comuni.

Convenzioni su come chiamare i componenti possono essere trovate nelle sezioni relative agli stessi.

Markup

TLDR; Metti lo script in basso.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>MyApp</title>
</head>
<body>
  <div ng-app="myApp">
    <div ng-view></div>
  </div>
  <script src="angular.js"></script>
  <script src="app.js"></script>
</body>
</html>

Mantieni il codice semplice e metti le direttive AngularJS in fondo. In questo modo è più semplice la lettura e la mantenibilità del codice HTML.

<form class="frm" ng-submit="login.authenticate()">
  <div>
    <input class="ipt" type="text" placeholder="name" require ng-model="user.name">
  </div>
</form>

Gli altri attrubuti HTML dovrebbero seguire la seguente guida

Ottimizzare il ciclo digest

  • Eseguire un watch solo per le variabili più importanti (es: usando un tipo di comunicazione real-time, non permettere un ciclo $digest per ogni messaggio ricevuto).
  • Per contenuti che sono inizializzati solo una volta e mai cambiati, usare un single-time watcher come bindonce.
  • Rendere le computazioni in $watch il più semplici possibile. Rendendo pesanti e lenti i calcoli in un singolo $watch, si abbasseranno le prestazioni dell'intera applicazione (il ciclo $digest è eseguito in un singolo thread a causa della natura single-threaded di JavaScript).
  • Settare il ternzo parametro nella funzione $timeout a false, per evitare un ulteriore ciclo $digest quando variabili non necessarie sono implicate nella funzione callback di $timeout.

Altro

  • Usare:
    • $timeout invece di setTimeout
    • $interval invece di setInterval
    • $window invece di window
    • $document invece di document
    • $http invece di $.ajax

Questo renderà il testing più semplice e, in alcuni casi, impedirà comportamenti inaspettati (come il dimenticarsi $scope.$apply in setTimeout).

  • Automatizzare il lavoro usando utility come:

  • Usare promise ($q) invece dei callback. Questo renderà il codice più elegante e pulito, oltre che salvarvi dall'inferno dei callback.

  • Usare $resource invece di $http. Il più alto livello d'astrazione vi salverà dalle ridondanze.

  • Usare un pre-minificatore per AngularJS (ad esempio ngmin o ng-annotate) per evitare problemi dopo la minificazione.

  • Non usare variabili globali. Risolvere tutte le dipendenze usando il Dipendency Injection.

  • Non riempire lo $scope se non con variabili e funzioni usate nel template.

  • E' da preferire l'utilizzo di controller invece di ngInit. Il solo utilizzo appropriato di ngInit è per rinominare particolari proprietà di ngRepeat. A parte quest'ultimo caso, si dovrebbero usare controller per inizializzare variabili nello scope.

  • Non usare il prefisso $ per i nomi di variabili, proprietà o metodi. Questo prefisso è riservato ad AngularJS.

  • Quando si risolvono dipendenze attraverso il meccanismo DI di AngularJS, ordinare tutte le dipendenze in base al loro tipo - le dipendenze built-in di AngularJS dovrebbero essere le prime, seguite da quelle create da voi:

module.factory('Service', function ($rootScope, $timeout, MyCustomDependency1, MyCustomDependency2) {
  return {
    //Something
  };
});

Module

  • Il nome dei module dovrebbe essere assegnato secondo il lowerCamelCase. Per indicare che un module b è submodule di a si possono concatenare usando un namespacing come: a.b.

Ci sono due metodi più comuni per strutturare i modulo:

  1. In base alla funzionalità
  2. In base al tipo di componente

Al momento non c'è grande differenza, ma il primo metodo sembra più pulito. Inoltre, se verrà implementato il lazy-loading modules (il che non è tra i piani di AngularJS), le prestazioni delle app aumenteranno.

Controller

  • Non manipolare il DOM nei controller: questo renderà i controller difficili da testare e violerà il principio di separazione degli interessi. Usare, invece, le directive.
  • Il nome dei controller è assegnato in base alla loro funzionalità (ad esempio shopping cart, homepage, admin panel) più il suffisso 'Ctrl'. I nomi utilizzano in questo caso l'UpperCamelCase (HomePageCtrl, ShoppingCartCtrl, AdminPanelCtrl, ecc.).
  • I controller non dovrebbero mai essere definiti come globali (anche se AngularJS lo permette, inquinare il namspace globale è una brutta partica).
  • Usare la sintassi ad array per la definizione dei controller:
module.controller('MyCtrl', ['dependency1', 'dependency2', ..., 'dependencyn', function (dependency1, dependency2, ..., dependencyn) {
  //...body
}]);

Usando questo tipo di definizione si evitano problemi con la minificazione. So possono generare definizioni ad array da quelle standard utilizzando tool come ng-annotate o le task di grunt grunt-ng-annotate Usare il nome originale delle dipendenze dei controller. Questo vi aiuterà nel produrre un codice più leggibile:

module.controller('MyCtrl', ['$scope', function (s) {
  //...body
}]);

è più leggibile di:

module.controller('MyCtrl', ['$scope', function ($scope) {
  //...body
}]);

Questo principio si applica soprattutto quando i file sono così grandi da aver bisogno di scrollare la pagina. Questo farebbe dimenticare facilmente al lettore quale variabile è legata a quale dipendenza.

  • Rendere i controller il più leggeri possibile. Astrarre le funzioni comuni in service.
  • Comunicare all'interno dei controller invocando metodi (possibile quando un figlio vuole comunicare con il genitore) o con i metodi $emit, $broadcast e $on. I messaggi emessi e trasmessi dovrebbero ridursi al minimo.
  • Creare una lista di tutti i messaggi che sono passati usando $emit e $broadcast e manovrarli con attenzione per evitare collisioni di nomi ed bug.
  • Quando si ha bisogno di formattare dati, incapsulare la logica di formattazione in un filter e dichiararlo come dipendenza:
module.filter('myFormat', function () {
  return function () {
    //body...
  };
});

module.controller('MyCtrl', ['$scope', 'myFormatFilter', function ($scope, myFormatFilter) {
  //body...
}]);
  • In caso di controller annidati, usare "nested scoping" (la sintassi controllerAs):

app.js

module.config(function ($routeProvider) {
  $routeProvider
    .when('/route', {
      templateUrl: 'partials/template.html',
      controller: 'HomeCtrl',
      controllerAs: 'home'
    });
});

HomeCtrl

function HomeCtrl() {
  this.bindingValue = 42;
}

template.html

<div ng-bind="home.bindingValue"></div>

Directive

  • Assegnare i nomi alle directive seguendo il lowerCamelCase
  • Usare scope invece di $scope alle funzioni link. Per le funzioni compile e post/pre link, avrete già definito i parametri che verranno passati quando la funzione verrà invocata e non vi sarà possibile cambiarli usando il DI. Questo stile è utilizzato anche nel codice di AngularJS
  • Usare prefissi personalizzati per le direttive per evitare collisioni con librerie di terze parti.
  • Non usare i prefissi ng e ui, poichè sono già utilizzati da AngularJS e AngularJS UI.
  • La manipolazione del DOM deve essere effettuata solo attraverso le directive.
  • Creare scope isolati quando si creano directiv riusabili.
  • Usare direttive come attributi o elementi invece di commenti o classi: questo renderà il codice più leggibile.
  • Usare $scope.$on('$destroy', fn) per pulire. Questo è molto utile specialmente quando si fa il wrapping di plugin e directive di terze parti.
  • Non dimenticare di usare $sce quando si ha a che fare con contenuti non affidabili

Filter

  • Assegnare i nomi ai filter seguendo il lowerCamelCase.
  • I filter dovrebbero essere più leggeri possibile. Vengono spesso invocati durante il ciclo $digest, quindi un filtro poco performante potrebbe incidere sulle performance della vostra applicazione.
  • Usare ogni filter per un solo scopo mantenendoli coerenti. Manipolazioni più complesse possono essere ottenute concatenando più filter.

Service

Questa sezione include informazioni sui componenti service di AngularJS. Questi non dipendono dal tipo di definizione (es: come provider, .factory, .service) a meno che questo non è esplicitamente menzionato.

  • Usare camelCase per assegnare nomi ai service:

    • UpperCamelCase (PascalCase) per service usati come costruttori. Es:

      module.controller('MainCtrl', function ($scope, User) {
        $scope.user = new User('foo', 42);
      });
      
      module.factory('User', function () {
        return function User(name, age) {
          this.name = name;
          this.age = age;
        };
      });
    • lowerCamelCase per gli altri casi.

  • Incapsulare tutte le logiche di business in service.

  • Service che rappresentano domini dovrebbero essere definiti come service piuttosto che factory. In questo modo ci si può avvantagiare dell' ereditarietà "klassical" in modo più semplice:

function Human() {
  //body
}
Human.prototype.talk = function () {
  return "I'm talking";
};

function Developer() {
  //body
}
Developer.prototype = Object.create(Human.prototype);
Developer.prototype.code = function () {
  return "I'm coding";
};

myModule.service('Human', Human);
myModule.service('Developer', Developer);
  • Per cache di session-level si può usare $cacheFactory. Questo dovrebbe essere usato come risultato cache di richieste o pesanti calcoli.
  • Se un dato service richiede una configurazione, definirlo come provider e configurarlo nel callback di config:
angular.module('demo', [])
.config(function ($provide) {
  $provide.provider('sample', function () {
    var foo = 42;
    return {
      setFoo: function (f) {
        foo = f;
      },
      $get: function () {
        return {
          foo: foo
        };
      }
    };
  });
});

var demo = angular.module('demo');

demo.config(function (sampleProvider) {
  sampleProvider.setFoo(41);
});

Template

  • Usare ng-bind o ng-cloak invece di {{ }} per evitare il flashing content.
  • Evitare di scrivere espressioni complesse nei template.
  • Quando si ha bisogno di settare src ad un'immagine in modo dimamico, usare ng-src invece di src con {{ }}.
  • Quando si ha bisogno di settare href ad un tag dimaicamente, usare ng-href invece di href con {{ }}.
  • Invece di usare variabili di scope come stringa e usarli nell'attributo style racchiusi da {{ }}, utilizzare la directive ng-style con parametri oggetti e variabili dello scope come valori:
<script>
...
$scope.divStyle = {
  width: 200,
  position: 'relative'
};
...
</script>

<div ng-style="divStyle">my beautifully styled div which will work in IE</div>;

Routing

  • Usare resolve per risolvere le dipendenze prima di mostrare le view.

Testing

In corso di stesura.

Finchè la sezione non è pronta, si può fare riferimento a questo link.

Collaborazioni

Dal momento che l'obiettivo di questa guida stilistica è di essere portata avanti dalla community, eventuali collaborazioni sono grandemente apprezzate. Ad esempio, si può contribuire estendendo la sezione di Testing o traducendo la guida nella propria lingua

Collaboratori

mgechev pascalockert mainyaa rubystream lukaszklis
mgechev pascalockert mainyaa rubystream lukaszklis
cironunes cavarzan tornad jmblog bargaorobalo
cironunes cavarzan tornad jmblog bargaorobalo
astalker valgreens bitdeli-chef dchest gsamokovarov
astalker valgreens bitdeli-chef dchest gsamokovarov
ntaoo hermankan jesselpalmer capaj jordanyee
ntaoo hermankan jesselpalmer capaj jordanyee
nacyot kirstein mo-gr cryptojuice olov
nacyot kirstein mo-gr cryptojuice olov
vorktanamobay thomastuts grapswiz coderhaoxin dreame4
vorktanamobay thomastuts grapswiz coderhaoxin dreame4