From 542d762b6e24770e105a2ceafe141650dfe477da Mon Sep 17 00:00:00 2001 From: Angelo Chiello Date: Fri, 4 Dec 2015 13:32:18 +0100 Subject: [PATCH 001/114] [it-IT] PR #534 --- i18n/it-IT.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/it-IT.md b/i18n/it-IT.md index 4730777b..aac021a3 100644 --- a/i18n/it-IT.md +++ b/i18n/it-IT.md @@ -2019,9 +2019,9 @@ Invece usa la più semplice sintassi setter. // customer.service.js angular .module - .service('customersService', customersService); + .service('customerService', customerService); - function customersService() { } + function customerService() { } ``` ### Nomi dei componenti directive From 28170ece320c3cc0cb270d69a209ab655267e735 Mon Sep 17 00:00:00 2001 From: Angelo Chiello Date: Fri, 4 Dec 2015 14:04:55 +0100 Subject: [PATCH 002/114] [it-IT] PR #538 --- i18n/it-IT.md | 94 +++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/i18n/it-IT.md b/i18n/it-IT.md index aac021a3..052c00a2 100644 --- a/i18n/it-IT.md +++ b/i18n/it-IT.md @@ -267,7 +267,7 @@ Invece usa la più semplice sintassi setter. /* evitare */ angular .module('app') - .controller('Dashboard', function() { }); + .controller('DashboardController', function() { }); .factory('logger', function() { }); ``` @@ -277,9 +277,9 @@ Invece usa la più semplice sintassi setter. // dashboard.js angular .module('app') - .controller('Dashboard', Dashboard); + .controller('DashboardController', DashboardController); - function Dashboard() { } + function DashboardController() { } ``` ```javascript @@ -308,14 +308,14 @@ Invece usa la più semplice sintassi setter. ```html -
+
{{ name }}
``` ```html -
+
{{ customer.name }}
``` @@ -333,7 +333,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* evitare */ - function Customer($scope) { + function CustomerController($scope) { $scope.name = {}; $scope.sendMessage = function() { }; } @@ -341,7 +341,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato - tuttavia vedi la prossima sezione */ - function Customer() { + function CustomerController() { this.name = {}; this.sendMessage = function() { }; } @@ -356,7 +356,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* evitare */ - function Customer() { + function CustomerController() { this.name = {}; this.sendMessage = function() { }; } @@ -364,7 +364,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato */ - function Customer() { + function CustomerController() { var vm = this; vm.name = {}; vm.sendMessage = function() { }; @@ -407,7 +407,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* evitare */ - function Sessions() { + function SessionsController() { var vm = this; vm.gotoSession = function() { @@ -425,7 +425,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato */ - function Sessions() { + function SessionsController() { var vm = this; vm.gotoSession = gotoSession; @@ -455,7 +455,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* evitare */ - function Sessions(data) { + function SessionsController(data) { var vm = this; vm.gotoSession = gotoSession; @@ -475,7 +475,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato */ - function Sessions(sessionDataService) { + function SessionsController(sessionDataService) { var vm = this; vm.gotoSession = gotoSession; @@ -505,7 +505,7 @@ Invece usa la più semplice sintassi setter. * evitare * Uso di espressioni di funzione. */ - function Avengers(avengersService, logger) { + function AvengersController(avengersService, logger) { var vm = this; vm.avengers = []; vm.title = 'Avengers'; @@ -537,7 +537,7 @@ Invece usa la più semplice sintassi setter. * Usare dichiarazione di funzione * e membri che fanno in binding in alto. */ - function Avengers(avengersService, logger) { + function AvengersController(avengersService, logger) { var vm = this; vm.avengers = []; vm.getAvengers = getAvengers; @@ -574,7 +574,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* evitare */ - function Order($http, $q, config, userInfo) { + function OrderController($http, $q, config, userInfo) { var vm = this; vm.checkCredit = checkCredit; vm.isCreditOk; @@ -605,7 +605,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato */ - function Order(creditService) { + function OrderController(creditService) { var vm = this; vm.checkCredit = checkCredit; vm.isCreditOk; @@ -947,11 +947,11 @@ Invece usa la più semplice sintassi setter. // controller che chiama la factory del servizio ai dati angular .module('app.avengers') - .controller('Avengers', Avengers); + .controller('AvengersController', AvengersController); - Avengers.$inject = ['dataservice', 'logger']; + AvengersController.$inject = ['dataservice', 'logger']; - function Avengers(dataservice, logger) { + function AvengersController(dataservice, logger) { var vm = this; vm.avengers = []; @@ -1333,7 +1333,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* evitare */ - function Avengers(dataservice) { + function AvengersController(dataservice) { var vm = this; vm.avengers = []; vm.title = 'Avengers'; @@ -1347,7 +1347,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato */ - function Avengers(dataservice) { + function AvengersController(dataservice) { var vm = this; vm.avengers = []; vm.title = 'Avengers'; @@ -1382,9 +1382,9 @@ Invece usa la più semplice sintassi setter. /* evitare */ angular .module('app') - .controller('Avengers', Avengers); + .controller('AvengersController', AvengersController); - function Avengers(movieService) { + function AvengersController(movieService) { var vm = this; // non risolta vm.movies; @@ -1407,7 +1407,7 @@ Invece usa la più semplice sintassi setter. $routeProvider .when('/avengers', { templateUrl: 'avengers.html', - controller: 'Avengers', + controller: 'AvengersController', controllerAs: 'vm', resolve: { moviesPrepService: function(movieService) { @@ -1420,10 +1420,10 @@ Invece usa la più semplice sintassi setter. // avengers.js angular .module('app') - .controller('Avengers', Avengers); + .controller('AvengersController', AvengersController); - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { + AvengersController.$inject = ['moviesPrepService']; + function AvengersController(moviesPrepService) { var vm = this; vm.movies = moviesPrepService.movies; } @@ -1443,7 +1443,7 @@ Invece usa la più semplice sintassi setter. $routeProvider .when('/avengers', { templateUrl: 'avengers.html', - controller: 'Avengers', + controller: 'AvengersController', controllerAs: 'vm', resolve: { moviesPrepService: moviesPrepService @@ -1458,10 +1458,10 @@ Invece usa la più semplice sintassi setter. // avengers.js angular .module('app') - .controller('Avengers', Avengers); + .controller('AvengersController', AvengersController); - Avengers.$inject = ['moviesPrepService']; - function Avengers(moviesPrepService) { + AvengersController.$inject = ['moviesPrepService']; + function AvengersController(moviesPrepService) { var vm = this; vm.movies = moviesPrepService.movies; } @@ -1483,9 +1483,9 @@ Invece usa la più semplice sintassi setter. /* evita - non a prova di minificazione*/ angular .module('app') - .controller('Dashboard', Dashboard); + .controller('DashboardController', DashboardController); - function Dashboard(common, dataservice) { + function DashboardController(common, dataservice) { } ``` @@ -1493,7 +1493,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* evita - non a prova di minificazione*/ - angular.module('app').controller('Dashboard', d);function d(a, b) { } + angular.module('app').controller('DashboardController', d);function d(a, b) { } ``` ### Indentificazione manuale delle dipendenze @@ -1511,9 +1511,9 @@ Invece usa la più semplice sintassi setter. /* evitare */ angular .module('app') - .controller('Dashboard', + .controller('DashboardController', ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} + function DashboardController($location, $routeParams, common, dataservice) {} ]); ``` @@ -1521,7 +1521,7 @@ Invece usa la più semplice sintassi setter. /* evitare */ angular .module('app') - .controller('Dashboard', + .controller('DashboardController', ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); function Dashboard($location, $routeParams, common, dataservice) { @@ -1532,11 +1532,11 @@ Invece usa la più semplice sintassi setter. /* consigliato */ angular .module('app') - .controller('Dashboard', Dashboard); + .controller('DashboardController', DashboardController); Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - function Dashboard($location, $routeParams, common, dataservice) { + function DashboardController($location, $routeParams, common, dataservice) { } ``` @@ -1623,10 +1623,10 @@ Invece usa la più semplice sintassi setter. ```javascript angular .module('app') - .controller('Avengers', Avengers); + .controller('AvengersController', AvengersController); /* @ngInject */ - function Avengers(storage, avengerService) { + function AvengersController(storage, avengerService) { var vm = this; vm.heroSearch = ''; vm.storeHero = storeHero; @@ -1643,10 +1643,10 @@ Invece usa la più semplice sintassi setter. ```javascript angular .module('app') - .controller('Avengers', Avengers); + .controller('AvengersController', AvengersController); /* @ngInject */ - function Avengers(storage, avengerService) { + function AvengersController(storage, avengerService) { var vm = this; vm.heroSearch = ''; vm.storeHero = storeHero; @@ -1657,7 +1657,7 @@ Invece usa la più semplice sintassi setter. } } - Avengers.$inject = ['storage', 'avengerService']; + AvengersController.$inject = ['storage', 'avengerService']; ``` Nota: Se `ng-annotate` rileva che l'iniezione è già stata fatta (p.e. `@ngInject` è stato rilevato), non duplicherà il codice di `$inject`. @@ -1670,7 +1670,7 @@ Invece usa la più semplice sintassi setter. $routeProvider .when('/avengers', { templateUrl: 'avengers.html', - controller: 'Avengers', + controller: 'AvengersController', controllerAs: 'vm', resolve: { /* @ngInject */ moviesPrepService: function(movieService) { @@ -1906,7 +1906,7 @@ Invece usa la più semplice sintassi setter. avenger-profile.directive.spec.js ``` - Nota: Un'altra convenzione comune è dare il nome al file del controller senza la parola `controller` nel nome del file come `avengers.js` invece di `avengers.controller.js`. Tutte le altre convenzioni continuano ancora a mantenere il suffisso del tipo. I controller sono i tipi di componenti più comuni perciò questo risparmia digitazione continuando ad essere facilmente identificabili. Consiglio di scegliere 1 convenzione e rimanere consistente nel tuo team. La mia preferenza va a `avengers.controller.js`. + Nota: Un'altra convenzione comune è dare il nome al file del controller senza la parola `controller` nel nome del file come `avengers.js` invece di `avengers.controller.js`. Tutte le altre convenzioni continuano ancora a mantenere il suffisso del tipo. I controller sono i tipi di componenti più comuni perciò questo risparmia digitazione continuando ad essere facilmente identificabili. Consiglio di scegliere 1 convenzione e rimanere consistente nel tuo team. La mia preferenza va a `avengers.controller.js` che identifica `AvengersController`. ```javascript /** From 8e431e4cc4f8f0823a89ef8b7fac41e9c546651e Mon Sep 17 00:00:00 2001 From: Angelo Chiello Date: Fri, 4 Dec 2015 14:06:47 +0100 Subject: [PATCH 003/114] [it-IT] PR #539 --- i18n/it-IT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/it-IT.md b/i18n/it-IT.md index 052c00a2..e4a43354 100644 --- a/i18n/it-IT.md +++ b/i18n/it-IT.md @@ -1534,7 +1534,7 @@ Invece usa la più semplice sintassi setter. .module('app') .controller('DashboardController', DashboardController); - Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; + DashboardController.$inject = ['$location', '$routeParams', 'common', 'dataservice']; function DashboardController($location, $routeParams, common, dataservice) { } From 71f1ace0cd718caa9b933989e747a5ca8c118c25 Mon Sep 17 00:00:00 2001 From: Angelo Chiello Date: Fri, 4 Dec 2015 14:32:05 +0100 Subject: [PATCH 004/114] [it-IT] PR #537 --- i18n/it-IT.md | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/i18n/it-IT.md b/i18n/it-IT.md index e4a43354..8d97d7b4 100644 --- a/i18n/it-IT.md +++ b/i18n/it-IT.md @@ -2890,19 +2890,31 @@ Usa file template o snippet che ti aiutino a seguire stili e schemi consistentem ### WebStorm ###### [Stile [Y252](#stile-y252)] - - Snippet Angular e file di template che seguono queste linee guida. Le puoi importare dentro i tuoi settaggi di WebStorm: + - Live template per Angular che seguono queste linee guida. - - Scarica i [file dei template e gli snippet di Angular per WebStorm](assets/webstorm-angular-file-template.settings.jar?raw=true) - - Apri WebStorm e vai al menù `File` - - Scegli la voce di menù `Import Settings` - - Seleziona il file e clicca `OK` + - Scarica [webstorm-angular-live-templates.xml](assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml?raw=true) + - Mettili nella tua Place it in your [cartella dei template](https://www.jetbrains.com/webstorm/help/project-and-ide-settings.html) + - Riavvia WebStorm - In un file JavaScript digita questi comandi seguiti da `TAB` ```javascript - ng-c // crea un controller Angular - ng-f // crea una factory Angular - ng-m // crea un modulo Angular - ``` + // Questi sono snippet completi che contengono una IIFE + ngapp // crea un modulo setter Angular + ngcontroller // crea un controller Angular + ngdirective // crea una directive Angular + ngfactory // crea una factory Angular + ngfilter // crea un filter Angular + ngservice // crea un service Angular + + // Questi sono snippet parziali intesi per essere concatenati + ngconfig // definisce una funzione della fase di configuration + ngmodule // crea un modulo getter Angular + ngroute // crea una definizione Angular 'when' di ngRoute + ngrun // definisce una funzione di fase run + ngstate // crea una definizione di stato Angular per UI Router + ``` + + *Template individuali sono inoltre disponibili per essere scaricati all’interno della cartella [webstorm-angular-live-templates](assets/webstorm-angular-live-templates?raw=true)* ### Atom ###### [Stile [Y253](#stile-y253)] @@ -2955,7 +2967,7 @@ Usa file template o snippet che ti aiutino a seguire stili e schemi consistentem ngstate // crea una definizione di stato di UI Router Angular ngconfig // definisce un funzione per la fase di cofigurazione ngrun // definisce una funzione per la fase di esecuzione - ngroute // definisce una ngRoute Angular con la definizione 'when' + ngwhen // definisce una ngRoute Angular con la definizione 'when' ngtranslate // usa il service $translate con le proprie promesse ``` From da9ea06c3d8edbf175989e12fc15c8b65f49114e Mon Sep 17 00:00:00 2001 From: Angelo Chiello Date: Fri, 4 Dec 2015 14:35:24 +0100 Subject: [PATCH 005/114] [it-IT] Pr #540 --- i18n/it-IT.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/it-IT.md b/i18n/it-IT.md index 8d97d7b4..8560fbd5 100644 --- a/i18n/it-IT.md +++ b/i18n/it-IT.md @@ -1064,7 +1064,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato */ - /* calendarRange.directive.js */ + /* calendar-range.directive.js */ /** * @desc directive di ordini che è specifica al modulo ordini in una azienda di nome Acme @@ -1081,7 +1081,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato */ - /* customerInfo.directive.js */ + /* customer-info.directive.js */ /** * @desc directive delle vendite che può essere usato dovunque nella applicazione di vendita di una azienda di nome Acme From 0aa044d59bbf62d271b762d6a5821f37cd25274e Mon Sep 17 00:00:00 2001 From: Angelo Chiello Date: Fri, 4 Dec 2015 14:36:33 +0100 Subject: [PATCH 006/114] [it-IT] PR #549 --- i18n/it-IT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/it-IT.md b/i18n/it-IT.md index 8560fbd5..5dc1f30a 100644 --- a/i18n/it-IT.md +++ b/i18n/it-IT.md @@ -90,7 +90,7 @@ Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di a ```javascript /* consigliato */ - // someController.js + // some.controller.js angular .module('app') .controller('SomeController', SomeController); From 8776f166c4aba2a98999aad7e5f8dc131a23a057 Mon Sep 17 00:00:00 2001 From: Angelo Chiello Date: Fri, 4 Dec 2015 14:43:28 +0100 Subject: [PATCH 007/114] [it-IT] PR #556 --- i18n/it-IT.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/i18n/it-IT.md b/i18n/it-IT.md index 5dc1f30a..feab5418 100644 --- a/i18n/it-IT.md +++ b/i18n/it-IT.md @@ -395,6 +395,17 @@ Invece usa la più semplice sintassi setter. }); } ``` + Nota: Quando lavori con basi di codice molto estese, usare un nome che sia molto descrittivo può facilitare nella cognizione e rintracciabilità. Evita nomi oltremodo lunghi che sono proni ad errori. + + ```html + + + ``` + + ```html + + + ``` ### Membri che possono fare il bind in alto ###### [Stile [Y033](#stile-y033)] From 13067ca4af5b904f535b2a08fdcdf7dd495d3f9d Mon Sep 17 00:00:00 2001 From: Angelo Chiello Date: Fri, 4 Dec 2015 14:55:32 +0100 Subject: [PATCH 008/114] [it-IT] PR #564 --- i18n/it-IT.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/i18n/it-IT.md b/i18n/it-IT.md index feab5418..6af8965b 100644 --- a/i18n/it-IT.md +++ b/i18n/it-IT.md @@ -432,6 +432,7 @@ Invece usa la più semplice sintassi setter. }; vm.sessions = []; vm.title = 'Sessions'; + } ``` ```javascript @@ -458,6 +459,7 @@ Invece usa la più semplice sintassi setter. function search() { /* */ } + } ``` ![Controller che usa "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-1.png) @@ -482,6 +484,7 @@ Invece usa la più semplice sintassi setter. vm.search = search; vm.sessions = []; vm.title = 'Sessions'; + } ``` ```javascript @@ -494,6 +497,7 @@ Invece usa la più semplice sintassi setter. vm.search = search; vm.sessions = []; vm.title = 'Sessions'; + } ``` ### Dichiarazioni di funzione per nascondere i dettagli di implementazione From a56be823624e2078c7bc716dc5fefebd094ea661 Mon Sep 17 00:00:00 2001 From: Angelo Chiello Date: Fri, 4 Dec 2015 15:01:50 +0100 Subject: [PATCH 009/114] [it-IT] PR #562 --- i18n/it-IT.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/i18n/it-IT.md b/i18n/it-IT.md index 6af8965b..36ec00aa 100644 --- a/i18n/it-IT.md +++ b/i18n/it-IT.md @@ -1239,6 +1239,8 @@ Invece usa la più semplice sintassi setter. }, link: linkFunc, controller : ExampleController, + // nota: Questo dovrebbe essere 'ExampleController' (il nome del controller esportato, come stringa) + // qualora faccia riferimento ad un controller definito nel proprio file separato. controllerAs: 'vm', bindToController: true // perché lo scope è isolato }; From 9e5677e92dd89292a1aa4935b43b0c608b72e4d0 Mon Sep 17 00:00:00 2001 From: Angelo Chiello Date: Fri, 4 Dec 2015 15:20:00 +0100 Subject: [PATCH 010/114] [it-IT] "Angular Team Endorsed" section Added "Angular Team Endorsed" section --- i18n/it-IT.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/i18n/it-IT.md b/i18n/it-IT.md index 36ec00aa..ebabd2c3 100644 --- a/i18n/it-IT.md +++ b/i18n/it-IT.md @@ -1,5 +1,9 @@ # Guida stilistica ad Angular +## Approvato dal Team di Angular Team +Uno speciale ringraziamento a Igor Minar, a capo del Team di Angular, per la revisione, aver contribuito al feedback e la fiducia accordatami per la conduzione di queste linee guida. + +## Scopo *Guida stilistica dogmatica ad Angular per i team di [@john_papa](//twitter.com/john_papa)* Se stai cercando una guida stilistica dogmatica per le sintassi, convenzioni e struttura di applicazioni AngularJS, allora questo fa per te. Gli stili sono basati sulla mia esperienza di sviluppo con [AngularJS](//angularjs.org), presentazioni, [corsi di formazioni di Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) e del lavoro in team. From c6bb26926cf09874004f6ff7a12031dbc3b9707d Mon Sep 17 00:00:00 2001 From: Angelo Chiello Date: Fri, 4 Dec 2015 15:21:16 +0100 Subject: [PATCH 011/114] [it-IT] Typo in "Angular Team Endorsed" section --- i18n/it-IT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/it-IT.md b/i18n/it-IT.md index ebabd2c3..a3af20bf 100644 --- a/i18n/it-IT.md +++ b/i18n/it-IT.md @@ -1,6 +1,6 @@ # Guida stilistica ad Angular -## Approvato dal Team di Angular Team +## Approvato dal Team di Angular Uno speciale ringraziamento a Igor Minar, a capo del Team di Angular, per la revisione, aver contribuito al feedback e la fiducia accordatami per la conduzione di queste linee guida. ## Scopo From e103833270995778cd3262e9384fda87d253fb28 Mon Sep 17 00:00:00 2001 From: Victor Nascimento Date: Sat, 12 Dec 2015 18:23:12 -0300 Subject: [PATCH 012/114] Fixed broken link to pt-BR translation The link was pointing to PT-BR, which is wrong. The correct link is pt-BR, lowercase. --- i18n/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/README.md b/i18n/README.md index dfe00179..826b9786 100644 --- a/i18n/README.md +++ b/i18n/README.md @@ -9,7 +9,7 @@ The [original English version](http://jpapa.me/ngstyles) is the source of truth, 1. [Italian](it-IT.md) by [Angelo Chiello](https://github.com/angelochiello) 1. [Japanese](ja-JP.md) by [@noritamago](https://github.com/noritamago) 1. [Macedonian](mk-MK.md) by [Aleksandar Bogatinov](https://github.com/Bogatinov) - 1. [Portuguese-Brazil](PT-BR.md) by [Vinicius Sabadim Fernandes](https://github.com/vinicius-sabadim) + 1. [Portuguese-Brazil](pt-BR.md) by [Vinicius Sabadim Fernandes](https://github.com/vinicius-sabadim) 1. [Russian](ru-RU.md) by [Vasiliy Mazhekin](https://github.com/mazhekin) 1. [Simplified Chinese](zh-CN.md) by [Zhao Ke](https://github.com/natee) 1. [Spanish](es-ES.md) by [Alberto Calleja](https://github.com/AlbertoImpl) and [Gilberto](https://github.com/ingilniero) From a06151bd2415c8a34a84b876768fbe6fab9bf591 Mon Sep 17 00:00:00 2001 From: cresencio Date: Mon, 21 Dec 2015 19:12:44 -0600 Subject: [PATCH 013/114] Fix link download for sublime snippets --- i18n/es-ES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/es-ES.md b/i18n/es-ES.md index b47fd693..984274e2 100644 --- a/i18n/es-ES.md +++ b/i18n/es-ES.md @@ -2682,7 +2682,7 @@ Usa Plantillas o snippets para ayudarte a seguir estilos consistentes o patrones - Snippets de Angular que siguen estos estilos y directrices. - - Descarga los [snippets de Angular para Sublime](assets/sublime-angular-snippets.zip?raw=true) + - Descarga los [snippets de Angular para Sublime](../assets/sublime-angular-snippets?raw=true) - Colócalos en tu directorio de Packages - Reinicia Sublime - En un archivo de JavaScript escibe estos comandos seguidos de un `TAB` From a4b9d94bd700530c5c0d9ffe0e1ca70b9af6ce75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Korfal=C4=B1?= Date: Wed, 23 Dec 2015 08:39:31 +0200 Subject: [PATCH 014/114] Update README.md Turkish link and credits have been added --- i18n/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/i18n/README.md b/i18n/README.md index dfe00179..d90fe22f 100644 --- a/i18n/README.md +++ b/i18n/README.md @@ -13,6 +13,7 @@ The [original English version](http://jpapa.me/ngstyles) is the source of truth, 1. [Russian](ru-RU.md) by [Vasiliy Mazhekin](https://github.com/mazhekin) 1. [Simplified Chinese](zh-CN.md) by [Zhao Ke](https://github.com/natee) 1. [Spanish](es-ES.md) by [Alberto Calleja](https://github.com/AlbertoImpl) and [Gilberto](https://github.com/ingilniero) + 1. [Turkish](tr-TR.md) by [Uğur Korfalı](https://github.com/kel-sakal-biyik) ## Contributing From fe959a6a56b68d0b5b37d8c283922341f25b15de Mon Sep 17 00:00:00 2001 From: Joshua Date: Tue, 22 Dec 2015 22:51:34 -0800 Subject: [PATCH 015/114] Add Korean translation --- i18n/README.md | 2 +- i18n/ko_KR.md | 3255 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 3256 insertions(+), 1 deletion(-) create mode 100644 i18n/ko_KR.md diff --git a/i18n/README.md b/i18n/README.md index 826b9786..9a016d3c 100644 --- a/i18n/README.md +++ b/i18n/README.md @@ -13,7 +13,7 @@ The [original English version](http://jpapa.me/ngstyles) is the source of truth, 1. [Russian](ru-RU.md) by [Vasiliy Mazhekin](https://github.com/mazhekin) 1. [Simplified Chinese](zh-CN.md) by [Zhao Ke](https://github.com/natee) 1. [Spanish](es-ES.md) by [Alberto Calleja](https://github.com/AlbertoImpl) and [Gilberto](https://github.com/ingilniero) - + 1. [Korean](ko-KR.md) by [Joshua Ji](https://github.com/zirho) ## Contributing Language translations are welcomed and encouraged. The success of these translations depends on the community. I highly encourage new translation contributions and help to keep them up to date. diff --git a/i18n/ko_KR.md b/i18n/ko_KR.md new file mode 100644 index 00000000..3b71c616 --- /dev/null +++ b/i18n/ko_KR.md @@ -0,0 +1,3255 @@ +# Angular Style Guide + +## Angular Team의 지지를 받습니다. +Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타일 가이드를 위해 리뷰, 기여, 피드백을 해주었고 저를 믿어주고 이끌어 주었습니다. + +## Purpose +*팀환경을 위한 방향을 제시하는 Angular 스타일 가이드 by [@john_papa](//twitter.com/john_papa)* + +만약 Angular [Angular](//angularjs.org) 어플리케이션의 문법, 컨벤션, 구조화를 위한 스타일 가이드를 찾고 있다면 제대로 오셨습니다. 여기 제시된 스타일들은 제 팀 단위 개발 경험, 프레젠테이션, [Pluralsight training courses](http://pluralsight.com/training/Authors/Details/john-papa)를 토대로 만들어졌습니다. + +이 스타일 가이드의 목적은 Angular 어플리케이션을 만드는 길잡이 역할을 하기 위함이며 더 나아가 왜 내가 이런 것들을 선택했는지 보여주기 위함입니다. +>만약 이 가이드가 마음에 든다면 Pluralsight 에 올려놓은 저의 강의를 참고하시기 바랍니다. [Angular Patterns: Clean Code](http://jpapa.me/ngclean) + + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + +## Community Awesomeness and Credit +저는 Angular 커뮤니티의 대단함을 알게 되었습니다. 그들은 자신들의 경험을 공유하는데 열정적이기 때문입니다. 나의 친구이자 Angular 전문가인 Todd Motto 와 나는 많은 스타일과 컨벤션을 위해 공동작업을 하였습니다. 대부분 우리는 서로 동의하였지만 어떤 부분에서는 의견이 갈렸습니다. Todd의 접근방법이 궁금하고 이를 비교해보고 싶으신 분들은 다음 링크에 가서 확인해보시면 좋을 것 같습니다 [Todd's guidelines](https://github.com/toddmotto/angularjs-styleguide). + +제 스타일의 많은 부분은 [Ward Bell](http://twitter.com/wardbell) 과 함께했던 2인 1조의 개발 세션을 통해서 많이 가져왔습니다. 저의 친구 Ward는 이 가이드의 원초적인 전개에 많은 도움을 주었습니다. + +## See the Styles in a Sample App +예제 앱에 적용된 스타일을 참고하세요. +이 가이드가 무엇을, 왜, 어떻게 하는지 다 설명을 하겠지만, 실제로 적용된 것을 보는 게 더 도움이 될 거라고 봅니다. 이 가이드에 제시된 스타일과 양식을 따르는 예제 앱이 함께 제공되고 있습니다. 여기에 가시면 modular 라는 해당 [예제 앱을 modular](https://github.com/johnpapa/ng-demos) 라는 폴더 안에서 보실 수 있습니다. 가서 코드를 확인하시고, 복제하시고, 개입도 해보시기 바랍니다. [실행하는 방법은 readme 에 작성되어 있습니다.](https://github.com/johnpapa/ng-demos/tree/master/modular) + +##Translations +커뮤니티를 통해 유지보수가 되는 [Angular 스타일 가이드의 번역문](https://github.com/johnpapa/angular-styleguide/tree/master/i18n)들은 여기에서 보실 수 있습니다. + +## Table of Contents + + 1. [Single Responsibility](#single-responsibility) + 1. [IIFE](#iife) + 1. [Modules](#modules) + 1. [Controllers](#controllers) + 1. [Services](#services) + 1. [Factories](#factories) + 1. [Data Services](#data-services) + 1. [Directives](#directives) + 1. [Resolving Promises for a Controller](#resolving-promises-for-a-controller) + 1. [Manual Annotating for Dependency Injection](#manual-annotating-for-dependency-injection) + 1. [Minification and Annotation](#minification-and-annotation) + 1. [Exception Handling](#exception-handling) + 1. [Naming](#naming) + 1. [Application Structure LIFT Principle](#application-structure-lift-principle) + 1. [Application Structure](#application-structure) + 1. [Modularity](#modularity) + 1. [Startup Logic](#startup-logic) + 1. [Angular $ Wrapper Services](#angular--wrapper-services) + 1. [Testing](#testing) + 1. [Animations](#animations) + 1. [Comments](#comments) + 1. [JSHint](#js-hint) + 1. [JSCS](#jscs) + 1. [Constants](#constants) + 1. [File Templates and Snippets](#file-templates-and-snippets) + 1. [Yeoman Generator](#yeoman-generator) + 1. [Routing](#routing) + 1. [Task Automation](#task-automation) + 1. [Filters](#filters) + 1. [Angular Docs](#angular-docs) + 1. [Contributing](#contributing) + 1. [License](#license) + +## Single Responsibility + +### Rule of 1 +###### [Style [Y001](#style-y001)] + + - 각각의 파일에 컴포넌트를 저장하세요. + + 아래 예제는 'app' 모듈과 종속모듈을 정의하고 컨트롤러, 팩토리를 모두 한 파일에서 저장합니다. + + ```javascript + /* avoid */ + angular + .module('app', ['ngRoute']) + .controller('SomeController', SomeController) + .factory('someFactory', someFactory); + + function SomeController() { } + + function someFactory() { } + ``` + + 컴포넌트들은 각각의 파일에 따로 저장되었습니다. + + ```javascript + /* recommended */ + + // app.module.js + angular + .module('app', ['ngRoute']); + ``` + + ```javascript + /* recommended */ + + // some.controller.js + angular + .module('app') + .controller('SomeController', SomeController); + + function SomeController() { } + ``` + + ```javascript + /* recommended */ + + // someFactory.js + angular + .module('app') + .factory('someFactory', someFactory); + + function someFactory() { } + ``` + +**[Back to top](#table-of-contents)** + +## IIFE +### JavaScript Closures +###### [Style [Y010](#style-y010)] + + - Angular 컴포넌트들은 즉시 호출 함수구문을 이용해 감싸도록 합니다. (IIFE) + + *이유*: IIFE 방식은 글로벌 범위 변수들을 제거합니다. 이 방법을 통해서 글로벌 범위에서 변수와 함수 선언들이 예상 밖으로 오랫동안 유지되어 메모리를 잠식하는 것을 방지합니다. 또한 이 방법은 글로벌 변수들의 충돌도 막아줍니다. + + *이유*: 실 서버로 코드가 배포되기 전 코드는 최소화하고 묶어져서 하나의 파일로 만들어집니다. 이 때 변수의 충돌이나 너무 많은 글로벌 변수로 문제가 생길 수 있습니다. IIFE는 각각 파일마다 변수 범위를 제공하여 이를 막아줍니다. + + ```javascript + /* avoid */ + // logger.js + angular + .module('app') + .factory('logger', logger); + + // logger function is added as a global variable + function logger() { } + + // storage.js + angular + .module('app') + .factory('storage', storage); + + // storage function is added as a global variable + function storage() { } + ``` + + ```javascript + /** + * recommended + * + * no globals are left behind + */ + + // logger.js + (function() { + 'use strict'; + + angular + .module('app') + .factory('logger', logger); + + function logger() { } + })(); + + // storage.js + (function() { + 'use strict'; + + angular + .module('app') + .factory('storage', storage); + + function storage() { } + })(); + ``` + + - 주의: 나머지의 예제들에서는 코드를 간결하게 하기 위해서 IIFE를 사용하지 않을 것입니다. 실제 사용할 때는 IIFE로 하세요. + + - 주의: IIFE는 유닛 테스트를 위한 테스트 코드들이 Angular 표현이나 헬퍼 함수들 같은 프라이빗 함수나 변수들에 접근을 못하게 할 수도 있습니다. 하지만 이런 경우 퍼블릭 함수나 변수를 통해서 접근하거나 이 프라이빗 속성들을 노출함으로 테스트를 진행할 수 있습니다. 예를 들어 factory 나 불변 상수에 헬퍼 함수나 레귤러 익스프레션 또는 불변 상수를 옮김으로 가능합니다. + +**[Back to top](#table-of-contents)** + +## Modules + +### Avoid Naming Collisions +###### [Style [Y020](#style-y020)] + + - 하위 모듈을 위해 구분자와 함께 유일한 이름을 지정하세요. + + *이유*: 유일, 독특한 이름들은 모듈이름이 충돌하는 것을 방지합니다. 구분자는 그 모듈과 하위모듈 구조를 정의하는데 도움이 됩니다. 예를 들어 'app' 은 당신의 루트 모듈이라면 'app.dashboard'와 'app.users'는 'app' 모듈이 사용하는 하위모듈의 이름으로 지정할 수 있습니다. + +### Definitions (aka Setters) +###### [Style [Y021](#style-y021)] + + - 세터 구문을 사용하여 반환된 모듈을 변수에 저장하지마세요. + + *이유*: 1 파일에 1 컴포넌트를 넣을 경우, 변수에 넣어서 그 변수를 재사용하는 일은 없다고 봐야합니다. + + ```javascript + /* avoid */ + var app = angular.module('app', [ + 'ngAnimate', + 'ngRoute', + 'app.shared', + 'app.dashboard' + ]); + ``` + + 대신 간단한 세터 구무을 사용하고, 체인으로 나머지 부분을 처리하세요. + + ```javascript + /* recommended */ + angular + .module('app', [ + 'ngAnimate', + 'ngRoute', + 'app.shared', + 'app.dashboard' + ]); + ``` + +### Getters +###### [Style [Y022](#style-y022)] + + - 모듈을 이용할 때, 변수에 할당하는 것을 피하고 그 대신 게터 구문을 이용한 체이닝을 사용하세요. + + *이유*: 이렇게 해야 더 이해하기 쉽고 변수 충돌이나 누출을 방지할 수 있습니다. + + ```javascript + /* avoid */ + var app = angular.module('app'); + app.controller('SomeController', SomeController); + + function SomeController() { } + ``` + + ```javascript + /* recommended */ + angular + .module('app') + .controller('SomeController', SomeController); + + function SomeController() { } + ``` + +### Setting vs Getting +###### [Style [Y023](#style-y023)] + + - 한 번만 set 하고 나머지 인스턴스를 위해서는 get을 사용하세요. + + *이유*: 모듈은 한 번만 만들어지고 설정되어야 하고, 그 후로는 그 모듈을 받아와서 사용해야 합니다. + + ```javascript + /* recommended */ + + // to set a module + angular.module('app', []); + + // to get a module + angular.module('app'); + ``` + +### Named vs Anonymous Functions +###### [Style [Y024](#style-y024)] + + - 콜백 함수를 넘길 때 변수로 할당된 함수를 사용하고 익명 함수 사용을 피하세요. + + *이유*: 이렇게 하면 이해하기 좋은 코드가 작성되어 고치기 훨씬 쉽습니다. 그리고 네스티드 콜백 양을 줄일 수 있습니다. + + ```javascript + /* avoid */ + angular + .module('app') + .controller('DashboardController', function() { }) + .factory('logger', function() { }); + ``` + + ```javascript + /* recommended */ + + // dashboard.js + angular + .module('app') + .controller('DashboardController', DashboardController); + + function DashboardController() { } + ``` + + ```javascript + // logger.js + angular + .module('app') + .factory('logger', logger); + + function logger() { } + ``` + +**[Back to top](#table-of-contents)** + +## Controllers + +### controllerAs View Syntax +###### [Style [Y030](#style-y030)] + + - '전형적인 $scope 을 사용한 콘트롤러' 대신 [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) 구문을 사용하세요. + + + *이유*: 컨트롤러는 새로이 만들어진 하나의 객채 생성하여 리턴합니다. 그리고 `controllerAs` 구문은 `전형적인 $scope 구문` 보다 더 자바스크립트의 생성자와 흡사하게 작동합니다. + + + *이유*: 이는 뷰에서 해당 변수를 사용할 때 점 접근자를 이용한 속성에 대한 바인딩의 사용을 조장하게 됩니다.(e.g. `name` 대신 `customer.name` ) 이는 후에 점 접근자를 사용하지 않아서 발생할 수 있는 참조 오류를 피할 수 있게 해주며 문맥상으로 훨씬 이해가 쉬운 코드를 작성하게 도와줍니다. + + + *이유*: 이는 네스티드 컨트롤러의 뷰에서 `$parent` 호출의 사용을 피할 수 있게 해줍니다. + + ```html + +
+ {{ name }} +
+ ``` + + ```html + +
+ {{ customer.name }} +
+ ``` + +### controllerAs Controller Syntax +###### [Style [Y031](#style-y031)] + + - `전형적인 컨트롤러 `$scope` 구문 대신 `controllerAs` 구문을 사용하세요. + + - `controllerAs` 구문은 `$scope` 에 바인딩 하기위해 컨트롤러 안에서 `this`를 사용합니다. + + *이유*: `controllerAs`는 `$scope` 보다 통어적인 장점입니다. 이를 이용하더라도 계속해서 view 에 바인드 할 수 있고 `$scoep`에도 접근이 가능합니다.. + + *이유*: 이는 컨트롤러 안에서 더 나은 방법을 사용하거나, factory에 메서드를 옮기고 컨트롤러에서 factory를 참조하는 것이 더 나은 방법임에도 불구하고 `$scope` 메서드를 사용하게되는 유혹을 피할 수 있도록 도와줍니다. 컨트롤러 내에서 `$scope`을 사용하는 것은 꼭 필요할 때만 하도록 하세요. 예를 들어 퍼블리싱과 섭스크라이빙 이벤트는 [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), 또는 [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on)를 사용하세요. + + ```javascript + /* avoid */ + function CustomerController($scope) { + $scope.name = {}; + $scope.sendMessage = function() { }; + } + ``` + + ```javascript + /* recommended - but see next section */ + function CustomerController() { + this.name = {}; + this.sendMessage = function() { }; + } + ``` + +### controllerAs with vm +###### [Style [Y032](#style-y032)] + + - `controllerAs` 구문을 사용할 때는 `this`를 이용해 capture 변수를 사용하세요. + + *이유*: `this` 예약어는 구문 변수로 컨트롤러 안의 함수에서 사용될 때 그 값이 변경될 수 있기 때문입니다. `this`를 다른 변수에 캡쳐한 후 사용하면 이 문제를 피할 수 있습니다. + + + ```javascript + /* avoid */ + function CustomerController() { + this.name = {}; + this.sendMessage = function() { }; + } + ``` + + ```javascript + /* recommended */ + function CustomerController() { + var vm = this; + vm.name = {}; + vm.sendMessage = function() { }; + } + ``` + + 주의: 특정 코드의 바로 윗줄에 특정 코맨트를 추가함으로서 [jshint](http://www.jshint.com/) 경고를 무시하게 할 수 있습니다. 하지만 함수의 이름이 대문자일 경우에는 불필요합니다. 이 경우 함수는 생성자로 여겨지고 그 자체가 Angular에서 컨트롤러이기 때문입니다. + + + ```javascript + /* jshint validthis: true */ + var vm = this; + ``` + + 주의: `controller as`를 이용하여 watch를 만들 때는 아래의 구문을 이용하여 `vm.*` 에 대한 watch를 할 수 있습니다. (watch는 추가적인 실행 사이클을 필요로 할 수 있기 때문에 사용할 때는 주의를 기울이세요.) + + ```html + + ``` + + ```javascript + function SomeController($scope, $log) { + var vm = this; + vm.title = 'Some Title'; + + $scope.$watch('vm.title', function(current, original) { + $log.info('vm.title was %s', original); + $log.info('vm.title is now %s', current); + }); + } + ``` + + 주의: 대량의 코드에 대한 작업을 할 경우에, 인식과 검색에 대한 오버해드를 줄이는데 도움을 줄 수 있도록 서술적인 이름을 사용하도록 하세요. 그렇다고 부담될 정도의 긴 이름은 피해주세요. + + ```html + + + ``` + + ```html + + + ``` + +### Bindable Members Up Top +###### [Style [Y033](#style-y033)] + + - 바인딩이 가능한 맴버들을 가장 위쪽으로 올리세요. 알파벳 순서로 정렬하세요. 코드 전체에 선언 부분을 섞어서 정렬하지 마세요. + + *이유*: 바인딩할 맴버를 위쪽에 올려두면 뷰에서 어떤 맴버를 사용하는지 즉각적으로 구분하는데 도움이 되고 코드 읽기가 쉬워집니다. + + *이유*: 인라인 익명함수의 세팅이 쉬워집니다. 하지만 이런 함수들의 코드 길이가 1줄을 넘어가면 읽기가 어려워 집니다. 함수 선언은 바인딩 맴버들 아래쪽에 하세요. (함수들은 끌어올려질 거에요) 함수 정의는 아래쪽에 하세요. 바인더블 맴버들을 위쪽에 두세요. 그러면 코드 읽기가 쉬워집니다. + + ```javascript + /* avoid */ + function SessionsController() { + var vm = this; + + vm.gotoSession = function() { + /* ... */ + }; + vm.refresh = function() { + /* ... */ + }; + vm.search = function() { + /* ... */ + }; + vm.sessions = []; + vm.title = 'Sessions'; + } + ``` + + ```javascript + /* recommended */ + function SessionsController() { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = refresh; + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + + //////////// + + function gotoSession() { + /* */ + } + + function refresh() { + /* */ + } + + function search() { + /* */ + } + } + ``` + + !["Above the Fold"를 사용하는 컨트롤러](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) + + 주의: 만약 코드 가독성에 영향을 주지않고 1줄이라면 그냥 위쪽에 두어도 됩니다. + + ```javascript + /* avoid */ + function SessionsController(data) { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = function() { + /** + * lines + * of + * code + * affects + * readability + */ + }; + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + } + ``` + + ```javascript + /* recommended */ + function SessionsController(sessionDataService) { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = sessionDataService.refresh; // 1 liner is OK + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + } + ``` + +### Function Declarations to Hide Implementation Details +###### [Style [Y034](#style-y034)] + + - 함수 선언문을 사용하여 구체적인 구현내용을 숨기세요. 바인딩 맴버들은 위쪽에 두세요. 컨트롤러 안에서 함수를 바인딩 하려면 파일 아래쪽에 위치한 함수 선언문을 참조하도록 하세요. 이렇게 하면 바인더블 섹션에 직접적으로 묶여지게 됩니다. 좀 더 구체적인 정보는 여기를 참고하세요 [this post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). + + *이유*: 바인딩할 맴버를 위쪽에 올려두면 뷰에서 어떤 맴버를 사용하는지 즉각적으로 구분하는데 도움이 되고 코드 읽기가 쉬워집니다. + + *이유*: 구체적인 함수 내용을 파일 아래쪽에 옮겨두면 뷰의 복잡성을 줄여줍니다. 그래서 중요한 내용을 상단에서 볼 수 있습니다. + + 왜: 함수 선언은 인터프리터에 의해서 나중에 위쪽으로 올려집니다. 그래서 아래서 선언된 함수를 위쪽에서 참조하는 것은 문제가 없습니다. (함수 선언문을 사용하는 것과 마찬가지 효과 입니다.) + + 왜: 함수 선언문을 사용할 경우 함수의 참조 순서에 의해서 코드가 실행중단 되는 것을 걱정하지 않아도 됩니다. 함수 표현의 경우 `var a`에서 `var b`를 참조할 경우 코드는 런타임 오류로 멈추게 된다. + + *이유*: 함수 표현에서는 순서가 아주 중요합니다. + + ```javascript + /** + * avoid + * Using function expressions. + */ + function AvengersController(avengersService, logger) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + var activate = function() { + return getAvengers().then(function() { + logger.info('Activated Avengers View'); + }); + } + + var getAvengers = function() { + return avengersService.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + + vm.getAvengers = getAvengers; + + activate(); + } + ``` + + 다음 예제에는 중요한 것들이 두루 포함되어 있습니다. 아래의 예제에서, 중요한 것들은 위쪽에 두었습니다. `vm.avengers` 나 `vm.title` 같은 컨트롤러 바인딩 맴버들을 의미합니다. 구체적인 구현은 아래쪽에 두었습니다. 이렇게 하면 코드 읽기가 쉬워집니다. + + ```javascript + /* + * recommend + * Using function declarations + * and bindable members up top. + */ + function AvengersController(avengersService, logger) { + var vm = this; + vm.avengers = []; + vm.getAvengers = getAvengers; + vm.title = 'Avengers'; + + activate(); + + function activate() { + return getAvengers().then(function() { + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + return avengersService.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Defer Controller Logic to Services +###### [Style [Y035](#style-y035)] + + - 컨트롤러에서 service 와 factory 로 로직을 넘겨서 처리하도록 하세요. + + *이유*: 함수로 노출하는 서비스에 로직을 넣을 경우 다양한 컨트롤러에서 참조하여 재활용이 가능하기 때문입니다. + + *이유*: 서비스에 넣어진 로직은 유닛 테스트용으로 분리가 쉽게 됩니다. 컨트롤러 안에서 로직을 호출하는 것도 쉽게 흉내낼 수 있습니다. + + *이유*: 디펜던시를 없애고 구체적 구현을 컨트롤러로 부터 감출 수 있습니다. + + *이유*: 컨트롤러를 슬림하고 간결하고 포커스 되어있도록 유지할 수 있습니다. + + ```javascript + + /* avoid */ + function OrderController($http, $q, config, userInfo) { + var vm = this; + vm.checkCredit = checkCredit; + vm.isCreditOk; + vm.total = 0; + + function checkCredit() { + var settings = {}; + // Get the credit service base URL from config + // Set credit service required headers + // Prepare URL query string or data object with request data + // Add user-identifying info so service gets the right credit limit for this user. + // Use JSONP for this browser if it doesn't support CORS + return $http.get(settings) + .then(function(data) { + // Unpack JSON data in the response object + // to find maxRemainingAmount + vm.isCreditOk = vm.total <= maxRemainingAmount + }) + .catch(function(error) { + // Interpret error + // Cope w/ timeout? retry? try alternate service? + // Re-reject with appropriate error for a user to see + }); + }; + } + ``` + + ```javascript + /* recommended */ + function OrderController(creditService) { + var vm = this; + vm.checkCredit = checkCredit; + vm.isCreditOk; + vm.total = 0; + + function checkCredit() { + return creditService.isOrderTotalOk(vm.total) + .then(function(isOk) { vm.isCreditOk = isOk; }) + .catch(showError); + }; + } + ``` + +### Keep Controllers Focused +###### [Style [Y037](#style-y037)] + + - 한 뷰를 위해 한 컨트롤러를 정의하세요. 다른 뷰를 위해 컨트롤러를 재사용하지마세요. 대신 재사용 가능한 로직을 팩토리에 넣고 컨트롤러를 간단하고 뷰에 포커스 되도록 유지하세요. + + *이유*: 한 컨트롤러를 여러 뷰에서 사용하는 것은 불안정합니다. 좋은 end-to-end (e2w) 테스트 시험 범위는 큰 어플리케이션 전반적인 안정성을 요구합니다. + +### Assigning Controllers +###### [Style [Y038](#style-y038)] + + - 한 컨트롤러가 한 뷰와 쌍을 이루어야 할 때 그리고 컴포넌트가 다른 컨트롤러나 뷰에서 재사용 되어야 할 때, 라우트에서 컨트롤러를 정의하세요. + + 주의: 만약 뷰가 라우트가 아닌 다른 방법으로 로딩 되었을 때는 `ng-controller="Avengers as vm" 구문을 사용하세요. + + *이유*: 라우트에서 컨트롤러를 엮는 방법은 컨트롤러와 뷰를 엮을 때 다른 라우트를 사용할 수 있도록 합니다. 컨트롤러의 할당이 [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController) 이 태그를 이용한 것이라면, 그 뷰는 항상 동일한 컨트롤러와 작동하게 됩니다. + + ```javascript + /* avoid - when using with a route and dynamic pairing is desired */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html' + }); + } + ``` + + ```html + +
+
+ ``` + + ```javascript + /* recommended */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'Avengers', + controllerAs: 'vm' + }); + } + ``` + + ```html + +
+
+ ``` + +**[Back to top](#table-of-contents)** + +## Services + +### Singletons +###### [Style [Y040](#style-y040)] + + - 서비스는 `new` 키워드를 통해 인스턴스화 됩니다. `this`를 사용하여 public 메소드와 변수에 접근합니다. 이는 팩토리와 흡사하기 때문에 일관성 있게 팩토리를 사용하도록 하세요. + + 주의: [모든 Angular 서비스는 싱글톤](https://docs.angularjs.org/guide/services). 인젝터 당 주어진 서비스의 인스턴스는 하나만 존재한다는 뜻입니다. + + ```javascript + // service + angular + .module('app') + .service('logger', logger); + + function logger() { + this.logError = function(msg) { + /* */ + }; + } + ``` + + ```javascript + // factory + angular + .module('app') + .factory('logger', logger); + + function logger() { + return { + logError: function(msg) { + /* */ + } + }; + } + ``` + +**[Back to top](#table-of-contents)** + +## Factories + +### Single Responsibility +###### [Style [Y050](#style-y050)] + + - 팩토리는 캡슐화 되어 단 [하나의 책임](http://en.wikipedia.org/wiki/Single_responsibility_principle)만 가져야 합니다. 팩토리가 단일 목적을 넘어 사용되게 된다면 다른 팩토리를 만들어야 합니다. + +### Singletons +###### [Style [Y051](#style-y051)] + + - 팩토리는 싱글톤이고 서비스의 맴버들을 가진 오브젝트를 리턴합니다. + + 주의: [모든 Angular 서비스들은 싱글톤](https://docs.angularjs.org/guide/services). + +### Accessible Members Up Top +###### [Style [Y052](#style-y052)] + + - 노출하고 싶은 호출 가능한 맴버(인터페이스)들은 서비스의 위쪽에 위치시키세요. 여기 링크에서 제시하는 방식을 사용하세요. [모듈 패턴 파해치기](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). + + *이유*: 호출 가능한 맴버들을 상단에 배치하는 것은 가독성을 높여주고 어떤 맴버들이 외부로 노출되고 호출 될 수 있는지 그리고 단위 테스트를 해야하는지 순식간에 파악가능하도록 해줍니다. + + *이유*: 특히 파일이 길어져서 노출 함수나 변수를 알아차리기 위해 스크롤 해야하는 것을 방지하게 됩니다. + + *이유*: 코드 작성을 진행하면서 바로 함수를 세팅하는게 쉬울 수 있습니다. 하지만 그 함수들이 한줄 이상이 되면 가독성이 떨어지고 스크롤을 많이 사용해야 합니다. 리턴되는 서비스에 호출 가능한 인터페이스를 정의하는 것은 구현 부분을 아래로 배치되도록 합니다. 위쪽에 호출 가능한 인터페이스를 배치하는 것은 읽기 쉬운 코드를 만들어 줍니다. + + ```javascript + /* avoid */ + function dataService() { + var someValue = ''; + function save() { + /* */ + }; + function validate() { + /* */ + }; + + return { + save: save, + someValue: someValue, + validate: validate + }; + } + ``` + + ```javascript + /* recommended */ + function dataService() { + var someValue = ''; + var service = { + save: save, + someValue: someValue, + validate: validate + }; + return service; + + //////////// + + function save() { + /* */ + }; + + function validate() { + /* */ + }; + } + ``` + + 호스트 오브젝트 내에서 이런 식의 바인딩이 반영이 되어서, 원시 값들은 모듈 패턴 노출 방식으로 업데이트 되지 않습니다. + + ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-2.png) + +### Function Declarations to Hide Implementation Details +###### [Style [Y053](#style-y053)] + + - 함수 정의를 사용하여 구체적 구현내용을 숨기세요. 노출시키고 싶은 맴버들을 상단에 배치하세요. 상단의 맴버에서 아래쪽에 정의된 함수들을 할당하세요. 더 자세한 정보는 여기를 보세요. [this post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). + + *이유*: 상단에 접근 가능한 맴버들을 배치함으로서 읽기 쉬운 코드를 만들어주고 외부에서 접근 가능한 팩토리 함수를 즉각적으로 알아볼 수 있도록 도와줍니다. + + *이유*: 구체적 함수 정의 부분을 파일의 아래쪽에 배치함으로 뷰의 복잡성을 이동시키고 중요한 것들을 상단에서 바로 볼 수 있습니다. + + *이유*: 함수 정의 부분은 아래쪽에 두더라도 인터프리터에 의해서 상단으로 끌어올려집니다. 그래서 정의되기 전에 호출 또는 참조되어도 문제가 발생하지 않아요. (함수 표현으로 할 경우 문제가 될 수 있죠.) + + *이유*: 함수 정의로 할 경우 순서에 상관이 없기 때문에 아무 위치에서 호출해도 걱정할 필요가 없습니다. + + *이유*: 함수 표현으로 할 경우 순서에 의해서 코드가 깨질 수 있기 때문에 중요합니다. + + ```javascript + /** + * avoid + * Using function expressions + */ + function dataservice($http, $location, $q, exception, logger) { + var isPrimed = false; + var primePromise; + + var getAvengers = function() { + // implementation details go here + }; + + var getAvengerCount = function() { + // implementation details go here + }; + + var getAvengersCast = function() { + // implementation details go here + }; + + var prime = function() { + // implementation details go here + }; + + var ready = function(nextPromises) { + // implementation details go here + }; + + var service = { + getAvengersCast: getAvengersCast, + getAvengerCount: getAvengerCount, + getAvengers: getAvengers, + ready: ready + }; + + return service; + } + ``` + + ```javascript + /** + * recommended + * Using function declarations + * and accessible members up top. + */ + function dataservice($http, $location, $q, exception, logger) { + var isPrimed = false; + var primePromise; + + var service = { + getAvengersCast: getAvengersCast, + getAvengerCount: getAvengerCount, + getAvengers: getAvengers, + ready: ready + }; + + return service; + + //////////// + + function getAvengers() { + // implementation details go here + } + + function getAvengerCount() { + // implementation details go here + } + + function getAvengersCast() { + // implementation details go here + } + + function prime() { + // implementation details go here + } + + function ready(nextPromises) { + // implementation details go here + } + } + ``` + +**[Back to top](#table-of-contents)** + +## Data Services + +### Separate Data Calls +###### [Style [Y060](#style-y060)] + + - 데이터 통신 부분은 팩토리에게 부여하세요. 데이터 서비스들이 XHR 호출, 로컬 저장소, 메모리 저장 등 모든 데이터 조작을 책임지도록 하세요. + + *이유*: 컨트롤러의 책무는 정보를 모아서 뷰를 통해 화면을 그리는 것입니다. 컨트롤러가 데이터를 가져오는 과정에 개입하는 것은 좋지 않아요. 다만 어느 구성요소에게 요구해야 할지만 알면 됩니다. 데이터 서비스로 데이터를 어떻게 가져올지 분리하고 컨트롤러는 간단하게 하고 뷰에 더욱 집중하도록 하세요. + + *이유*: 데이터 서비스를 사용하는 컨트롤러를 테스트할 때 이렇게 하면 (mock or real) 테스트가 모두 쉬워집니다. + + *이유*: 데이터 서비스 구현 부분은 아주 구체적, 특정적인 코드일 가능성일 수 있습니다. 이는 헤더, 통신 방식 또는 `$http` 같은 다른 서비스를 이용할 수도 있습니다. 컨트롤러 같은 실제 사용자들에게서 이를 분리하는 것은 구현 방식을 나중에 변경할 때에도 좋습니다. + + ```javascript + /* recommended */ + + // dataservice factory + angular + .module('app.core') + .factory('dataservice', dataservice); + + dataservice.$inject = ['$http', 'logger']; + + function dataservice($http, logger) { + return { + getAvengers: getAvengers + }; + + function getAvengers() { + return $http.get('/api/maa') + .then(getAvengersComplete) + .catch(getAvengersFailed); + + function getAvengersComplete(response) { + return response.data.results; + } + + function getAvengersFailed(error) { + logger.error('XHR Failed for getAvengers.' + error.data); + } + } + } + ``` + + 중요: 아래처럼, 컨트롤러 같은, 호출 부에서 데이터 서비스를 호출하여 사용하는 것은 구현 코드를 감추어 줍니다. + + ```javascript + /* recommended */ + + // controller calling the dataservice factory + angular + .module('app.avengers') + .controller('AvengersController', AvengersController); + + AvengersController.$inject = ['dataservice', 'logger']; + + function AvengersController(dataservice, logger) { + var vm = this; + vm.avengers = []; + + activate(); + + function activate() { + return getAvengers().then(function() { + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + return dataservice.getAvengers() + .then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Return a Promise from Data Calls +###### [Style [Y061](#style-y061)] + + - `$http` 처럼 프라미스를 리턴하는 데이터 서비스를 호출 할 때는, 호출 받는 함수에서 프라미스를 리턴하세요. + + *이유*: 프라미스를 되받게 되면 프라미스에 체인 호출을 하여 계속해서 데이터 호출 완료 후의 흐름을 작성할 수 있습니다. + + ```javascript + /* recommended */ + + activate(); + + function activate() { + /** + * Step 1 + * Ask the getAvengers function for the + * avenger data and wait for the promise + */ + return getAvengers().then(function() { + /** + * Step 4 + * Perform an action on resolve of final promise + */ + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + /** + * Step 2 + * Ask the data service for the data and wait + * for the promise + */ + return dataservice.getAvengers() + .then(function(data) { + /** + * Step 3 + * set the data and resolve the promise + */ + vm.avengers = data; + return vm.avengers; + }); + } + ``` + +**[Back to top](#table-of-contents)** + +## Directives +### Limit 1 Per File +###### [Style [Y070](#style-y070)] + + - 파일 하나당 하나의 디렉티브만 정의하세요. 파일 이름을 디렉티브 이름으로 저장하세요. + + *이유*: 하나의 파일에 모든 디렉티브 정의를 넣으면 쉬울 수 있습니다. 하지만 앱 전체, 특정 모듈들 혹은 한 모듈에서 재사용되는 부분만 꺼내어 활용하는 것은 너무 어렵게 됩니다. + + *이유*: 하나의 파일에 하나의 디렉티브를 넣으면 관리가 쉬워집니다. + + > 주의: "**베스트 프랙티스**: 디렉티브들은 자기 일이 끝난 후에 청소를 해야 합니다. `element.on('$destroy', ...)` 또는 `scope.$on('$destroy', ...)`를 사용해서 디렉티브가 제거되었을 경우에 청소할 수 있습니다." ... Angular 문서에서 발췌 + + ```javascript + /* avoid */ + /* directives.js */ + + angular + .module('app.widgets') + + /* order directive that is specific to the order module */ + .directive('orderCalendarRange', orderCalendarRange) + + /* sales directive that can be used anywhere across the sales app */ + .directive('salesCustomerInfo', salesCustomerInfo) + + /* spinner directive that can be used anywhere across apps */ + .directive('sharedSpinner', sharedSpinner); + + function orderCalendarRange() { + /* implementation details */ + } + + function salesCustomerInfo() { + /* implementation details */ + } + + function sharedSpinner() { + /* implementation details */ + } + ``` + + ```javascript + /* recommended */ + /* calendar-range.directive.js */ + + /** + * @desc order directive that is specific to the order module at a company named Acme + * @example
+ */ + angular + .module('sales.order') + .directive('acmeOrderCalendarRange', orderCalendarRange); + + function orderCalendarRange() { + /* implementation details */ + } + ``` + + ```javascript + /* recommended */ + /* customer-info.directive.js */ + + /** + * @desc sales directive that can be used anywhere across the sales app at a company named Acme + * @example
+ */ + angular + .module('sales.widgets') + .directive('acmeSalesCustomerInfo', salesCustomerInfo); + + function salesCustomerInfo() { + /* implementation details */ + } + ``` + + ```javascript + /* recommended */ + /* spinner.directive.js */ + + /** + * @desc spinner directive that can be used anywhere across apps at a company named Acme + * @example
+ */ + angular + .module('shared.widgets') + .directive('acmeSharedSpinner', sharedSpinner); + + function sharedSpinner() { + /* implementation details */ + } + ``` + + 주의: 디렉티브는 다양한 범위에서 사용이 가능하기 때문에, 이름을 지을 때 선택지가 너무 많습니다. 디렉티브와 디렉티브 파일명이 명확하고 선명한 것을 고르세요. 아래 몇몇 예제를 보시고 더욱 다양한 추천방식은 여기를 참고하세요 [Naming](#naming). + +### Manipulate DOM in a Directive +###### [Style [Y072](#style-y072)] + + - DOM을 직접 다루게 되었다면, 디렉티브를 사용하세요. 만약 CSS나 [animation services](https://docs.angularjs.org/api/ngAnimate), Angular templating, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) or [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide) 등의 방식을 사용할 수 있다면 이를 사용하세요. 예를 들, 만약 디렉티브의 기능이 간단히 보여줌, 숨김 기능만 있다면 ngHide/ngShow를 사용하세요. + + *이유*: DOM을 다루는 코드는 테스트, 수정이 어렵고 대부분 더 나은 구현 방법이 존재합니다. (e.g. CSS, 애니매이션, 템플릿) + +### Provide a Unique Directive Prefix +###### [Style [Y073](#style-y073)] + + - `acmeSalesCustomerInfo` 가 HTML에서 `acme-sales-customer-info`로 정의 되는 것 처럼 유일하고 짧고 설명적인 접두어를 사용하세요. + + *이유*: 유일하고 짧은 접두어는 디렉티브의 문맥과 출신을 파악하게 해줍니다. 예를 들어 `cc-`는 아마 CodeCamper 앱을 지칭하려고 사용되었을 수 있습니다. 또한 `acme-`는 Acme company에서 사용된 디렉티브를 지칭할 수 있습니다. + + 주의: `ng-`는 Angular 디렉티브로 예약되어 있기 때문에 사용하지 마세요. 넓게 검색한 후에 충돌이 없는지 확인하고 사용하세요. `ion-` 같은 경우 [Ionic Framework](http://ionicframework.com/) 프로젝트의 접두어로 사용되고 있으니까요. + +### Restrict to Elements and Attributes +###### [Style [Y074](#style-y074)] + + - 혼자서 사용될 수 있다고 판단되는 디렉티브를 만들 때는 restrict `E` (custom element)를 사용하세요. 어트리뷰트를 만들려먼 restrict `A` (custom attribute)를 사용하세요. 대부분, 자신이 컨트롤 하려면 `E`가 적당합니다. 일반적 가이드라인은 `EA` 이지만 홀로 사용되는 엘레멘트 이거나 이미 존재하는 DOM 엘레멘트를 향상시키기 위해서 속성을 변경하는 경우에는 따로 나누어 구현하세요. + + *이유*: 그게 적합하기 때문입니다. + + *이유*: 우리가 디렉티브가 클래스로 사용되도록 할 수도 있지만, 만약 디렉티브가 진짜 엘레멘트나 어트리뷰트 처럼 작동하고 그럴 때 더 적합하다고 생각된다면 말입니다. + + 주의: Angular 1.3+에서 기본값은 EA입니다. + + ```html + +
+ ``` + + ```javascript + /* avoid */ + angular + .module('app.widgets') + .directive('myCalendarRange', myCalendarRange); + + function myCalendarRange() { + var directive = { + link: link, + templateUrl: '/template/is/located/here.html', + restrict: 'C' + }; + return directive; + + function link(scope, element, attrs) { + /* */ + } + } + ``` + + ```html + + +
+ ``` + + ```javascript + /* recommended */ + angular + .module('app.widgets') + .directive('myCalendarRange', myCalendarRange); + + function myCalendarRange() { + var directive = { + link: link, + templateUrl: '/template/is/located/here.html', + restrict: 'EA' + }; + return directive; + + function link(scope, element, attrs) { + /* */ + } + } + ``` + +### Directives and ControllerAs +###### [Style [Y075](#style-y075)] + + - 일관되게 컨트롤러와 뷰를 연결시킬 때 `controller as` 구문을 사용하세요. + + *이유*: 어렵지도 않고 그게 적합하기 때문이죠. + + 주의: 아래 있는 디렉티브는 controllerAs를 사용해서 링크나 디렉티브 컨트롤러 안에서 스콥을 사용하는 방법을 명시하고 있습니다. 한 곳에서 다 보여주기 위해서 인라인 템플릿을 사용했습니다. + + 주의: 종속 인젝션을 고려할 때는 여기를 참고하세요. [수동 종속 명시](#manual-annotating-for-dependency-injection). + + 주의: 디랙티브의 컨트롤러는 디렉티브 클로져의 외부에 존재한다는 것을 기억하세요. 이 방식은 `return` 이후에 injection이 만들어지고 접근이 불가하게 되는 문제를 막아줍니다. + + ```html +
+ ``` + + ```javascript + angular + .module('app') + .directive('myExample', myExample); + + function myExample() { + var directive = { + restrict: 'EA', + templateUrl: 'app/feature/example.directive.html', + scope: { + max: '=' + }, + link: linkFunc, + controller: ExampleController, + // note: This would be 'ExampleController' (the exported controller name, as string) + // if referring to a defined controller in its separate file. + controllerAs: 'vm', + bindToController: true // because the scope is isolated + }; + + return directive; + + function linkFunc(scope, el, attr, ctrl) { + console.log('LINK: scope.min = %s *** should be undefined', scope.min); + console.log('LINK: scope.max = %s *** should be undefined', scope.max); + console.log('LINK: scope.vm.min = %s', scope.vm.min); + console.log('LINK: scope.vm.max = %s', scope.vm.max); + } + } + + ExampleController.$inject = ['$scope']; + + function ExampleController($scope) { + // Injecting $scope just for comparison + var vm = this; + + vm.min = 3; + + console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); + console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); + console.log('CTRL: vm.min = %s', vm.min); + console.log('CTRL: vm.max = %s', vm.max); + } + ``` + + ```html + +
hello world
+
max={{vm.max}}
+
min={{vm.min}}
+ ``` + + 주의: 링크 함수로 컨트롤러를 인젝트 할 때 컨트롤러의 이름을 정할 수 있습니다. 그리고 그 컨트롤러의 프로퍼티 형식으로 디렉티브 어트리뷰트에 접근이 가능합니다. + + ```javascript + // Alternative to above example + function linkFunc(scope, el, attr, vm) { + console.log('LINK: scope.min = %s *** should be undefined', scope.min); + console.log('LINK: scope.max = %s *** should be undefined', scope.max); + console.log('LINK: vm.min = %s', vm.min); + console.log('LINK: vm.max = %s', vm.max); + } + ``` + +###### [Style [Y076](#style-y076)] + + - 디렉티브에서 `controller as`를 사용할 때 `bindToController = true`를 사용하면 컨트롤러의 스콥을 외부 스콥에 바인딩할 수 있습니다. + + *이유*: 외부 스콥을 쉽게 디렉티브의 컨트롤러 스콥에 바인딩할 수 있습니다. + + 주의: `bindToController`는 Angular 1.3.0 부터 사용이 가능합니다. + + ```html +
+ ``` + + ```javascript + angular + .module('app') + .directive('myExample', myExample); + + function myExample() { + var directive = { + restrict: 'EA', + templateUrl: 'app/feature/example.directive.html', + scope: { + max: '=' + }, + controller: ExampleController, + controllerAs: 'vm', + bindToController: true + }; + + return directive; + } + + function ExampleController() { + var vm = this; + vm.min = 3; + console.log('CTRL: vm.min = %s', vm.min); + console.log('CTRL: vm.max = %s', vm.max); + } + ``` + + ```html + +
hello world
+
max={{vm.max}}
+
min={{vm.min}}
+ ``` + +**[Back to top](#table-of-contents)** + +## Resolving Promises for a Controller +### Controller Activation Promises +###### [Style [Y080](#style-y080)] + + - 컨트롤러의 시작-준비 로직을 `activate` 함수 안에 넣으세요. + + *이유*: 시작-준비 로직을 컨트롤러 안의 일관된 부분에 위치 시키세요. 이는 일관성 있는 테스트를 하게 해주고, 시작-준비 코드가 군데군데 퍼지는 것을 막아줍니다. + + *이유*: `activate` 함수를 만들어두면 컨트롤러/뷰를 새로고침하는 곳에서 재사용하기가 편리합니다. 그리고 필요한 코드들이 함께 있도록 해주고, 사용자가 뷰에 빠르게 도달하도록 해줍니다. `ng-view` 나 `ui-view`에서 애니매이션을 만들기가 쉬워지고 사용자들이 인터페이스가 즉각적이라고 느껴지게 해줍니다. + + 주의: 만약 컨트롤러를 사용하기 전 시점에서 라우팅을 조건적으로 취소해야 한다면 [route resolve](#style-y081)를 대신 사용하세요. + + ```javascript + /* avoid */ + function AvengersController(dataservice) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + dataservice.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + ``` + + ```javascript + /* recommended */ + function AvengersController(dataservice) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + activate(); + + //////////// + + function activate() { + return dataservice.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Route Resolve Promises +###### [Style [Y081](#style-y081)] + + - 컨트롤러가 활성화되기 전에 프라미스 값이 구해져야 한다면, 컨트롤러 로직이 실행되기 전에 `$routeProvider`에서 종속 값들을 다 구하도록 하세요. 만약 컨트롤러가 활성화되기 전에 라우트를 조건적으로 중단해야 한다면, 라우트 resolver를 이용하세요. + + + - 화면의 변화가 일어나기 전에 라우틀를 취소해야 할지 말지 결정해야 한다면 라우트 resolve를 이용하세요. + + *이유*: 컨트롤러가 로드되기 전에 데이터를 필요로 할 수도 있기 때문입니다. 그 데이터는 특정 팩토리나 [$http](https://docs.angularjs.org/api/ng/service/$http)를 통해서 프라미스부터 올 수 있습니다. [라우트 resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider)는 컨트롤러 로직이 실행되기 전 프라미스를 구할 수 있도록 해줍니다. 그래서 프라미스를 구한 후 특정 액션을 취할 수 있습니다. + + *이유*: 코드는 라우트와 컨트롤러의 준비-시작 함수 다음 실행이 됩니다. 뷰는 바로 로드가 시작됩니다. 데이터 바인딩은 활성 프라미스가 구해지면 시작됩니다. "busy" 애니매이션은 뷰의 변경 중 보여질 수 있습니다. (`ng-view` or `ui-view`를 통해서) + + 주의: 코드는 프라미스를 통해서 라우트 전에 실행됩니다. 프라미스를 거절하면 라우팅을 중단합니다. Resolve는 라우트가 값을 가져오기 전까지 다음 화면이 기다리게 합니다. "busy" 애니매이션은 화면 변경 중, resolve 전에 보여질 수 있습니다. 뷰에 좀 더 빨리 도달하기를 원하고, 뷰에 갈 수 있는지 체크포인트가 필요하다면, [컨트롤러 `activate` 테크닉](#style-y080)를 대신 고려하세요. + + ```javascript + /* avoid */ + angular + .module('app') + .controller('AvengersController', AvengersController); + + function AvengersController(movieService) { + var vm = this; + // unresolved + vm.movies; + // resolved asynchronously + movieService.getMovies().then(function(response) { + vm.movies = response.movies; + }); + } + ``` + + ```javascript + /* better */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { + moviesPrepService: function(movieService) { + return movieService.getMovies(); + } + } + }); + } + + // avengers.js + angular + .module('app') + .controller('AvengersController', AvengersController); + + AvengersController.$inject = ['moviesPrepService']; + function AvengersController(moviesPrepService) { + var vm = this; + vm.movies = moviesPrepService.movies; + } + ``` + + 주의: 아래 예제는, 라우트 resolve 가 함수 정의를 가르키고 있음을 보여줍니다. 이는 디버깅과 디펜던시 인젝션을 핸들 하기 쉽게 해줍니다. + + ```javascript + /* even better */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { + moviesPrepService: moviesPrepService + } + }); + } + + function moviesPrepService(movieService) { + return movieService.getMovies(); + } + + // avengers.js + angular + .module('app') + .controller('AvengersController', AvengersController); + + AvengersController.$inject = ['moviesPrepService']; + function AvengersController(moviesPrepService) { + var vm = this; + vm.movies = moviesPrepService.movies; + } + ``` + + 주의: 예제의 `movieService` 의존성은 코드 최소화에 안전하지 않습니다. 코드 최소화에 안전하도록 만들려면 여기를 참고하세요. [dependency injection](#manual-annotating-for-dependency-injection), [minification and annotation](#minification-and-annotation). + +**[Back to top](#table-of-contents)** + +## Manual Annotating for Dependency Injection + +### UnSafe from Minification +###### [Style [Y090](#style-y090)] + + - 최소화-안전 접근법을 사용하지 않으면서 의존성 정의 단축 문법을 사용하지 않도록 하세요. + + *이유*: 컨트롤러나 팩토리 같은 컴포넌트에 전달되는 파라미터들은 최소화된 이름으로 변경되어 버립니다. 예를 들어 `common` 과 `dataservice`는 `a` 또는 `b` 처럼 Angular에서 발견될 수 없게 되어버립니다. + + ```javascript + /* avoid - not minification-safe*/ + angular + .module('app') + .controller('DashboardController', DashboardController); + + function DashboardController(common, dataservice) { + } + ``` + + 이 코드는 최소화된 변수명으로 런타임 에러를 발생시킬 수 있습니다. + + ```javascript + /* avoid - not minification-safe*/ + angular.module('app').controller('DashboardController', d);function d(a, b) { } + ``` + +### Manually Identify Dependencies +###### [Style [Y091](#style-y091)] + + - `$inject`를 사용하여 수동적으로 Angular 의존성을 정의하세요. + + *이유*: 이 방법은 [`ng-annotate`](https://github.com/olov/ng-annotate)에서 이용된 기술을 반영합니다. 또한 최소화 안전 의존성코드를 자동으로 최소화 시킬 때 추천합니다. 만약 `ng-annotate`이 이미 인젝션이 된 것을 탐지하면 반복해서 인잭션 하지 않게 됩니다. + + *이유*: 이 방법은 파라미터이 이름이 최소화 재생성 되었을 때 망가지기 쉬운 문제로 부터 보호해줍니다. `common` 와 `dataservice`는 `a` or `b`로 변경될 것이고 이는 Angular에서 찾을 수 없는 키워드입니다. + + *이유*: 어레이 형식의 한 줄 의존성 정의는 읽기가 어려우므로 피해야 합니다. 그리고 마지막 항목은 함수로 넘겨질 경우 앞쪽의 스트링으로 넘겨진 것들과 헷갈리기 쉽습니다. + + ```javascript + /* avoid */ + angular + .module('app') + .controller('DashboardController', + ['$location', '$routeParams', 'common', 'dataservice', + function Dashboard($location, $routeParams, common, dataservice) {} + ]); + ``` + + ```javascript + /* avoid */ + angular + .module('app') + .controller('DashboardController', + ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); + + function Dashboard($location, $routeParams, common, dataservice) { + } + ``` + + ```javascript + /* recommended */ + angular + .module('app') + .controller('DashboardController', DashboardController); + + DashboardController.$inject = ['$location', '$routeParams', 'common', 'dataservice']; + + function DashboardController($location, $routeParams, common, dataservice) { + } + ``` + + 주의: `$inject` 실행 부분이 return 명령어 아래쪽에 위치할 경우, 실행되지 않게 됩니다. (이는 디렉티브에서 발생할 수 있습니다.) 이를 해결하기 위해서는 컨트롤러를 디렉티브의 바깥쪽에 위치시키면됩니다. + + ```javascript + /* avoid */ + // inside a directive definition + function outer() { + var ddo = { + controller: DashboardPanelController, + controllerAs: 'vm' + }; + return ddo; + + DashboardPanelController.$inject = ['logger']; // Unreachable + function DashboardPanelController(logger) { + } + } + ``` + + ```javascript + /* recommended */ + // outside a directive definition + function outer() { + var ddo = { + controller: DashboardPanelController, + controllerAs: 'vm' + }; + return ddo; + } + + DashboardPanelController.$inject = ['logger']; + function DashboardPanelController(logger) { + } + ``` + +### Manually Identify Route Resolver Dependencies +###### [Style [Y092](#style-y092)] + + - `$inject`를 사용하면 수동으로 Angular 컴포넌트의 라우트 resolver 의존성을 구분하게 합니다. + + *이유*: 이 방법은 익명 함수를 라우트 resolver로부터 분리하여 읽기 쉬운 코드로 만들어줍니다. + + *이유*: `$inject` 구문은 의존성 최소화 안전화를 다루기 위해 쉽게 resolver로 진행할 수 있습니다. + + ```javascript + /* recommended */ + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { + moviesPrepService: moviesPrepService + } + }); + } + + moviesPrepService.$inject = ['movieService']; + function moviesPrepService(movieService) { + return movieService.getMovies(); + } + ``` + +**[Back to top](#table-of-contents)** + +## Minification and Annotation + +### ng-annotate +###### [Style [Y100](#style-y100)] + + - [Gulp](http://gulpjs.com) or [Grunt](http://gruntjs.com)를 사용할 때 [ng-annotate](//github.com/olov/ng-annotate)를 사용하세요. 그리고 `/* @ngInject */`이 코맨트 함수를 사용하여 자동화된 의존성 인젝션을 사용하세요. + + *이유*: 최소화-안전 방식을 사용하지 않는 의존성 정의를 지켜줍니다. + + *이유*: [`ng-min`](https://github.com/btford/ngmin)는 제외 되었습니다. + + >나는 Gulp를 선호합니다. 작성하고 읽고 디버깅하기 쉽다고 생각합니다. + + 아래 코드는 최소화 안전 의존성코드를 사용하지 않습니다. + + ```javascript + angular + .module('app') + .controller('AvengersController', AvengersController); + + /* @ngInject */ + function AvengersController(storage, avengerService) { + var vm = this; + vm.heroSearch = ''; + vm.storeHero = storeHero; + + function storeHero() { + var hero = avengerService.find(vm.heroSearch); + storage.save(hero.name, hero); + } + } + ``` + + When the above code is run through ng-annotate it will produce the following output with the `$inject` annotation and become minification-safe. + 위의 코드가 포함된 코드가 ng-annotate를 거치게 되면 `$inject` 부분을 생성하게 되어 최소화 안전 코드가 됩니다. + + ```javascript + angular + .module('app') + .controller('AvengersController', AvengersController); + + /* @ngInject */ + function AvengersController(storage, avengerService) { + var vm = this; + vm.heroSearch = ''; + vm.storeHero = storeHero; + + function storeHero() { + var hero = avengerService.find(vm.heroSearch); + storage.save(hero.name, hero); + } + } + + AvengersController.$inject = ['storage', 'avengerService']; + ``` + + 주의: 만약 `ng-annotate` 가 이미 인잭션 코드가 있음을 발견하면 이를 재생성하지 않습니다 (`@ngInject`를 발견하면). + + 주의: 라우트 resolver를 사용할 경우 그 함수에도 `/* @ngInject */`를 사용할 수 있습니다. 이것은 적당한 구문을 생성하여 최소화 안전 의존성을 지켜줍니다. + + ```javascript + // Using @ngInject annotations + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { /* @ngInject */ + moviesPrepService: function(movieService) { + return movieService.getMovies(); + } + } + }); + } + ``` + + > 주의: Angular 1.3 부터는 [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp) 디랙티브의 `ngStrictDi` 파라미터를 사용하면 잠재적으로 빠뜨려진 최소화 안전 코드를 찾아낼 수 있습니다. 이를 사용하면 "strict-di" 모드에서 만들어진 인젝터는 어플리케이션이 명시적 함수 구문을 사용하지 않는 함수를 호출하면 실행 실패를 유발 할 수 있습니다 (이는 최소화 안전하지 않습니다). 디버깅 시 이를 발생시킨 코드를 추적할 수 있는 정보를 콘솔에 뿌려줍니다. 나는 디버깅 시에만 `ng-strict-di`를 사용하는 것을 선호합니다. + `` + +### Use Gulp or Grunt for ng-annotate +###### [Style [Y101](#style-y101)] + + - [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) or [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate)를 사용하여 자동화 코드생성을 하세요. 의존성을 가지고 있는 모든 함수의 앞부분에 `/* @ngInject */`를 넣으세요. + + *이유*: ng-annotate는 대부분의 의존성을 잡아냅니다. 하지만 가끔 `/* @ngInject */`를 이용한 힌트가 필요할 수 있습니다. + + 아래 코드는 ngAnnotate를 이용하는 걸프 작업의 예입니다. + + ```javascript + gulp.task('js', ['jshint'], function() { + var source = pkg.paths.js; + + return gulp.src(source) + .pipe(sourcemaps.init()) + .pipe(concat('all.min.js', {newLine: ';'})) + // Annotate before uglify so the code get's min'd properly. + .pipe(ngAnnotate({ + // true helps add where @ngInject is not used. It infers. + // Doesn't work with resolve, so we must be explicit there + add: true + })) + .pipe(bytediff.start()) + .pipe(uglify({mangle: true})) + .pipe(bytediff.stop()) + .pipe(sourcemaps.write('./')) + .pipe(gulp.dest(pkg.paths.dev)); + }); + + ``` + +**[Back to top](#table-of-contents)** + +## Exception Handling + +### decorators +###### [Style [Y110](#style-y110)] + + - 설정하는 부분에서 [`$provide`](https://docs.angularjs.org/api/auto/service/$provide) 서비스를 사용할 때 [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator)를 사용하여 [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) 서비스를 이용하여 예외가 발생했을 때 대처하세요. + + *이유*: 개발기간과 실행시간에 발견되지 않은 Angular 예외를 일관되게 처리하도록 합니다. + + 주의: 다른 방법으로는 decorator를 사용하지 않고 기존 서비스를 덮어쓰기 하는 것입니다. 이 방법은 나름 좋습니다. 하지만 기본적인 방법을 사용하면서 확장하고 싶다면 decorator를 추천합니다. + + ```javascript + /* recommended */ + angular + .module('blocks.exception') + .config(exceptionConfig); + + exceptionConfig.$inject = ['$provide']; + + function exceptionConfig($provide) { + $provide.decorator('$exceptionHandler', extendExceptionHandler); + } + + extendExceptionHandler.$inject = ['$delegate', 'toastr']; + + function extendExceptionHandler($delegate, toastr) { + return function(exception, cause) { + $delegate(exception, cause); + var errorData = { + exception: exception, + cause: cause + }; + /** + * Could add the error to a service's collection, + * add errors to $rootScope, log errors to remote web server, + * or log locally. Or throw hard. It is entirely up to you. + * throw exception; + */ + toastr.error(exception.msg, errorData); + }; + } + ``` + +### Exception Catchers +###### [Style [Y111](#style-y111)] + + - 예외를 우아하게 처리하고 싶다면 팩토리를 만들어서 인터페이스를 노출하는 식으로 사용하세요. + + *이유*: 예를 들어 XHR 호출이나 프라미스 실패 시, 좀 더 일관적인 방식으로 코드에서 발생한 예외를 잡아줍니다. + + 주의: 예외 캐쳐는 당신이 예상했던 호출에서 특정 예외가 발생했을 때 그것을 잡아내고 대처하는데 좋습ㄴ디ㅏ. 예를 들어 원격 웹 서비스에 접속해서 데이터를 가져오는 XHR 호출을 만들 때 그 서비스로 부터 예외를 받아서 특정한 방식으로 대처할 수 있습니다. + + ```javascript + /* recommended */ + angular + .module('blocks.exception') + .factory('exception', exception); + + exception.$inject = ['logger']; + + function exception(logger) { + var service = { + catcher: catcher + }; + return service; + + function catcher(message) { + return function(reason) { + logger.error(message, reason); + }; + } + } + ``` + +### Route Errors +###### [Style [Y112](#style-y112)] + + - [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError)를 사용하여 라우팅 에러를 다루고 기록할 수 있습니다. + + *이유*: 라우팅 에러를 일관적인 방법으로 처리할 수 있습니다. + + *이유*: 잠재적으로 더 나은 사용자 경험을 제공합니다. 사용자가 라우팅 에러를 마주하게 되면 좀 더 친숙한 페이지로 안내하거나 복구 선택도 가능하게 할 수 있습니다. + + ```javascript + /* recommended */ + var handlingRouteChangeError = false; + + function handleRoutingErrors() { + /** + * Route cancellation: + * On routing error, go to the dashboard. + * Provide an exit clause if it tries to do it twice. + */ + $rootScope.$on('$routeChangeError', + function(event, current, previous, rejection) { + if (handlingRouteChangeError) { return; } + handlingRouteChangeError = true; + var destination = (current && (current.title || + current.name || current.loadedTemplateUrl)) || + 'unknown target'; + var msg = 'Error routing to ' + destination + '. ' + + (rejection.msg || ''); + + /** + * Optionally log using a custom service or $log. + * (Don't forget to inject custom service) + */ + logger.warning(msg, [current]); + + /** + * On routing error, go to another route/state. + */ + $location.path('/'); + + } + ); + } + ``` + +**[Back to top](#table-of-contents)** + +## Naming + +### Naming Guidelines +###### [Style [Y120](#style-y120)] + + - 다음의 패턴을 참고하여 모든 컴포넌트 파일 이름을 만드세요. 컴포넌트의 기능을 설명하는 이름에 (선택적으로) 그 것의 타입을 더 하면 됩니다. 제가 추천하는 이름은 `feature.type.js` 이런 식입니다. 대부분 다음과 같은 2 가지의 이름을 가지게 됩니다: + * 파일 이름 (`avengers.controller.js`) + * Angular 에 등록되는 컴포넌트 이름 (`AvengersController`) + + *이유*: 이름 짓는 규칙을 가지게 되면 금방 원하는 정보의 위치를 찾을 수 있습니다. + 프로젝트를 일관되게 하는 것은 필수적입니다. 팀 전체가 일관되는 것은 중요합니다. 회사 전체가 일관된 것은 어마한 효과를 가져옵니다. + + *이유*: 이름 규칙을 가지게 되면 일단 코드를 찾고 이해하는 데 매우 도움이 됩니다. + +### Feature File Names +###### [Style [Y121](#style-y121)] + + - 다음의 패턴을 참고하여 모든 컴포넌트 파일이름을 만드세요. 컴포넌트의 기능을 설명하는 이름에 (선택적으로) 그것의 타입을 더 하면 됩니다. 제가 추천하는 이름은 `feature.type.js` 이런 식입니다. + + *이유*: 컴포넌트를 찾는데 매우 일관적인 빠른 방법을 제공합니다. + + *이유*: 자동화 업무를할 때 파일 선택의 패턴을 동일하게 사용할 수 있습니다. + + ```javascript + /** + * common options + */ + + // Controllers + avengers.js + avengers.controller.js + avengersController.js + + // Services/Factories + logger.js + logger.service.js + loggerService.js + ``` + + ```javascript + /** + * recommended + */ + + // controllers + avengers.controller.js + avengers.controller.spec.js + + // services/factories + logger.service.js + logger.service.spec.js + + // constants + constants.js + + // module definition + avengers.module.js + + // routes + avengers.routes.js + avengers.routes.spec.js + + // configuration + avengers.config.js + + // directives + avenger-profile.directive.js + avenger-profile.directive.spec.js + ``` + + 주의: 또 다른 컨트롤러를 이름 짓는 통용 관습으로는 `controller`라는 단어를 제외하는 것입니다. `avengers.controller.js`가 아닌 `avengers.js` 처럼 말이죠. 다른 대부분의 컴포넌트들은 접미사를 그대로 사용하게 둡니다. 컨트롤러가 제일 흔하게 사용되는 컴포넌트 타입이기 때문에 controller라고 명시하지 않아도 쉽게 구분할 수 있습니다. 이 중 한 가지 관습을 선택한 다음 전체 팀이 모두 같은 관습을 사용하도록 하기를 권장합니다. 제가 선호하는 방식은 `avengers.controller.js` 입니다. + + ```javascript + /** + * recommended + */ + // Controllers + avengers.js + avengers.spec.js + ``` + +### Test File Names +###### [Style [Y122](#style-y122)] + + - 테스트 명세파일을 이름 지을 때는 추가 접미사 `spec`를 붙여서 만듭니다. + + *이유*: 일관적으로 빠르게 해당 컴포넌트를 찾을 수 있습니다. + + *이유*: [karma](http://karma-runner.github.io/)나 다른 테스트 러너에게 패턴 매칭으로 테스트 파일을 선택할 수 있게 할 수 있습니다. + + ```javascript + /** + * recommended + */ + avengers.controller.spec.js + logger.service.spec.js + avengers.routes.spec.js + avenger-profile.directive.spec.js + ``` + +### Controller Names +###### [Style [Y123](#style-y123)] + + - 컨트롤러 이름을 지을 때는 그의 기능을 따서 일관된 이름을 짓도록 합니다. 생성자를 이름 지을 때는 대문자 캐멀 케이스를 사용하세요. + + *이유*: 일관된 방식으로 찾아내고 참조할 수 있도록 합니다. + + *이유*: 대문자 캐멀 케이스는 생성자로 구별되는 오브젝트를 구별하는 방식입니다. + + ```javascript + /** + * recommended + */ + + // avengers.controller.js + angular + .module + .controller('HeroAvengersController', HeroAvengersController); + + function HeroAvengersController() { } + ``` + +### Controller Name Suffix +###### [Style [Y124](#style-y124)] + + - 컨트롤러 이름에 `Controller`라는 접미사를 붙여주세요. + + *이유*: `Controller`접미사를 사용하는 것은 좀 더 일반적이고 또한 좀 더 명시적으로 설명적이기 때문입니다. + + ```javascript + /** + * recommended + */ + + // avengers.controller.js + angular + .module + .controller('AvengersController', AvengersController); + + function AvengersController() { } + ``` + +### Factory and Service Names +###### [Style [Y125](#style-y125)] + + - 각각의 기능에 따라 팩토리와 서비스의 이름을 일관적으로 지어주세요. 대문자 캐멀 케이싱을 사용하세요. `$`를 접두어로 사용하는 것은 피하세요. 예를 들어 이름이 명사이거나 다른 이유로 이름 자체가 뭘 하는지 모호한 경우에만 접미사로 `Service`를 사용하세요. + + *이유*: 일관된 방식으로 팩토리를 찾아내고 참조할 수 있도록 합니다. + + *이유*: `$` 접두어가 붙어있는 내장된 팩토리와 서비스들과 이름 충돌이 일어나지 않도록 하세요. + + *이유*: `logger`같은 서비스는 굉장히 목적이 뚜렷하기 때문에 접미어가 필요하지 않습니다. + + *이유*: `avengers`같은 서비스 이름은 명사이고 확실한 기능이 떠오르지 않기 때문에 `avengersService`처럼 접미어를 붙여야 합니다. + + ```javascript + /** + * recommended + */ + + // logger.service.js + angular + .module + .factory('logger', logger); + + function logger() { } + ``` + + ```javascript + /** + * recommended + */ + + // credit.service.js + angular + .module + .factory('creditService', creditService); + + function creditService() { } + + // customer.service.js + angular + .module + .service('customerService', customerService); + + function customerService() { } + ``` + +### Directive Component Names +###### [Style [Y126](#style-y126)] + + - Use consistent names for all directives using camel-case. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). + - 캐멀 캐이스를 이용해서 디렉티브 이름을 일관적으로 지어주세요. 짧은 접두어를 사용하여 이 디렉티브가 어떤 프로젝트 혹은 회사에 소속되어 있는지 알려주세요. + + *이유*: 일관된 방식으로 컴포넌트를 찾아내고 참조할 수 있도록 합니다. + + ```javascript + /** + * recommended + */ + + // avenger-profile.directive.js + angular + .module + .directive('xxAvengerProfile', xxAvengerProfile); + + // usage is + + function xxAvengerProfile() { } + ``` + +### Modules +###### [Style [Y127](#style-y127)] + + - 여러개의 모듈이 존재할 경우, 중심이 되는 모듈은 `app.module.js` 로 이름을 짓고, 다른 의존 모듈들은 각자의 대표 기능을 따서 이름을 지으면 됩니다. 예를 들어 어드민 모듈은 `admin.module.js`으로 말이죠. 각가의 등록된 이름은 `app`과 `admin`이 될 겁니다. + + *이유*: 큰 어플리케이션으로 확장하기 위해서 여러 모듈을 사용할 때 일관성을 유지할 수 있습니다. + + *이유*: 자동화 작업을 위해 모듈 정의를 모두 로드하고, (바인딩을 위한) 모든 다른 angular 파일을 로드하는 것을 쉽게 만들어 줍니다. + +### Configuration +###### [Style [Y128](#style-y128)] + + - 모듈의 이름을 따서 만든 파일에 설정 부분을 분리해서 넣도록 합니다. 중심 모듈인 `app`모듈의 설정파일은 `app.config.js`가 될 겁니다. (또는 간단히 `config.js`) `admin.module.js` 의 설정 파일은 `admin.config.js`이 됩니다. + + *이유*: 설정 파일을 모듈 정의, 컴포넌트, 작동 코드로 부터 분리합니다. + + *이유*: 모듈 설정 부분을 구분할 수 있는 곳을 제공합니다. + +### Routes +###### [Style [Y129](#style-y129)] + + - 라우팅 설정 부분을 따로 만들어서 저장합니다. 예를 들어 `app.route.js`는 중심 모듈의 라우팅, `admin.route.js`는 `admin`모듈의 라우팅 설정이 됩니다. 비록 작은 앱이라도 저는 분리해서 저장하는 것을 선호합니다. + +**[Back to top](#table-of-contents)** + +## Application Structure LIFT Principle +### LIFT +###### [Style [Y140](#style-y140)] + + - 앱을 구조적으로 만들게 되면 당신은 코드를 빠르게 `L`위치 추적 할 수 있고, 한눈에 `I`구분할 수 있고, `F`단순한 구조를 유지할 수 있고, DRY(반복하지 마세요)를 `T`유지할 수 있습니다. 구조는 다음 4 가지 가이드라인을 따르면 됩니다. + + LIFT *이유*: 규모를 잘 조절할 수 있는 일관된 구조를 제공합니다. 코드를 쉽게 찾을 수 있으므로 개발자의 효율을 쉽게 증대시킵니다. 다음 질문을 함으로서 앱이 구조가 잘 갖추어 져 있는지 확인할 수 있습니다. + + 만약 다음 가이드라인에 적합하지 않는 부분이 있다고 느껴지면, 위로 돌아가서 LIFT 가이드라인을 다시 살펴보세요. + + 1. `L`코드를 쉽게 찾아낼 수 있음 + 2. `I`첫눈에 구분할 수 있음 + 3. `F`단순한 구조를 유지할 수 있음 + 4. `T`반복작업을 피할 수 있음 + +### Locate +###### [Style [Y141](#style-y141)] + + - 코드를 찾아내는 방법을 간단하고, 직관적이고 빠르게 해야 합니다. + + *이유*: 저는 이 부분이 어마어마하게 중요하다는 것을 알게 되었습니다. 팀 사람들이 그들이 필요한 파일을 빨리 찾을 수 없다면, 최대의 효율을 내서 일할 수 없습니다. 구조는 변경되어야 합니다. 간혹 당신은 파일명을 알 수 없고 어떤 파일과 관련이 있는지 알 수 없습니다. 그래서 최고의 직관적인 위치나 그 근처의 위치에 파일을 두는 것은 어마어마한 시간을 아껴줍니다. 설명적인 폴더 구조는 이를 도와줄 수 있습니다. + + ``` + /bower_components + /client + /app + /avengers + /blocks + /exception + /logger + /core + /dashboard + /data + /layout + /widgets + /content + index.html + .bower.json + ``` + +### Identify +###### [Style [Y142](#style-y142)] + + - 파일명을 보면 즉시 그 안에 뭐가 있고 어떤 기능을 담고 있는지 알 수 있어야 합니다. + + *이유*: 파일을 찾으러 다니면서 소비되는 시간이 줄어들수록 업무의 효율은 증가합니다. 만약 그러다가 파일명이 길어진다면, 그냥 길게 쓰세요. 파일명이 충분히 설명적이어도 되고 그 안의 내용은 그 설명에 부합하는 1개의 컴포넌트 기능이어야 합니다. 한 파일 안에 여러 컨트롤러나 여러 서비스 또는 그 두 가지 들이 여럿 들어있으면 안 됩니다. 가끔 1파일 규칙을 어기게 되는 경우가 있습니다. 여러 가지의 작은 기능들인데 서로 관련되어 있는 경우입니다. 하지만 그렇게 하더라고 쉽게 구별할 수 있기 때문입니다. + +### Flat +###### [Style [Y143](#style-y143)] + + - 최대한 수평적 구조를 갖도록 하세요. 7개 이상의 파일을 갖게 되면, 나눌 수 있도록 고려해보세요. + + *이유*: 7층의 폴더구조를 오가며 파일을 찾는 일은 쉽지가 않습니다. 웹사이트의 메뉴를 생각해보세요 ... 2단계 이상이 된다면 심각하게 재고를 해야 합니다. 폴더 구조에서는 그런 엄격한 룰은 존재하지 않지만, 한 폴더 안에 7-10개의 파일이 존재한다면, 하위 폴더를 만들 적절한 시기일 수 있습니다. 자신이 편하게 느끼는 정도로 말이죠. 명백한 이유가 있다면 하위 폴더를 만들되 그전까지는 나머지 LIFT를 지키기 위해 폴더를 수평적으로 유지하세요. + +### T-DRY (Try to Stick to DRY) +###### [Style [Y144](#style-y144)] + + - 반복작업 하지마세요. 그렇다고 너무 오버해서 가독성을 희생하지도 마세요. + + *이유*: 반복작업을 하지 않는 것은 중요합니다. 하지만 다른 LIFT 요소를 희생해야 한다면 그리 과하게 DRY를 지킬 필요는 없습니다. 그래서 저는 T-DRY 라고 명명합니다. (try-DRY) 나는 session-view.html 이라고 view를 위해 이름짓고 싶지 않습니다. 왜냐면 명백히 이것은 view 이기 때문이죠. 만약 이것이 명백하지 않고 다른 관습에 의한 게 아니라면 저는 그렇게 이름을 지을 겁니다. + +**[Back to top](#table-of-contents)** + +## Application Structure + +### Overall Guidelines +###### [Style [Y150](#style-y150)] + + - 구현을 위한 생각을 가지되, 멀리 보고 고려하세요. 달리 말하자면, 작게 시작하지만 앞으로 얼마나 커질 수 있을지 항상 기억하세요. 모든 앱 코드는 `app`이라는 최상위 폴더에 들어갈 겁니다. 한 파일 안에는 한 가지 기능만 들어갑니다. 컨트롤러, 서비스, 모듈, 뷰는 각각의 파일을 가지고 그안에 넣습니다. 제 3의 외부 코드들은 `app`폴더가 아닌 다른 상위 폴더를 가지게 하세요. 내가 작성한 코드가 아니니 그 것들이 제 앱을 어지럽히기 원치 않으니까요 (`bower_components`, `scripts`, `lib`). + 주의: 구조를 만드는 것에 좀 더 구체적인 이유를 알고 싶으면 여기를 참고하세요. [this original post on application structure](http://www.johnpapa.net/angular-app-structuring-guidelines/). + +### Layout +###### [Style [Y151](#style-y151)] + + - 어플리케이션의 전체 레이아웃을 정의하는 컴포넌트는 `layout`이라는 폴더 안에 넣어주세요. 이는 쉘 뷰나 컨트롤러를 포함할 수 있고 앱, 네비게이션, 메뉴, 내용 부분, 그리고 다른 부분을 감사는 컨테이너로 역할을 할수 있습니다. + + *이유*: 레이아웃과 관련된 모든 것들을 한 곳에 넣어두고 전체 어플리케이션에서 재사용을 하도록 정리하세요. + +### Folders-by-Feature Structure +###### [Style [Y152](#style-y152)] + + - 폴더를 만들 때는 그 기능을 따서 이름을 지으세요. 폴더가 점점 커져서 7개 파일 이상 가지게 되면 따로 폴더를 만들기를 고려하세요. 기준은 사람마다 다를 수 있습니다. 필요에 따라 조절하면서 진행하세요. + + *이유*: 개발자는 한눈에 각 코드의 위치를 파악하고 어떤 용도로 필요한지 구별할 수 있습니다. 최대한 폴더 구조가 수평적일수록 반복적이고 불필요한 이름이 적게 사용되게 됩니다. + + *이유*: LIFT 가이드 라인에 다 만족됩니다. + + *이유*: 코드 내용들을 LIFT 가이드라인을 다 만족하도록 정리하면 복잡하게 어질러질 가능성을 줄여줍니다. + + *이유*: 만약 많은 파일을 위치시켜야 한다면 일관적인 구조라면 쉽게 위치시킬 수 있지만 수평적 구조에서는 좀 더 어려울 수 있습니다. + + ```javascript + /** + * recommended + */ + + app/ + app.module.js + app.config.js + components/ + calendar.directive.js + calendar.directive.html + user-profile.directive.js + user-profile.directive.html + layout/ + shell.html + shell.controller.js + topnav.html + topnav.controller.js + people/ + attendees.html + attendees.controller.js + people.routes.js + speakers.html + speakers.controller.js + speaker-detail.html + speaker-detail.controller.js + services/ + data.service.js + localstorage.service.js + logger.service.js + spinner.service.js + sessions/ + sessions.html + sessions.controller.js + sessions.routes.js + session-detail.html + session-detail.controller.js + ``` + + ![심플한 앱 구조](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) + + 주의: 타입별로 묶어서 폴더를 만드는 식으로 앱 구조를 만들지 마세요. 한 가지 기능에 대해서 일하려면 여러 폴더를 옮겨 다녀야 합니다. 이는 앱이 5, 10 또는 25 이상의 뷰와 컨트롤러(또는 다른 기능들)를 갖게되면 기능으로 나뉜 폴더일 경우보다 금방 거추장스러워 집니다. + + + ```javascript + /* + * avoid + * Alternative folders-by-type. + * I recommend "folders-by-feature", instead. + */ + + app/ + app.module.js + app.config.js + app.routes.js + directives.js + controllers/ + attendees.js + session-detail.js + sessions.js + shell.js + speakers.js + speaker-detail.js + topnav.js + directives/ + calendar.directive.js + calendar.directive.html + user-profile.directive.js + user-profile.directive.html + services/ + dataservice.js + localstorage.js + logger.js + spinner.js + views/ + attendees.html + session-detail.html + sessions.html + shell.html + speakers.html + speaker-detail.html + topnav.html + ``` + +**[Back to top](#table-of-contents)** + +## Modularity + +### Many Small, Self Contained Modules +###### [Style [Y160](#style-y160)] + + - 한 가지에 대해서 책임을 캡슐화하는 작은 모듈들을 만드세요. + + *이유*: 모듈화된 어플리케이션은 끼우고 사용하는 방법을 쉽게 합니다. 어플리케이션의 수직적 조각을 만들도록 도와주는데 이는 점차 역할을 합니다. 이것은 우리가 몇 가지 기능을 개발하는 동시에 조립해서 사용할 수 있다는 뜻입니다. + +### Create an App Module +###### [Style [Y161](#style-y161)] + + - 중심적 역할을 하는 근본 모듈을 만들어서 모든 나머지 모듈과 기능들을 조직하고 협업되도록 하세요. 이 모듈의 이름은 전체 어플리케이션의 이름을 따서 지으세요. + + *이유*: Angular는 모듈화와 분리 패턴을 권장합니다. 근본 모듈을 만들어서 이 모듈이 다른 모듈들을 사용하게 함으로써 전체 모듈에 새로운 모듈을 추가하는 등의 작업을 직관적으로 만들어줍니다. + +### Keep the App Module Thin +###### [Style [Y162](#style-y162)] + + - 어플리케이션 모듈에는 조직하는 로직만 넣도록 합니다. 기능들은 각각의 모듈에 들어가게 합니다. + + *이유*: 어플리케이션을 구성하는 로직 외, 원격 데이터를 가져오거나 뷰를 보여주거나 등의 로직을 근본 모듈에 넣게 되면 기능 모듈들을 재사용 어렵게 하고 기능을 잠시 꺼두기 어렵게 합니다. + + *이유*: 근본 모듈은 이 앱을 정의하는데 어떤 모듈들이 도움을 주는지 설명하는 목록이 됩니다. + +### Feature Areas are Modules +###### [Style [Y163](#style-y163)] + + - 레이아웃 같은 기능 구역을 나타내는 모듈들, 재사용 가능하고 공유가 가능한 서비스, 대시보드, 특화된 기능들을 모듈로 만드세요 (예를 들어 고객, 어드민, 판매). + + *이유*: 자극 자족적인 모듈들은 어플리케이션에 아주 작은 충돌 혹은 아무 충돌 없이 추가될 수 있습니다. + + *이유*: 스프린트나 이터레이션은 기능 구역에 집중될 수 있고 마지막 부분에 켜질 수 있습니다. + + *이유*: 기능 구역을 구분하여 모듈화 하는것은 고립되고 재사용 코드로서 모듈 테스트에서 사용하기 쉽게 합니다. + +### Reusable Blocks are Modules +###### [Style [Y164](#style-y164)] + + - 예외처리, 로깅, 진단, 보안, 그리고 로컬 데이터 저장 등의 일반적인 서비스들은 재사용 가능한 어플리케이션 블럭으로 모듈화 될 수 있습니다. + + *이유*: 이런 성격의 기능들은 많은 어플리케이션에서 사용될 수 있습니다. 이런 부분들을 분리하여 각각 모듈로 만들어두면 다른 어플리케이션에서 일반적인 부분으로 두루 사용될 수 있습니다. + +### Module Dependencies +###### [Style [Y165](#style-y165)] + + - 어플리케이션의 루트 모듈은 앱 특화된 기능모듈 그리고 재사용되고 공유된 모듈들에 의존하게 됩니다. + + ![모듈화 그리고 의존성](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) + + *이유*: 주요 앱 모듈은 어플리케이션의 기능들을 빠르게 구분할 수 있는 목록을 가지고 있습니다. + + *이유*: 각각의 기능 구역은 자신이 의존하고 있는 모듈의 목록을 가지고 있습니다. 이는 다른 어플리케이션에서 작동될 때 그 모듈을 참조함으로 문제없이 작동할 수 있습니다. + + *이유*: 공유 데이터 서비스 같은 인트라-앱 기능은 `app.core`(자신이 선호하는 이름으로 지으세요)라는 모듈에 넣어두면 찾기도 쉽고 공유하기도 쉽습니다. + + 주의: 이것은 일관성을 위한 전략입니다. 다른 많은 좋은 옵션들도 있습니다. 어떤 것이든 일관되고 Angular의 의존성 규칙을 따르고, 유지보수가 쉽고 확장성이 있다면 선택해도 됩니다. + + > 내가 사용하는 구조는 프로젝트마다 조금씩 다릅니다. 하지만 모두 이 구조화, 모듈화에 대한 가이드라인을 지킵니다. 실제 구현방식은 팀마다 조금씩 다를 수 있습니다. 다르게 표현하자면, 완전히 똑같은 구조를 사용하려고 애쓰다가 전체를 거부하지 마세요. 일관성과 유지보수 그리고 효율을 마음에 두면서 자신의 구조를 직접 판단해보세요. + + > 또한 작은 앱들에서는 공유되는 의존 모듈들을 앱 모듈 폴더에 넣는 것도 고려하세요. 단 기능 모듈들이 직접 의존하지 않는 경우에 입니다. 이렇게 하면 작은 앱들은 유지하기가 쉽습니다. 하지만 다른 어플리케이션에서 이 모듈을 재사용하는것은 어려워 집니다. + +**[Back to top](#table-of-contents)** + +## Startup Logic + +### Configuration +###### [Style [Y170](#style-y170)] + + - Angular 앱을 실행하기 전에 인젝트 코드는 [모듈 설정](https://docs.angularjs.org/guide/module#module-loading-dependencies)에 설정되어 있어야 합니다. 이상적인 위치는 프로바이더와 상수 입니다. + + *이유*: 이는 설정하는 부분을 간결하게 합니다. 설정이 여기저기 퍼져있으면 유지보수하기 힘들고 헷갈리수 있습니다. + + ```javascript + angular + .module('app') + .config(configure); + + configure.$inject = + ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; + + function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { + exceptionHandlerProvider.configure(config.appErrorPrefix); + configureStateHelper(); + + toastr.options.timeOut = 4000; + toastr.options.positionClass = 'toast-bottom-right'; + + //////////////// + + function configureStateHelper() { + routerHelperProvider.configure({ + docTitle: 'NG-Modular: ' + }); + } + } + ``` + +### Run Blocks +###### [Style [Y171](#style-y171)] + + - 어떠한 코드든 어플리케이션이 실행 될 때 실행되어야 한다면, 팩토리로 정의되어서 함수로 노출이 되고 [run block](https://docs.angularjs.org/guide/module#module-loading-dependencies)에 인젝트 되어야 합니다. + + *이유*: 실행 구역에 직접 작성된 코드는 테스트하기 어렵습니다. 팩토리에 넣어두면 추상화하고 흉내내기가 쉽습니다. + + ```javascript + angular + .module('app') + .run(runBlock); + + runBlock.$inject = ['authenticator', 'translator']; + + function runBlock(authenticator, translator) { + authenticator.initialize(); + translator.initialize(); + } + ``` + +**[Back to top](#table-of-contents)** + +## Angular $ Wrapper Services + +### $document and $window +###### [Style [Y180](#style-y180)] + + - `document` 와 `window` 대신 [`$document`](https://docs.angularjs.org/api/ng/service/$document) 와 [`$window`](https://docs.angularjs.org/api/ng/service/$window)를 사용하세요. + + *이유*: 이 서비스들은 Angular에 의해서 감추어집니다. 그리고 window와 document를 사용해서 테스트 하는 것 보다 테스트를 쉽게 해줍니다. 또한 document와 window를 흉내 내도록 코드를 만들어야 하는것을 피하게 해줍니다. + +### $timeout and $interval +###### [Style [Y181](#style-y181)] + + - `setTimeout` 와 `setInterval` 대신 [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) 와 [`$interval`](https://docs.angularjs.org/api/ng/service/$interval)를 사용하세요. + + *이유*: 이 서비스들은 Angular에 의해서 감추어집니다. 데이터 바인딩을 유지시켜주기 때문에 테스트하기 좋고 Angular의 실행 주기를 다루는데 더 쉽습니다. + +**[Back to top](#table-of-contents)** + +## Testing +단위 테스팅은 깨끗한 코드를 유지하도록 도와줍니다. 여기에 단위 테스팅을 위한 약간의 권장 사항을 링크와 함께 제공하려고 합니다. + +### Write Tests with Stories +###### [Style [Y190](#style-y190)] + + - Write a set of tests for every story. Start with an empty test and fill them in as you write the code for the story. + - 모든 각각의 스토리를 위해서 테스트 세트를 작성하세요. 빈 테스트로 시작을 하고 스토리에 맞추어 코드를 작성하면서 채워나가세요. + + *이유*: 테스트 설명을 작성함으로 스스로의 스토리가 어떻게 작동해야 하고 어떻게 작동하지 말아야 하는지 투명하게 정의할 수 있습니다. 그리고 성공을 어떻게 측정해야 하는지도 포함됩니다. + + ```javascript + it('should have Avengers controller', function() { + // TODO + }); + + it('should find 1 Avenger when filtered by name', function() { + // TODO + }); + + it('should have 10 Avengers', function() { + // TODO (mock data?) + }); + + it('should return Avengers via XHR', function() { + // TODO ($httpBackend?) + }); + + // and so on + ``` + +### Testing Library +###### [Style [Y191](#style-y191)] + + - [Jasmine](http://jasmine.github.io/) 또는 [Mocha](http://mochajs.org)를 사용하여 단위 테스트를 하세요. + + *이유*: Jasmine과 Mocha 모두 Angular 커뮤니티에서 두루 사용되고 있습니다. 둘다 안정적이고 잘 유지보수 되고 있으며 확실하게 기능 테스트를 할 수 있습니다. + + 주의: 모카를 사용할 경우 [Chai](http://chaijs.com)와 같은 assert 라이브러리를 선택하세요. 저는 개인적으로 Mocha를 선호합니다. + +### Test Runner +###### [Style [Y192](#style-y192)] + + - [Karma](http://karma-runner.github.io)를 테스트 실행자로 사용하세요. + + *이유*: Karma는 설정하기 쉽고 그냥 한 번 실행할 수도 있고 코드가 변경되면 자동으로 실행할 수 도 있습니다. + + *이유*: Karma는 그 자체로 또는 Grunt나 Gulp를 이용하여 지속적인 통합을 연결해 진행하기 쉽도록 해줍니다. + + *이유*: 어떤 IDE들은 Karma와 통합하기 시작했습니다. [WebStorm](http://www.jetbrains.com/webstorm/), [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225) + + *이유*: [Grunt](http://www.gruntjs.com) (with [grunt-karma](https://github.com/karma-runner/grunt-karma)) and [Gulp](http://www.gulpjs.com)와 같은 자동화 선두주자들과 협업이 아주 좋습니다. Gulp를 사용한다면 [Karma](https://github.com/karma-runner/karma)를 플러그인을 사용하지 않고 직접적으로 API를 호출할 수 있습니다. + + ```javascript + /* recommended */ + + // Gulp example with Karma directly + function startTests(singleRun, done) { + var child; + var excludeFiles = []; + var fork = require('child_process').fork; + var karma = require('karma').server; + var serverSpecs = config.serverIntegrationSpecs; + + if (args.startServers) { + log('Starting servers'); + var savedEnv = process.env; + savedEnv.NODE_ENV = 'dev'; + savedEnv.PORT = 8888; + child = fork(config.nodeServer); + } else { + if (serverSpecs && serverSpecs.length) { + excludeFiles = serverSpecs; + } + } + + karma.start({ + configFile: __dirname + '/karma.conf.js', + exclude: excludeFiles, + singleRun: !!singleRun + }, karmaCompleted); + + //////////////// + + function karmaCompleted(karmaResult) { + log('Karma completed'); + if (child) { + log('shutting down the child process'); + child.kill(); + } + if (karmaResult === 1) { + done('karma: tests failed with code ' + karmaResult); + } else { + done(); + } + } + } + ``` + +### Stubbing and Spying +###### [Style [Y193](#style-y193)] + + - 스터빙이나 스파잉을 위해서는 [Sinon](http://sinonjs.org/)를 사용하세요. + + *이유*: Sinon은 Jasmine과 Mocha 둘다와 잘 작동하고 스터빙과 스파잉 기능을 이용해 확장할 수 있습니다. + + *이유*: Sinon은 Jasmine과 Mocha 사이에서 왔다 갔다 하기 쉽게 해줍니다. 만약 둘다 시도해보고 싶은 경우에 말이죠. + + *이유*: 만약 테스트가 실패하면 Sinon은 설명적인 메시지를 보여줍니다. + +### Headless Browser +###### [Style [Y194](#style-y194)] + + - 테스트를 서버에서 실행하고 싶다면 [PhantomJS](http://phantomjs.org/)를 이용하세요. + + *이유*: PhantomJS는 "시각적" 브라우저를 필요로 하지 않고 테스트를 브라우저에서 실행할 수 있도록 도와줍니다. 크롬이나 사파리 IE 또는 어떠한 브라우저도 설치할 필요가 없습니다. + + 주의: 대상 고객을 위해서 자신의 설치환경에서 모든 브라우저를 테스트해야 합니다. PhantomJS 테스트를 했다고 안심하면 안된다는 말입니다. + +### Code Analysis +###### [Style [Y195](#style-y195)] + + - 테스트에 JSHint를 사용하세요. + + *이유*: 테스트역시 코드입니다. JSHint는 테스트 코드의 질을 보증하고 테스트가 부정확하게 진행되는 것을 막아줍니다. + +### Alleviate Globals for JSHint Rules on Tests +###### [Style [Y196](#style-y196)] + + - `describe`와 `expect`같은 일반적인 글로벌 함수를 통과시키기 위해서 JSHint규칙을 완화하세요. Mocha 가 사용하는 표현식에 대한 규칙도 완화하세요. + + *이유*: 테스트 코드들도 실서버에 사용될 코드와 같은 주의와 질을 요구합니다. 하지만 테스트 코드에서 사용된 테스팅 프레임워크의 글로벌 변수같은 경우 아래 코드를 테스트 스팩에 포함하여 룰을 완화할 수 있습니다. + + ```javascript + /* jshint -W117, -W030 */ + ``` + Or you can add the following to your JSHint Options file. + + ```javascript + "jasmine": true, + "mocha": true, + ``` + + ![테스팅 도구들](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) + + +### Organizing Tests +###### [Style [Y197](#style-y197)] + + - 단위 테스트 파일들을 자신의 클라이언트 코드와 쌍으로 띄워두세요. 서버 통합을 확인하거나 여러 컴포넌트의 테스트 요건을 분리된 `tests` 폴더에 넣어두세요. + + *이유*: 단위 테스트는 소스 코드의 특정 컴포넌트와 파일들과 직접적인 상호작용을 합니다. + + *이유*: 항상 눈에 보여지게 됨으로 최신으로 유지하기가 쉽습ㄴ디ㅏ. TDD 또는 개발중 테스트 또는 개발 후 테스트 중 어떤 것을 사용하든 테스트 스팩은 나란히 보여지고 눈에서 멀어지기 어렵고 마음에서도 멀어지기 어렵습니다. 그러니 코드를 테스팅하는 코드도 유지보수하기 쉬워집니다. + + *이유*: 소스코드를 수정하게 될 때 테스트도 동시에 수정하기가 매우 쉽습니다. 한폴더에 있고 보여지니까요. + + *이유*: 나중에 소스코드를 옮기게 되어도 코드와 나란히 있기 때문에 함께 옮기기도 쉽고 편합니다. + + *이유*: 테스트 스팩을 곁에 두는 것은 코드를 파악하려는 사람에게 컴포넌트가 어떻게 동작하게 만들어졌는지 한계는 무엇인지 파악하기 쉽게 합니다. + + *이유*: Grunt나 gulp를 이용하면 배포 서버에 올려지는 코드에서 테스트 스팩코드를 분리하는게 어렵지 않습니다. + + ``` + /src/client/app/customers/customer-detail.controller.js + /customer-detail.controller.spec.js + /customers.controller.js + /customers.controller.spec.js + /customers.module.js + /customers.route.js + /customers.route.spec.js + ``` + +**[Back to top](#table-of-contents)** + +## Animations + +### Usage +###### [Style [Y210](#style-y210)] + + - 자연스러운 [Angular 애니매이션](https://docs.angularjs.org/guide/animations)을 사용하여 화면의 상태와 주요한 시각적 요소에 변화를 주세요. [ngAnimate 모듈](https://docs.angularjs.org/api/ngAnimate)를 포함하세요. 3 가지 중요 포인트는 자연스럽게, 부드럽게, 끊김없이 입니다. + + *이유*: 적합하게 사용된 자연스러운 애니매이션은 사용자 경험을 향상시킵니다. + + *이유*: 자연스러운 애니매이션은 티날정도로 화면의 변화를 향상시킵니다. + +### Sub Second +###### [Style [Y211](#style-y211)] + + - 애니매이션은 짧게 사용하세요. 저는 보통 300ms 로 시작해서 적당하다 싶게 조절합니다. + + *이유*: 긴 애니메이션은 유저 경험에 반대 영향을 줄 수 있고 늦게 반응하는 어플리케이션이라는 시각효과를 줌으로서 인지 성능에 안좋은 영향을 줍니다. + +### animate.css +###### [Style [Y212](#style-y212)] + + - 평범한 애니메이션을 위해서는 [animate.css](http://daneden.github.io/animate.css/)를 사용하세요. + + *이유*: animate.css이 제공하는 애니메이션들은 빠르고 부드럽고 쉽게 사용할 수 있습니다. + + *이유*: 애니매이션이 일관되게 사용되도록 해줍니다. + + *이유*: animate.css 널리 사용되고 있고 테스트 되었습니다. + + 주의: [Angular 애니메이션에 대한 좋은 포스팅 by Matias Niemelä](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) 여기를 참고하세요. + +**[Back to top](#table-of-contents)** + +## Comments + +### jsDoc +###### [Style [Y220](#style-y220)] + + - 프로그램 문서를 만들고자 한다면, [`jsDoc`](http://usejsdoc.org/) 구문을 사용하여 함수이름 설명, 파라미터 리턴값 등을 명세하세요. `@namespace` 와 `@memberOf`를 사용하여 앱 구조와 맞추세요. + + *이유*: 문서를 처음부터 다 작성하지말고 코드로 부터 문서를 만들거나 재생성 할 수 있습니다. + + *이유*: 널리알려진 산업 툴을 사용함으로 일관성을 유지할수있습니다. + + ```javascript + /** + * Logger Factory + * @namespace Factories + */ + (function() { + angular + .module('app') + .factory('logger', logger); + + /** + * @namespace Logger + * @desc Application wide logger + * @memberOf Factories + */ + function logger($log) { + var service = { + logError: logError + }; + return service; + + //////////// + + /** + * @name logError + * @desc Logs errors + * @param {String} msg Message to log + * @returns {String} + * @memberOf Factories.Logger + */ + function logError(msg) { + var loggedMsg = 'Error: ' + msg; + $log.error(loggedMsg); + return loggedMsg; + }; + } + })(); + ``` + +**[Back to top](#table-of-contents)** + +## JS Hint + +### Use an Options File +###### [Style [Y230](#style-y230)] + + - JS Hint를 사용하여 JavaScript 코드를 청소하세요. JS Hint 옵션 파일을 수정하고 소스 컨트롤에 추가하세요. 구체적인 옵션은 여기를 참고하세요 [JS Hint docs](http://www.jshint.com/docs/). + + *이유*: 소스 컨트롤에 코드를 전송하기 전에 경고를 받을 수 있습니다. + + *이유*: 팀 전체적으로 일관성을 유지할 수있습니다. + + ```javascript + { + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "es3": false, + "forin": true, + "freeze": true, + "immed": true, + "indent": 4, + "latedef": "nofunc", + "newcap": true, + "noarg": true, + "noempty": true, + "nonbsp": true, + "nonew": true, + "plusplus": false, + "quotmark": "single", + "undef": true, + "unused": false, + "strict": false, + "maxparams": 10, + "maxdepth": 5, + "maxstatements": 40, + "maxcomplexity": 8, + "maxlen": 120, + + "asi": false, + "boss": false, + "debug": false, + "eqnull": true, + "esnext": false, + "evil": false, + "expr": false, + "funcscope": false, + "globalstrict": false, + "iterator": false, + "lastsemic": false, + "laxbreak": false, + "laxcomma": false, + "loopfunc": true, + "maxerr": false, + "moz": false, + "multistr": false, + "notypeof": false, + "proto": false, + "scripturl": false, + "shadow": false, + "sub": true, + "supernew": false, + "validthis": false, + "noyield": false, + + "browser": true, + "node": true, + + "globals": { + "angular": false, + "$": false + } + } + ``` + +**[Back to top](#table-of-contents)** + +## JSCS + +### Use an Options File +###### [Style [Y235](#style-y235)] + + - JSCS를 사용하여 JavaScript의 코딩 스타일을 체크하세요. JSCS 옵션파일을 수정하고 소스 컨트롤에 함께 넣어두세요. 더 많은 옵션 정보를 위해서는 여기를 확인하세요 [JSCS docs](http://www.jscs.info). + + *이유*: 소스 컨트롤에 코드를 전송하기 전에 경고를 받을 수 있습니다. + + *이유*: 팀 전체적으로 일관성을 유지할 수있습니다. + + ```javascript + { + "excludeFiles": ["node_modules/**", "bower_components/**"], + + "requireCurlyBraces": [ + "if", + "else", + "for", + "while", + "do", + "try", + "catch" + ], + "requireOperatorBeforeLineBreak": true, + "requireCamelCaseOrUpperCaseIdentifiers": true, + "maximumLineLength": { + "value": 100, + "allowComments": true, + "allowRegex": true + }, + "validateIndentation": 4, + "validateQuoteMarks": "'", + + "disallowMultipleLineStrings": true, + "disallowMixedSpacesAndTabs": true, + "disallowTrailingWhitespace": true, + "disallowSpaceAfterPrefixUnaryOperators": true, + "disallowMultipleVarDecl": null, + + "requireSpaceAfterKeywords": [ + "if", + "else", + "for", + "while", + "do", + "switch", + "return", + "try", + "catch" + ], + "requireSpaceBeforeBinaryOperators": [ + "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", + "&=", "|=", "^=", "+=", + + "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", + "|", "^", "&&", "||", "===", "==", ">=", + "<=", "<", ">", "!=", "!==" + ], + "requireSpaceAfterBinaryOperators": true, + "requireSpacesInConditionalExpression": true, + "requireSpaceBeforeBlockStatements": true, + "requireLineFeedAtFileEnd": true, + "disallowSpacesInsideObjectBrackets": "all", + "disallowSpacesInsideArrayBrackets": "all", + "disallowSpacesInsideParentheses": true, + + "jsDoc": { + "checkAnnotations": true, + "checkParamNames": true, + "requireParamTypes": true, + "checkReturnTypes": true, + "checkTypes": true + }, + + "disallowMultipleLineBreaks": true, + + "disallowCommaBeforeLineBreak": null, + "disallowDanglingUnderscores": null, + "disallowEmptyBlocks": null, + "disallowTrailingComma": null, + "requireCommaBeforeLineBreak": null, + "requireDotNotation": null, + "requireMultipleVarDecl": null, + "requireParenthesesAroundIIFE": true + } + ``` + +**[Back to top](#table-of-contents)** + +## Constants + +### Vendor Globals +###### [Style [Y240](#style-y240)] + + - 외부 라이브러리의 글로벌 변수를 위해서 Angular 상수를 생성하세요. + + *이유*: 외부 라이브러리를 글로벌 변수에 만들지 않고 인젝트 하는 방법을 제공합니다. 이렇게 하면 의존성 컴포넌트알게 되기 때문에 코드의 테스트 용이성이 높아집니다. (누설된 추상화를 방지) 또한 필요한 부분에 의존 컴포넌트들을 흉내내기 쉽게 해줍니다. + + ```javascript + // constants.js + + /* global toastr:false, moment:false */ + (function() { + 'use strict'; + + angular + .module('app.core') + .constant('toastr', toastr) + .constant('moment', moment); + })(); + ``` + +###### [Style [Y241](#style-y241)] + + - 변하지 않고 다른 서비스로부터 오지 않는 값들은 불변 상수를 이용하세요. 다양한 어플리케이션에서 재사용될 수 있는 모듈 내에서 사용되는 불변상 수들은 모듈의 이름을 딴 상수 파일을 만들어서 넣어두세요. 이 작업이 필요하기 전까지는 불변 상수는 메인 모듈의 `constants.js` 파일에 넣어두면 됩니다. + + *이유*: 자주 변하지 않더라도 변할 가능성이 있는 값들은 서비스로부터 받아서 사용해야 소스코드를 변경하지 않아도 되게 됩니다. 예를 들어 데이터를 받아오는 url 값은 상수로 저장해서 사용할 수도 있지만, 더 좋은 곳은 웹서비스로 부터 받아오는 것입니다. + + *이유*: 불변 상수는 브로바이더를 포함한 어떤 angular 컴포넌트에도 인젝트 될 수 있습니다. + + *이유*: 어떤 어플리케이션이 모듈화되어서 분리되어있다면 다른 어플리케이션에서 재사용 될 수 있습니다. 각각의 독립적인 모듈은 각각의 의존 상수를 불러옴으로 작동할 수 있어야 합니다. + + ```javascript + // Constants used by the entire app + angular + .module('app.core') + .constant('moment', moment); + + // Constants used only by the sales module + angular + .module('app.sales') + .constant('events', { + ORDER_CREATED: 'event_order_created', + INVENTORY_DEPLETED: 'event_inventory_depleted' + }); + ``` + +**[Back to top](#table-of-contents)** + +## File Templates and Snippets +Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. +파일 템플릿이나 스니펫을 사용하면 일관적인 스타일과 패턴을 지킬수 있습니다. 웹 개발용 에디터와 IDE들에서 사용 가능한 템플릿과 스니펫을 알려 드리겠습니다. + +### Sublime Text +###### [Style [Y250](#style-y250)] + + - Angular 스니팻은 이 스타일과 가이드라인을 따릅니다. + + - Download the [Sublime Angular snippets](assets/sublime-angular-snippets?raw=true) + - Place it in your Packages folder + - Restart Sublime + - In a JavaScript file type these commands followed by a `TAB` + + ```javascript + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngmodule // creates an Angular module + ngservice // creates an Angular service + ngfilter // creates an Angular filter + ``` + +### Visual Studio +###### [Style [Y251](#style-y251)] + + - Angular 파일 템플릿은 이 스타일과 가이드라인을 따릅니다 [SideWaffle](http://www.sidewaffle.com). + + - Download the [SideWaffle](http://www.sidewaffle.com) Visual Studio extension (vsix file) + - Run the vsix file + - Restart Visual Studio + +### WebStorm +###### [Style [Y252](#style-y252)] + + - Angular 라이브 템플릿들은 이 스타일과 가이드라인을 따릅니다. + + - Download the [webstorm-angular-live-templates.xml](assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml?raw=true) + - Place it in your [templates folder](https://www.jetbrains.com/webstorm/help/project-and-ide-settings.html) + - Restart WebStorm + - In a JavaScript file type these commands followed by a `TAB`: + + ```javascript + // These are full file snippets containing an IIFE + ngapp // creates an Angular module setter + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngfilter // creates an Angular filter + ngservice // creates an Angular service + + // These are partial snippets intended to be chained + ngconfig // defines a configuration phase function + ngmodule // creates an Angular module getter + ngroute // defines an Angular ngRoute 'when' definition + ngrun // defines a run phase function + ngstate // creates an Angular UI Router state definition + ``` + + *각각의 템플릿 또한 여기서 다운받을 수 있습니다. [webstorm-angular-live-templates](assets/webstorm-angular-live-templates?raw=true)* + +### Atom +###### [Style [Y253](#style-y253)] + + - Angular 스니팻은 이 스타일과 가이드라인을 따릅니다. + ``` + apm install angularjs-styleguide-snippets + ``` + or + - Open Atom, then open the Package Manager (Packages -> Settings View -> Install Packages/Themes) + - Search for the package 'angularjs-styleguide-snippets' + - Click 'Install' to install the package + + - In a JavaScript file type these commands followed by a `TAB` + + ```javascript + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngmodule // creates an Angular module + ngservice // creates an Angular service + ngfilter // creates an Angular filter + ``` + +### Brackets +###### [Style [Y254](#style-y254)] + + - Angular 스니팻은 이 스타일과 가이드라인을 따릅니다. + + - Download the [Brackets Angular snippets](assets/brackets-angular-snippets.yaml?raw=true) + - Brackets Extension manager ( File > Extension manager ) + - Install ['Brackets Snippets (by edc)'](https://github.com/chuyik/brackets-snippets) + - Click the light bulb in brackets' right gutter + - Click `Settings` and then `Import` + - Choose the file and select to skip or override + - Click `Start Import` + + - In a JavaScript file type these commands followed by a `TAB` + + ```javascript + // These are full file snippets containing an IIFE + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngapp // creates an Angular module setter + ngservice // creates an Angular service + ngfilter // creates an Angular filter + + // These are partial snippets intended to chained + ngmodule // creates an Angular module getter + ngstate // creates an Angular UI Router state definition + ngconfig // defines a configuration phase function + ngrun // defines a run phase function + ngwhen // defines an Angular ngRoute 'when' definition + ngtranslate // uses $translate service with its promise + ``` + +### vim +###### [Style [Y255](#style-y255)] + + - Angular 스니팻은 이 스타일과 가이드라인을 따릅니다. + + - Download the [vim Angular snippets](assets/vim-angular-snippets?raw=true) + - set [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) + - copy snippets to snippet directory + + - vim UltiSnips snippets that follow these styles and guidelines. + + - Download the [vim Angular UltiSnips snippets](assets/vim-angular-ultisnips?raw=true) + - set [UltiSnips](https://github.com/SirVer/ultisnips) + - copy snippets to UltiSnips directory + + ```javascript + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngmodule // creates an Angular module + ngservice // creates an Angular service + ngfilter // creates an Angular filter + ``` + +### Visual Studio Code + +###### [Style [Y256](#style-y256)] + + - [Visual Studio Code](http://code.visualstudio.com) 스니팻은 이 스타일과 가이드라인을 따릅니다. + + - Download the [VS Code Angular snippets](assets/vscode-snippets/javascript.json?raw=true) + - copy snippets to snippet directory, or alternatively copy and paste the snippets into your existing ones + + ```javascript + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngmodule // creates an Angular module + ngservice // creates an Angular service + ``` + +**[Back to top](#table-of-contents)** + +## Yeoman Generator +###### [Style [Y260](#style-y260)] + +[HotTowel yeoman generator](http://jpapa.me/yohottowel)를 이용하여 앱을 만들면 이 스타일 가이드를 따르는 앱의 시작 부분을 생성하여 줍니다. + +1. Install generator-hottowel + + ``` + npm install -g generator-hottowel + ``` + +2. Create a new folder and change directory to it + + ``` + mkdir myapp + cd myapp + ``` + +3. Run the generator + + ``` + yo hottowel helloWorld + ``` + +**[Back to top](#table-of-contents)** + +## Routing +클라이언트-사이드 라우팅은 화면과 작은 템플릿과 디렉티브들로 구성된 합성 뷰 사이의 네비게이션 흐름을 생성하는데 중요합니다. + +###### [Style [Y270](#style-y270)] + + - 클라이언트-사이드 라우팅을 위해서는 [AngularUI 라우터](http://angular-ui.github.io/ui-router/)를 사용하세요. + + *이유*: UI 라우터는 Angular 라우터의 모든 기능을 제공하고 추가적으로 내포 라우터, 상태를 제공합니다. + + *이유*: 구문법은 Angular 라우터와 매우 흡사하고 UI Router로 갈아타기 쉽게 되어있습니다. + + - 주의: `routerHelperProvider` 와 같은 프로바이더를 사용하여 진행 단계간에 파일들의 전체적인 설정을 도울 수 있습니다. + + ```javascript + // customers.routes.js + angular + .module('app.customers') + .run(appRun); + + /* @ngInject */ + function appRun(routerHelper) { + routerHelper.configureStates(getStates()); + } + + function getStates() { + return [ + { + state: 'customer', + config: { + abstract: true, + template: '', + url: '/customer' + } + } + ]; + } + ``` + + ```javascript + // routerHelperProvider.js + angular + .module('blocks.router') + .provider('routerHelper', routerHelperProvider); + + routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; + /* @ngInject */ + function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { + /* jshint validthis:true */ + this.$get = RouterHelper; + + $locationProvider.html5Mode(true); + + RouterHelper.$inject = ['$state']; + /* @ngInject */ + function RouterHelper($state) { + var hasOtherwise = false; + + var service = { + configureStates: configureStates, + getStates: getStates + }; + + return service; + + /////////////// + + function configureStates(states, otherwisePath) { + states.forEach(function(state) { + $stateProvider.state(state.state, state.config); + }); + if (otherwisePath && !hasOtherwise) { + hasOtherwise = true; + $urlRouterProvider.otherwise(otherwisePath); + } + } + + function getStates() { return $state.get(); } + } + } + ``` + +###### [Style [Y271](#style-y271)] + + - 뷰를 위한 라우팅 값을 모듈 안에 넣어두세요. 각각의 모듈은 뷰를 위한 라우팅 정보를 모듈 안에 두어야 합니다. + + *이유*: 각각의 모듈은 단독적으로 실행될 수 있어야 합니다. + + *이유*: 모듈을 제거하거나 추가할 때, 앱은 존재하는 뷰에 대해서만 라우팅 포인트를 가지고 있어야 합니다. + + *이유*: 홀로 남겨진 라우트가 생길 염려없이 어플리케이션의 부분을 활성화 비활성화 하기 쉽도록 해줍니다. + +**[Back to top](#table-of-contents)** + +## Task Automation +[Gulp](http://gulpjs.com) 또는 [Grunt](http://gruntjs.com)를 사용하여 자동화 처리를 사용하세요. Gult는 설정보다는 코드 자체에 무게를 더 주는 반면 Grunt는 설정을 더 중요하게 생각합니다. 개인적으로는 읽고 작성하기가 쉬워서 Gulp를 선호합니다. 하지만 둘다 정말 멋집니다. + +> 여기를 참고하여 gulp 자동화 업무 패턴을 배우세요 [Gulp Pluralsight course](http://jpapa.me/gulpps). + +###### [Style [Y400](#style-y400)] + + - 업무 자동화를 이용하여 `*.module.js` 패턴의 모듈 정의 파일을 리스팅 하세요. + + *이유*: Angular는 모듈이 등록되어 사용되기 전에 모듈 정의가 필요합니다. + + *이유*: `*.module.js`와 같은 패턴을 이용하여 모듈 이름을 지어놨기 때문에 찾아내기 쉽습니다. + + ```javascript + var clientApp = './src/client/app/'; + + // Always grab module files first + var files = [ + clientApp + '**/*.module.js', + clientApp + '**/*.js' + ]; + ``` + +**[Back to top](#table-of-contents)** + +## Filters + +###### [Style [Y420](#style-y420)] + + - 복잡한 오브젝트 그래프의 모든 프로퍼티를 스캔하기 위한 필터 사용을 자제하세요. 프로퍼티를 선택하기 위해 필터를 사용하세요. + + *이유*: 현명하게 사용되지 못한 필터는 남용될 수 있고 성능에 부정적 영향을 줄 수 있습니다. 예를 들어 크고 깊은 오브젝트 그래프를 필터로 걸르면 안 됩니다. + +**[Back to top](#table-of-contents)** + +## Angular docs +나머지 부분, API 참고는 [Angular 문서](//docs.angularjs.org/api)여기로 가시면 됩니다. + +## Contributing + +수정과 추가를 위해서는 이슈를 먼저 발행하시기 바랍니다. 이 가이드에 질문이 있으면 리파지토리에 이슈를 남겨주세요. 오타를 발견하면 pull request를 만들어주세요. 이렇게 하는 이유는 github의 기능을 최대한 사용해서 이슈와 PR이 어떻게 이루어 졌는지를 알려주기 위함입니다. 이런 정보는 구글로 검색도 가능합니다. 왜냐구요? 이상하게도 당신이 질문이 있다면 다른사람들도 그런 질문을 가지기 때문입니다. 여기서 어떻게 기여할 수 있는지 배울 수 있습니다. + +*이 저장소에 기여함으로서 당신의 콘텐츠가 이 저장소의 라이센트의 대상이 됨을 동의합니다.* + +### Process + 1. Discuss the changes in a GitHub issue. + 2. Open a Pull Request, reference the issue, and explain the change and why it adds value. + 3. The Pull Request will be evaluated and either merged or declined. + +## License + +_tldr; Use this guide. Attributions are appreciated._ + +### Copyright + +Copyright (c) 2014-2015 [John Papa](http://johnpapa.net) + +### (The MIT License) +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**[Back to top](#table-of-contents)** From fb271b037fd984b0e44d7e3433e7c343303eceea Mon Sep 17 00:00:00 2001 From: Ivan Coronado Date: Tue, 12 Jan 2016 08:55:00 +0100 Subject: [PATCH 016/114] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f58b961..e4e8c082 100644 --- a/README.md +++ b/README.md @@ -1472,7 +1472,8 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see } }); } - + + moviesPrepService.$inject = ['movieService']; function moviesPrepService(movieService) { return movieService.getMovies(); } From fdb82a32fda2a3f4521b65cdaa8d6ae7865e2de9 Mon Sep 17 00:00:00 2001 From: Lennon Jesus Date: Tue, 12 Jan 2016 15:30:39 -0200 Subject: [PATCH 017/114] completing pt-BR and improving translation --- i18n/pt-BR.md | 344 +++++++++++++++++++++++++------------------------- 1 file changed, 175 insertions(+), 169 deletions(-) diff --git a/i18n/pt-BR.md b/i18n/pt-BR.md index de5ee76c..db9539a2 100644 --- a/i18n/pt-BR.md +++ b/i18n/pt-BR.md @@ -293,11 +293,11 @@ ou *Controladores* - Utilize a sintaxe [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) ao invés da sintaxe `clássica controller com $scope`. - **Por que?**: Controllers são construídos, "iniciados", e fornecem um nova instância única, e a sintaxe `controllerAs` é mais próxima de um construtor JavaScript do que a `sintaxe clássica do $scope`. + **Por que?** Controllers são construídos, "iniciados", e fornecem um nova instância única, e a sintaxe `controllerAs` é mais próxima de um construtor JavaScript do que a `sintaxe clássica do $scope`. - **Por que?**: Isso promove o uso do binding de um objeto "pontuado", ou seja, com propriedades na View (ex. `customer.name` ao invés de `name`), que é mais contextual, legível, e evita qualquer problema com referências que podem ocorrer sem a "pontuação" + **Por que?** Isso promove o uso do binding de um objeto "pontuado", ou seja, com propriedades na View (ex. `customer.name` ao invés de `name`), que é mais contextual, legível, e evita qualquer problema com referências que podem ocorrer sem a "pontuação" - **Por que?**: Ajuda a evitar o uso de chamadas ao `$parent` nas Views com controllers aninhados. + **Por que?** Ajuda a evitar o uso de chamadas ao `$parent` nas Views com controllers aninhados. ```html @@ -319,9 +319,9 @@ ou *Controladores* - A sintaxe `controllerAs` usa o `this` dentro dos controllers que fica ligado ao `$scope`. - **Por que?**: O `controllerAs` é uma forma mais simples de lidar com o `$scope`. Você ainda poderá fazer o bind para a View e ainda poderá acessar os métodos do `$scope`. + **Por que?** O `controllerAs` é uma forma mais simples de lidar com o `$scope`. Você ainda poderá fazer o bind para a View e ainda poderá acessar os métodos do `$scope`. - **Por que?**: Ajuda a evitar a tentação de usar os métodos do `$scope` dentro de um controller quando seria melhor evitá-los ou movê-los para um factory. Considere utilizar o `$scope` em um factory, ou em um controller apenas quando necessário. Por exemplo, quando publicar e subscrever eventos usando [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), ou [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) considere mover estes casos para um factory e invocá-los a partir do controller. + **Por que?** Ajuda a evitar a tentação de usar os métodos do `$scope` dentro de um controller quando seria melhor evitá-los ou movê-los para um factory. Considere utilizar o `$scope` em um factory, ou em um controller apenas quando necessário. Por exemplo, quando publicar e subscrever eventos usando [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), ou [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) considere mover estes casos para um factory e invocá-los a partir do controller. ```javascript /* evite */ @@ -343,7 +343,7 @@ ou *Controladores* - Utilize uma variável de captura para o `this` quando usar a sintaxe `controllerAs`. Escolha um nome de variável consistente como `vm`, que representa o ViewModel. - **Por que?**: A palavra-chave `this` é contextual e quando usada em uma função dentro de um controller pode mudar seu contexto. Capturando o contexto do `this` evita a ocorrência deste problema. + **Por que?** A palavra-chave `this` é contextual e quando usada em uma função dentro de um controller pode mudar seu contexto. Capturando o contexto do `this` evita a ocorrência deste problema. ```javascript /* evite */ @@ -382,9 +382,9 @@ ou *Controladores* - Coloque os objetos que precisam de bind no início do controller, em ordem alfabética, e não espalhados através do código do controller. - **Por que?**: Colocar os objetos que precisam de bind no início torna mais fácil de ler e te ajuda a instantaneamente identificar quais objetos do controller podem ser utilizados na View. + **Por que?** Colocar os objetos que precisam de bind no início torna mais fácil de ler e te ajuda a instantaneamente identificar quais objetos do controller podem ser utilizados na View. - **Por que?**: Setar funções anônimas pode ser fácil, mas quando essas funções possuem mais de 1 linha do código elas podem dificultar a legibilidade. Definir as funções abaixo dos objetos que necessitam de bind (as funções serão elevadas pelo JavaScript Hoisting) move os detalhes de implementação para o final do controller, mantém os objetos que necessitam de bind no topo, e deixa o código mais fácil de se ler. + **Por que?** Setar funções anônimas pode ser fácil, mas quando essas funções possuem mais de 1 linha do código elas podem dificultar a legibilidade. Definir as funções abaixo dos objetos que necessitam de bind (as funções serão elevadas pelo JavaScript Hoisting) move os detalhes de implementação para o final do controller, mantém os objetos que necessitam de bind no topo, e deixa o código mais fácil de se ler. ```javascript /* evite */ @@ -471,15 +471,15 @@ ou *Controladores* - Utilize declarações de funções para esconder detalhes de implementação. Mantenha seus objetos que necessitam de bind no topo. Quando você precisar fazer o bind de uma função no controller, aponte ela para a declaração de função que aparece no final do arquivo. Ela está ligada diretamente aos objetos que precisam de bind no início do arquivo. Para mais detalhes veja [este post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - **Por que?**: Colocar os objetos que precisam de bind no início torna mais fácil de ler e te ajuda a instantaneamente identificar quais objetos do controller podem ser utilizados na View. (Mesmo do item anterior.) + **Por que?** Colocar os objetos que precisam de bind no início torna mais fácil de ler e te ajuda a instantaneamente identificar quais objetos do controller podem ser utilizados na View. (Mesmo do item anterior.) - **Por que?**: Colocar os detalhes de implementação de uma função no final do arquivo coloca a complexidade fora do foco, logo, você pode focar nas coisas importantes no topo. + **Por que?** Colocar os detalhes de implementação de uma função no final do arquivo coloca a complexidade fora do foco, logo, você pode focar nas coisas importantes no topo. - **Por que?**: Declarações de funções são içadas, logo, não existe problema de se utilizar uma função antes dela ser definida (como haveria com expressões de função). + **Por que?** Declarações de funções são içadas, logo, não existe problema de se utilizar uma função antes dela ser definida (como haveria com expressões de função). - **Por que?**: Você nunca precisará se preocupar com declarações de funções quebrarem seu código por colocar `var a` antes de `var b` por que `a` depende de `b`. + **Por que?** Você nunca precisará se preocupar com declarações de funções quebrarem seu código por colocar `var a` antes de `var b` por que `a` depende de `b`. - **Por que?**: A ordenação é crítica em expressões de função. + **Por que?** A ordenação é crítica em expressões de função. ```javascript /** @@ -545,11 +545,11 @@ ou *Controladores* - Remova a lógica do controller delegando ela a services e factories. - **Por que?**: A lógica pode ser reutilizada em múltiplos controllers quando colocada em um service e exposta através de uma função. + **Por que?** A lógica pode ser reutilizada em múltiplos controllers quando colocada em um service e exposta através de uma função. - **Por que?**: A lógica em um serviço pode ser mais facilmente isolada em um teste unitário, enquanto a lógica feita no controlador pode ser facilmente [mockada](http://www.thoughtworks.com/pt/insights/blog/mockists-are-dead-long-live-classicists). + **Por que?** A lógica em um serviço pode ser mais facilmente isolada em um teste unitário, enquanto a lógica feita no controlador pode ser facilmente [mockada](http://www.thoughtworks.com/pt/insights/blog/mockists-are-dead-long-live-classicists). - **Por que?**: Remove as dependências e esconde os detalhes de implementação do controlador. + **Por que?** Remove as dependências e esconde os detalhes de implementação do controlador. ```javascript /* evite */ @@ -583,9 +583,9 @@ ou *Controladores* ### Keep Controllers Focused - - Defina um controller para a view, e tente não reutilizar o controller para outras views. Ao invés disso, coloque as lógicas reaproveitáveis em factories e mantenha o controller simples e focado em sua view. + - Defina um controller para a view e tente não reutilizar o controller para outras views. Ao invés disso, coloque as lógicas reaproveitáveis em factories e mantenha o controller simples e focado em sua view. - **Por que?**: Reutilizar controllers em várias views é arriscado e um boa cobertura de testes end to end (e2e) é obrigatório para se garantir estabilidade em grandes aplicações. + **Por que?** Reutilizar controllers em várias views é arriscado e um boa cobertura de testes end to end (e2e) é obrigatório para se garantir estabilidade em grandes aplicações. ### Assigning Controllers @@ -593,7 +593,7 @@ ou *Controladores* Nota: Se uma View é carregada de outra forma que não seja através de uma rota, então utilize a sintaxe `ng-controller="Avengers as vm"`. - **Por que?**: Parear os controllers nas rotas permite diferentes rotas invocarem diferentes pares de controllers e views. Quando um controller é utilizado na view usando a sintaxe [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), esta view sempre será associada ao mesmo controller. + **Por que?** Parear os controllers nas rotas permite diferentes rotas invocarem diferentes pares de controllers e views. Quando um controller é utilizado na view usando a sintaxe [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), esta view sempre será associada ao mesmo controller. ```javascript /* evite - quando utilizar com uma rota e emparelhamento dinâmico é desejado */ @@ -701,11 +701,11 @@ ou *Membros acessíveis no topo* - Exponha os membros que podem ser invocados no serviço (a interface) no topo, utilizando uma técnica derivada do [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - **Por que?**: Colocando no topo os membros que podem ser invocados da factory, a leitura torna-se mais fácil e ajuda a identificar imediatamente quais membros da factory podem ser invocados e testados através de teste unitário (e/ou mock). + **Por que?** Colocando no topo os membros que podem ser invocados da factory, a leitura torna-se mais fácil e ajuda a identificar imediatamente quais membros da factory podem ser invocados e testados através de teste unitário (e/ou mock). - **Por que?**: É especialmente útil quando o arquivo torna-se muito longo e ajuda a evitar a necessidade de rolagem para ver o que é exposto. + **Por que?** É especialmente útil quando o arquivo torna-se muito longo e ajuda a evitar a necessidade de rolagem para ver o que é exposto. - **Por que?**: Definir as funções conforme você escreve o código pode ser fácil, mas quando essas funções tem mais que 1 linha de código, elas podem reduzir a leitura e causar rolagem. Definir a interface no topo do que pode ser invocado da factory, torna a leitura mais fácil e mantém os detalhes de implementação mais abaixo. + **Por que?** Definir as funções conforme você escreve o código pode ser fácil, mas quando essas funções tem mais que 1 linha de código, elas podem reduzir a leitura e causar rolagem. Definir a interface no topo do que pode ser invocado da factory, torna a leitura mais fácil e mantém os detalhes de implementação mais abaixo. ```javascript /* evite */ @@ -758,15 +758,15 @@ ou *Declarações de função para esconder detalhes de implementação* - Use function declarations (declarações de função) para esconder detalhes de implementação. Mantenha os membros acessíveis da factory no topo. Aponte as function declarations que aparecem posteriormente no arquivo. Para mais detalhes leia [esse post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - **Por que?**: Colocando os membros acessíveis no topo, a leitura torna-se mais fácil e ajuda a identificar imediatamente quais membros da factory podem ser acessados externamente. + **Por que?** Colocando os membros acessíveis no topo, a leitura torna-se mais fácil e ajuda a identificar imediatamente quais membros da factory podem ser acessados externamente. - **Por que?**: Colocando os detalhes de implementação da função posteriormente no arquivo move a complexidade para fora da visão, permitindo que você veja as coisas mais importantes no topo. + **Por que?** Colocando os detalhes de implementação da função posteriormente no arquivo move a complexidade para fora da visão, permitindo que você veja as coisas mais importantes no topo. - **Por que?**: Function declarations (declarações de função) são içadas (hoisted) para que não hajam preocupações em utilizar uma função antes que ela seja definida (como haveria com function expressions (expressões de função)). + **Por que?** Function declarations (declarações de função) são içadas (hoisted) para que não hajam preocupações em utilizar uma função antes que ela seja definida (como haveria com function expressions (expressões de função)). - **Por que?**: Você nunca deve se preocupar com function declaration (declarações de função) onde `var a` está antes de `var b` vai ou não quebrar o seu código porque `a` depende de `b`. + **Por que?** Você nunca deve se preocupar com function declaration (declarações de função) onde `var a` está antes de `var b` vai ou não quebrar o seu código porque `a` depende de `b`. - **Por que?**: A ordem é crítica com function expressions (expressões de função) + **Por que?** A ordem é crítica com function expressions (expressões de função) ```javascript /** @@ -861,11 +861,11 @@ ou *Chamadas de dados separadas* - A lógica de refatoração (refactor) para operações com dados e interação com dados na factory. Faça serviços de dados responsáveis por chamadas XHR, armazenamento local (local storage), armazenamento em memória (stashing) ou outras operações com dados. - **Por que?**: A responsabilidade dos controladores (controllers) é para a apresentação e coleta de informações da view. Eles não devem se importar como os dados são adquiridos, somente como "perguntar" por eles. Separar os serviços de dados (data services), move a lógica de como adquiri-los para o serviço e deixa o controlador (controller) mais simples e focado na view. + **Por que?** A responsabilidade dos controladores (controllers) é para a apresentação e coleta de informações da view. Eles não devem se importar como os dados são adquiridos, somente como "perguntar" por eles. Separar os serviços de dados (data services), move a lógica de como adquiri-los para o serviço e deixa o controlador (controller) mais simples e focado na view. - **Por que?**: Isso torna mais fácil testar (mock ou real) as chamadas de dados quando estiver testando um controlador (controller) que utiliza um serviço de dados (data service). + **Por que?** Isso torna mais fácil testar (mock ou real) as chamadas de dados quando estiver testando um controlador (controller) que utiliza um serviço de dados (data service). - **Por que?**: A implementação de um serviço de dados (data service) pode ter um código bem específico para lidar com o repositório de dados. Isso pode incluir cabeçalhos (headers), como comunicar com os dados ou outros serviços, como $http. Separando a lógica de dados em um serviço, coloca toda a lógica somente em um local e esconde a implementação de consumidores de fora (talvez um controlador (controller)), tornado mais fácil mudar a implementação. + **Por que?** A implementação de um serviço de dados (data service) pode ter um código bem específico para lidar com o repositório de dados. Isso pode incluir cabeçalhos (headers), como comunicar com os dados ou outros serviços, como $http. Separando a lógica de dados em um serviço, coloca toda a lógica somente em um local e esconde a implementação de consumidores de fora (talvez um controlador (controller)), tornado mais fácil mudar a implementação. ```javascript /* recomendado */ @@ -937,7 +937,7 @@ ou *Retorne uma promessa de chamadas de dados* - Quando chamar um serviço de dados (data service) que retorna uma promessa (promise), como o $http, retorne uma promessa (promise) na função que está chamando também. - **Por que?**: Você pode encandear as promessas (promises) juntas e definir ações após a promessa (promise) da chamada do dado ser completada, resolvendo ou rejeitando a promessa (promise). + **Por que?** Você pode encandear as promessas (promises) juntas e definir ações após a promessa (promise) da chamada do dado ser completada, resolvendo ou rejeitando a promessa (promise). ```javascript /* recomendado */ @@ -987,9 +987,9 @@ ou *Limite 1 por arquivo* - Crie uma diretiva (directive) por arquivo. Nomeie o arquivo pela diretiva. - **Por que?**: É fácil misturar todas as diretivas em um arquivo, mas é difícil depois separá-las, já que algumas são compartilhadas entre aplicativos, outras pelos módulos (modules) e algumas somente para um módulo. + **Por que?** É fácil misturar todas as diretivas em um arquivo, mas é difícil depois separá-las, já que algumas são compartilhadas entre aplicativos, outras pelos módulos (modules) e algumas somente para um módulo. - **Por que?**: Uma diretiva (directive) por arquivo é mais fácil de dar manutenção. + **Por que?** Uma diretiva (directive) por arquivo é mais fácil de dar manutenção. ```javascript /* evite */ @@ -1008,17 +1008,17 @@ ou *Limite 1 por arquivo* .directive('sharedSpinner', sharedSpinner); - function orderCalendarRange() { - /* detalhes de implementação */ - } + function orderCalendarRange() { + /* detalhes de implementação */ + } - function salesCustomerInfo() { - /* detalhes de implementação */ - } + function salesCustomerInfo() { + /* detalhes de implementação */ + } - function sharedSpinner() { - /* detalhes de implementação */ - } + function sharedSpinner() { + /* detalhes de implementação */ + } ``` ```javascript @@ -1079,14 +1079,14 @@ ou *Limite a manipulação do DOM* - Quando estiver manipulando o DOM diretamente, utilize uma diretiva (directive). Se formas alternativas podem ser utilizadas, como: utilizar CSS para setar estilos ou [serviços de animação (animation services)](https://docs.angularjs.org/api/ngAnimate), Angular templating, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) ou [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), então prefira utilizá-los. Por exemplo, se uma diretiva simplesmente esconde ou mostra um elemento, use ngHide/ngShow. - **Por que?**: A manipulação do DOM pode ser difícil de testar, debugar, e há melhores maneiras (ex: CSS, animações (animations), templates). + **Por que?** A manipulação do DOM pode ser difícil de testar, debugar, e há melhores maneiras (ex: CSS, animações (animations), templates). ### Provide a Unique Directive Prefix ou *Forneça um prefixo único para as diretivas* - Forneça um curto, único e descritivo prefixo para a diretiva, como `acmeSalesCustomerInfo`, que é declarado no HTML como `acme-sales-customer-info`. - **Por que?**: Um prefixo curto e único identifica o contexto e a origem da diretiva. Por exemplo, o prefixo `cc-` pode indicar que a diretiva é parte de um aplicativo da CodeCamper, enquanto a diretiva `acme-` pode indicar uma diretiva para a companhia Acme. + **Por que?** Um prefixo curto e único identifica o contexto e a origem da diretiva. Por exemplo, o prefixo `cc-` pode indicar que a diretiva é parte de um aplicativo da CodeCamper, enquanto a diretiva `acme-` pode indicar uma diretiva para a companhia Acme. Nota: Evite `ng-`, pois são reservadas para as diretivas do AngularJS. Pesquise largamente as diretivas utilizadas para evitar conflitos de nomes, como `ion-` que são utilizadas para o [Ionic Framework](http://ionicframework.com/). @@ -1095,9 +1095,9 @@ ou *Restringir para elementos e atributos* - Quando criar uma diretiva que faça sentido por si só como um elemento, utilize restrição `E` (elemento personalizado) e opcionalmente `A` (atributo personalizado). Geralmente, se ela pode ter o seu próprio controlador (controller), `E` é o apropriado. Em linhas gerais, `EA` é permitido, mas atente para a implementação como elemento quando faz sentido por si só e como atributo quando estende algo existente no DOM. - **Por que?**: Faz sentido. + **Por que?** Faz sentido. - **Por que?**: Nós podemos utilizar uma diretiva como uma classe (class), mas se a diretiva está realmente agindo como um elemento, faz mais sentido utilizar como um elemento, ou pelo menos como um atributo. + **Por que?** Nós podemos utilizar uma diretiva como uma classe (class), mas se a diretiva está realmente agindo como um elemento, faz mais sentido utilizar como um elemento, ou pelo menos como um atributo. Nota: EA é o padrão para o Angular 1.3 + @@ -1157,7 +1157,7 @@ ou *Diretivas e "ControladorComo"* - Utilize a sintaxe `controller as` com uma diretiva para consistência com o uso de `controller as` com os pares view e controlador (controller). - **Por que?**: Faz sentido e não é difícil. + **Por que?** Faz sentido e não é difícil. Nota: A diretiva (directive) abaixo demonstra algumas maneiras que você pode utilizar escopos (scopes) dentro de link e controller de uma diretiva, utilizando controllerAs. Eu coloquei o template somente para manter tudo em um mesmo local. @@ -1221,7 +1221,7 @@ ou *Ativação de promessas no controlador* - Resolva a lógica de inicialização no controlador (controller) em uma função `iniciar`. - **Por que?**: Colocando a lógica de inicialização em um lugar consistente no controlador (controller), torna mais fácil de localizar, mais consistente para testar e ajuda a evitar o espalhamento da lógica de inicialização pelo controlador (controller). + **Por que?** Colocando a lógica de inicialização em um lugar consistente no controlador (controller), torna mais fácil de localizar, mais consistente para testar e ajuda a evitar o espalhamento da lógica de inicialização pelo controlador (controller). Nota: Se você precisa cancelar a rota condicionalmente antes de utilizar o controlador (controller), utilize uma resolução de rota (route resolve). @@ -1264,7 +1264,7 @@ ou *Resolução de promessas na rota* - Quando o controlador (controller) depende de uma promessa ser resolvida, resolva as dependências no `$routeProvider` antes da lógica do controlador (controller) ser executada. Se você precisa cancelar a rota condicionalmente antes do controlador (controller) ser ativado, utilize uma resolução de rota (route resolve). - **Por que?**: Um controlador (controller) pode precisar de dados antes de ser carregado. Esses dados podem vir de uma promessa (promise) através de uma factory personalizada ou [$http](https://docs.angularjs.org/api/ng/service/$http). Utilizar [resolução de rota (route resolve)](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) permite as promessas (promises) serem resolvidas antes da lógica do controlador (controller) ser executada, então ele pode executar ações através dos dados dessa promessa (promise). + **Por que?** Um controlador (controller) pode precisar de dados antes de ser carregado. Esses dados podem vir de uma promessa (promise) através de uma factory personalizada ou [$http](https://docs.angularjs.org/api/ng/service/$http). Utilizar [resolução de rota (route resolve)](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) permite as promessas (promises) serem resolvidas antes da lógica do controlador (controller) ser executada, então ele pode executar ações através dos dados dessa promessa (promise). ```javascript /* evite */ @@ -1330,7 +1330,7 @@ ou *Não seguro para Minificação* - Evite usar o atalho de sintaxe de declarar dependências sem usar uma abordagem segura para minificação. - **Por que?**: Os parâmetros do componente (por ex. controller, factory, etc) serão convertidos em variáveis encurtadas. Por exemplo, `common` e `dataservice` podem virar `a` ou `b` e não serem encontrados pelo AngularJS. + **Por que?** Os parâmetros do componente (por ex. controller, factory, etc) serão convertidos em variáveis encurtadas. Por exemplo, `common` e `dataservice` podem virar `a` ou `b` e não serem encontrados pelo AngularJS. ```javascript /* evite - não seguro para minificação*/ @@ -1354,11 +1354,11 @@ ou *Identifique Dependências Manualmente* - Use `$inject` para identificar manualmente suas dependências de componentes do AngularJS. - **Por que?**: Esta técnica espelha a técnica usada por [`ng-annotate`](https://github.com/olov/ng-annotate), a qual eu recomendo para automatizar a criação de dependências seguras para minificação. Se `ng-annotate` detectar que a injeção já foi feita, ela não será duplicada. + **Por que?** Esta técnica espelha a técnica usada por [`ng-annotate`](https://github.com/olov/ng-annotate), a qual eu recomendo para automatizar a criação de dependências seguras para minificação. Se `ng-annotate` detectar que a injeção já foi feita, ela não será duplicada. - **Por que?**: Isto salvaguarda suas dependências de serem vulneráveis a problemas de minificação quando parâmetros podem ser encurtados. Por exemplo, `common` e `dataservice` podem se tornar `a` ou `b` e não serem encontrados pelo AngularJS. + **Por que?** Isto salvaguarda suas dependências de serem vulneráveis a problemas de minificação quando parâmetros podem ser encurtados. Por exemplo, `common` e `dataservice` podem se tornar `a` ou `b` e não serem encontrados pelo AngularJS. - **Por que?**: Evite criar dependências in-line pois listas longas podem ser difíceis de ler no array. Além disso, pode ser confuso o array ser uma série de strings enquanto o último item é a função do componente. + **Por que?** Evite criar dependências in-line pois listas longas podem ser difíceis de ler no array. Além disso, pode ser confuso o array ser uma série de strings enquanto o último item é a função do componente. ```javascript /* evite */ @@ -1428,9 +1428,9 @@ ou *Identifique Dependências do Resolvedor de Rotas Manualmente* - Use $inject para identificar manualmente as dependências do seu resolvedor de rotas para componentes do AngularJS. - **Por que?**: Esta técnica separa a função anônima do resolvedor de rota, tornando-a mais fácil de ler. + **Por que?** Esta técnica separa a função anônima do resolvedor de rota, tornando-a mais fácil de ler. - **Por que?**: Uma chamada a `$inject` pode facilmente preceder o resolvedor para fazer qualquer dependência segura para minificação. + **Por que?** Uma chamada a `$inject` pode facilmente preceder o resolvedor para fazer qualquer dependência segura para minificação. ```javascript /* recomendado */ @@ -1461,9 +1461,9 @@ ou *Minificação e Anotação* - Use [ng-annotate](//github.com/olov/ng-annotate) para [Gulp](http://gulpjs.com) ou [Grunt](http://gruntjs.com) e comente as funções que precisam de injeção de dependência automatizada usando `/** @ngInject */` - **Por que?**: Isso protege seu código de qualquer dependência que pode não estar usando práticas seguras para minificação. + **Por que?** Isso protege seu código de qualquer dependência que pode não estar usando práticas seguras para minificação. - **Por que?**: [`ng-min`](https://github.com/btford/ngmin) está deprecated. + **Por que?** [`ng-min`](https://github.com/btford/ngmin) está deprecated. > Eu prefiro Gulp pois sinto que é mais fácil de escrever, de ler, e de debugar. @@ -1537,7 +1537,7 @@ ou *Minificação e Anotação* - Utilize [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) ou [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) para tarefas de build automatizadas. Injete `/* @ngInject */` antes de qualquer função que tenha dependências. - **Por que?**: ng-annotate vai capturar todas as dependências, mas as vezes requer dicas utilizando a sintaxe `/* @ngInject */` . + **Por que?** ng-annotate vai capturar todas as dependências, mas as vezes requer dicas utilizando a sintaxe `/* @ngInject */` . O código abaixo é um exemplo de uma task Gulp utilizando ngAnnotate @@ -1572,7 +1572,7 @@ ou *decoradores* - Utilize um [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator), no seu config utilizando o serviço [`$provide`](https://docs.angularjs.org/api/auto/service/$provide), no serviço [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) para realizar ações customizadas quando um erro ocorrer. - **Por que?**: Fornece um caminho consistente para manipular erros não tratados pelo Angular em tempo de desenvolvimento ou execução (run-time). + **Por que?** Fornece um caminho consistente para manipular erros não tratados pelo Angular em tempo de desenvolvimento ou execução (run-time). Nota: Outra opção é sobrescrever o serviço ao invés de utilizar um decorator. Esta é uma boa opção, mas se você quer manter o comportamento padrão e estender, o decorator é recomendado. @@ -1613,7 +1613,7 @@ ou *Coletores de exceção* - Criar um factory que expõe uma interface para capturar e tratar adequadamente as exceções. - **Por que?**: Fornece uma forma consistente de coletar exceções que podem ser lançadas no seu código (ex. durante uma chamada XHR ou uma promessa (promise) que falhou). + **Por que?** Fornece uma forma consistente de coletar exceções que podem ser lançadas no seu código (ex. durante uma chamada XHR ou uma promessa (promise) que falhou). Nota: O coletor de exceção é bom para coletar e reagir às exceções específicas das chamadas que você sabe que podem ser lançadas. Por exemplo, quando realizar uma chamada XHR que retorna dados de um serviço remoto e você quer coletar qualquer exceção desse serviço, reagindo de uma maneira única. @@ -1643,9 +1643,9 @@ ou *Coletores de exceção* - Gerencie e log todos os erros de routing utilizando o [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). - **Por que?**: Fornece uma maneira consistente de gerenciar erros relacionados a routing. + **Por que?** Fornece uma maneira consistente de gerenciar erros relacionados a routing. - **Por que?**: Potencialmente fornece uma melhor experiência de usuário se um erro de routing ocorrer e você redirecionar o usuário para uma tela amigável com mais detalhes ou opções de recuperação. + **Por que?** Potencialmente fornece uma melhor experiência de usuário se um erro de routing ocorrer e você redirecionar o usuário para uma tela amigável com mais detalhes ou opções de recuperação. ```javascript /* recomendado */ @@ -1681,18 +1681,18 @@ ou *Nomenclatura* * o nome do arquivo (`avengers.controllers.js`) * o nome de componente registrado pelo Angular (`AvengersController`) - **Por que?**: As convenções de nomenclatura ajudam a fornecer uma maneira consistente de encontrar algo à primeira vista. Consistência dentro do projeto é vital. Consistência dentro de um time é importante. Consistência em toda a empresa proporciona uma enorme eficiência. + **Por que?** As convenções de nomenclatura ajudam a fornecer uma maneira consistente de encontrar algo à primeira vista. Consistência dentro do projeto é vital. Consistência dentro de um time é importante. Consistência em toda a empresa proporciona uma enorme eficiência. - **Por que?**: As convenções de nomenclatura deveriam simplesmente te ajudar a encontrar trechos do seu código mais rápido e torná-lo mais fácil de se entender. + **Por que?** As convenções de nomenclatura deveriam simplesmente te ajudar a encontrar trechos do seu código mais rápido e torná-lo mais fácil de se entender. ### Feature File Names ou *Nome para funcionalidades* - Use nomes consistentes para todos os componentes seguindo um padrão que descreve a funcionalidade do componente e, em seguida, (opcionalmente) o seu tipo. Meu padrão recomendado é `feature.type.js`. - *Por que?*: Fornece uma maneira consistente para identificar componentes mais rapidamente. + **Por que?** Fornece uma maneira consistente para identificar componentes mais rapidamente. - *Por que?*: Fornece um padrão apropriado pra qualquer tarefa automatizada. + **Por que?** Fornece um padrão apropriado pra qualquer tarefa automatizada. ```javascript /** @@ -1757,9 +1757,9 @@ ou *Nome para aquivos de testes* - Nomeie as especificações de testes de forma similar aos componentes que elas testam, com o sufixo `spec`. - **Por que?**: Fornece um modo consistente para identificar rapidamente os componentes. + **Por que?** Fornece um modo consistente para identificar rapidamente os componentes. - **Por que?**: Fornece padrões de correspondência para o [karma](http://karma-runner.github.io/) ou outros test runners. + **Por que?** Fornece padrões de correspondência para o [karma](http://karma-runner.github.io/) ou outros test runners. ```javascript /** @@ -1776,9 +1776,9 @@ ou *Nomes para controller* - Use nomes consistentes para todos os controllers nomeados após as sua funcionalidade. Use UpperCamelCase para os controllers, assim como para seus construtores. - **Por que?**: Fornece um modo consistente para identificar e referenciar os controllers. + **Por que?** Fornece um modo consistente para identificar e referenciar os controllers. - **Por que?**: O UpperCamelCase é o modo mais comum para identificar objetos que serão instanciados através de construtores. + **Por que?** O UpperCamelCase é o modo mais comum para identificar objetos que serão instanciados através de construtores. ```javascript /** @@ -1798,7 +1798,7 @@ ou *sufixo "Controllers"* - Complemente o nome do controller com ou sem o sufixo `Controller`. Escolha uma opção, não ambas. - **Por que?**: O sufixo `Controller` é mais usado e mais descritivo. + **Por que?** O sufixo `Controller` é mais usado e mais descritivo. ```javascript /** @@ -1831,9 +1831,9 @@ ou *Nomes para factory* - Use nomes consistentes para todas as factories nomeadas após sua funcionalidade. Use a conveção camelCase para services e factories. Evite prefixos com `$`. - **Por que?**: Fornece um modo consistente de identificar e referenciar rapidamente as factories. + **Por que?** Fornece um modo consistente de identificar e referenciar rapidamente as factories. - **Por que?**: Evite colisão de nomes com factories e services pré-programados que usam o prefixo `$`. + **Por que?** Evite colisão de nomes com factories e services pré-programados que usam o prefixo `$`. ```javascript /** @@ -1853,7 +1853,7 @@ ou *Nomes para directive* - Use nomes consistentes para todas as directives usando a convenção camelCase. Use um prefixo curto para descrever a área a qual a directive pertence (como prefixo da compania ou do projeto). - **Por que?**: Fornece um modo consistente de identificar e referenciar rapidamente os componentes. + **Por que?** Fornece um modo consistente de identificar e referenciar rapidamente os componentes. ```javascript /** @@ -1875,18 +1875,18 @@ ou *Módulos* - Quando há vários módulos, o arquivo principal deste módulo é nomeado `app.module.js`, enquanto os módulos dependentes são nomeados de acordo com o que eles representam. Por exemplo, um módulo admin é nomeado `admin.module.js`. Os nomes dos respectivos módulos registrados seriam `app` e `admin`. - **Por que?**: Fornece consistência para múltiplos módulos, e para expansão para grandes aplicações. + **Por que?** Fornece consistência para múltiplos módulos, e para expansão para grandes aplicações. - **Por que?**: Fornece um modo fácil para automação de tarefas, a fim de carregar todos as definições dos módulos em primeiro lugar, então os demais arquivos (empacotamento). + **Por que?** Fornece um modo fácil para automação de tarefas, a fim de carregar todos as definições dos módulos em primeiro lugar, então os demais arquivos (empacotamento). ### Configuration ou *Configuração* - Separe a configuração do módulo em seu próprio arquivo, nomeado após o módulo. Um arquivo de configuração para o módulo principal `app` é nomeado `app.config.js` (ou simplesmente `config.js`). Uma configuração para o módulo `admin.module.js` é nomeada `admin.config.js`. - **Por que?**: Separa a configuração do módulo da definição, dos componentes e do código ativo. + **Por que?** Separa a configuração do módulo da definição, dos componentes e do código ativo. - **Por que?**: Fornece um local identificável para definir as configurações de um módulo. + **Por que?** Fornece um local identificável para definir as configurações de um módulo. ### Routes ou *Rotas* @@ -1916,7 +1916,7 @@ ou *Localizar* - Torne a localização do seu código: intuitiva, simples e rápida. - **Por que?**: Acho que isso é super importante para um projeto. Se a equipe não pode encontrar rapidamente os arquivos que precisam para trabalhar, eles não serão capazes de trabalhar da forma mais eficiente possível, e a estrutura precisa mudar. Você pode não saber o nome do arquivo ou onde os arquivos relacionados estão, por isso, colocando-os nos locais mais intuitivos e próximos uns dos outros, economiza uma boa parcela de tempo. Uma pasta descrevendo a estrutura pode ajudá-lo. + **Por que?** Acho que isso é super importante para um projeto. Se a equipe não pode encontrar rapidamente os arquivos que precisam para trabalhar, eles não serão capazes de trabalhar da forma mais eficiente possível, e a estrutura precisa mudar. Você pode não saber o nome do arquivo ou onde os arquivos relacionados estão, por isso, colocando-os nos locais mais intuitivos e próximos uns dos outros, economiza uma boa parcela de tempo. Uma pasta descrevendo a estrutura pode ajudá-lo. ``` /bower_components @@ -1941,21 +1941,21 @@ ou *Identificar* - Quando você olhar para um arquivo, prontamente você deve saber o que ele contém e o que representa. - **Por que?**: Você gasta menos tempo caçando e procurando por código, e torna-se mais eficiente. Se isso significa nomes de arquivos mais longos, então que assim seja. Seja descritivo nos nomes de arquivos e mantenha o conteúdo do arquivo somente com 1 componente. Evite arquivos com vários controladores (controllers), múltiplos serviços (services), ou uma mistura. Existem exceções de 1 regra por arquivo quando eu tenho um conjunto de recursos muito pequenos que estão todos relacionados uns aos outros, eles ainda são facilmente identificáveis. + **Por que?** Você gasta menos tempo caçando e procurando por código, e torna-se mais eficiente. Se isso significa nomes de arquivos mais longos, então que assim seja. Seja descritivo nos nomes de arquivos e mantenha o conteúdo do arquivo somente com 1 componente. Evite arquivos com vários controladores (controllers), múltiplos serviços (services), ou uma mistura. Existem exceções de 1 regra por arquivo quando eu tenho um conjunto de recursos muito pequenos que estão todos relacionados uns aos outros, eles ainda são facilmente identificáveis. ### Flat ou *Plano* - Mantenha uma estrutura plana de pastas o máximo que for possível. Quando você tiver 7 arquivos ou mais, comece a considerar separá-los. - **Por que?**: Ninguém quer pesquisar 7 níveis de pastas para encontrar um arquivo. Pense sobre menus em web sites - nada mais profundo do que 2 níveis deve ser levado a sério. Em uma estrutura de pastas não há nenhum número mágico, mas quando uma pasta tem 7-10 arquivos, pode ser a hora de criar subpastas. Baseie-se no seu nível de conforto. Use uma estrutura mais plana até que haja um valor óbvio (para ajudar o resto do LIFT) na criação de uma nova pasta. + **Por que?** Ninguém quer pesquisar 7 níveis de pastas para encontrar um arquivo. Pense sobre menus em web sites - nada mais profundo do que 2 níveis deve ser levado a sério. Em uma estrutura de pastas não há nenhum número mágico, mas quando uma pasta tem 7-10 arquivos, pode ser a hora de criar subpastas. Baseie-se no seu nível de conforto. Use uma estrutura mais plana até que haja um valor óbvio (para ajudar o resto do LIFT) na criação de uma nova pasta. ### T-DRY (Try to Stick to DRY) ou *Tente manter-se em DRY - Não repita a si mesmo* - Mantenha-se DRY, mas não fique louco e sacrifique a legibilidade. - **Por que?**: Não ficar se repetindo é importante, mas não é crucial se acabar sacrificando os outros itens do LIFT, por isso eu chamo de T-DRY (Tente não ficar se repetindo). Eu não quero escrever session-view.html para uma view, porque obviamente é uma view. Se não é óbvio ou uma convenção, então eu renomeio. + **Por que?** Não ficar se repetindo é importante, mas não é crucial se acabar sacrificando os outros itens do LIFT, por isso eu chamo de T-DRY (Tente não ficar se repetindo). Eu não quero escrever session-view.html para uma view, porque obviamente é uma view. Se não é óbvio ou uma convenção, então eu renomeio. **[De volta ao topo](#tabela-de-conte%C3%BAdo)** @@ -1973,20 +1973,20 @@ ou *Orientações gerais* - Coloque os componentes que definem o layout geral do aplicativo em uma pasta chamada `layout`. Eles podem incluir uma view e um controller que agem como recipiente para o app, navegação, menus, áreas de conteúdo, e outras regiões. - **Por que?**: Organiza todos os layouts em um único lugar reutilizado em toda a aplicação. + **Por que?** Organiza todos os layouts em um único lugar reutilizado em toda a aplicação. ### Folders-by-Feature Structure ou *Estrutura de Pastas-por-Recurso* - Crie pastas nomeadas para cada recurso que elas representam. Quando uma pasta cresce ao ponto de conter mais de 7 arquivos, comece considerar a criação de uma pasta para eles. O seu limite pode ser diferente, por isso, ajuste conforme necessário. - **Por que?**: O desenvolvedor pode localizar o código, identificar o que cada arquivo representa em resumo, a estrutura é plana como deve ser, e não há nenhum nome repetido ou redundante. + **Por que?** O desenvolvedor pode localizar o código, identificar o que cada arquivo representa em resumo, a estrutura é plana como deve ser, e não há nenhum nome repetido ou redundante. - **Por que?**: As orientações LIFT estão todas sendo respeitadas. + **Por que?** As orientações LIFT estão todas sendo respeitadas. - **Por que?**: Através da organização do conteúdo, ajuda a reduzir o app de tornar-se desordenado e mantêm alinhado com as diretrizes LIFT. + **Por que?** Através da organização do conteúdo, ajuda a reduzir o app de tornar-se desordenado e mantêm alinhado com as diretrizes LIFT. - **Por que?**: Quando há um grande número de arquivos (10+) localizá-los é mais fácil com estruturas de pastas consistentes e mais difícil em estruturas planas. + **Por que?** Quando há um grande número de arquivos (10+) localizá-los é mais fácil com estruturas de pastas consistentes e mais difícil em estruturas planas. ```javascript /** @@ -2079,33 +2079,33 @@ ou *Muitos módulos pequenos e independentes* - Crie pequenos módulos que encapsulem uma única responsabilidade. - **Por que?**: Aplicações modulares tornam fácil o acoplamento, pois permitem que as equipes de desenvolvimento construam fatias verticais das aplicações e juntem tudo de forma incremental. Isto significa que podemos acoplar novos recursos enquanto os desenvolvemos. + **Por que?** Aplicações modulares tornam fácil o acoplamento, pois permitem que as equipes de desenvolvimento construam fatias verticais das aplicações e juntem tudo de forma incremental. Isto significa que podemos acoplar novos recursos enquanto os desenvolvemos. ### Create an App Module ou *Crie um módulo da aplicação* - Crie um módulo raiz para a aplicação, cujo papel é: reunir todos os outros módulos e funcionalidades da sua aplicação. Nomeie ele de acordo com a sua aplicação. - **Por que?**: Angular incentiva padrões de modularidade e de separação. Criando um módulo raiz da aplicação cujo papel é o de amarrar os outros módulos juntos, fica muito simples de adicionar ou remover módulos na sua aplicação. + **Por que?** Angular incentiva padrões de modularidade e de separação. Criando um módulo raiz da aplicação cujo papel é o de amarrar os outros módulos juntos, fica muito simples de adicionar ou remover módulos na sua aplicação. ### Keep the App Module Thin ou *Mantenha o módulo da aplicação leve* - Somente coloque a lógica para reunir o aplicativo no módulo da aplicação. Deixe os recursos em seus próprios módulos. - *Why?*: Adding additional roles to the application root to get remote data, display views, or other logic not related to pulling the app together muddies the app module and make both sets of features harder to reuse or turn off. - **Por que?**: Colocar funções adicionais na raiz da aplicação para obter dados remoto, modos de exibição, ou outra lógica não relacionada com o acoplamento do aplicativo, torna mais difícil reutilizar os recursos ou mesmo, desligá-los. + **Por que?** Adding additional roles to the application root to get remote data, display views, or other logic not related to pulling the app together muddies the app module and make both sets of features harder to reuse or turn off. + **Por que?** Colocar funções adicionais na raiz da aplicação para obter dados remoto, modos de exibição, ou outra lógica não relacionada com o acoplamento do aplicativo, torna mais difícil reutilizar os recursos ou mesmo, desligá-los. - **Por que?**: O módulo da aplicação torna-se um manifesto que descreve os módulos que ajudam a definir a aplicação. + **Por que?** O módulo da aplicação torna-se um manifesto que descreve os módulos que ajudam a definir a aplicação. ### Feature Areas are Modules ou *Áreas de recursos são módulos* - Crie módulos que representem áreas de recursos, como: layout, serviços compartilhados e reutilizados, dashboards e recursos específicos do aplicativo (por exemplo, clientes, administrativo, vendas). - **Por que?**: Módulos independentes podem ser adicionados na aplicação com pouco ou nenhum esforço. + **Por que?** Módulos independentes podem ser adicionados na aplicação com pouco ou nenhum esforço. - **Por que?**: Sprints ou iterações podem focar em áreas de recursos e acoplá-los na aplicação ao fim da sprint ou iteração. + **Por que?** Sprints ou iterações podem focar em áreas de recursos e acoplá-los na aplicação ao fim da sprint ou iteração. **Por que²**: Separando as áreas de recursos em módulos, fica fácil de testar os módulos em isolamento e de reutilizar o código. @@ -2114,7 +2114,7 @@ ou *Blocos reutilizáveis são módulos* - Crie módulos que representam blocos reutilizáveis da aplicação para serviços comuns, como: tratamento de exceção, log, diagnósticos, segurança e armazenamento local. - **Por que?**: Esses tipos de recursos são necessários em muitas aplicações, então mantê-los separados em seus próprios módulos os torna genéricos e assim, podem ser reutilizados em diferentes aplicações. + **Por que?** Esses tipos de recursos são necessários em muitas aplicações, então mantê-los separados em seus próprios módulos os torna genéricos e assim, podem ser reutilizados em diferentes aplicações. ### Module Dependencies ou *Dependências do módulo* @@ -2123,11 +2123,11 @@ ou *Dependências do módulo* ![Moduluaridade e Dependências](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) - **Por que?**: O módulo principal do aplicativo contém um rápido manifesto para identificar os recursos da aplicação. + **Por que?** O módulo principal do aplicativo contém um rápido manifesto para identificar os recursos da aplicação. - **Por que?**: Cada área de recurso contém um manifesto mostrando as suas dependências, assim, ela pode ser colocada como uma dependência em outras aplicação e ainda continuar funcionando. + **Por que?** Cada área de recurso contém um manifesto mostrando as suas dependências, assim, ela pode ser colocada como uma dependência em outras aplicação e ainda continuar funcionando. - **Por que?**: Recursos intra-aplicação como serviços compartilhados de dados, tornam-se muito mais fácil de localizar e compartilhar atráves do `app.core` (escolha o seu nome favorito para esse módulo). + **Por que?** Recursos intra-aplicação como serviços compartilhados de dados, tornam-se muito mais fácil de localizar e compartilhar atráves do `app.core` (escolha o seu nome favorito para esse módulo). Nota: Essa é uma estratégia para consistência. Existem muitas outras boas opções. Escolha uma que seja consistente, que siga as regras de dependência do Angular, e que seja fácil de manter e escalar. @@ -2139,28 +2139,28 @@ ou *Dependências do módulo* ## Angular $ Wrapper Services -### $document and $window +### $document e $window - - Use [`$document`](https://docs.angularjs.org/api/ng/service/$document) and [`$window`](https://docs.angularjs.org/api/ng/service/$window) instead of `document` and `window`. + - Use [`$document`](https://docs.angularjs.org/api/ng/service/$document) e [`$window`](https://docs.angularjs.org/api/ng/service/$window) ao invés de `document` e `window`. - *Why?*: These services are wrapped by Angular and more easily testable than using document and window in tests. This helps you avoid having to mock document and window yourself. + **Por que?** Estes services são encapsulados pelo Angular e mais fáceis de testar do que o uso de document e window. Isso ajuda a evitar que você tenha que mockar document e window por sua própria conta. -### $timeout and $interval +### $timeout e $interval - - Use [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) and [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) instead of `setTimeout` and `setInterval` . + - Use [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) e [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) ao invés de `setTimeout` e `setInterval` . - *Why?*: These services are wrapped by Angular and more easily testable and handle AngularJS's digest cycle thus keeping data binding in sync. + **Por que?** Estes services são encapsulados pelo Angular e mais fáceis de testar e lidar com o ciclo do AngularJS's mantendo o data binding em sincronismo. **[De volta ao topo](#tabela-de-conte%C3%BAdo)** ## Testing Testes unitários ajudam a manter o código limpo, tal como, eu inclui algumas recomendações de fundamentos para testes unitários com links para mais informações. -### Escreva testes com Histórias(Stories) +### Escreva testes com Histórias (Stories) - Escreva um grupo de testes para cada história. Comece com um teste em branco e preencha-o conforme você for escrevendo o código para a história. - *Por que?*: Escrevendo uma descrição de teste te ajudará a definir claramente o que a sua história vai fazer ou não vai fazer e como você poderá mensurar o sucesso. + **Por que?** Escrevendo uma descrição de teste te ajudará a definir claramente o que a sua história vai fazer ou não vai fazer e como você poderá mensurar o sucesso. ```javascript it('should have Avengers controller', function() { @@ -2182,11 +2182,11 @@ Testes unitários ajudam a manter o código limpo, tal como, eu inclui algumas r // and so on ``` -### Library para Testes +### Frameworks para Testes - Para teste unitários use [Jasmine](http://jasmine.github.io/) ou [Mocha](http://visionmedia.github.io/mocha/). - *Por que?*: Ambos, Jasmine e Mocha são amplamente utilizados na comunidade AngularJS. Ambos são estáveis, são mantidos e provém features de teste robustas. + **Por que?** Ambos, Jasmine e Mocha são amplamente utilizados na comunidade AngularJS. Ambos são estáveis, são mantidos e provém features de teste robustas. Nota: Se escolher Mocha, também considere a escolha de uma Assert Library como [Chai](http://chaijs.com). @@ -2194,27 +2194,27 @@ Testes unitários ajudam a manter o código limpo, tal como, eu inclui algumas r - Use [Karma](http://karma-runner.github.io) como seu test runner. - *Por que?*: Karma é fácil de configurar para executar apenas uma vez ou automaticamente enquanto você altera seu código. + **Por que?** Karma é fácil de configurar para executar apenas uma vez ou automaticamente enquanto você altera seu código. - *Por que?*: Karma se integra facilmente com seu processo de Integração Contínua ou através do Grunt ou Gulp. + **Por que?** Karma se integra facilmente com seu processo de Integração Contínua ou através do Grunt ou Gulp. - *Por que?*: Algumas IDE's estão começando a se integrar com o Karma, como [WebStorm](http://www.jetbrains.com/webstorm/) e [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). + **Por que?** Algumas IDE's estão começando a se integrar com o Karma, como [WebStorm](http://www.jetbrains.com/webstorm/) e [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). - *Por que?*: Karma funciona muito bem com os líderes de automação de tarefas, como [Grunt](http://www.gruntjs.com) (com [grunt-karma](https://github.com/karma-runner/grunt-karma)) e [Gulp](http://www.gulpjs.com) (com [gulp-karma](https://github.com/lazd/gulp-karma)). + **Por que?** Karma funciona muito bem com os líderes de automação de tarefas, como [Grunt](http://www.gruntjs.com) (com [grunt-karma](https://github.com/karma-runner/grunt-karma)) e [Gulp](http://www.gulpjs.com) (com [gulp-karma](https://github.com/lazd/gulp-karma)). ### Stubbing e Spying - Utilize Sinon para stubbing e spying. - *Por que?*: Sinon funciona bem tanto com Jasmine quanto com Mocha e amplia as features de stubbing e spying que eles oferecem. + **Por que?** Sinon funciona bem tanto com Jasmine quanto com Mocha e amplia as features de stubbing e spying que eles oferecem. - *Por que?*: Sinon faz ficar mais fácil alternar entre Jasmine e Mocha, se você quiser tentar ambos. + **Por que?** Sinon faz ficar mais fácil alternar entre Jasmine e Mocha, se você quiser tentar ambos. ### Headless Browser - Use [PhantomJS](http://phantomjs.org/) para executar seus testes no servidor. - *Por que?*: PhantomJS é um headless browser que executa os testes sem um navegador "visual". Ou seja, você não precisa instalar Chrome, Safari, IE ou outros navegadores no seu servidor. + **Por que?** PhantomJS é um headless browser que executa os testes sem um navegador "visual". Ou seja, você não precisa instalar Chrome, Safari, IE ou outros navegadores no seu servidor. Nota: Você deve continuar testando em todos os navegadores em seu ambiente, conforme apropriado para seu público alvo. @@ -2222,13 +2222,13 @@ Testes unitários ajudam a manter o código limpo, tal como, eu inclui algumas r - Execute JSHint no seus testes. - *Por que?*: Testes são códigos. JSHint ajuda a identificar problemas de qualidade de código que podem fazer com que o teste execute de maneira errada. + **Por que?** Testes são códigos. O JSHint ajuda a identificar problemas de qualidade de código que podem fazer com que o teste execute de maneira errada. ### Ignore algumas regras globais do JSHint no seus testes - Faça com que as regras de teste permitam globais comuns, tais como `describe` e `expect`. - *Por que?*: Seus testes são codigos e como tal necessitam da mesma atenção e regras de qualidade que todo o seu código de produção. No entanto, as variáveis globais usadas pelo framework de teste, por exemplo, podem ser ignoradas para que você as utilize em seus testes. + **Por que?** Seus testes são codigos e como tal necessitam da mesma atenção e regras de qualidade que todo o seu código de produção. No entanto, as variáveis globais usadas pelo framework de teste, por exemplo, podem ser ignoradas para que você as utilize em seus testes. ```javascript /* global sinon, describe, it, afterEach, beforeEach, expect, inject */ @@ -2239,44 +2239,46 @@ Testes unitários ajudam a manter o código limpo, tal como, eu inclui algumas r **[De volta ao topo](#tabela-de-conte%C3%BAdo)** ## Animations +ou *Animações* -### Usage +### Utilização - - Use subtle [animations with AngularJS](https://docs.angularjs.org/guide/animations) to transition between states for views and primary visual elements. Include the [ngAnimate module](https://docs.angularjs.org/api/ngAnimate). The 3 keys are subtle, smooth, seamless. + - Use [animações com AngularJS](https://docs.angularjs.org/guide/animations) para transitar suavemente entre a visualização de views e elementos visuais primários. Inclúa o módulo [ngAnimate](https://docs.angularjs.org/api/ngAnimate). Os três princípios são sutilidade, suavidade e sem remendos. - *Why?*: Subtle animations can improve User Experience when used appropriately. + **Por que?** Animações sutis podem melhorar a Experiência de Usuário (UX) quando usadas adequadamente. - *Why?*: Subtle animations can improve perceived performance as views transition. + **Por que?** Animações sutis podem aumentar a sensação de performance durante a alteração entre views. ### Sub Second - - Use short durations for animations. I generally start with 300ms and adjust until appropriate. + - Use animações de curta duração. Eu geralmente começo com 300ms e ajusto conforme necessário. - *Why?*: Long animations can have the reverse affect on User Experience and perceived performance by giving the appearance of a slow application. + **Por que?** Animações de longa duração podem impactar negativamente na experiência do usuário e em sua percepção de desempenho, dando a impressão de ser uma aplicação lenta. ### animate.css - - Use [animate.css](http://daneden.github.io/animate.css/) for conventional animations. + - Use [animate.css](http://daneden.github.io/animate.css/) para animações convencionais. - *Why?*: The animations that animate.css provides are fast, smooth, and easy to add to your application. + **Por que?** As animações fornecidas por animate.css são rápidas, suaves e fáceis de adicionar à sua aplicação. - *Why?*: Provides consistency in your animations. + **Por que?** Provê consistência em suas animações. - *Why?*: animate.css is widely used and tested. + **Por que?** animate.css amplamente utilizado e testado. - Note: See this [great post by Matias Niemelä on Angular animations](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) + Nota: Leia este [excelente post do Matias Niemelä sobre Angular Animations](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) **[De volta ao topo](#tabela-de-conte%C3%BAdo)** ## Comments +ou *Comentários* ### jsDoc - - If planning to produce documentation, use [`jsDoc`](http://usejsdoc.org/) syntax to document function names, description, params and returns. Use `@namespace` and `@memberOf` to match your app structure. + - Se você planeja produzir documentação, use a sintaxe [`jsDoc`](http://usejsdoc.org/) para documentar nomes, descrições, parâmetros e retornos de funções. Use `@namespace` e `@memberOf` para adequar à estrutura de sua aplicação. - *Why?*: You can generate (and regenerate) documentation from your code, instead of writing it from scratch. + **Por que?** Você pode gerar (e regerar) documentação a partir do seu código ao invés de escrever do zero. - *Why?*: Provides consistency using a common industry tool. + **Por que?** Fornece consistência utilizando uma ferramenta comum no mercado. ```javascript /** @@ -2321,13 +2323,13 @@ Testes unitários ajudam a manter o código limpo, tal como, eu inclui algumas r ## JS Hint -### Use an Options File +### Use um arquivo de Options - - Use JS Hint for linting your JavaScript and be sure to customize the JS Hint options file and include in source control. See the [JS Hint docs](http://www.jshint.com/docs/) for details on the options. + - Use JS Hint para inspecionar seu JavaScript e não se esqueça de customizar o arquivo de configurações e versioná-lo no controle de versão. Veja [JS Hint docs](http://www.jshint.com/docs/) para detalhes a respeito das opções. - *Why?*: Provides a first alert prior to committing any code to source control. + **Por que?** Fornece um primeiro alerta antes de commitar qualquer código ao controle de versão. - *Why?*: Provides consistency across your team. + **Por que?** Provê consistência a seu time. ```javascript { @@ -2403,7 +2405,7 @@ Testes unitários ajudam a manter o código limpo, tal como, eu inclui algumas r - Cria uma *Constant* no Angular para variáveis globais de bibliotecas de terceiros. - *Por que?*: Fornece uma forma de injetar bibliotecas de terceiros que de outra forma seriam globais. Isso melhora a testabilidade do código permitindo a você conhecer mais facilmente quais dependências os seus componentes têm (evita vazamento de abstrações). Também permite que você simule estas dependências, o que faz sentido. + **Por que?** Fornece uma forma de injetar bibliotecas de terceiros que de outra forma seriam globais. Isso melhora a testabilidade do código permitindo a você conhecer mais facilmente quais dependências os seus componentes têm (evita vazamento de abstrações). Também permite que você simule estas dependências, o que faz sentido. ```javascript // constants.js @@ -2422,67 +2424,71 @@ Testes unitários ajudam a manter o código limpo, tal como, eu inclui algumas r **[De volta ao topo](#tabela-de-conte%C3%BAdo)** ## File Templates and Snippets -Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. +*ou Arquivos de Templates e Fragmentos* + +Use arquivos de templates ou fragmentos para auxiliar na consistência de estilos e padrões. Aqui estão alguns templates e/ou snippets(fragmentos) para algumas IDEs e editores de texto para desenvolvimento web. ### Sublime Text - - Angular snippets that follow these styles and guidelines. + - Angular snippets que seguem esses estilos e diretrizes. - - Download the [Sublime Angular snippets](assets/sublime-angular-snippets.zip) - - Place it in your Packages folder - - Restart Sublime - - In a JavaScript file type these commands followed by a `TAB` + - Download [Sublime Angular snippets](assets/sublime-angular-snippets.zip) + - Coloque-os na pasta de pacotes + - Reinicie o Sublime Text + - Em um arquivo JavaScript, use os comandos abaixo seguidos de `TAB` ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module + ngcontroller // cria um Angular controller + ngdirective // cria uma Angular directive + ngfactory // cria uma Angular factory + ngmodule // cria um Angular module ``` ### Visual Studio - - Angular file templates that follow these styles and guidelines can be found at [SideWaffle](http://www.sidewaffle.com) + - Angular snippets que seguem esses estilos e diretrizes podem ser encontrados em [SideWaffle](http://www.sidewaffle.com) - - Download the [SideWaffle](http://www.sidewaffle.com) Visual Studio extension (vsix file) - - Run the vsix file - - Restart Visual Studio + - Download [SideWaffle](http://www.sidewaffle.com) Visual Studio extension (arquivo vsix) + - Execute o arquivo vsix + - Reinicie o Visual Studio ### WebStorm - - Angular snippets and file templates that follow these styles and guidelines. You can import them into your WebStorm settings: + - Angular snippets que seguem esses estilos e diretrizes. Você pode importa-los na sua configuração do WebStorm: - - Download the [WebStorm Angular file templates and snippets](assets/webstorm-angular-file-template.settings.jar) - - Open WebStorm and go to the `File` menu - - Choose the `Import Settings` menu option - - Select the file and click `OK` - - In a JavaScript file type these commands followed by a `TAB`: + - Download [WebStorm Angular file templates and snippets](assets/webstorm-angular-file-template.settings.jar) + - Abra o WebStorm e selecione a opção `File` no menu + - Selecione a opção `Import Settings` no menu + - Localize o arquivo e selecione `OK` + - Em um arquivo JavaScript, use os comandos abaixo seguidos de `TAB`: ```javascript - ng-c // creates an Angular controller - ng-f // creates an Angular factory - ng-m // creates an Angular module + ng-c // cria um Angular controller + ng-f // cria uma Angular factory + ng-m // cria um Angular module ``` **[De volta ao topo](#tabela-de-conte%C3%BAdo)** ## Angular docs -For anything else, API reference, check the [Angular documentation](//docs.angularjs.org/api). +*ou Documentação Angular* + +Para qualquer outra coisa e documentação da API, visite a [documentação do Angular](//docs.angularjs.org/api). -## Contributing +## Contribuindo -Open an issue first to discuss potential changes/additions. If you have questions with the guide, feel free to leave them as issues in the repository. If you find a typo, create a pull request. The idea is to keep the content up to date and use github’s native feature to help tell the story with issues and PR’s, which are all searchable via google. Why? Because odds are if you have a question, someone else does too! You can learn more here at about how to contribute. +Primeiro, abra uma issue para discutir potenciais alterações / adições. Se você tiver dúvidas com o guia, sinta-se livre para abrir uma issue. Se você encontrar um erro de digitação, crie um pull request. A idéia é manter o conteúdo atualizado e usar o recurso nativo do github para ajudar a contar a história com issues e pull requests, que são pesquisáveis pelo Google. Por quê? Porque as probabilidades são de que se você tem uma dúvida, alguém também tem! Você pode saber mais aqui sobre como contribuir. -*By contributing to this repository you are agreeing to make your content available subject to the license of this repository.* +*Contribuindo com este repositório você aceita disponibilizar sua contribuição sob os termos de licenciamento deste repositório.* -### Process - 1. Discuss the changes in an Issue. - 1. Open a Pull Request, reference the issue, and explain the change and why it adds value. - 1. The Pull Request will be evaluated and either merged or declined. +### Processo + 1. Discuta as alterações em uma issue. + 1. Abra uma Pull Request, referencie a issue, eplique a alteração e como ela agrega valor. + 1. A Pull Request será avaliada e poderá ser aceita ou descartada. -## License +## Licença -_tldr; Use this guide. Attributions are appreciated._ +_tldr; Use este guia. Atribuições(menções) são apreciadas._ ### (The MIT License) From d80c275ccb38309ae9399b244316216e40a7bd3a Mon Sep 17 00:00:00 2001 From: John Papa Date: Tue, 12 Jan 2016 20:04:54 -0500 Subject: [PATCH 018/114] Revert "Update README.md" --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index e4e8c082..2f58b961 100644 --- a/README.md +++ b/README.md @@ -1472,8 +1472,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see } }); } - - moviesPrepService.$inject = ['movieService']; + function moviesPrepService(movieService) { return movieService.getMovies(); } From 77e5694f8d4e1800f91575e3bfcca82eb713414c Mon Sep 17 00:00:00 2001 From: 7cat <7upcat@gmail.com> Date: Wed, 13 Jan 2016 09:13:59 +0800 Subject: [PATCH 019/114] =?UTF-8?q?=E4=BF=AE=E8=AE=A2=E4=BA=86=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E8=87=AA=E5=8A=A8=E5=8C=96=E7=AB=A0=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84Code=20Over=20Configuration=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 参考Convention Over Configuration(约定优先原则)的翻译,修订了gulp&grunt比较中的 code over configuration 及configuration over code 的翻译,并保留了相关术语以便读者更容易理解。 --- i18n/zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/zh-CN.md b/i18n/zh-CN.md index 92e43d8a..ddf3a8c7 100644 --- a/i18n/zh-CN.md +++ b/i18n/zh-CN.md @@ -3023,7 +3023,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 **[返回顶部](#目录)** ## 任务自动化 -用[Gulp](http://gulpjs.com)或者[Grunt](http://gruntjs.com)来创建自动化任务。Gulp偏向于代码在配置之上,Grunt更倾向于配置高于代码。我更倾向于使用gulp,因为gulp写起来比较简单。 +用[Gulp](http://gulpjs.com)或者[Grunt](http://gruntjs.com)来创建自动化任务。Gulp偏向于代码优先原则(code over configuration)而Grunt更倾向于配置优先原则(configuration over code)。我更倾向于使用gulp,因为gulp写起来比较简单。 > 可以在我的[Gulp Pluralsight course](http://jpapa.me/gulpps)了解更多gulp和自动化任务的信息 From f3b5e7d86c1689d695c348e954b3d23d449164d5 Mon Sep 17 00:00:00 2001 From: 7cat <7upcat@gmail.com> Date: Wed, 13 Jan 2016 17:33:16 +0800 Subject: [PATCH 020/114] Revise Organizing Tests Chapter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原文内容: Place unit test files (specs) side-by-side with your client code. Place specs that cover server integration or test multiple components in a separate tests folder. 原文翻译: 把单元测试的文件放到一个独立的tests文件夹中,它和你的客户端代码并列。 缺失了可多个组件共用的测试代码以及服务器集成相关代码部分应该单独放置在一个单独的文件夹下的内容。 --- i18n/zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/zh-CN.md b/i18n/zh-CN.md index ddf3a8c7..370d259b 100644 --- a/i18n/zh-CN.md +++ b/i18n/zh-CN.md @@ -2455,7 +2455,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ### 组织测试 ###### [Style [Y197](#style-y197)] - - 把单元测试的文件放到一个独立的`tests`文件夹中,它和你的客户端代码并列。 + - 将单元测试文件(specs)同被测试客户端代码并列放在同一个文件夹下,将多个组件共用的测试文件以及服务器集成测试的文件放到一个单独的`tests`文件夹下。 *为什么?*:单元测试和源代码中的每一个组件和文件都有直接的相关性。 From 852233cb0da39c76e9b7ee7a11171a6298044235 Mon Sep 17 00:00:00 2001 From: Ugur Korfali Date: Fri, 15 Jan 2016 08:35:28 +0200 Subject: [PATCH 021/114] Turkish translation --- i18n/README.md | 1 + i18n/tr-TR.md | 3233 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 3234 insertions(+) create mode 100644 i18n/tr-TR.md diff --git a/i18n/README.md b/i18n/README.md index 9a016d3c..709fc26d 100644 --- a/i18n/README.md +++ b/i18n/README.md @@ -14,6 +14,7 @@ The [original English version](http://jpapa.me/ngstyles) is the source of truth, 1. [Simplified Chinese](zh-CN.md) by [Zhao Ke](https://github.com/natee) 1. [Spanish](es-ES.md) by [Alberto Calleja](https://github.com/AlbertoImpl) and [Gilberto](https://github.com/ingilniero) 1. [Korean](ko-KR.md) by [Joshua Ji](https://github.com/zirho) + 1. [Turkish](tr-TR.md) by [Uğur Korfalı](https://github.com/kel-sakal-biyik) ## Contributing Language translations are welcomed and encouraged. The success of these translations depends on the community. I highly encourage new translation contributions and help to keep them up to date. diff --git a/i18n/tr-TR.md b/i18n/tr-TR.md new file mode 100644 index 00000000..3a22b9fe --- /dev/null +++ b/i18n/tr-TR.md @@ -0,0 +1,3233 @@ +# Angular Stil Rehberi + +## Angular Ekibinden Destek +Angular takım lideri Igor Minar'a, rehberimi incelediği, geri bildirimde bulunduğu ve rehber olma görevini bana emanet ettiği için özellikle teşekkür ederim. + +##Amaç +*[@john_papa](//twitter.com/john_papa)'dan Takımlar için seçeneklendirilmiş stil rehberi* + +Eğer Angular projeleriniz için seçeneklendirilmiş bir sintaks, yöntem ve yapılandırma rehberi arıyorsanız, buyrun gelin. Bu stiller benim [Angular](//angularjs.org) sunumlarım, [Pluralsight eğitim kurslarım](http://pluralsight.com/training/Authors/Details/john-papa) ve takım çalışmalarımdan edindiğim deneyimlerle oluşturulmuştur. + +Bu rehberin amacı, kullandığım yöntemleri göstererek, hatta daha önemlisi neden bu yöntemleri seçtiğimi açıklayarak, Angular uygulamalarınızı geliştirirken size yol göstermektir. + +>Eğer bu rehberi beğendiyseniz, [Angular Patterns: Clean Code](http://jpapa.me/ngclean) isimli kursuma Pluralsight sitesinden bir bakın. Bu rehberle pekiltirici olacaktır. + + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + +## Topluluğun Aşmışlığı ve Referanslar +Asla izole olarak çalışmayın. Angular topluluğunu, deneyimlerini paylaşma konusunda tutukulu buluyorum. Örneğin, arkadaşım ve bir Angular uzmanı Todd Motto ile birçok stil ve yöntem üzerinde işbirliği yaptık. Birçoğunda hemfikir olduk, birkaçında farklı yollar izledik. [Todd'un rehberi'ni](https://github.com/toddmotto/angularjs-styleguide) de onun yaklaşımını anlamak ve karşılaştırma yapmak için incelemenizi öneririm + +Bir çok yöntem [Ward Bell](http://twitter.com/wardbell) ile yaptığımız eşli programlama seanslarında ortaya çıktı. Arkadaşım Ward bu rehberin nihai evrimine büyük katkılarda bulundu. + +## Örnek uygulama üzerinde yöntemler +Bu rehber *ne*, *neden* ve *nasıl* sorularına odaklanırken, yöntemleri deneyimlemenin yardımcı olacaığını düşünüyorum. Bu rehbere, bu rehberdeki yöntemleri ve tasarım desenlerini kullanan örnek bir uygulama eşlik ediyor. Bu uygulamayı [burada](https://github.com/johnpapa/ng-demos), `modular` klasörünün altında bulabilirsiniz. Üzerinde denemeler yapmaktan çekinmeyin. [Çalıştırma talimatları readme dosyasındadır](https://github.com/johnpapa/ng-demos/tree/master/modular). + +##Çeviriler +[Bu Angular rehberinin çevirileri](https://github.com/johnpapa/angular-styleguide/tree/master/i18n) gönüllü yardımcılar tarafından sağlanmaktadır + +## İçerik Listesi + + 1. [Tek İşlevsellik](#tek-islevsellik) + 1. [IIFE](#iife) + 1. [Modüller](#moduller) + 1. [Controller'lar](#controllerlar) + 1. [Servisler](#servisler) + 1. [Factory'ler](#factoryler) + 1. [Veri Servisleri](#veri-servisleri) + 1. [Directive'ler](#directiveler) + 1. [Promise'leri Controller'lar İçin Çözümlemek](#promiseleri-controllerlar-icin-cozumlemek) + 1. [Dependency Injection ve Manuel Annotation](#dependency-injection-ve-manuel-annotation) + 1. [Minification ve Annotation](#minification-ve-annotation) + 1. [Exception Yakalama](#exception-yakalama) + 1. [İsimlendirme](#isimlendirme) + 1. [Uygulama Yapısı ve LIFT Prensibi](#uygulama-yapisi-ve-lift-prensibi) + 1. [Uygulama Yapısı](#uygulama-yapisi) + 1. [Modülerlik](#modulerlik) + 1. [Başlangıç Mantığı](#baslangic-mantigi) + 1. [Angular Servisleri](#angular-servisleri) + 1. [Testler](#testler) + 1. [Animasyonlar](#animasyonlar) + 1. [Yorumlar](#yorumlar) + 1. [JSHint](#js-hint) + 1. [JSCS](#jscs) + 1. [Constant'lar](#constantlar) + 1. [Dosya Şablonları ve Snippetler](#dosya-sablonlari-ve-snippetler) + 1. [Yeoman Anayapı Üreticisi](#yeoman-anayapı-ureticisi) + 1. [Routing](#routing) + 1. [Görev Otomasyonu](#gorev-otomasyonu) + 1. [Filtreler](#filtreler) + 1. [Angular Dökümantasyonu](#angular-dokumantasyonu) + 1. [Katkıda Bulunmak](#katkida-bulunmak) + 1. [Lisans](#lisans) + +## Tek İşlevsellik + +### Kural 1 +###### [Stil [Y001](#style-y001)] + + - Her dosyaya yalnızca bir component tanımlayın. + Göreceğimiz örnek `app` modülünü ve bağımlılıklarını, conroller'ını ve factory'sini aynı dosyada tanımlıyor. + + ```javascript + /* sakınılacak stil */ + angular + .module('app', ['ngRoute']) + .controller('SomeController', SomeController) + .factory('someFactory', someFactory); + + function SomeController() { } + + function someFactory() { } + ``` + + Bu örnekte ise aynı component'lar farklı dosyalara ayrılmış durumdalar + + ```javascript + /* önerilen stil */ + + // app.module.js + angular + .module('app', ['ngRoute']); + ``` + + ```javascript + /* önerilen stil */ + + // someController.js + angular + .module('app') + .controller('SomeController', SomeController); + + function SomeController() { } + ``` + + ```javascript + /* önerilen stil */ + + // someFactory.js + angular + .module('app') + .factory('someFactory', someFactory); + + function someFactory() { } + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## IIFE +### JavaScript Kapsamları(Closures) +###### [Stil [Y010](#style-y010)] + - Angular component'larınızı Hemen Çalışan Fonksiyon İfadeleri (HÇFİ) ile kapsayın + > *Not: Hemen Çalışan Fonksiyon İfadeleri İngilizcede (Immediately Invoked Function Expression) olarak geçer. Bu fonksiyon bloğu içerisinde kalan kısım, tanımlanmasının ardından hemen çalıştırılır, fonksiyonun çağrılmasını beklemez* + + *Neden?*: HÇFİ değişkenleri global olarak tanımlanmaktan çıkarır. Bu yöntem değişkenlerin ve fonksiyonların global olarak beklenenden daha uzun tanımlı kalmasını ve aynı isimde olan değişken ve fonksiyonlarla çakışmasını engeller. + + *Neden?*: Kodunuz sıkıştırıldığı zaman ve üretim ortamın için tek bir javascript dosyası halinde paketlendiğinde, birçok yerel ve global değişken için çakışma hataları alabilirsiniz. HÇFİ sizi bu çakışmalara karşı korur ve her dosya için kendi değişken kapsamını tanımlar. + + ```javascript + /* sakınılacak stil */ + // logger.js + angular + .module('app') + .factory('logger', logger); + + // logger fonksiyonu global olarak tanımlanıyor + function logger() { } + + // storage.js + angular + .module('app') + .factory('storage', storage); + + // storage fonksiyonu global olarak tanımlanıyor + function storage() { } + ``` + + ```javascript + /** + * önerilen stil + * + * global tanımlamamız yok + */ + + // logger.js + (function() { + 'use strict'; + + angular + .module('app') + .factory('logger', logger); + + function logger() { } + })(); + + // storage.js + (function() { + 'use strict'; + + angular + .module('app') + .factory('storage', storage); + + function storage() { } + })(); + ``` + + - Not: Dökümanın sadeliğini korumak namına, bundan sonraki örneklerin HÇFİ fonksiyonları içinde tanımlandığını farzedin. + + - Not: HÇFİ'ler test kodunuzun fonksiyona özel değişkenlere erişmenizi engeller (Regular Expression, Yardımcı fonksiyonlar gibi). O yüzden bu fonksiyonları kendi başlarına test etmek daha iyidir. Ama yine de bu özel fonksiyonları component dışından erişilebilir kılarak test edebilirsiniz. + +**[İçerik Listesi](#icerik-listesi)** + +## Modüller + +### İsim Çakışmalarından Kaçının +###### [Stil [Y020](#style-y020)] + + - Alt modüller için eşsiz isimlendirme yöntemleri kullanın. + + *Neden?*: Eşsiz isimler modül isimlerinin çakışmasını engeller. Ayraçlar, modüller ve alt modüller arasındaki hiyerarşiyi kurmaya yardımcı olur. Örneğin `app` sizin ana modülünüz olsun. `app.dashboard` ve `app.users` modülleri alt modülleriniz olur ve `app` modülüne bağımlılık olarak eklenirler. + +### Modül Tanımlama Yöntemi (Setters) +###### [Stil [Y021](#style-y021)] + + - Modüllerinizi bir değişkene atama yapmadan setter sintaksını kullanarak tanımlayın. + + *Neden?*: Her component için bir dosya yöntemi ile, nadiren modülünüzü bir değişkene atama ihtiyacı hissedersiniz. + + ```javascript + /* kaçınılacak stil */ + var app = angular.module('app', [ + 'ngAnimate', + 'ngRoute', + 'app.shared', + 'app.dashboard' + ]); + ``` + + Setter sintaksı kullanımı + + ```javascript + /* önerilen stil */ + angular + .module('app', [ + 'ngAnimate', + 'ngRoute', + 'app.shared', + 'app.dashboard' + ]); + ``` + +### Modüle Ulaşma Yöntemi +###### [Stil [Y022](#style-y022)] + + - Modülünüze ulaşıp kullanırken değişkene atamak yerine getter sintaksını zincirleyerek kullanın. + + *Neden?*: Bu yöntem kodunuzu daha okunabilir kılar ve değişken çakışmalarını ve sızıntılarını engeller. + + ```javascript + /* kaçınılacak stil */ + var app = angular.module('app'); + app.controller('SomeController', SomeController); + + function SomeController() { } + ``` + + ```javascript + /* önerilen stil */ + angular + .module('app') + .controller('SomeController', SomeController); + + function SomeController() { } + ``` + +### Yaratma ve Kullanma +###### [Stil [Y023](#style-y023)] + + - Modülünüzü sadece bir kere yaratın ve diğer durumlar için getter sintaksını kullanın. + + *Neden?*: Modül sadece birkere yaratılmalıdır. Sonrasında bu yaratılan modül kullanılırç + + ```javascript + /* önerilen stil */ + + // modül yaratılır + angular.module('app', []); + + // modül kullanılır + angular.module('app'); + ``` + +### İsimli ve Anonoim Fonksiyonlar +###### [Style [Y024](#style-y024)] + + - Modülünüzün component'lerinin fonksiyonlarını isimli fonksiyonlar olarak tanımlayın. + + *Neden?*: Bu yöntem kodunuzu daha okunabilir kılar ve hata ayıklamak için kolaylık sağlar. Ayrcıa iç içe geçmiş fonksiyon bloklarının önüne geçcer. + + ```javascript + /* kaçınılacak stil */ + angular + .module('app') + .controller('Dashboard', function() { }) + .factory('logger', function() { }); + ``` + + ```javascript + /* önerilen stil */ + + // dashboard.js + angular + .module('app') + .controller('Dashboard', Dashboard); + + function Dashboard() { } + ``` + + ```javascript + // logger.js + angular + .module('app') + .factory('logger', logger); + + function logger() { } + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Controller'lar + +### controllerAs View Sintaksı +###### [Stil [Y030](#style-y030)] + + - [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) sintaksını klasik $scope'lu controller sintaksına tercih edin. + + *Neden?*: Controller'lar `new` kelimesi ile yaratılır ve uygulamanız içerisinde sadece bir örneği bulunur. `controllerAs` yöntemi JavaScript'in constructor yapısına daha yakındır. + + *Neden?*: View katmanında noktalı notasyonun kullanımını teşvik eder. (örneğin `customer.name` yerine `name`). Bu yöntem daha kolay okunur ve referans problemlerinin oluşmasını engeller. + + *Neden?*: İçiçe olan controller'larda veriye ulaşırken `$parent` kullanmanızı engeller. + + ```html + +
+ {{ name }} +
+ ``` + + ```html + +
+ {{ customer.name }} +
+ ``` + +### controllerAs Controller Sintaksı +###### [Stil [Y031](#style-y031)] + + - `controllerAs` sintaksını klasik $scope'lu controller sintaksına tercih edin. + + - `controllerAs` sintaksı controller içerisinde `this` kelimesini kullanır ve $scope'a bağlanırç + + *Neden?*: `controllerAs` `$scope` için bir sintaks süslemedir. Hala View'a bağlayabilir ve `$scope` fonksiyonlarına ulaşabilirsiniz. + + *Neden?*: `$scope` metodlarının bir Factory içerisinde tanımlanıp controller içerisinde çağrılmasındansa, controller içerisinde direk kullanılması eğilimine engel olur. `$scope`'u controller içerisinde sadece ihtiyaç olduğunda kullanın. Örneğin [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast) veya [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) kullanırkenö bunları bir factory içine taşıyıp, controller içerisinden çağırın. + + ```javascript + /* kaçınılan stil */ + function Customer($scope) { + $scope.name = {}; + $scope.sendMessage = function() { }; + } + ``` + + ```javascript + /* önerilen stil - ama bir sonraki bölüme bakın */ + function Customer() { + this.name = {}; + this.sendMessage = function() { }; + } + ``` + +### vm ile controllerAs +###### [Stil [Y032](#style-y032)] + + - `controllerAs` sintaksını kullanırken `this` kelimesi için yakalayıcı bir değişken kullanın. `vm` gibi tutarlı bir isim seçin. `vm`, ViewModel'in kısaltılmışıdır. + + *Neden?*: `this` kelimesi kullanıldığı yere göre içeriğini değiştirebilir. Baştan yakalayıcı bir değişkene atayarak hep aynı içeriği tutması sağlanır. + + ```javascript + /* kaçınılan stil */ + function Customer() { + this.name = {}; + this.sendMessage = function() { }; + } + ``` + + ```javascript + /* önerilen stil */ + function Customer() { + var vm = this; + vm.name = {}; + vm.sendMessage = function() { }; + } + ``` + + Not: [jshint](http://www.jshint.com/) uyarılarını kodun üstüne yorum ekleyerek engelleyebilirsiniz. Eğer fonksiyonunu UpperCasing yöntemi ile isimlendirdiyse buna ihtiyaç olmaz. Çünkü bu yöntem bu fonksiyonun bir constructor fonksiyonu olduğunu belirtir, ki Angular controller'ları de bir constructor fonksiyonudur. + + ```javascript + /* jshint validthis: true */ + var vm = this; + ``` + + Not: vm değişkenlerini izlerken ($watch) aşağıdaki sintaksı kullanabilirsiniz. ($watch yaratırken dikkatli olunmalıdır. Çümkü digest cycle'a yük bindrir.) + + ```html + + ``` + + ```javascript + function SomeController($scope, $log) { + var vm = this; + vm.title = 'Some Title'; + + $scope.$watch('vm.title', function(current, original) { + $log.info('vm.title was %s', original); + $log.info('vm.title is now %s', current); + }); + } + ``` + +### Bağlanacaklar Yukarı +###### [Style [Y033](#style-y033)] + + - Bağlanacak olan değişkenleri controller fonksiyonuzda aflabetik sıralanmış olarak tepeye koyun. Kod içerisinde dağılmış olarak bırakmayın. + + *Neden?*: Bağlancak değişkenleri tepeye koymak kod okunabilirliğini arttırır ve bir bakışta View'a hangi değişkenlerin bağlanacağını görebilirsiniz. + + *Neden?*: Anonim fonksiyonlar yaratmak hızlı ve kolaydır, ama bu fonksiyonlar bir satırdan fazla olurlarsa okunabilirliği düşürürler. Fonksiyonları bağlanabilir değişkenlerin altında tanımlarsanız (JavaScript'in hoisting özelliği ile yukarı taşınacaklardır) kodun okunması daha kolay olur. + + ```javascript + /* kaçınılacak stil */ + function Sessions() { + var vm = this; + + vm.gotoSession = function() { + /* ... */ + }; + vm.refresh = function() { + /* ... */ + }; + vm.search = function() { + /* ... */ + }; + vm.sessions = []; + vm.title = 'Sessions'; + ``` + + ```javascript + /* önerilen stil */ + function Sessions() { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = refresh; + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + + //////////// + + function gotoSession() { + /* */ + } + + function refresh() { + /* */ + } + + function search() { + /* */ + } + ``` + + ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) + + Not: Eğer fonksiyon bir satıra sığıyorsa, okunabilirlik etkilenmez ve bağlanacak değişkenlerle beraber tutulabilir. + + ```javascript + /* kaçınılacak stil */ + function Sessions(data) { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = function() { + /** + * lines + * of + * code + * affects + * readability + */ + }; + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + ``` + + ```javascript + /* önerilen stil */ + function Sessions(sessionDataService) { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = sessionDataService.refresh; // 1 liner is OK + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + ``` + +### Fonksiyon Tanımlamaları ve İmplementasyon Detaylarının Saklanması +###### [Stil [Y034](#style-y034)] + + - Fonksiyon tanımlamalarınızı implementasyon detaylarını saklamak için kullanın. View'a bağlanacak öğeleri yukarıda tanımlayın. Controller'ınızda bir fonksiyonu bağlama ihtiyacı hissettiğinizde, bu öğeyi bir fonksiyon tanımlamasına eşitleyin. Fonksiyonun implementasyon detaylarını kodun ileriki satırlarında yapın. Bu direk olarak "Bağlanacaklar Yukarı" başlığı ile ilintili. Daha fazla detay için bu [makaleme](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code) bakabilirsiniz. + + *Neden?*: Bağlanacak öğeleri yukarı taşımak okumayı kolaylaştırır ve controlller içerisinde hangi öğelerin View'a bağlandığını anında görmemizi sağlar. + + *Neden?*: Fonksiyonun implementasyonunu dosya içerisinde daha aşağılara taşımak kompleks kısımları göz önünden uzak tutar ve asıl önemli olan kısma odaklanmayı sağlarç + + *Neden?*: Fonksiyon tanımlamaları(declerations) JavaScript'in *hoisting* özelliğinden faydalandığı için fonksiyonun tanımlamasından önce çağrılmasından endişe duymaya gerek yoktur. (Fonksiyon eşitlemeleri(expression) için bu durum geçerli değildir) + + *Neden?*: Fonksiyon tanımlamaları ile değişkenlerin yerlerini değiştirirken kodunuz kırılır mı diye endişe duymaya gerek yoktur. + + *Neden?*: Fonksiyon eşitlemelerinde sıra önemlidir. + + ```javascript + /** + * kaçınılacak stil + * Fonksiyon eşitlemeleri kullanmak. + */ + function Avengers(avengersService, logger) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + var activate = function() { + return getAvengers().then(function() { + logger.info('Activated Avengers View'); + }); + } + + var getAvengers = function() { + return avengersService.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + + vm.getAvengers = getAvengers; + + activate(); + } + ``` + + Bir önceki örnekte önemli olan noktaların kod içerisinde nasıl dağıldığına dikkat edin. Aşağıdaki örnekte, önemli olan kısım yukarıda toplanmıştır. Örneğin, controller'a bağlı `vm.avengers` ve `vm.title` öğeleri. İmplementasyonun detayları aşağıda yer alıyor. Kodu okumak böyle daha kolay. + + ```javascript + /* + * önerilen stil + * Fonksiyon tanımlamaları kullanarak + * bağlanacakları yukarı taşımak. + */ + function Avengers(avengersService, logger) { + var vm = this; + vm.avengers = []; + vm.getAvengers = getAvengers; + vm.title = 'Avengers'; + + activate(); + + function activate() { + return getAvengers().then(function() { + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + return avengersService.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Mantıksal kodu Controller'lardan Servislere Kaydırın +###### [Style [Y035](#style-y035)] + + - Controller içerisindeki mantıksal kodu servisler ve factory'ler aracılığıyle yönetin. + + *Neden?*: Mantıksal kod servislere taşınırsa farklı controller'larda tekrar terkrar kullanılabilir. + + *Neden?*: Servise taşınmış mantıksal kod daha kolay test edilebilir ve controller içerisinde kolayca taklit edilebilir(mocking) + + *Neden?*: Controller'dan bağımlılıkları kaldırır ve implementasyon detaylarını gizler. + + *Neden?*: Controller'ı kısa ve öz tutar. + + ```javascript + + /* kaçınılacak stil */ + function Order($http, $q, config, userInfo) { + var vm = this; + vm.checkCredit = checkCredit; + vm.isCreditOk; + vm.total = 0; + + function checkCredit() { + var settings = {}; + // Konfigürasyonlardan URL'yi al + // Gerekli header'ları belirler + // URL'yi gerekli parametrelerle hazırla + // Kullanıcıyı belirleyecek veriyi ekle ki bu kullanıcı için doğru limit bulunsun + // CORS desteklemeyen tarayıcılar için JSONP kullan + return $http.get(settings) + .then(function(data) { + // Response ile gelen JSON objesinden maksimum kalan tutarı al + vm.isCreditOk = vm.total <= maxRemainingAmount + }) + .catch(function(error) { + // Hatayı işlet + // Tekrar dene? Başka bir servisi dene? + // Kullanıcıya anlayabileceği uygun bir hata göster + }); + }; + } + ``` + + ```javascript + /* önerilen stil */ + function Order(creditService) { + var vm = this; + vm.checkCredit = checkCredit; + vm.isCreditOk; + vm.total = 0; + + function checkCredit() { + return creditService.isOrderTotalOk(vm.total) + .then(function(isOk) { vm.isCreditOk = isOk; }) + .catch(showError); + }; + } + ``` + +### Controller'ın Odağını Koruyun +###### [Stil [Y037](#style-y037)] + + - Bir controller'ı bir view tanımlayın ve başka view'lar için kullanmaya çalışmayın. Onun yerine tekrar kullanılabilir mantıksal kodu farcory'lere taşıyıp, controller'ı sade ve view'a odaklı bırakın. + + *Neden?*: Controller'ları değişik view'larla birlikte kullanmak kodu kırılgan yapar ve iyi bir uçtan uca test kapsamı için kararlılık gereklidir. + +### Controller Atamaları +###### [Stil [Y038](#style-y038)] + + - Eğer bir controller bir view ile eşleşmek zorunda ise ve o view başka controller'lar tarafından da kullanılıyorsa, o zaman controller'ı router serviyesinde tanımlayın. + + Not: Eğer view router dışında başka biryerden yükleniyorsa, view içerisinde `ng-controller="Avengers as vm"` sintaksını kullanın. + + *Neden?*: Controller'ı router ile eşlemek, farklı route'ların farklı controller ve view eşlerini çağırmasına olanak sağlar. Eğer controller [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController) yöntemi kullanılarak view ile eşlendiyse, o view hep o controller'ı kullanacaktır. + + ```javascript + /* kaçınılacak stil */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html' + }); + } + ``` + + ```html + +
+
+ ``` + + ```javascript + /* önerilen stil */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'Avengers', + controllerAs: 'vm' + }); + } + ``` + + ```html + +
+
+ ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Servisler + +### Singleton +###### [Stil [Y040](#style-y040)] + + - Servisler `new` kelimesi ile yaratılır, Paylaşımlı metod ve değişkenler için `this` kelimesini kullanın. Servisler Factory'lere çok benzedikleri için, tutarlılık açısından servisler yerine factory kullanın. + + Not: [Bütün Angular servisleri singleton yapıdadır](https://docs.angularjs.org/guide/services). Bu yaratılan servisin aynı anda tek bir örneğinin injectörler tarafından kullanıldığı anlamına gelir. + + ```javascript + // service + angular + .module('app') + .service('logger', logger); + + function logger() { + this.logError = function(msg) { + /* */ + }; + } + ``` + + ```javascript + // factory + angular + .module('app') + .factory('logger', logger); + + function logger() { + return { + logError: function(msg) { + /* */ + } + }; + } + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Factory'ler + +### Tek Sorumluluk +###### [Stil [Y050](#style-y050)] + + - Factory'lerin tek sorumluluğu olmalıdır [single responsibility](http://en.wikipedia.org/wiki/Single_responsibility_principle), ve kendi içeriğini kapsamalıdır. Factory sorumluluğunun dışına taşmaya başlarsa, bu yeni sorumluluk için ayrı bir factory yaratılmalıdır. + +### Singleton +###### [Stil [Y051](#style-y051)] + + - Factoryler singleton yapıdadır ve servisin metodları ve değişkenlerinin bulunduğu objeleri dönerler. + + Not: [Bütün Angular servisleri singleton yapıdadır](https://docs.angularjs.org/guide/services). + +### Ulaşılabilirler Yukarı! +###### [Style [Y052](#style-y052)] + + - Servisin çağrılabilen metodlarını [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript) yapısını kullanarak kodun tepesinde tanımlayın. + + *Neden?*: Çağrılabilen metodları kodun tepesinde tanımlamak okunabilirliği arttırır ve bir bakışta bu servisin hangi metodlarının dışarıdan çağırılabileceğini anlamamıza yardımcı olur. Ayrıca hangi metodların unit testlerinin yazılması gerektiği hakkında fikir verir. + + *Neden?*: Bu yöntem özellikle dosya uzamaya başladığında daha da yardımcı olur. Hangi metodların çağrılabilir olduğunu görmek için aşağıya kadar kaymamızı engeller. + + *Neden?*: Fonksiyonları olduğu yerde tanımlamak kolay olabilir, ama fonksiyonlar bir satırdan daha uzun olmaya başladıklarında okunabilirliği azaltırlar ve aşağı doğru daha fazla kaydırma yapmanıza sebep olurlar. Çağrılabilecek metodları tepede tanımlayıp implementasyon detaylarını aşağıda yapmak okunabilirliği arttırır. + + ```javascript + /* kaçınılacak stil */ + function dataService() { + var someValue = ''; + function save() { + /* */ + }; + function validate() { + /* */ + }; + + return { + save: save, + someValue: someValue, + validate: validate + }; + } + ``` + + ```javascript + /* önerilen stil */ + function dataService() { + var someValue = ''; + var service = { + save: save, + someValue: someValue, + validate: validate + }; + return service; + + //////////// + + function save() { + /* */ + }; + + function validate() { + /* */ + }; + } + ``` + + Primitif değerler revealing module pattern yöntemi kullanıldığında güncellenemezler. + + ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-2.png) + +### Fonksiyon Tanımlamaları ve İmplementasyon Detaylarının Saklanması +###### [Stil [Y053](#style-y053)] + + - Fonksiyon tanımlamalarınızı implementasyon detaylarını saklamak için kullanın. View'a bağlanacak öğeleri yukarıda tanımlayın. Controller'ınızda bir fonksiyonu bağlama ihtiyacı hissettiğinizde, bu öğeyi bir fonksiyon tanımlamasına eşitleyin. Fonksiyonun implementasyon detaylarını kodun ileriki satırlarında yapın. Bu direk olarak "Bağlanacaklar Yukarı" başlığı ile ilintili. Daha fazla detay için bu [makaleme](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code) bakabilirsiniz. + + *Neden?*: Bağlanacak öğeleri yukarı taşımak okumayı kolaylaştırır ve controller içerisinde hangi öğelerin View'a bağlandığını anında görmemizi sağlar. + + *Neden?*: Fonksiyonun implementasyonunu dosya içerisinde daha aşağılara taşımak kompleks kısımları göz önünden uzak tutar ve asıl önemli olan kısma odaklanmayı sağlarç + + *Neden?*: Fonksiyon tanımlamaları(declerations) JavaScript'in *hoisting* özelliğinden faydalandığı için fonksiyonun tanımlamasından önce çağrılmasından endişe duymaya gerek yoktur. (Fonksiyon eşitlemeleri(expression) için bu durum geçerli değildir) + + *Neden?*: Fonksiyon tanımlamaları ile değişkenlerin yerlerini değiştirirken kodunuz kırılır mı diye endişe duymaya gerek yoktur. + + *Neden?*: Fonksiyon eşitlemelerinde sıra önemlidir. + + ```javascript + /** + * kaçınılacak stil + * Fonksiyon eşitlemelerini kullanarak + */ + function dataservice($http, $location, $q, exception, logger) { + var isPrimed = false; + var primePromise; + + var getAvengers = function() { + // implementasyon detayları + }; + + var getAvengerCount = function() { + // implementasyon detayları + }; + + var getAvengersCast = function() { + // implementasyon detayları + }; + + var prime = function() { + // implementasyon detayları + }; + + var ready = function(nextPromises) { + // implementasyon detayları + }; + + var service = { + getAvengersCast: getAvengersCast, + getAvengerCount: getAvengerCount, + getAvengers: getAvengers, + ready: ready + }; + + return service; + } + ``` + + ```javascript + /** + * önerilen stil + * Fonksiyon tanımlamaları kullanarak + * ve ulaşılabilir metodları yukarıda tanımlayarak. + */ + function dataservice($http, $location, $q, exception, logger) { + var isPrimed = false; + var primePromise; + + var service = { + getAvengersCast: getAvengersCast, + getAvengerCount: getAvengerCount, + getAvengers: getAvengers, + ready: ready + }; + + return service; + + //////////// + + function getAvengers() { + // implementasyon detayları + } + + function getAvengerCount() { + // implementasyon detayları + } + + function getAvengersCast() { + // implementasyon detayları + } + + function prime() { + // implementasyon detayları + } + + function ready(nextPromises) { + // implementasyon detayları + } + } + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Veri Servisleri + +### Veri İsteklerinizi Ayırın +###### [Style [Y060](#style-y060)] + + - Veri işlemlerinizi ve işleme mantığınızı bir factory servisine alarak kodunuzu düzenleyin. Veri servislerini sadece ajax çağrıları, verileri lokal depo ya da bellekte saklama, veya diğer veri işlemlerinden sorumlu olacak şekilde tasarlayın. + + *Neden?*: Controller'ın görevi sadece view için gerekli verileri toplamaktır. Bu verilerin nasıl edinildiği ile ilgilenmez, sadece bu verileri nereden temin edeceğini bilir. Veri servislerini ayırmak, veri işleme mantığını servise taşır ve controller'ın daha basit kalmasını ve sadece view'a odaklı kalmasını sağlar. + + *Neden?*: Bu yöntemle veri servisi kullanan controller'ların test edilebilmesini kolaylaştırır. + + *Neden?*: Veri servisi implementasyonu veri havuzlarını yönetmek için çok belirgin bir kod yapısına sahiptir. Bu, veri ile nasıl iletişilebileceğini anlatan header'lar yada `$http` gibi başka servisler içerebilir. Veri işleme mantığını ayırıp bir veri servisinde toplamak bu işlemlerin tek bir yerden yönetilmesini ve implementasyonun bu servisi kullananlardan (örneğin kontolörler) saklanmasını sağlar. Ayrıca implementasyonu değiştirmek kolaylaşır. + + ```javascript + /* önerilen stil */ + + // veri servisi factory'si + angular + .module('app.core') + .factory('dataservice', dataservice); + + dataservice.$inject = ['$http', 'logger']; + + function dataservice($http, logger) { + return { + getAvengers: getAvengers + }; + + function getAvengers() { + return $http.get('/api/maa') + .then(getAvengersComplete) + .catch(getAvengersFailed); + + function getAvengersComplete(response) { + return response.data.results; + } + + function getAvengersFailed(error) { + logger.error('XHR Failed for getAvengers.' + error.data); + } + } + } + ``` + + Note: The data service is called from consumers, such as a controller, hiding the implementation from the consumers, as shown below. + Not: Veri servisi controller gibi onu kullanan yerlerden çağrılır ve aşağıdaki örnekteki gibi implementasyon detaylarını kullanılan yerlerden saklar. + + ```javascript + /* önerilen stil */ + + // veri servisi factroy'sini çağıran controller + angular + .module('app.avengers') + .controller('Avengers', Avengers); + + Avengers.$inject = ['dataservice', 'logger']; + + function Avengers(dataservice, logger) { + var vm = this; + vm.avengers = []; + + activate(); + + function activate() { + return getAvengers().then(function() { + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + return dataservice.getAvengers() + .then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Veri Çağrılarının Sonucunda bir Promise Döndürün +###### [Style [Y061](#style-y061)] + + - Promise döndüren bir veri servisini çağırdığınızda, siz de çağrıyı yapan fonksiyona bir Promise döndürün. + + *Neden?*: Böylece Promise'lerinizi zincirleyebilirsiniz ve veri çağrısı bitip çöczümlendiğinde sonuca göre aksiyon alabilirsiniz. + + ```javascript + /* önerilen stil */ + + activate(); + + function activate() { + /** + * 1. Adım + * getAvengers fonksiyonundan avenger + * verisini isteyin ve promise'i bekleyin + */ + return getAvengers().then(function() { + /** + * 4. Adım + * Son promise'in sonunda bir aksiyon al + */ + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + /** + * 2. Adım + * Veri servisinden veriyi iste ve + * promise'i bekle + */ + return dataservice.getAvengers() + .then(function(data) { + /** + * 3. Adım + * Veriyi kaydet ve promise'i çözümle + */ + vm.avengers = data; + return vm.avengers; + }); + } + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Directive'ler +### Bir Dosyaya Bir Directive +###### [Stil [Y070](#style-y070)] + + - Her bir directive için ayrı bir dosya yaratın ve dosyanın adını directive'in adı ile aynı tutun. + + *Neden?*: Bütün directive'leri bir dosya içerisinde toplamak kolaydır, ancak daha sonra bu directive'leri ayırıp farklı uygulamalarda, modüllerde kullanmak zorlaşır. + + *Neden?*: Her dosyada bir directive'in olması sürdürülebilirliği kolaylaştırır. + + > Not: "**En iyi uygulama**: Directive'ler kendilerini temizlemelilerdir. `element.on('$destroy', ...)` ya da `scope.$on('$destroy', ...)` kullanarak directive kaldırıldığında bir temizlik fonksiyonu çalıştırabilirsiniz" ... Angular dökümantasyonundan. + + ```javascript + /* sakınılacak stil */ + /* directives.js */ + + angular + .module('app.widgets') + + /* order modülüne özel directive */ + .directive('orderCalendarRange', orderCalendarRange) + + /* sales uygulamasının heryerinde kullanılabilecek bir directive */ + .directive('salesCustomerInfo', salesCustomerInfo) + + /* bütün uygulamalarda kullanılabilecek bir directive */ + .directive('sharedSpinner', sharedSpinner); + + function orderCalendarRange() { + /* İmplementasyon detayları */ + } + + function salesCustomerInfo() { + /* İmplementasyon detayları */ + } + + function sharedSpinner() { + /* İmplementasyon detayları */ + } + ``` + + ```javascript + /* önerilen stil */ + /* calendarRange.directive.js */ + + /** + * @desc order modülüne özel directive + * @example
+ */ + angular + .module('sales.order') + .directive('acmeOrderCalendarRange', orderCalendarRange); + + function orderCalendarRange() { + /* İmplementasyon detayları */ + } + ``` + + ```javascript + /* önerilen stil */ + /* customerInfo.directive.js */ + + /** + * @desc uygulama içerisinde heryede kullanılabilecek sales directive'i + * @example
+ */ + angular + .module('sales.widgets') + .directive('acmeSalesCustomerInfo', salesCustomerInfo); + + function salesCustomerInfo() { + /* İmplementasyon detayları */ + } + ``` + + ```javascript + /* önerilen stil */ + /* spinner.directive.js */ + + /** + * @desc bütün uygulamalarda kullanılabilecek spinner directive'i + * @example
+ */ + angular + .module('shared.widgets') + .directive('acmeSharedSpinner', sharedSpinner); + + function sharedSpinner() { + /* İmplementasyon detayları */ + } + ``` + + Note: There are many naming options for directives, especially since they can be used in narrow or wide scopes. Choose one that makes the directive and its file name distinct and clear. Some examples are below, but see the [Naming](#naming) section for more recommendations. + + Not: Directive'ler için birçok isimlendirme seçeneği mevcut, özellikle dar ya da geniş kapsamda kullanılanlar için. Directive'i ve dosya ismini belirgin ve açık ifade edecek isimler seçin. Aşağıda bazı örnekler bulabilirsiniz, ama daha fazla tavsiye için [İsimlendirme](#naming) bölümüne bakın. + +### Directive İçerisinde DOM Değişiklikleri +###### [Stil [Y072](#style-y072)] + + - DOM'a direk olark müdahele etmek için directive kullanın. Eğer CSS kullanmak ya da [animasyon servisleri](https://docs.angularjs.org/api/ngAnimate), Angular şablonlandırma, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) ya da [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide) ile amacınıza ulaşabiliyorsanız bu yöntemleri tercih edin. Örneğin eğer bir directive sadece bir elemanı saklayıp gösteriyorsa ngHide/ngShow kullanın. + + *Neden?*: DOM manipulation can be difficult to test, debug, and there are often better ways (e.g. CSS, animations, templates) + *Neden?*: DOM değişikliklerini test ve debug etmek güç olabilir, ve genellikle daha iyi bir yöntem bulabilirsiniz (örneğin CSS, animasyon, şablonlar) + +### Eşsiz Bir Directive Ön eki Kullanın +###### [Stil [Y073](#style-y073)] + + - Eşsiz, kısa ve tanımlayıcı bir ön ek kullanın. Örneğin `acmeSalesCustomerInfo`. HTML'de `acme-sales-customer-info` şeklinde tanımlanır. + + *Neden?*: Eşsiz ön ek directive'in kapsamını ve orijinini ifade eder. Örneğin `cc-` directive'in CodeCamper uygulamasına ait olduğunu ifade ederken, `acme-` bu directive'in Acme firmasına ait olduğunu ifade edevilir + + Note: Avoid `ng-` as these are reserved for Angular directives. Research widely used directives to avoid naming conflicts, such as `ion-` for the [Ionic Framework](http://ionicframework.com/). + + Not: `ng-` Angular tafafından kullanıldığı için bu ön eki kullanmaktan kaçının. Ön ekinizi belirlemeden önce çakışmaların önüne geçmek için iyice araştırın. Örneğin `ion-` ön eki [Ionic Framework](http://ionicframework.com/) tarafından kullanılmaktadır. + +### Directive'inizin Yazım Türünü Element ve Attribute Olarak Sınırlayın +###### [Stil [Y074](#style-y074)] + + - When creating a directive that makes sense as a stand-alone element, allow restrict `E` (custom element) and optionally restrict `A` (custom attribute). Generally, if it could be its own control, `E` is appropriate. General guideline is allow `EA` but lean towards implementing as an element when it's stand-alone and as an attribute when it enhances its existing DOM element. + + - Kendi başına element olarak anlamlı bir directive yaratırken restrict `E` (özel element) ve tercihen restrict `A` (özel attribute) kullanın. Genellikle, eğer kendi kendini kontrol eden bir directive ise `E` uygun olur. Genel olarak `EA` kullanmaya izin verilir ama eğer directive tek başına bir element ise element(E) olarak, hazırda var olan bir element'i iyileştiren bir directive'se attribute(A) olarak sınırlamaya yönelin. + + *Neden?*: Çünkü mantıklı. + + *Neden?*: Directive'i class olarak da kullanmaya olanak sağlansa da, eğer directive gerçekten kendi başına bir element olarak davranıyorsa element(E) olarak sınırlamak ya da en azından attribute(A) olarak sınırlamak mantıklı olur. + + Not: EA, Angular 1.3 + için varsayılan sınırlandırma seçeneğidir. + + ```html + +
+ ``` + + ```javascript + /* sakınılacak stil */ + angular + .module('app.widgets') + .directive('myCalendarRange', myCalendarRange); + + function myCalendarRange() { + var directive = { + link: link, + templateUrl: '/template/is/located/here.html', + restrict: 'C' + }; + return directive; + + function link(scope, element, attrs) { + /* */ + } + } + ``` + + ```html + + +
+ ``` + + ```javascript + /* önerilen stil */ + angular + .module('app.widgets') + .directive('myCalendarRange', myCalendarRange); + + function myCalendarRange() { + var directive = { + link: link, + templateUrl: '/template/is/located/here.html', + restrict: 'EA' + }; + return directive; + + function link(scope, element, attrs) { + /* */ + } + } + ``` + +### Directive'ler ve controllerAs +###### [Stil [Y075](#style-y075)] + + - Tutarlı olmak için directive'le birlikte `controller as` sintaksını kullanın. + + *Neden?*: Mantıklı ve zor değil. + + Not: Aşağıdaki örnek scope'u link ve directive controller'ı içerisinde controllerAs yöntemi ile nasıl kullanılacağını gösterir. Örneğin bölünmemesi amacı ile HTML şablonunu directive içerisinde tuttum. + + Not: Bağımlılık Enjeksiyonu (Dependency Injection) ile ilgili olarak , [Manuel Olarak Bağımlılıkları Belirlemek](#manual-annotating-for-dependency-injection) kısmına bakın. + + Not: Directive'in controller'ının directive'in kapsamının(closure) dışında olduğunu unutmayın. Bu stil `return`'den sonra enjeksiyonların ulaşılamaz şekilde yaratılması probleminin önüne geçer. + + ```html +
+ ``` + + ```javascript + angular + .module('app') + .directive('myExample', myExample); + + function myExample() { + var directive = { + restrict: 'EA', + templateUrl: 'app/feature/example.directive.html', + scope: { + max: '=' + }, + link: linkFunc, + controller: ExampleController, + controllerAs: 'vm', + bindToController: true // because the scope is isolated + }; + + return directive; + + function linkFunc(scope, el, attr, ctrl) { + console.log('LINK: scope.min = %s *** should be undefined', scope.min); + console.log('LINK: scope.max = %s *** should be undefined', scope.max); + console.log('LINK: scope.vm.min = %s', scope.vm.min); + console.log('LINK: scope.vm.max = %s', scope.vm.max); + } + } + + ExampleController.$inject = ['$scope']; + + function ExampleController($scope) { + // Kıyaslama yapmak için $scope enjecte ediliyor + var vm = this; + + vm.min = 3; + + console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); + console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); + console.log('CTRL: vm.min = %s', vm.min); + console.log('CTRL: vm.max = %s', vm.max); + } + ``` + + ```html + +
hello world
+
max={{vm.max}}
+
min={{vm.min}}
+ ``` + + Not: Ayrıca controller'ı link fonksiyonuna enjekte ederken isimlendirebilirsiniz ve böylece directive attribute'larına controller'ın elemanları olarak erişebilirsiniz. + + ```javascript + // Yukarıdaki örneğe alternatif + function linkFunc(scope, el, attr, vm) { + console.log('LINK: scope.min = %s *** should be undefined', scope.min); + console.log('LINK: scope.max = %s *** should be undefined', scope.max); + console.log('LINK: vm.min = %s', vm.min); + console.log('LINK: vm.max = %s', vm.max); + } + ``` + +###### [Stil [Y076](#style-y076)] + + - `controller as` sintaksını kullanırken `bindToController = true` seçeneğini kullanın. Bu dış $scope'u directive'in controller $scope'una bağlamanızı sağlar. + + *Neden?*: Dış $scope'u directive'in controller'ın $scope'una bağlamayı kolaylaştırır. + + Not: `bindToController` özelliği Angular 1.3.0 ile birlikte gelmiştir. + + ```html +
+ ``` + + ```javascript + angular + .module('app') + .directive('myExample', myExample); + + function myExample() { + var directive = { + restrict: 'EA', + templateUrl: 'app/feature/example.directive.html', + scope: { + max: '=' + }, + controller: ExampleController, + controllerAs: 'vm', + bindToController: true + }; + + return directive; + } + + function ExampleController() { + var vm = this; + vm.min = 3; + console.log('CTRL: vm.min = %s', vm.min); + console.log('CTRL: vm.max = %s', vm.max); + } + ``` + + ```html + +
hello world
+
max={{vm.max}}
+
min={{vm.min}}
+ ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Promise'leri Controller'lar İçin Çözümlemek +### Controller Aktifleştirme Promise'leri +###### [Stil [Y080](#style-y080)] + + - Controller'ın başlangıç mantığını `activate` fonksiyonu içerisinde çözün. + + *Neden?*: Başlangıç mantığını controller içerisinde tutarlı bir yerde tutmak yerini bulmayı kolaylaştırır, test için tutarlı hale getirir ve başlangıç mantığını controller içerisinde dağıtmaya yardımcı olur. + + *Neden?*: `activate` fonksiyonu başlangıç mantığını controller/View baştan başlatılmak istendiğinde tekrar kullanmaya elverişli hale getirir, mantığı bir arada tutar, kullanıcıyı View'a daha hızlı ulaştırır, `ng-view` ya da `ui-view` için animasyonları kolaylaştırır ve kullanıcıya daha hızlı hissettirir. + + Not: Eğer durumsal olarak route'u controller başlamadan önce iptal etmek istiyorsanız [route resolve](#style-y081) kullanın. + + ```javascript + /* sakınılacak stil */ + function Avengers(dataservice) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + dataservice.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + ``` + + ```javascript + /* önerilen stil */ + function Avengers(dataservice) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + activate(); + + //////////// + + function activate() { + return dataservice.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Promise'leri Route için Çözümlemek +###### [Stil [Y081](#style-y081)] + + - Bir controller aktive olmadan önce bir promise'in çözülmesine bağlı ise, o bağımlılıkları `$routeProvider` içerisinde controller çalışmaya başlamadan önce çözün. Eğer durumsal olarak bir route'un controller çalışmadan önce iptal olmasını istiyorsanız, route resolver kullanın. + + - View'a geçiş yapmadan önce geçişi iptal etmek istiyorsanız route resolver kullanın. + + *Neden?*: Bir controller yüklenmeden önce veri yüklenmesi ihtiyacında olabilir. Bu veri bir bir factory aracılığı ile promise'den ya da [$http](https://docs.angularjs.org/api/ng/service/$http)'den gelebilir. [route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) kullanmak promise'in controller çalıştırılmadan önce çözümlenmesini sağlar, böylece controller promise'den gelen veriye göre aksiyon alabilir. + + *Neden?*: Kod route'dan ve controller'ın aktivasyon fonksiyonundan sonra çalıştırılır. View hemen yüklenir. Data binding aktivasyon promise'i çözümlendikten hemen sonra yapılır. Bir “meşgul” animasyonu view geçişi esnasında gösterilebilir (`ng-view` veya `ui-view`) + + Note: Kod route'dan önce promise aracılığı ile çalıştırılır. Reject olan promise route'u iptal eder. Resolve olması view'ın route promise'inin çözülmesini bekletir. Bir meşgul” animasyonu resolve'dan önce ve view geçişi süresince gösterilebilir. Eğer view'ın daha hızlı yüklenmesini istiyorsanız ve view'ın yüklenebilir olup olmadığını kontrol ettiğiniz bir nokta yok ise [controller `activate` tekniği](#style-y080)'ni kullanın. + + ```javascript + /* sakınılacak stil */ + angular + .module('app') + .controller('Avengers', Avengers); + + function Avengers(movieService) { + var vm = this; + // unresolved + vm.movies; + // resolved asynchronously + movieService.getMovies().then(function(response) { + vm.movies = response.movies; + }); + } + ``` + + ```javascript + /* daha iyi stil */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'Avengers', + controllerAs: 'vm', + resolve: { + moviesPrepService: function(movieService) { + return movieService.getMovies(); + } + } + }); + } + + // avengers.js + angular + .module('app') + .controller('Avengers', Avengers); + + Avengers.$inject = ['moviesPrepService']; + function Avengers(moviesPrepService) { + var vm = this; + vm.movies = moviesPrepService.movies; + } + ``` + + Not: Aşağıdaki örnek route resolve'un bir isimli fonksiyonuna işaret ettiğini gösterir, debug etmesi ve dependency injection kontrolü daha kolaydır. + + ```javascript + /* çok daha iyi stil */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'Avengers', + controllerAs: 'vm', + resolve: { + moviesPrepService: moviesPrepService + } + }); + } + + function moviesPrepService(movieService) { + return movieService.getMovies(); + } + + // avengers.js + angular + .module('app') + .controller('Avengers', Avengers); + + Avengers.$inject = ['moviesPrepService']; + function Avengers(moviesPrepService) { + var vm = this; + vm.movies = moviesPrepService.movies; + } + ``` + Note: Örnek kodun `movieService` bağımlılığı minification işlemi için uygun değildir. Minification uyumlu hale getirme detayları için [dependency injection](#manual-annotating-for-dependency-injection) ve [minification and annotation](#minification-and-annotation) bölümlerine bakın. + +**[İçerik Listesi](#icerik-listesi)** + +## Dependency Injection ve Manuel Annotation + +### Minification Uyumluluk +###### [Stil [Y090](#style-y090)] + + - Bağımlılıkları belirlerken kısayol sintaksını kullanmaktan kaçının. + + *Neden?*: Component'e aid değişkenler (controller, factory, etc) minification işlemi sonrası karıştırılmış değişkenlere çevrilecektir. Örneğin, `common` ve `dataservice`, `a` ve `b` değişkenlerine dönüşebilir ve Angular tarafından bulunamayabilir. + + ```javascript + /* sakınılacak stil - minification uyumlu değil*/ + angular + .module('app') + .controller('Dashboard', Dashboard); + + function Dashboard(common, dataservice) { + } + ``` + + Bu kod minificationdan sonra karışık değişkenler üretebilir ve runtime hataları ile karşılaşabilirsiniz. + + ```javascript + /* sakınılacak stil - minification uyumlu değil*/ + angular.module('app').controller('Dashboard', d);function d(a, b) { } + ``` + +### Manuel Olarak Bağımlılıkları Tanımlamak +###### [Stil [Y091](#style-y091)] + + - Angular component'lerinize manuel olarak bağımlılıklarınızı tanımlamak için `$inject` kullanın. + + *Neden?*: Bu teknik [`ng-annotate`](https://github.com/olov/ng-annotate) tarafından kullanılan tekniği taklit eder, ki benim minification uyumlu bağımlılıkları otomatik olarak yaratmak için önerdiğim yöntemdir. Eğer `ng-annotate` bir bağımlılığın daha önceden eklendiğini farkederse, bu bağımlılığı çoklamaz. + + *Neden?*: Bu bağımlılıklarınızın minification sürecinde değiştirilirken hataya açık hale gelmelerini engeller. `common` ve `dataservice`, `a` ve `b` haline gelebilir ve Angular tarafından bulunamayabilir. + + *Neden?*: Uzun array listelerini okumak zor olacağından satıriçinde bağımlılık tanımlamaktan kaçının. Ayrıca array'iniz string serilerinden oluşurken son elemanının bir fonksiyon olması kafa karıştırıcı olabilir. + + ```javascript + /* kaçınılacak stil */ + angular + .module('app') + .controller('Dashboard', + ['$location', '$routeParams', 'common', 'dataservice', + function Dashboard($location, $routeParams, common, dataservice) {} + ]); + ``` + + ```javascript + /* kaçınılacak stil */ + angular + .module('app') + .controller('Dashboard', + ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); + + function Dashboard($location, $routeParams, common, dataservice) { + } + ``` + + ```javascript + /* önerilen stil */ + angular + .module('app') + .controller('Dashboard', Dashboard); + + Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; + + function Dashboard($location, $routeParams, common, dataservice) { + } + ``` + + Not: Eğer fonksiyonunuz bir return statement'ının altındaysa `$inject` ulaşılmaz hale gelebilir (directive içerisinde bu durum gerçekleşebilir). Bunu controller'ı directive dışarısına taşıyarak çözebilirsiniz. + + ```javascript + /* sakınılacak stil */ + // directive tanımlaması içerisinde + function outer() { + var ddo = { + controller: DashboardPanelController, + controllerAs: 'vm' + }; + return ddo; + + DashboardPanelController.$inject = ['logger']; // Unreachable + function DashboardPanelController(logger) { + } + } + ``` + + ```javascript + /* önerilen stil */ + // outside a directive definition + function outer() { + var ddo = { + controller: DashboardPanelController, + controllerAs: 'vm' + }; + return ddo; + } + + DashboardPanelController.$inject = ['logger']; + function DashboardPanelController(logger) { + } + ``` + +### Manuel Olarak Route Resolver Bağımlılıklarını Tanımlamak +###### [Stil [Y092](#style-y092)] + + - Angular component'lerinize manuel olarak bağımlılıklarınızı tanımlamak için `$inject` kullanın. + + *Neden?*: Bu teknik route resolver için anonim fonksiyonu kırar ve okunmasını kolaylaştırır. + + *Neden?*: `$inject` kolayca resolver öncesine konulabilir ve bağımlılıkları minification için uygun hale getiirir. + + ```javascript + /* önerilen stil */ + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { + moviesPrepService: moviesPrepService + } + }); + } + + moviesPrepService.$inject = ['movieService']; + function moviesPrepService(movieService) { + return movieService.getMovies(); + } + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Minification ve Annotation + +### ng-annotate +###### [Stil [Y100](#style-y100)] + + - [Gulp](http://gulpjs.com) ya da [Grunt](http://gruntjs.com) ile birlikte [ng-annotate](//github.com/olov/ng-annotate) kullanın ve otomatik dependency injection'a ihtiyacı olan fonksiyonları `/* @ngInject */` ile yorumlayın + + *Neden?*: Bu kodunuzu minification uyumlu yazılması unutulmuş bağımlılıklara karşı korur. + + *Neden?*: [`ng-min`](https://github.com/btford/ngmin) artık kullanılmıyor + + >Ben kişisel olarak Gulp kullanmayı tercih ediyorum. Yazması, okuması ve debug etmesi çok daha kolay. + + Aşağıdaki kod minification uyumlu bağımlılıklar içermemektedir. + + ```javascript + angular + .module('app') + .controller('Avengers', Avengers); + + /* @ngInject */ + function Avengers(storage, avengerService) { + var vm = this; + vm.heroSearch = ''; + vm.storeHero = storeHero; + + function storeHero() { + var hero = avengerService.find(vm.heroSearch); + storage.save(hero.name, hero); + } + } + ``` + + Yukarıdaki kod ng-annotate ile çalıştırıldığı zaman ürettiği kod `$inject` annotation'unu içerecek ve minification uyumlu hale gelecek. + + ```javascript + angular + .module('app') + .controller('Avengers', Avengers); + + /* @ngInject */ + function Avengers(storage, avengerService) { + var vm = this; + vm.heroSearch = ''; + vm.storeHero = storeHero; + + function storeHero() { + var hero = avengerService.find(vm.heroSearch); + storage.save(hero.name, hero); + } + } + + Avengers.$inject = ['storage', 'avengerService']; + ``` + + Not: Eğer `ng-annotate` bağımlılığın daha önceden eklendiğini anlarsa, `$inject` kodunu çoğaltmayacaktır. + + Not: Yukarıdaki kod ng-annotate ile çalıştırıldığı zaman ürettiği kod `$inject` annotation'unu içerecek ve minification uyumlu hale gelecek. + + ```javascript + // Using @ngInject annotations + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'Avengers', + controllerAs: 'vm', + resolve: { /* @ngInject */ + moviesPrepService: function(movieService) { + return movieService.getMovies(); + } + } + }); + } + ``` + + > Note: Angular 1.3'den itibaren [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp) directive'inin `ngStrictDi` parametresini minification uyumlu olmayan bağımlılıkları yakalamak için kullanabilirsiniz. Bu parametre aktif olduğu zaman injector "strict-di" modunda yaratılacak ve uygulamanın açık annotation kullanmayan fonksiyonların çalışmamasını sağlayacak. Debbuging bilgisi konsola yazılacak ve problemi yaratan fonksiyonu bulacaktır. Ben `ng-strict-di` parametresini sadece debugging için kullanmayı tercih ediyorum. + `` + +### ng-annotate için Gulp ya da Grunt Kullanın +###### [Stil [Y101](#style-y101)] + + - Otomatik derleme görevleriniz için [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) ya da [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) kullanın. Bağımlılığı olan fonksiyonların başına `/* @ngInject */` satırını koyun. + + *Neden?*: ng-annotate çoğu bağımlılığı yakalayacaktır, ama bazen `/* @ngInject */` sintaksı ile done vermenizi bekler. + + Takip eden kod gulp ve ngAnnotate kullanımına örnektir + + ```javascript + gulp.task('js', ['jshint'], function() { + var source = pkg.paths.js; + + return gulp.src(source) + .pipe(sourcemaps.init()) + .pipe(concat('all.min.js', {newLine: ';'})) + // uglify etmeden önce annotate edin ki kod düzgün minified olsun. + .pipe(ngAnnotate({ + // true helps add where @ngInject is not used. + // Resolve ile birlikte çalışmaz. Orası için kesin olarak belirtilmelidir + add: true + })) + .pipe(bytediff.start()) + .pipe(uglify({mangle: true})) + .pipe(bytediff.stop()) + .pipe(sourcemaps.write('./')) + .pipe(gulp.dest(pkg.paths.dev)); + }); + + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Exception Yakalama + +### decorator'ler +###### [Stil [Y110](#style-y110)] + + - [`$provide`](https://docs.angularjs.org/api/auto/service/$provide) servisinin config aşamasında [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator) kullanın, [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) servisinde exception'u yakaladığınız zaman alacağınız özel aksiyonları tanımlayın. + + *Neden?*: Geliştirme ve çalıştırma esnasında Angular tarafından yakalanamayan exception'ları yakalamak için tutarlı bir yol sağlar. + + Note: Diğer bir yöntem ise decorator kullanmak yerine servisi override etmektir. Bu da iyi bir seçenektir, ancak eğer varsayılan davranışı korumak istiyorsanız. Davranışı değiştirmek istiyorsanız decorator tavsiye edilir. + + ```javascript + /* önerilen stil */ + angular + .module('blocks.exception') + .config(exceptionConfig); + + exceptionConfig.$inject = ['$provide']; + + function exceptionConfig($provide) { + $provide.decorator('$exceptionHandler', extendExceptionHandler); + } + + extendExceptionHandler.$inject = ['$delegate', 'toastr']; + + function extendExceptionHandler($delegate, toastr) { + return function(exception, cause) { + $delegate(exception, cause); + var errorData = { + exception: exception, + cause: cause + }; + + /** + * Hatayı service'in kolleksiyonuna ekleyebilirsiniz, + * $rootScope'a ekleyebilirsiniz, uzaktaki bir sunucuya yazabilirsiniz + * ya da lokal olarak loglayabilirsiniz. Ya da direk console'a yazdırabilirsinz. Tamamen size kalmış. + * throw exception; + */ + toastr.error(exception.msg, errorData); + }; + } + ``` + +### Exception Yakalayıcılar +###### [Style [Y111](#style-y111)] + + - Exceptionları yakalamak ve yönetmek için bir interface döndüren factory servisi yazın. + + *Neden?*: Kodunuzda fırlatılan exceptionların yakalanması için tutarlı bir yol sağlar (e.g. during XHR calls or promise failures). + + Not: Exception yakalayıcı çağrılarınızda fırlatılabilecek exceptionları yakalamak ve aksiyon almak için iyi bir yöntemdir. Örneğin, uzaktaki bir web servisinden veri almak için bir XHR çağrısı yaparken, o sunucudan fırlatılan exceptionları yakalamak ve buna göre aksiyon almak isteyebilirsiniz. + + ```javascript + /* önerilen stil */ + angular + .module('blocks.exception') + .factory('exception', exception); + + exception.$inject = ['logger']; + + function exception(logger) { + var service = { + catcher: catcher + }; + return service; + + function catcher(message) { + return function(reason) { + logger.error(message, reason); + }; + } + } + ``` + +### Route Hataları +###### [Style [Y112](#style-y112)] + + - Bütün routing hatalarını [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError) kullanarak yakalayın ve loglayın. + + *Neden?*: Bütün routing hatalarını yakalamak için tutarlı bir yöntem. + + *Neden?*: Potensiyel olarak bir routing hatası yakalandığında kullanıcıları hatanın detayları ve nereye yönlenebileceklerini gördüğü bir sayfaya yönlendirmek daha iyi bir kullanıcı deneyimi sağlar. + + ```javascript + /* önerilen stil */ + var handlingRouteChangeError = false; + + function handleRoutingErrors() { + /** + * Route iptali: + * Hata olduğunda dashboard ekranına git + * Eğer iki kere denenirse bir çıkış yapısı sun. + */ + $rootScope.$on('$routeChangeError', + function(event, current, previous, rejection) { + if (handlingRouteChangeError) { return; } + handlingRouteChangeError = true; + var destination = (current && (current.title || + current.name || current.loadedTemplateUrl)) || + 'unknown target'; + var msg = 'Error routing to ' + destination + '. ' + + (rejection.msg || ''); + + /** + * Tercih olarak özel bir servis ya da $log kullanarak logla. + * (Özel servisi inject etmeyi unutma) + */ + logger.warning(msg, [current]); + + /** + * Routing hatası aldığında başka bir sayfaya yönlendir. + */ + $location.path('/'); + + } + ); + } + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## İsimlendirme + +### İsimlendirme Rehberi +###### [Stil [Y120](#style-y120)] + + - Bütün componentler için, tipini ve içeriğini belirten tutarlı isimler kullanın. Benim tavsiye ettiğim şablon `içerik.tip.js`. Çoğu asset için genelde 2 isim vardır: + * dosya adı (`avengers.controller.js`) + * Angular'a kayıt olan component ismi (`AvengersController`) + + *Neden?*: İsimlendirme gelenekleri arananın bir bakışta bulunması için tutarlı bir yol sunar. Proje genelinde tutarlılık hayatidir. Takım içerisinde tutartlılık önemlidir. Şirket içersinde tutartlılık ise inanılmaz bir verim sağlar. + + *Neden?*: İsimlendirme geleneği, aradığınız kodu basitçe bulmanızı sağlar ve anlaşılmasını kolaylaştırır. + +### İçerik Dosya İsimleri +###### [Stil [Y121](#style-y121)] + + - Bütün componentler için, tipini ve içeriğini belirten tutarlı isimler kullanın. + + *Neden?*: Component'leri hızlı bir şekilde tanımlamak için tutarlı bir yol sunar. + + *Neden?*: Otomatik görevlerde dosya isimleri için pattern matching sağlar. + + ```javascript + /** + * yaygın seçenekler + */ + + // Controllers + avengers.js + avengers.controller.js + avengersController.js + + // Services/Factories + logger.js + logger.service.js + loggerService.js + ``` + + ```javascript + /** + * önerilen stil + */ + + // controllers + avengers.controller.js + avengers.controller.spec.js + + // services/factories + logger.service.js + logger.service.spec.js + + // constants + constants.js + + // module definition + avengers.module.js + + // routes + avengers.routes.js + avengers.routes.spec.js + + // configuration + avengers.config.js + + // directives + avenger-profile.directive.js + avenger-profile.directive.spec.js + ``` + + Note: Another common convention is naming controller files without the word `controller` in the file name such as `avengers.js` instead of `avengers.controller.js`. All other conventions still hold using a suffix of the type. Controllers are the most common type of component so this just saves typing and is still easily identifiable. I recommend you choose 1 convention and be consistent for your team. My preference is `avengers.controller.js`. + + Note: Controller dosyalarını isimlendirirken yaygın olan bir diğer gelenek ise `controller` kelimesini dosya isminden çıkarıp `avengers.controller.js` yerine `avengers.js` olarak bırakmaktır. Diğer bütün componentler için son ek tutulur. Controller'lar en yaygın kullanılan component tipidir, bu yüzden `controller` kelimesini çıkartmak bizi fazladan yazı yazmaktan kurtarır ve hala kolayca component'in ne olduğunu anlamamızı sağlar. Benim tavsiyen tek bir geleneğe sadık kalın ve takımınız içerisinde tutarlı olun. Benim tercihim `avengers.controller.js`. + + ```javascript + /** + * önerilen stil + */ + // Controllers + avengers.js + avengers.spec.js + ``` + +### Test Dosyası İsimleri +###### [Style [Y122](#style-y122)] + + - Test dosyalarını, test ettikleri component'in ismi ve `spec` son eki ile isimlendirin. + + *Neden?*: Component'leri hızlı bir şekilde tanımlamak için tutarlı bir yol sunar. + + *Neden?*: [karma](http://karma-runner.github.io/) ya da diğer test araçları için bir pattern matching yapısı sunar. + + ```javascript + /** + * önerilen stil + */ + avengers.controller.spec.js + logger.service.spec.js + avengers.routes.spec.js + avenger-profile.directive.spec.js + ``` + +### Controller İsimleri +###### [Style [Y123](#style-y123)] + + - Bütün controller'lar için içeriklerini ifade eden tutarlı isimlar kullanın. Contructor oldukları için UpperCamelCase yöntemini kullanın. + + *Neden?*: Controller'ları hızlıca ilişkilendirmek için tutarlı bir yol sunar. + + *Neden?*: UpperCamelCase isimlendirme yöntemi constructor kullanarak yaratılan objeler için gelenekseldir. + + ```javascript + /** + * önerilen stil + */ + + // avengers.controller.js + angular + .module + .controller('HeroAvengersController', HeroAvengersController); + + function HeroAvengersController() { } + ``` + +### Controller İsmi Son Eki +###### [Style [Y124](#style-y124)] + + - Controller ismine `Controller` son eki ekleyin. + + *Neden?*: `Controller` son eki daha yaygın kullanılıyor ve daha açıklayıcı. + + ```javascript + /** + * önerilen stil + */ + + // avengers.controller.js + angular + .module + .controller('AvengersController', AvengersController); + + function AvengersController() { } + ``` + +### Factory ve Service İsimleri +###### [Style [Y125](#style-y125)] + + - Bütün Service'ler ve Factory'ler için içeriklerini ifade eden tutarlı isimler kullanın. İsimlendirme yöntemi olarak camel-case kullanın. `$` ön ekini kullanmaktan kaçının. Ne oldukları tam olarak anlaşılmıyor ise `Service` son ekini kullanın. + + *Neden?*: Factory'leri hızlıca ilişkilendirmek için tutarlı bir yol sunar. + + *Neden?*: Angular ile birlikte gelen `$` ön ekini kullanan servisler ile çakışmayı önler. + + *Neden?*: Ne olduğu açık olan `logger` gibi servis isimleri son eke ihtiyaç duymaz. + + *Neden?*: `avengers` gibi servis isimleri isimdir ve son eke ihtiyaç duyar. `avengersService` olarak isimlendirilmelidir. + + ```javascript + /** + * önerilen stil + */ + + // logger.service.js + angular + .module + .factory('logger', logger); + + function logger() { } + ``` + + ```javascript + /** + * önerilen stil + */ + + // credit.service.js + angular + .module + .factory('creditService', creditService); + + function creditService() { } + + // customer.service.js + angular + .module + .service('customersService', customersService); + + function customersService() { } + ``` + +### Directive İsimleri +###### [Stil [Y126](#style-y126)] + + - Bütün directive'leri camel-case yöntemini kullanarak tutarlı bir şekilde isimlendirin. Directive'in nereye ait olduğunu belirten kısa ön ekler kullanın (firma ya da proje isimlerinin kısaltılmış hali örnek olabilir). + + *Neden?*: Component'leri hızlıca ilişkilendirmek için tutarlı bir yol sunar. + + ```javascript + /** + * önerilen stil + */ + + // avenger-profile.directive.js + angular + .module + .directive('xxAvengerProfile', xxAvengerProfile); + + // usage is + + function xxAvengerProfile() { } + ``` + +### Modüller +###### [Stil [Y127](#style-y127)] + + - Eğer birden fazla modülünüz varsa, ana modül ismi `app.module.js` olarak isimlendirilir ve diğer tüm bağımlılık modülleri sundukları özellikleri ifade edecek şekilde isimlendirlir. Örneğin, admin modülü `admin.module.js` olarak isimlendirilir. Kayıtlı modül isimlerimi `app` ve `admin` olur. + + *Neden?*: Birçok modülü olan uygulamalar ya da genişleyen büyük uygulamalar için tutarlılık sağlar. + + *Neden?*: Otomatik görevler içerisinde bütün dosyaları birleştiriyorsanız, önce modülleri daha sonra diğer angular dosyalarını sıralamanıza yardımcı olur. + +### Konfigürasyonlar +###### [Style [Y128](#style-y128)] + + - Separate configuration for a module into its own file named after the module. A configuration file for the main `app` module is named `app.config.js` (or simply `config.js`). A configuration for a module named `admin.module.js` is named `admin.config.js`. + + - Modül konfigüreasyonunu ayrı bir dosyada modülün ismi ile bulundurun. `app` modülünün konfigürasyon dosyasının adı `app.config.js` olmalıdır (ya da basitçe `config.js`). `admin.module.js`'nün konfigürasyon dosyası `admin.config.js` olarak isimlendirilir. + + *Neden?*: Konfigürasyonları modül tanımlamalarından, component'lerden ve çalışan koddan ayırır. + + *Neden?*: Modül konfigürasyonlarının yerinin anlaşılmasını kolaylaştırır. + +### Route'lar +###### [Stil [Y129](#style-y129)] + + - Separate route configuration into its own file. Examples might be `app.route.js` for the main module and `admin.route.js` for the `admin` module. Even in smaller apps I prefer this separation from the rest of the configuration. + + - Route konfigürasyonlarını ayrı bir dosyaya alın. Ana modül için `app.route.js` ve `admin` modülü için `admin.route.js` şeklinde. Küçük uygulamalar için bile ben bu ayrıma gitmeyi tercih ediyorum. + +**[İçerik Listesi](#icerik-listesi)** + +## Uygulama Yapısı ve LIFT Prensibi +### LIFT +###### [Stil [Y140](#style-y140)] + + - Uygulamanızı, kodunuzu kolayca bulup (`L`ocate), bir bakışta tanımlayıp (`I`dentify), en az dallanmış selikde klasörlediğiniz (`F`lattest structure) ve kendinizi tekrar etmediğiniz (`T`ry to stay DRY) bir şekilde yapılandırın. Yapnınız bu 4 temel prensibi izlemeli. + + *Neden LIFT?*: Kolaylıkla büyüyebilen tutarlı bir yapı sunar, modülerdir ve kodlara kolayca ulaşmayı sağlayarak yazılımcı verimini arttırır. Yapınızı kontrol etmenin bir başka yöntemi ise kendinize: Bir özellik eklemek için ihtiyacım olan dosyaları kolayca bulup açabiliyor muyum? diye sormanızdır + + Yapımdan memnun olmadığım zamanlarda geri dönüp LIFT prensibinin kurallarını tekrar gözden geçiriyorum + + 1. Kodunuzu kolayca bulabiliyor musunuz? (`L`ocating our code is easy) + 2. Bir bakışta ne iş yaptığını tanımlayabiliyor musunuz? (`I`dentify code at a glance) + 3. Çok dallanmamış bir klasör yapınız mı var? (`F`lat structure as long as we can) + 4. Bir işi tekrar tekrar yapıyor musunuz? (`T`ry to stay DRY (Don’t Repeat Yourself) or T-DRY) + +### Yer Tespiti +###### [Stil [Y141](#style-y141)] + + - Kodunuzun kolayca tahmin edilebilir bir yerde ve hızlıca ulaşılabilir olduğundan emin olun. + + *Neden?*: Bunun bir proje için çok önemli olduğunu düşünüyorum. Eğer takımınız çalışacağı dosyaları kolayca bulamıyorlarsa, verimli bir şekilde çalışamıyorlarsa yapınız değişmeli demektir. Bir dosyanın ve onunla ilintili olan dosyaların nerede olduğunu bilmiyor olabilirsiniz. Dosyları ve ilintili olan dosyaları kolayca tahmin edilebilecek bir klasöre koymak çok zaman kazanmanızı sağlar. Betimleyici bir klasör yapısı size bunu sağlar. + + ``` + /bower_components + /client + /app + /avengers + /blocks + /exception + /logger + /core + /dashboard + /data + /layout + /widgets + /content + index.html + .bower.json + ``` + +### Tanımlama +###### [Stil [Y142](#style-y142)] + + - Bir dosyaya baktğınız anda onun ne içerdiğini ve temsil ettiğini anlamalısınız. + + *Neden?*: Kodun ne yaptığını anlamak için daha az didiklersiniz ve daha verimli olursunuz. Eğer bunun için daha uzun dosya isimlerine ihtiyaç duyarsanız varsın öyle olsun. Dosya isimleriniz açıklayıcı olsun ve içerisinde sadece bir component bulundursun. Birçok servis ve controller içeren dosyalardan uzak durun. Her dosyada tek bir component kuralının istisnası ancak çok küçük ve ilintili component'lerin aynı dosyada bulunması olabilir. + +### Düz Klasör Yapısı +###### [Stil [Y143](#style-y143)] + + - Olabildiğince dallanmayan bir klasör yapısı kurun. 7'den fazla dosya bir klasörde oluyorsa alt klasörlere bölmeyi düşünün. + + *Neden?*: Hiçkimse bir dosya bulmak için yedi kat klasör gezmek istemez. Websitelerindeki menüleri düşünün… 2 kademeden derin bir menü yapmadan önce iyice düşünmek gerekir. Klasör yapılarında böyle kesin bir sayı yoktur, ancak bir klasörde 7-10 dosya olursa, alt klasörlere bölmeyi düşünmeye başlayabiliriz. Bu sizin rahat hissetmeniz ile ilgilidir. Gerçekten anlamlı olmadıkça alt klasör oluşturmayın ve düz klasör yapısını koruyun. + +### Kendini Tekrar Etmemeye Çalışma (Try to Stick to DRY) +###### [Stil [Y144](#style-y144)] + + - Kendinizi tekrar etmeyin, ama abartıp okunabilirliği azaltmayın. + + *Neden?*: Kendini tekrar etmemek önemlidir, ama LIFT'in diğer prensiplerini bozuyorsa önemini yitirir, o yüzden kendinizi tekrar etmemeye çalışın diyorum. Mesela session-view.html diye isimlendirmek istemiyorum çünkü bir view dosyası olduğu çok açık. Eğer açık değilse ya da geleneğiniz buysa o zaman böyle isimlendirin. + +**[İçerik Tablosu](#icerik-listesi)** + +## Uygulama Yapısı + +### Genel +###### [Stil [Y150](#style-y150)] + + - Kısa vadeli düşünerek implementasyon yapın ama vizyonunuzu uzun vadeli tutun. Diğer bir deyişle, küçük parçalarla başlayın ama uygulamanın nereye doğru gittiğini aklınızda tutun. Uygulamanın bütün kodu `app` adlı bir klasör altında duracak. Bütün içerik bir dosyaya bir özellik şeklinde olacak. Her controller, servis, modül ve view kendi dosyalarında olacaklar. Bütün 3. parti kütüphaneler başka bir klasör altında toplanmalı, `app` klasörü altında değil. O kodları ben yazmadım ve benim uygulamamı karmaşıklaştırmasını istemiyorum (ör. `bower_components`, `scripts`, `lib`). + + Not: Bu yapının hakkında daha fazla bilgi istiyorsanız: [uygulama yapısı hakkındaki orjinal makalem](http://www.johnpapa.net/angular-app-structuring-guidelines/). + +### Layout (yerleşim) +###### [Stil [Y151](#style-y151)] + + - Genel yerleşimi belirleyen bütün component'leri `layout` klasörü altına koyun. Buna ana view ve controller dosyası , navigasyon, menüler, içerik alanları ve diğer alanlar dahil olabilir. + + *Nden?*: Büyün yerleşim öğelerinizi tek bir yerde toplar ve bütün uygulama içerisinde tekrar kullanabilirsiniz. + +### Özelliklere Göre Klasör Yapısı +###### [Stil [Y152](#style-y152)] + + - İçerdikleri özelliğe göre klasör isimleri verin. Eğer bir klasör 7'den fazla dosya içermeye başlarsa bir alt klasör yaratmayı düşünün. Alt klasöre bölme limitiniz farklı olabilir, kendinize göre ayarlayın. + + *Neden?*: Bir yazılımcı kodun yerini bulabilir, tek bakışta ne iş yaptığını anlayabilir, klasör yapısı olabildiğince düz olur ve tekrar eden ve gereksiz isimler olmaz. + + *Neden?*: LIFT prensiplerinin hepsini kapsar. + + *Neden?*: İçeriği organize ederek uygulamanın karmaşıklaşmasını azaltır ve LIFT prensipleri ile örtüştürür. + + *Neden?*: Eğer 10'dan fazla dosya varsa bunların yerini tespit etmek tutarlı bir klasör yapısı ile düz klasör yapısına göre daha kolay olur. + + ```javascript + /** + * önerilen stil + */ + + app/ + app.module.js + app.config.js + components/ + calendar.directive.js + calendar.directive.html + user-profile.directive.js + user-profile.directive.html + layout/ + shell.html + shell.controller.js + topnav.html + topnav.controller.js + people/ + attendees.html + attendees.controller.js + people.routes.js + speakers.html + speakers.controller.js + speaker-detail.html + speaker-detail.controller.js + services/ + data.service.js + localstorage.service.js + logger.service.js + spinner.service.js + sessions/ + sessions.html + sessions.controller.js + sessions.routes.js + session-detail.html + session-detail.controller.js + ``` + + ![Örnek Uygulama Yapısı](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) + + Not: Uygulamanızı tipe göre klasör yapısı ile oluşturmayın. Bu yüzden bir özellik üzerinde çalışırken birden çok klasör içerisinde çalışmanız gerekir ve dosya sayısı arttıkça sizi hantallaştırır. Özelliğe göre klasör yapısına göre dosyaları bulmak daha zordur. + + ```javascript + /* + * sakınılacak stil + * Tipe Göre Klasör yöntemi. + * Bunun yerine ben İçeriğe Göre Klasörleme yöntemini tercih ediyorum. + */ + + app/ + app.module.js + app.config.js + app.routes.js + directives.js + controllers/ + attendees.js + session-detail.js + sessions.js + shell.js + speakers.js + speaker-detail.js + topnav.js + directives/ + calendar.directive.js + calendar.directive.html + user-profile.directive.js + user-profile.directive.html + services/ + dataservice.js + localstorage.js + logger.js + spinner.js + views/ + attendees.html + session-detail.html + sessions.html + shell.html + speakers.html + speaker-detail.html + topnav.html + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Modülerlik + +### Küçük, Kendi Başına Yeterli Modüller +###### [Stil [Y160](#style-y160)] + + - Tek işlevsellik içeren küçük modüller yaratın. + + *Neden?*: Modüler uygulamalar ile "ekle çalıştır" yapmak daha kolaydır. Takım olarak geliştirmeye olanak sağlar ve dikey kesitlerde uygulamayı beraber geliştirebilirsiniz. Bu sayede yeni bir özelliik geliştirildiğinde uygulamaya hemen ekleyebiliriz. + +### Bir App Modülü Yaratın +###### [Stil [Y161](#style-y161)] + + - Bir ana uygulama modülü yaratın ve bunun rolü diğer bütün modülleri ve özellikleri bir araya getirmek olsun. Bu modülü uygulamanızın adı ile isimlendirin. + + *Neden?*: Angular modülerliği ve ayrıştırma desenlerini destekler. Bütün modüllerinizi bir arada tutan ana bir modül yaratmak çok basit bir şekilde bu modüllere ekleme yada bazılarını çıkarmanıza olanak sağlar. + + +### App Modülünü Sade Tutun +###### [Stil [Y162](#style-y162)] + + - Sadece bütün modülleri bir araya getirme mantığını buraya koyun. Özellikleri modüller kendi içlerinde tutsunlar. + + *Neden?*: App modülüne diğer modülleri birbirine bağlamak dışında veri alma, view gösterme ya da başka işleyiş mantıkları koymanız App modülünüzü bulandırır ve özellik setlerinin tekrar kullanılmasını yada kapsam dışına alınmasını zorlaştırır. + + *Neden?*: App modülü bu uygulamanın hangi modüllerden oluştuğunu gösteren bir manifesto haline gelir. + +### Özellik Kapsamları ve Modüller +###### [Stil [Y163](#style-y163)] + + - Özellik kapsamlarını ifade eden modüller yaratın; yerleşim, tekrar kullanılan ve paylaşılan servisler, dashboard ve uygulamaya özel özellikler gibi (Ör. kullanıcılar, admin, satış). + + *Neden?*: Kendi başına yeterli modüller uygulamaya çok küçük pürüzlerle eklenir. + + *Neden?*: Sprint'ler ve iterasyonlar özellik geliştirmeye odaklanabilir ve sprint ya da iterasyon sonunda özellik uygulamaya eklenebilir. + + *Neden?*: Özellik kapsamlarını modüllere ayırmak onları izole şekilde daha kolay test edilebilir hale getirir ve tekrar kullanılmalarını kolaylaştırır. + +### Tekrar Kullanılabilir Bloklar Modüllerdir +###### [Stil [Y164](#style-y164)] + + - Tekrar kullanılabilir uygulama blokları için modüller yaratın. Örneğin exception yakalam, loglama, güvenlik, sistem kontrolü, lokal veri depolama gibi. + + *Neden?*: Bu tarz özelliklere birçok uygulamada ihtiyaç duyulur. Bunları ayrı kendi modüllerinde ayrı tutmak uygulamalar arasında tekrar kullanılmalarına olanak sağlar. + +### Modül Bağımlılıkları +###### [Stil [Y165](#style-y165)] + + - Ana uygulama modülü uygulamaya özel modüllere ve paylaşılan, tekrar kullanılabilen modüllere bağlıdır. + + ![Modülerlik ve Bağımlılık](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) + + *Neden?*: Uygulama ana modülü uygulamanın özelliklerini içeren kolayca tanımlanabilen bir manifestodur. + + *Neden?*: Her özellik kapsamı nelere bağımlı olduğunu gösteren bir manifesto içerir, böylece başka uygulamalar içerisine bağımlılık olarak eklendiğinda hala çalışmaya devam eder. + + *Neden?*: Paylaşılan veri servisleri gibi uygulamalar arası özellikler kolay bulunabilir ve `app.core`'dan kolay bir şekilde paylaşılabilir (Bu modül için favori isminizi seçin). + + Not: Bu tutarlılığı sağlamak için bi stratejidir. Bunun için birçok iyi seçenek mevcut. Angular'ın bağımlılık kurallarını izleyen, kolay yönetilebilen ve genişleyebilen tutarlı olan birtanesini seçin. + + > Benim yapılarım projelerim arasında pek fazla değişmez ama hepsi yapı ve modülerlik için bu rehberi izler. İmplementasyon özelliklere ve takıma göre değişkenlik gösterebilir. Bir başka deyişle, kelimesi kelimesine bir yapıya tutunmayın ama tutarlılığı, yönetilebilirliği ve verimi göz önünde bulundurarak yapınızı ayaralayın. + + > Küçük bir uygulamada, özelliklerin direk bağımlılığı olmadığı, paylaşılan bütün bağımlılıkları ana modüle koymayı düşünebilirsiniz. Bu küçük uygulamaları daha kolay yönetilebilir hale getirir, ama bu uygulamada kullanılan modülleri bu uygulama dışında kullanmayı zorlaştırır. + +**[İçerik Listesi](#icerik-listesi)** + +## Başlangıç Mantığı + +### Konfigürasyon +###### [Stil [Y170](#style-y170)] + + - Kodunuzu, uygulamanız başlamadan önce çalışacak olan [modül konfigürasyonu](https://docs.angularjs.org/guide/module#module-loading-dependencies) içine koyun. Buraya koyulması beklenenler provider'lar ve constant'lardır. + + *Neden?*: Bu yöntem konfigürasyonların yapılacağı yerleri azaltmayı sağlar. + + ```javascript + angular + .module('app') + .config(configure); + + configure.$inject = + ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; + + function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { + exceptionHandlerProvider.configure(config.appErrorPrefix); + configureStateHelper(); + + toastr.options.timeOut = 4000; + toastr.options.positionClass = 'toast-bottom-right'; + + //////////////// + + function configureStateHelper() { + routerHelperProvider.configure({ + docTitle: 'NG-Modular: ' + }); + } + } + ``` + +### Run Blokları +###### [Stil [Y171](#style-y171)] + + - Uygulama başladığında çalışması gereken her kod bloğu bir fonksiyon ile dışarı açılan factory içerisinde tanımlanmalıdır, ve [run bloğuna](https://docs.angularjs.org/guide/module#module-loading-dependencies) inject edilmelidir. + + *Neden?*: Run bloğu içerisinde boşta duran bir kod zor test edilir. Bir factory içerisine koymak soyutlaştırmayı ve mock'lamayı kolaylaştırır. + + ```javascript + angular + .module('app') + .run(runBlock); + + runBlock.$inject = ['authenticator', 'translator']; + + function runBlock(authenticator, translator) { + authenticator.initialize(); + translator.initialize(); + } + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Angular Servisleri + +### $document ve $window +###### [Stil [Y180](#style-y180)] + + - `document` ve `window` yerine [`$document`](https://docs.angularjs.org/api/ng/service/$document) ve [`$window`](https://docs.angularjs.org/api/ng/service/$window) kullanın. + + *Neden?*: Bu servisler Angular tarafından yaratılmıştır ve document ve window'a göre daha kolay test edilebilirler. Bu kendiniz için mock document ve window oluşturmanızı engeller. + +### $timeout and $interval +###### [Stil [Y181](#style-y181)] + + - `setTimeout` and `setInterval` yerine [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) ve [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) kullanın. + + *Neden?*: Bu servisler Angular tarafından yaratılmıştır, daha kolay test edilebilirler ve Angular'ın digest cycle'ını yönetebilir ve veriyi senkronize tutabilirler. + +**[İçerik Listesi](#icerik-listesi)** + +## Test + +Unit Test yapmak temiz kodu yönetmeye yardımcı olur, bu yüzden benim unit test temelleri için önerilerimi linkler ve daha fazla detay ile paylaşıyorum. + +### Testlerinizi Senaryolar İle Yazın +###### [Stil [Y190](#style-y190)] + + - Her senaryo için test kümeleri yazın. Boş bir test ile başlayın ve kodu yazarken içini doldurun. + + *Neden?*: Test tanımlamalarını yazmak senaryonuzun ne yapacağını, ne yapmayacağını ve başarısını nasıl ölçeceğinizi tanımlamaya yardımcı olur. + + ```javascript + it('Avengers controller`ı tanımlı olmalı', function() { + // TODO + }); + + it('İsme göre filtrelendiğinde 1 Avenger bulmalı', function() { + // TODO + }); + + it('10 Avenger olmalı', function() { + // TODO (mock veri?) + }); + + it('Avengerları XHR ile dönmeli', function() { + // TODO ($httpBackend?) + }); + + // and so on + ``` + +### Test Kütüphanesi +###### [Stil [Y191](#style-y191)] + + - Unit testleriniz için [Jasmine](http://jasmine.github.io/) ya da [Mocha](http://mochajs.org) kullanın. + + *Neden?*: Jasmine ve Mocha, ikisi de Angular topluluğu tarafından yaygınca kullanılmaktadır. İkisi de istikrarlı, iyi yönetilir, ve sağlam test özellikleri sunuyorlar. + + Not: Mocha kullanırken ayrıca [Chai](http://chaijs.com) gibi bir assert kütüphanesi kullanmayı gözönünde bulundurun. Ben Mocha'yı tercih ediyorum. + +### Test Çalıştırıcı +###### [Stil [Y192](#style-y192)] + + - Test çalıştırıcısı olarak [Karma](http://karma-runner.github.io)'yı kullanın. + + *Neden?*: Karma kolay ayarlanabilir ve kodunuzu değiştirdiğinizde otomatik olarak ya da birkereliğine çalışabilir bir araçtır. + + *Neden?*: Karma kendi başına ya da Grunt veya Gulp aracılığı ile Continuous Integration sisteminize kolaylıkla dahil olabilir. + + *Neden?*: [WebStorm](http://www.jetbrains.com/webstorm/) ve [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225) gibi bazı IDE'ler Karma ile entegre olmaya başladılar. + + *Neden?*: Karma, [Grunt](http://www.gruntjs.com) ([grunt-karma](https://github.com/karma-runner/grunt-karma) ile) ve [Gulp](http://www.gulpjs.com) gibi otomatik görev yönetici devleri ile uyumlu çalışır. Gulp kullanırken [Karma](https://github.com/karma-runner/karma)'yı direk olarak API'si ile kullanabilirsiniz. + + ```javascript + /* önerilen stil */ + + // Karman'nın direk kullanıldığı Gulp örneği + function startTests(singleRun, done) { + var child; + var excludeFiles = []; + var fork = require('child_process').fork; + var karma = require('karma').server; + var serverSpecs = config.serverIntegrationSpecs; + + if (args.startServers) { + log('Starting servers'); + var savedEnv = process.env; + savedEnv.NODE_ENV = 'dev'; + savedEnv.PORT = 8888; + child = fork(config.nodeServer); + } else { + if (serverSpecs && serverSpecs.length) { + excludeFiles = serverSpecs; + } + } + + karma.start({ + configFile: __dirname + '/karma.conf.js', + exclude: excludeFiles, + singleRun: !!singleRun + }, karmaCompleted); + + //////////////// + + function karmaCompleted(karmaResult) { + log('Karma completed'); + if (child) { + log('shutting down the child process'); + child.kill(); + } + if (karmaResult === 1) { + done('karma: tests failed with code ' + karmaResult); + } else { + done(); + } + } + } + ``` + +### Stubbing ve Spying +###### [Stil [Y193](#style-y193)] + + - Stubbing ve spying [Sinon](http://sinonjs.org/) kullanın. + + *Neden?*: Sinon, Jasmine ve Mocha ile uyumlu çalışır ve sundukları stubbing ve spying özelliklerini genişletir. + + *Neden?*: Sinon, Jasmine ve Karma arasında kolay geçiş sağlar, eğer ikisini de kullanmak istiyorsanız. + + *Neden?*: Sinon, assert'ler hata verdiğin güzel açıklamalı hata mesajları üretir. + +### Head'siz Tarayıcı +###### [Stil [Y194](#style-y194)] + + - Testlerinizi sunucu üzerinde çalıştırmak için [PhantomJS](http://phantomjs.org/) kullanın. + + *Neden?*: PhantomJS head'siz bir tarayıcıdır ve testlerinizi görsel bir tarayıcıya ihtiyaç duymadan çalıştırabilir. Böylece sunucunuza Chrome, Safari, IE ya da başka bir tarayıcı yüklemek zorunda kalmazsınız. + + Not: Siz yine de hedef kitlenizin kullanacağı tarayıcıları kendi ortamınızda test etmelisiniz. + +### Kod Analizi +###### [Stil [Y195](#style-y195)] + + - Run JSHint on your tests. + - Testlerinizde JSHint kullanın. + + *Neden?*: Testler de koddur. JSHint kodun hatılı çalışmasına sebep olan kod kalitesi problemlerini denetler. + +### JSHint Kurallarını Testler İçin Hafifletmek +###### [Stil [Y196](#style-y196)] + + - `describe` ve `expect` gibi yaygın kullanılan global'lere izin vermesi için kurallarınızı hafifletin. Expression'ları Mocha kullandığı için onları kontrol eden kuralları da hafifletin. + + *Neden?*: Testleriniz de birer koddur ve üretim ortamındaki kodunuz gibi onlar da aynı ilgi ve kod kalitesini hakeder. Ancak, test için test frameworkleri tarafından kullanılan global değişkenler için bu kurallar hafifletilmelidir. + + ```javascript + /* jshint -W117, -W030 */ + ``` + Or you can add the following to your JSHint Options file. + Ya da aşağıdaki ayarları JSHint konfigürasyon dosyanıza ekleyebilirsiniz + + ```javascript + "jasmine": true, + "mocha": true, + ``` + + ![Test Araçları](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) + +### Testleri Organize Etmek +###### [Stil [Y197](#style-y197)] + + - Test dosyalarınızı(specs) test ettiğiniz dosyalarla yanyana koyun. Birçok component'i ve sunucu entegrasyonunu test eden kodları `tests` klasörüne koyun. + + *Neden?*: Unit testlerin kaynak kodundaki component ile doğrudan ilişkisi vardır. + + *Neden?*: Sürekli gözönünde olduklarından güncel tutmak kolay olacaktır. TDD de yapsanız, kod yazarken ya da yazdıktan sonra da test yazsanız, kaynak kodu dosyanızla yanyana olan test dosyası gözünüzden ve aklınızdan kaçmaz, ve böylece güncellenme olasılığı ve kodunuzun test kapsamı artar. + + *Neden?*: Kaynak kodunu güncellediğinizde test kodunu da güncellemeniz kolaylaşır. + + *Neden?*: Kaynak kodu ile yanyana koymak test kodlarını bulmayı kolaylaştırır ve taşırken onunla birlikte gitmesini sağlar. + + *Neden?*: Testi yakınlarda tutmak, kaynak kodu okuyan kişinin component'in ne yaptığını ve limitlerini keşfetmesi için testi de okumasını kolaylaştırır. + + *Neden?*: Yayına çıkacak kodun içerisinde testlerin bulunmamasını sağlamak grunt ve gulp ile kolay olur. + + ``` + /src/client/app/customers/customer-detail.controller.js + /customer-detail.controller.spec.js + /customers.controller.js + /customers.controller.spec.js + /customers.module.js + /customers.route.js + /customers.route.spec.js + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Animasyonlar + +### Kullanım +###### [Stil [Y210](#style-y210)] + + - [Angular ile animasyonlar](https://docs.angularjs.org/guide/animations) state'ler arası ve birincil görsel elemanlar için kullanılıyor ise hafif olmalı. [ngAnimate module](https://docs.angularjs.org/api/ngAnimate) modülünü projenize dahil edin. 3 anahtar özellik: hafif, pürüzsüz, teklemeyen. + + *Neden?*: Uygun kullanıldığında hafif animasyonlar kullanıcı deneyimini arttırır. + + *Neden?*: Hafif animasyonlar view geçişleri sırasında hissedilen performansı arttırır. + +### Saniyenin Altında +###### [Stil [Y211](#style-y211)] + + - Animasyonlarınızı kısa sürelerde tamamlayın. Ben genellikle 300ms ile başlarım ve duruma göre ayarlarım. + + *Neden?*: Uzun animasyonlar kullanıcı deneyimi ve hissedilen performans üzerinde yavaş bir uygulamaymış gibi algılanıp ters etki yaratabilir. + +### animate.css +###### [Stil [Y212](#style-y212)] + + - Geleneksel animasyonlar için [animate.css](http://daneden.github.io/animate.css/) kullanın. + + *Neden?*: animate.css'in sunduğı animasyonlar hızlı, akıcı ve uygulamanıza kolay eklenebilir. + + *Neden?*: Animasyonlarınızda tutarlılık sağlar. + + *Neden?*: animate.css yaygınca kullanılıyor ve test edilmiş. + + Not: Bu müthiş makaleye göz gezdirin: [Matias Niemelä Angular animations anlatıyor](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) + +**[İçerik Listesi to top](#icerik-listesi)** + +## Yorumlar + +### jsDoc +###### [Stil [Y220](#style-y220)] + + - Kodunuz için dökümantasyon yaratmayı planlıyorsanız, [`jsDoc`](http://usejsdoc.org/) sintaksını kullanın. Fonksiyon isimleri, açıklamaları, parametreleri ve ne döndürdüklerini listeleyebilirsiniz. `@namespace` ve `@memberOf` anahtar kelimelerini uygulamanızın yapısı ile eşleştirmek için kullanın. + + *Neden?*: Sıfırdan yazmak yere kodunuz üzerinden dökümantasyonunuzu yaratabilirsiniz. + + *Neden?*: Yaygın bir araç kullanmak tutarlılık sağlar. + + ```javascript + /** + * Logger Factory + * @namespace Factories + */ + (function() { + angular + .module('app') + .factory('logger', logger); + + /** + * @namespace Logger + * @desc Uygulama geneli için logger + * @memberOf Factories + */ + function logger($log) { + var service = { + logError: logError + }; + return service; + + //////////// + + /** + * @name logError + * @desc Logs errors + * @param {String} msg Loglanacak mesaj + * @returns {String} + * @memberOf Factories.Logger + */ + function logError(msg) { + var loggedMsg = 'Error: ' + msg; + $log.error(loggedMsg); + return loggedMsg; + }; + } + })(); + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## JS Hint + +### Bir Seçenek Dosyası Kullanın +###### [Stil [Y230](#style-y230)] + + - JavaScript Kod denetleyicisi olarak JS Hint kullanın ve JS Hint seçeneklerini kendinize göre ayarlamayı unutmayın. Seçeneklerin detayları için [JS Hint dökümantasyonuna](http://www.jshint.com/docs/) bakın. + + *Neden?*: Kodunuzu versiyon kontrol sistemine göndermeden önce size bir ilk uyarı verir. + + *Neden?*: Takımınız içinde tutarlılık oluşturur. + + ```javascript + { + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "es3": false, + "forin": true, + "freeze": true, + "immed": true, + "indent": 4, + "latedef": "nofunc", + "newcap": true, + "noarg": true, + "noempty": true, + "nonbsp": true, + "nonew": true, + "plusplus": false, + "quotmark": "single", + "undef": true, + "unused": false, + "strict": false, + "maxparams": 10, + "maxdepth": 5, + "maxstatements": 40, + "maxcomplexity": 8, + "maxlen": 120, + + "asi": false, + "boss": false, + "debug": false, + "eqnull": true, + "esnext": false, + "evil": false, + "expr": false, + "funcscope": false, + "globalstrict": false, + "iterator": false, + "lastsemic": false, + "laxbreak": false, + "laxcomma": false, + "loopfunc": true, + "maxerr": false, + "moz": false, + "multistr": false, + "notypeof": false, + "proto": false, + "scripturl": false, + "shadow": false, + "sub": true, + "supernew": false, + "validthis": false, + "noyield": false, + + "browser": true, + "node": true, + + "globals": { + "angular": false, + "$": false + } + } + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## JSCS + +### Bir Seçenek Dosyası Kullanın +###### [Stil [Y235](#style-y235)] + + - Kod yazım stilinizi kontrol etmek için JSCS kullanın ve JSCS seçeneklerini kendinize göre ayarlamayı unutmayın. Seçeneklerin detayları için [JSCS dökümantasyonuna](http://www.jscs.info) bakın. + + *Neden?*: Kodunuzu versiyon kontrol sistemine göndermeden önce size bir ilk uyarı verir. + + *Neden?*: Takımınız içinde tutarlılık oluşturur. + + ```javascript + { + "excludeFiles": ["node_modules/**", "bower_components/**"], + + "requireCurlyBraces": [ + "if", + "else", + "for", + "while", + "do", + "try", + "catch" + ], + "requireOperatorBeforeLineBreak": true, + "requireCamelCaseOrUpperCaseIdentifiers": true, + "maximumLineLength": { + "value": 100, + "allowComments": true, + "allowRegex": true + }, + "validateIndentation": 4, + "validateQuoteMarks": "'", + + "disallowMultipleLineStrings": true, + "disallowMixedSpacesAndTabs": true, + "disallowTrailingWhitespace": true, + "disallowSpaceAfterPrefixUnaryOperators": true, + "disallowMultipleVarDecl": null, + + "requireSpaceAfterKeywords": [ + "if", + "else", + "for", + "while", + "do", + "switch", + "return", + "try", + "catch" + ], + "requireSpaceBeforeBinaryOperators": [ + "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", + "&=", "|=", "^=", "+=", + + "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", + "|", "^", "&&", "||", "===", "==", ">=", + "<=", "<", ">", "!=", "!==" + ], + "requireSpaceAfterBinaryOperators": true, + "requireSpacesInConditionalExpression": true, + "requireSpaceBeforeBlockStatements": true, + "requireLineFeedAtFileEnd": true, + "disallowSpacesInsideObjectBrackets": "all", + "disallowSpacesInsideArrayBrackets": "all", + "disallowSpacesInsideParentheses": true, + + "jsDoc": { + "checkAnnotations": true, + "checkParamNames": true, + "requireParamTypes": true, + "checkReturnTypes": true, + "checkTypes": true + }, + + "disallowMultipleLineBreaks": true, + + "disallowCommaBeforeLineBreak": null, + "disallowDanglingUnderscores": null, + "disallowEmptyBlocks": null, + "disallowTrailingComma": null, + "requireCommaBeforeLineBreak": null, + "requireDotNotation": null, + "requireMultipleVarDecl": null, + "requireParenthesesAroundIIFE": true + } + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Constant'lar + +### 3. Parti Kütüphane Global'leri +###### [Stil [Y240](#style-y240)] + + - 3. parti kütüphanelerin global değişkenleri için birer Angular constant'ı tanımlayın. + + *Neden?*: 3. parti kütüphaneleri kodunuza inject etmek için bir yol yaratır. Component'lerinizin bağımlılıklarını daha kolay anlamanızı sağlayarak kodunuzun test edilebilirliğini arttırır. (abstraction sızıntılarını engeller). Ayrıca bu bağımlılıkları mock'layabilmenizi sağlar. + + ```javascript + // constants.js + + /* global toastr:false, moment:false */ + (function() { + 'use strict'; + + angular + .module('app.core') + .constant('toastr', toastr) + .constant('moment', moment); + })(); + ``` + +###### [Stil [Y241](#style-y241)] + + - Değişmeyen ve başka servislerden gelmeyen değerler için constants kullanın. Eğer bu constant'lar birden fazla uygulamada kullanılabilecek bir modüle için yaratılıyor ise, her modül için constant'ları modül ile aynı isme sahip bir dosyaya koyun. Bu durum gerekli olmadığı takdirde, constant'ları ana modül içerisinde `constants.js` dosyasında tutun. + + *Neden?*: Değişme olasılığı olan, çok nadir de olsa, değerler bir servisten alınmalıdır, çünkü bu sayede kaynak kodunuzu değiştirmek zorunda kalmazsınız. Örneğin, bir veri servisi için url adresi constant içine yerleştirilebilir, ama daha iyi bir seçenek bunu bir web servisten yüklemek olacaktır. + + *Neden?*: Constant'lar angular component'lerine inject edilebilir, buna provider'lar dahildir. + + *Neden?*: Bir uygulama başka uygulamalarda kullanılabilecek modüllere ayrıldığında, her biri kendi başına bağımlı olduğu constant'lar ile birlikte çalışabilecek durumda olmalıdır. + + ```javascript + // Bütün uygulama tarafından kullanılan constant'lar + angular + .module('app.core') + .constant('moment', moment); + + // Sadece satış modülünün kullandığı constant'lar + angular + .module('app.sales') + .constant('events', { + ORDER_CREATED: 'event_order_created', + INVENTORY_DEPLETED: 'event_inventory_depleted' + }); + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Dosya Şablonları ve Snippet'ler + +Tutarlı pattern'ler ve stiller için dosya şablonları ve snippet'ler kullanın. Burada bazı web geliştirme editörleri ve IDE'leri için şablon ve snippet'leri bulabilirsiniz. + +### Sublime Text +###### [Stil [Y250](#style-y250)] + + - Bu stil ve rehberleri izleyen Angular snippet'leri + + - [Sublime Angular snippet'leri](assets/sublime-angular-snippets?raw=true) indirin + - Packages klasörünün içine koyun + - Sublime'ı baştan başlatın + - JavaScript dosyalarında bu komutlardan sonra `TAB` tuşuna basın + + ```javascript + ngcontroller // Angular controller'ı yaratır + ngdirective // Angular directive'i yaratır + ngfactory // Angular factory'si yaratır + ngmodule // Angular modülü yaratır + ngservice // Angular service'i yaratır + ngfilter // Angular filtresi yaratır + ``` + +### Visual Studio +###### [Stil [Y251](#style-y251)] + + - Bu stil ve rehberleri izleyen Angular snippet'leri [SideWaffle](http://www.sidewaffle.com) adresinde bulunabilir + + - [SideWaffle](http://www.sidewaffle.com) Visual Studio extension (vsix dosyası) indirin + - vsix dosyasını çalıştırın + - Visual Studio'yu baştan başlatın + +### WebStorm +###### [Stil [Y252](#style-y252)] + + - Bu stil ve rehberleri izleyen Angular snippet'leri. Webstorm ayarlarınızdan içeri aktarabilirsiniz: + + - [WebStorm Angular dosya şablonları ve snippet'leri](assets/webstorm-angular-file-template.settings.jar?raw=true)'ni indirin + - WebStorm'u açın ve `File` menüsüne gidin + - `Import Settings` seçeneğini seçin + - Dosyayı seçin ve `OK` tuşuna basın + - JavaScript dosyalarında bu komutlardan sonra `TAB` tuşuna basın + + ```javascript + ng-c // creates an Angular controller + ng-f // creates an Angular factory + ng-m // creates an Angular module + ``` + +### Atom +###### [Stil [Y253](#style-y253)] + + - Bu stil ve rehberleri izleyen Angular snippet'leri. + ``` + apm install angularjs-styleguide-snippets + ``` + ya da + - Atom'u açın, Pakey yöneticisini açın (Packages -> Settings View -> Install Packages/Themes) + - 'angularjs-styleguide-snippets' paketini aratın + - 'Install' tuşuna basıp paketi yükleyin + + - JavaScript dosyalarında bu komutlardan sonra `TAB` tuşuna basın + + ```javascript + ngcontroller // Angular controller'ı yaratır + ngdirective // Angular directive'i yaratır + ngfactory // Angular factory'si yaratır + ngmodule // Angular modülü yaratır + ngservice // Angular service'i yaratır + ngfilter // Angular filtresi yaratır + ``` + +### Brackets +###### [Stil [Y254](#style-y254)] + + - Bu stil ve rehberleri izleyen Angular snippet'leri. + - [Brackets Angular snippet'leri](assets/brackets-angular-snippets.yaml?raw=true)'ni indirin + - Brackets Extension Yöneticisini açın ( File > Extension manager ) + - ['Brackets Snippet'leri (by edc)'](https://github.com/chuyik/brackets-snippets)'ni yükleyin + - brackets'in sağ tarafındaki ampule tıklayın + - `Settings`'e tıklayın ve `Import` seçeneğini seçin + - Dosyayı seçin + - `Start Import` seçeneğini seçin + + - JavaScript dosyalarında bu komutlardan sonra `TAB` tuşuna basın + + ```javascript + // These are full file snippets containing an IIFE + ngcontroller // Angular controller'ı yaratır + ngdirective // Angular directive'i yaratır + ngfactory // Angular factory'si yaratır + ngapp // Angular modülü yaratır + ngservice // Angular service'i yaratır + ngfilter // Angular filtresi yaratır + + // These are partial snippets intended to chained + ngmodule // Angular module getter'i yaratır + ngstate // Angular UI Router tanımlayıcısı yaratır + ngconfig // konfigürasyon tanımlama fonksiyonu yaratır + ngrun // run bloğu tanımlama fonksiyonu yaratır + ngroute // Angular ngRoute 'when' tanımlama fonksiyonu yaratır + ngtranslate // $translate servisini promise'ile kullanan şablon yaratır + ``` + +### vim +###### [Stil [Y255](#style-y255)] + + - Bu stil ve rehberleri izleyen Angular vim snippet'leri. + + - [vim Angular snippet'leri](assets/vim-angular-snippets?raw=true)'ni indirin + - [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) dosyasını indirin + - snippet'leri snippet klasörüne kopyalayın + + + - Bu stil ve rehberleri izleyen Angular vim UltiSnips snippet'leri. + + - [vim Angular UltiSnips snippet'leri](assets/vim-angular-ultisnips?raw=true)'ni indirin + - [UltiSnips](https://github.com/SirVer/ultisnips) dosyasını indirin + - snippet'leri UltiSnips klasörüne kopyalayın + + ```javascript + ngcontroller // Angular controller'ı yaratır + ngdirective // Angular directive'i yaratır + ngfactory // Angular factory'si yaratır + ngmodule // Angular modülü yaratır + ngservice // Angular service'i yaratır + ngfilter // Angular filtresi yaratır + ``` + +### Visual Studio Code + +###### [Stil [Y256](#style-y256)] + + - [Visual Studio Code](http://code.visualstudio.com) stil ve rehberleri izleyen Angular vim snippet'leri + + - [VS Code Angular snippet'leri](assets/vscode-snippets/javascript.json?raw=true)'ni indirin + - copy snippets to snippet directory, or alternatively copy and paste the snippets into your existing ones + - snippet'leri snippet klasörüne kopyalayın, alternatif olarak snippetleri şu andaki snippetlerinizin olduğu yere kopyala/yapıştır yapın + + ```javascript + ngcontroller // Angular controller'ı yaratır + ngdirective // Angular directive'i yaratır + ngfactory // Angular factory'si yaratır + ngmodule // Angular modülü yaratır + ngservice // Angular service'i yaratır + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Yeoman Generator +###### [Stil [Y260](#style-y260)] + +[HotTowel yeoman üreticisi](http://jpapa.me/yohottowel)'ni Angular uygulamanız için bir başlangıç noktası yaratmak için ve stilleri takip etmek için kullanabilirsiniz. + +1. generator-hottowel'ı yükleyin + + ``` + npm install -g generator-hottowel + ``` + +2. Yeni bir klasör yaratın ve o klasörün içine girin + + ``` + mkdir myapp + cd myapp + ``` + +3. Üreticiyi çalıştırın + + ``` + yo hottowel helloWorld + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Routing + +Kullanıcı tarafında, küçük şablonlar ve directive'lerden oluşan view geçişleri için yapılan routing önemlidir. + +###### [Stil [Y270](#style-y270)] + + - Kullanıcı tarafındaki routing için [AngularUI Router](http://angular-ui.github.io/ui-router/)'ı kullanın. + + *Neden?*: UI Router Angular router'ın sağladığı bütün özellikleri sağlar ayrıca iç içe route ve state'ler için olanak sağlar. + + *Neden?*: Angular router sintaksına çok benzer ve UI Router'a geçiş yapmak kolaydır. + + - Not: `routerHelperProvider` gibi bir provider kullanarak dosyalar arası state konfigürasyonlarını ayarlama yapabilirsiniz. + + ```javascript + // customers.routes.js + angular + .module('app.customers') + .run(appRun); + + /* @ngInject */ + function appRun(routerHelper) { + routerHelper.configureStates(getStates()); + } + + function getStates() { + return [ + { + state: 'customer', + config: { + abstract: true, + template: '', + url: '/customer' + } + } + ]; + } + ``` + + ```javascript + // routerHelperProvider.js + angular + .module('blocks.router') + .provider('routerHelper', routerHelperProvider); + + routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; + /* @ngInject */ + function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { + /* jshint validthis:true */ + this.$get = RouterHelper; + + $locationProvider.html5Mode(true); + + RouterHelper.$inject = ['$state']; + /* @ngInject */ + function RouterHelper($state) { + var hasOtherwise = false; + + var service = { + configureStates: configureStates, + getStates: getStates + }; + + return service; + + /////////////// + + function configureStates(states, otherwisePath) { + states.forEach(function(state) { + $stateProvider.state(state.state, state.config); + }); + if (otherwisePath && !hasOtherwise) { + hasOtherwise = true; + $urlRouterProvider.otherwise(otherwisePath); + } + } + + function getStates() { return $state.get(); } + } + } + ``` + +###### [Stil [Y271](#style-y271)] + + - View'lar için route'ları modül içerisinde tanımlayın. Her modül kend view'ları için gerekli olan route'ları barındırmalıdır. + + *Neden?*: Her modül kendi başına yeterli olmalıdır. + + *Neden?*: Bir modülü kaldırırken ya da eklerken, uygulama sadece var olan view'lara giden route'ları içermelidir. + + *Neden?*: Bu uygulamanın bazu bölümlerini açıp kaparken etrafta ulaşılamayacak route'lar kalmasını engeller. + +**[İçerik Listesi](#icerik-listesi)** + +## Görev Otomasyonu +Otomatik görevler yaratmak için [Gulp](http://gulpjs.com) ya da [Grunt](http://gruntjs.com) kullanın. Gulp convention over configuration paradigmasını benimserken, Grunt configuration over code prensibini benimser. Ben şahsen Gulp'ı tercih ediyorum. Yazması ve okuması daha kolay geliyor, ama ikisi de çok başarılılar. + +> [Gulp Pluralsight kursu](http://jpapa.me/gulpps)mda gulp ve otomatik görevler hakkında daha çok şey öğrenebilirsiniz. + +###### [Stil [Y400](#style-y400)] + + - Modül tanımlayıcı dosyalarını `*.module.js` diğer bütün uygulama JavaScript dosyalarından önce listelemek için otomatik görevleri kullanın. + + *Neden?*: Angular modüllerin kullanılmadan önce kayıt olmasını bekler. + + *Neden?*: Modülleri `*.module.js` gibi belirgin bir tarz ile isimlendirmeniz onları yakalamanızı ve listelemenizi kolaylaştırır. + + ```javascript + var clientApp = './src/client/app/'; + + // Always grab module files first + var files = [ + clientApp + '**/*.module.js', + clientApp + '**/*.js' + ]; + ``` + +**[İçerik Listesi](#icerik-listesi)** + +## Filtreler + +###### [Stil [Y420](#style-y420)] + + - Komplek objelerin bütün property'lerini taramak için filtreleri kullanmaktan kaçının. Filtreleri property'leri seçmek için kullanın. + + *Neden?*: Filtrelerin kullanımı kolaylıkla suistimal ediliyor ve uygulamanın performansını kötü etkiliyor. Örneğin bir filtrenin büyük ve derin bir objeyi taraması. + +**[İçerik Listesi](#icerik-listesi)** + +## Angular dökümantasyonu + +Burada anlatılanların dışındaki herşey için ve API referansı için [Angular dökümantasyonu](//docs.angularjs.org/api)'na bakın. + +## Katkıda bulunma + +Potensiyel değişiklik/eklemeler için öncelikle bir issue açın. Eğer rehber hakkında sorularınız varsa, onları da issue olarak açabilirsiniz. Eğer bir yazım hatası bulursanız pull request açın. Anafikir içeriği güncel tutmak ve github'ın kendi özelliklerini kullanarak bu işi issuelar ve pull request'lerle ilerletmek, bunların hepsi google tarafından indeksleniyor ve aramalar da çıkıyor. Neden? Çünkü bir sorunuz var ise, aynı soruyu başkası da soruyor olabilir! Burada nasıl katkıda bulunabileceğinize dair daha fazla bigi bulabilirsiniz. + +*Bu repository'ye katkıda bulunarak içeriğinizin bu repository'nin lisansına dahil olduğunu kabul etmiş bulunursunuz.* + +### Süreç + 1. Değişiklikleri bir issue ile tartışın. + 2. Bir Pull Request açın, issue'yu referans gösterin, ve değişikliği açıklayın. Bu değişiklik nasıl bir değer katıyor? + 3. Pull Requestiniz incelenecektir ve ya merge edilecek ya da geri çevrilecektir. + +## Lisans + +_uzun lafın kısası; Bu rehberi kullanın. Katkılarınız makbule geçecektir._ + +### Copyright + +Copyright (c) 2014-2015 [John Papa](http://johnpapa.net) + +### (The MIT License) +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**[İçerik Listesi](#icerik-listesi)** From 9b16f40fab7d22d5ed9ca5b3ae9d82d3cc403831 Mon Sep 17 00:00:00 2001 From: Prayag Verma Date: Sun, 17 Jan 2016 19:13:50 +0530 Subject: [PATCH 022/114] Update license year to 2016 --- LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index d8360108..83192343 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 John Papa +Copyright (c) 2014-2016 John Papa Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 2f58b961..71bee4fa 100644 --- a/README.md +++ b/README.md @@ -3216,7 +3216,7 @@ _tldr; Use this guide. Attributions are appreciated._ ### Copyright -Copyright (c) 2014-2015 [John Papa](http://johnpapa.net) +Copyright (c) 2014-2016 [John Papa](http://johnpapa.net) ### (The MIT License) Permission is hereby granted, free of charge, to any person obtaining From 9031917eb2b4383fc0da9e4ffb7ede44fa208dc3 Mon Sep 17 00:00:00 2001 From: Prayag Verma Date: Mon, 1 Feb 2016 21:15:44 +0530 Subject: [PATCH 023/114] Minor English tweak Adding missing `to` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71bee4fa..bf072a51 100644 --- a/README.md +++ b/README.md @@ -2283,7 +2283,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see ### Create an App Module ###### [Style [Y161](#style-y161)] - - Create an application root module whose role is pull together all of the modules and features of your application. Name this for your application. + - Create an application root module whose role is to pull together all of the modules and features of your application. Name this for your application. *Why?*: Angular encourages modularity and separation patterns. Creating an application root module whose role is to tie your other modules together provides a very straightforward way to add or remove modules from your application. From f7d5837f118e26ae8341c325de9add18c287474b Mon Sep 17 00:00:00 2001 From: Arianrhod Date: Tue, 2 Feb 2016 15:19:26 +0800 Subject: [PATCH 024/114] Fix hashes in zh-CN translation --- i18n/zh-CN.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/i18n/zh-CN.md b/i18n/zh-CN.md index 370d259b..7436eafd 100644 --- a/i18n/zh-CN.md +++ b/i18n/zh-CN.md @@ -39,7 +39,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 1. [压缩和注释](#压缩和注释) 1. [异常处理](#异常处理) 1. [命名](#命名) - 1. [应用程序结构LIFT原则](#应用程序结构lift原则) + 1. [应用程序结构的LIFT准则](#应用程序结构的lift准则) 1. [应用程序结构](#应用程序结构) 1. [模块化](#模块化) 1. [启动逻辑](#启动逻辑) @@ -387,14 +387,14 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 }); } ``` - + ###置顶绑定成员 ###### [Style [Y033](#style-y033)] - 把可绑定的成员放到controller的顶部,按字母排序,并且不要通过controller的代码传播。 - *为什么?*:虽然设置单行匿名函数很容易,但是当这些函数的代码超过一行时,这将极大降低代码的可读性。在可绑定成员下面定义函数(这些函数被提出来),把具体的实现细节放到下面,可绑定成员置顶,这会提高代码的可读性。 + *为什么?*:虽然设置单行匿名函数很容易,但是当这些函数的代码超过一行时,这将极大降低代码的可读性。在可绑定成员下面定义函数(这些函数被提出来),把具体的实现细节放到下面,可绑定成员置顶,这会提高代码的可读性。 ```javascript /* avoid */ @@ -480,7 +480,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ###### [Style [Y034](#style-y034)] - 使用函数声明来隐藏实现细节,置顶绑定成员,当你需要在controller中绑定一个函数时,把它指向一个在文件的后面会出现函数声明。更多详情请看[这里](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code)。 - + *为什么?*:易读,易识别哪些成员可以在View中绑定和使用。 *为什么?*:把函数的实现细节放到后面,你可以更清楚地看到重要的东西。 @@ -3071,7 +3071,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 2. 拉取一个pull request,引用这个问题,解释你做的修改和为什么要这样做。 3. pull request将会被进行评估,结果就是合并或是拒绝。 -## 许可证 +## 许可 - **tldr;** 如果可以的话,使用本规范的时候还是指明归属吧。 From 976874aa06079d506e8f4b6fea84b2d8dc8d7b1c Mon Sep 17 00:00:00 2001 From: renxiangyu Date: Tue, 2 Feb 2016 16:36:55 +0800 Subject: [PATCH 025/114] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8D=95=E8=AF=8D?= =?UTF-8?q?=E6=8B=BC=E5=86=99=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modify word spelling error --- i18n/zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/zh-CN.md b/i18n/zh-CN.md index 370d259b..1e0e2601 100644 --- a/i18n/zh-CN.md +++ b/i18n/zh-CN.md @@ -1020,7 +1020,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 *为什么?*:一个文件一个directive也更加容易维护。 - > 注: "**最佳实践**:Angular文档中有提过,directive应该自动回收,当directive被移除后,你可以使用`element.on('$destroy', ...)`或者`scope.$on('$destroy', ...)`来执行一个clearn-up函数。" + > 注: "**最佳实践**:Angular文档中有提过,directive应该自动回收,当directive被移除后,你可以使用`element.on('$destroy', ...)`或者`scope.$on('$destroy', ...)`来执行一个clean-up函数。" ```javascript /* avoid */ From 6809c48f1b984a3fb96be6dec5517055a6706b4d Mon Sep 17 00:00:00 2001 From: John Papa Date: Tue, 2 Feb 2016 09:07:20 -0500 Subject: [PATCH 026/114] expanded on the 1 file and added 002 for small functions --- README.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 71bee4fa..51740cd9 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,13 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see ### Rule of 1 ###### [Style [Y001](#style-y001)] - - Define 1 component per file. + - Define 1 component per file, recommended to be less than 500 lines of code. + + *Why?*: One component per file promotes easier unit testing and mocking. + + *Why?*: One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control. + + *Why?*: One component per file avoids hidden bugs that often arise when combining components in a file where they may share variables, create unwanted closures, or unwanted coupling with dependencies. The following example defines the `app` module and its dependencies, defines a controller, and defines a factory all in the same file. @@ -115,6 +121,26 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see **[Back to top](#table-of-contents)** +### Small Functions +###### [Style [Y002](#style-y002)] + + - Define small functions, no more than 75 LOC (less is better). + + *Why?*: Small functions are easier to test, especially when they do one thing and serve one purpose. + + *Why?*: Small functions promote reuse. + + *Why?*: Small functions are easier to read. + + *Why?*: Small functions are easier to maintain. + + *Why?*: Small functions help avoid hidden bugs that come with large functions that share variables with external scope, create unwanted closures, or unwanted coupling with dependencies. + + *Why?*: Small functions are easier to maintain. + + +**[Back to top](#table-of-contents)** + ## IIFE ### JavaScript Closures ###### [Style [Y010](#style-y010)] From 14dcebe18d0007f31ad7d223d20b7b9163a6c758 Mon Sep 17 00:00:00 2001 From: John Papa Date: Tue, 2 Feb 2016 09:08:59 -0500 Subject: [PATCH 027/114] dupe --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 51740cd9..6b249382 100644 --- a/README.md +++ b/README.md @@ -136,8 +136,6 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see *Why?*: Small functions help avoid hidden bugs that come with large functions that share variables with external scope, create unwanted closures, or unwanted coupling with dependencies. - *Why?*: Small functions are easier to maintain. - **[Back to top](#table-of-contents)** From 373de0996d000480f4d41824738d683e12420b11 Mon Sep 17 00:00:00 2001 From: John Papa Date: Tue, 2 Feb 2016 09:53:42 -0500 Subject: [PATCH 028/114] promise rejection 082 --- README.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6b249382..42be8ae9 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see 1. [Factories](#factories) 1. [Data Services](#data-services) 1. [Directives](#directives) - 1. [Resolving Promises for a Controller](#resolving-promises-for-a-controller) + 1. [Resolving Promises](#resolving-promises) 1. [Manual Annotating for Dependency Injection](#manual-annotating-for-dependency-injection) 1. [Minification and Annotation](#minification-and-annotation) 1. [Exception Handling](#exception-handling) @@ -136,7 +136,6 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see *Why?*: Small functions help avoid hidden bugs that come with large functions that share variables with external scope, create unwanted closures, or unwanted coupling with dependencies. - **[Back to top](#table-of-contents)** ## IIFE @@ -1365,7 +1364,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see **[Back to top](#table-of-contents)** -## Resolving Promises for a Controller +## Resolving Promises ### Controller Activation Promises ###### [Style [Y080](#style-y080)] @@ -1516,6 +1515,67 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see **[Back to top](#table-of-contents)** +### Handling Exceptions with Promises +###### [Style [Y082](#style-y082)] + + - The `catch` block of a promise must return a rejected promise to maintain the exception in the promise chain. + + *Why?*: If the `catch` block does not return a rejected promise, the caller of the promise will not know an exception occurred. The caller's `then` will execute. Thus, the user may never know what happened. + + *Why?*: To avoid swallowing errors and misinforming the user. + + Note: Consider putting any exception handling in a function in a shared module and service. + + + ```javascript + /* avoid */ + + function getCustomer(id) { + return $http.get('/api/customer/' + id) + .then(getCustomerComplete) + .catch(getCustomerFailed); + + function getCustomerComplete(data, status, headers, config) { + return data.data; + } + + function getCustomerFailed(e) { + var newMessage = 'XHR Failed for getCustomer' + if (e.data && e.data.description) { + newMessage = newMessage + '\n' + e.data.description; + } + e.data.description = newMessage; + logger.error(newMessage); + // *** + // Notice there is no return of the rejected promise + // *** + } + } + + /* recommended */ + function getCustomer(id) { + return $http.get('/api/customer/' + id) + .then(getCustomerComplete) + .catch(getCustomerFailed); + + function getCustomerComplete(data, status, headers, config) { + return data.data; + } + + function getCustomerFailed(e) { + var newMessage = 'XHR Failed for getCustomer' + if (e.data && e.data.description) { + newMessage = newMessage + '\n' + e.data.description; + } + e.data.description = newMessage; + logger.error(newMessage); + return $q.reject(e); + } + } + ``` + +**[Back to top](#table-of-contents)** + ## Manual Annotating for Dependency Injection ### UnSafe from Minification From dc70274f017a123154d867b6172ce745db0d7816 Mon Sep 17 00:00:00 2001 From: John Papa Date: Tue, 2 Feb 2016 09:57:39 -0500 Subject: [PATCH 029/114] exceptions --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 42be8ae9..db171012 100644 --- a/README.md +++ b/README.md @@ -1520,13 +1520,14 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see - The `catch` block of a promise must return a rejected promise to maintain the exception in the promise chain. + - Always handle exceptions in services/factories. + *Why?*: If the `catch` block does not return a rejected promise, the caller of the promise will not know an exception occurred. The caller's `then` will execute. Thus, the user may never know what happened. *Why?*: To avoid swallowing errors and misinforming the user. Note: Consider putting any exception handling in a function in a shared module and service. - ```javascript /* avoid */ From 88aa702e34a90bcfd3e0d82a9676cae43f23396a Mon Sep 17 00:00:00 2001 From: Maksym Chapliuk Date: Thu, 4 Feb 2016 13:40:46 +0200 Subject: [PATCH 030/114] [ru-RU] Fixed some typos in Russian translation --- i18n/ru-RU.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/i18n/ru-RU.md b/i18n/ru-RU.md index 5ca57cd2..54133476 100644 --- a/i18n/ru-RU.md +++ b/i18n/ru-RU.md @@ -2,7 +2,7 @@ *Angular соглашения по стилям для команд разработчиков, предложенные [@john_papa](//twitter.com/john_papa)* -Если вам нужны стандарты написания кода, соглашения, и руководства структурирования приложений AngularJS, то вы находитесь в правильном месте. Эти соглашения основаны на моем опыте программирования на [AngularJS](//angularjs.org), на моих презентациях [Pluralsight training courses](http://pluralsight.com/training/Authors/Details/john-papa), а также на совместной работе в командах разработчиков. +Если вам нужны стандарты написания кода, соглашения и руководства структурирования приложений AngularJS, то вы находитесь в правильном месте. Эти соглашения основаны на моем опыте программирования на [AngularJS](//angularjs.org), на моих презентациях [Pluralsight training courses](http://pluralsight.com/training/Authors/Details/john-papa), а также на совместной работе в командах разработчиков. Главной целью этого документа является желание предоставить вам наиболее полные инструкции для построения приложений AngularJS. Рекомендуя данные соглашения, я стараюсь акцентировать ваше внимание на цели и причины, зачем их нужно придерживаться. @@ -291,7 +291,7 @@ - Используйте синтаксис [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/), который работает поверх синтаксиса `классический контроллер со $scope`. - *Почему?*: Контроллер создается, как JavaScript-объект с ключевым словом "new" и затем предоставляется этот единственный экземпляр объекта. То есть синтаксис `controllerAs` намного ближе и похожее на конструктор языка JavaScript, чем `классический синтаксис $scope`. + *Почему?*: Контроллер создается, как JavaScript-объект с ключевым словом "new" и затем предоставляется этот единственный экземпляр объекта. То есть синтаксис `controllerAs` намного ближе и похоже на конструктор языка JavaScript, чем `классический синтаксис $scope`. *Почему?*: Это позволяет использовать в представлении связывание на свойство объекта "через точку" (например вместо `name` будет `customer.name`), что является более концептуальным, проще для чтения, и помогает избегать многих ссылочных проблем, которые могут возникнуть без использования "точки". @@ -362,7 +362,7 @@ } ``` - Замечание: Вы можете избежать любые [jshint](http://www.jshint.com/) предупреждения, если разместите над строкой кода комментарий, как приведенный ниже в примере. Это не требуется, если функция названа с помощью ВерхнегоРегистра(UpperCasing), так как согласно этой конвециии, это значит, что функция является контруктором контроллера Angular. + Замечание: Вы можете избежать любые [jshint](http://www.jshint.com/) предупреждения, если разместите над строкой кода комментарий, как в приведенном ниже примере. Это не требуется, если функция названа с помощью ВерхнегоРегистра(UpperCasing), так как согласно этой конвенциии, это означает, что функция является конструктором контроллера Angular. ```javascript /* jshint validthis: true */ @@ -442,7 +442,7 @@ ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-1.png) - Замечание: Если функция однострочная, то разместите ее и наверху, но так чтобы это не усложняло читабельность. + Замечание: Если функция однострочная, то разместите и ее наверху, но так чтобы это не усложняло читабельность. ```javascript /* избегайте этого */ @@ -558,7 +558,7 @@ *Почему?*: Логика может использоваться несколькими контроллерами, если она помещена в сервис и выставлена в виде функции. - *Почему?*: Вся логика в сервисе может быть легко изолирована в модульном тесте, а вызовы этой логики в контроллере могут фиктивно реализованы (mocked). + *Почему?*: Вся логика в сервисе может быть легко изолирована в модульном тесте, а вызовы этой логики в контроллере могут быть фиктивно реализованы (mocked). *Почему?*: Из контроллера удаляются зависимости и скрываются подробности реализации. @@ -717,7 +717,7 @@ ### Единственная Обязанность (Single Responsibility) ###### [Style [Y050](#style-y050)] - - Фабрики дожны иметь [единственную обязанность](http://en.wikipedia.org/wiki/Single_responsibility_principle), это следует из их контекста. Если фабрика начинает превышать ту цель для которой она создана, то нужно создать другую фабрику. + - Фабрики должны иметь [единственную обязанность](http://en.wikipedia.org/wiki/Single_responsibility_principle), это следует из их контекста. Если фабрика начинает превышать ту цель для которой она создана, то нужно создать другую фабрику. ### Синглтон ###### [Style [Y051](#style-y051)] @@ -729,7 +729,7 @@ ### Доступные Члены Наверх ###### [Style [Y052](#style-y052)] - - Помещайте вызываемые члены сервиса (интерфейс) наверх, используя технику [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). + - Помещайте вызываемые члены сервиса (интерфейс) наверху, используя технику [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). *Почему?*: Размещение вызываемых членов в верхней части улучшает читабельность и дает вам возможность быстро определить, какие члены сервиса могут быть вызваны и должны быть модульно оттестированы (либо фиктивно имплементированы - mocked). @@ -779,7 +779,7 @@ } ``` -Такой способ привязки реализовывается во всем объекте, и приватные члены не моут быть изменены из-за примененного паттерна Revealing Module. +Такой способ привязки реализовывается во всем объекте, и приватные члены не моут быть изменены из-за примененного паттерна Revealing Module. ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-2.png) @@ -894,7 +894,7 @@ *Почему?*: Это позволяет более проще тестировать (фиктивно или реально) вызовы данных в контроллере, который использует сервис для данных. - *Почему?*: Реализация сервиса данных может иметь очень специфичный код для операций с хранилищем данных. Могут использоваться заголовки для взаимодействовия с данными, или другие сервисы типа $http. Логика в сервисе данных инкапсулируется в одном месте и скрывает реализацию для внешних потребителей (контроллеров), также будет гораздо проще изменить реализацию в случае необходимости. + *Почему?*: Реализация сервиса данных может иметь очень специфичный код для операций с хранилищем данных. Могут использоваться заголовки для взаимодействовия с данными, или другие сервисы типа `$http`. Логика в сервисе данных инкапсулируется в одном месте и скрывает реализацию для внешних потребителей (контроллеров), также будет гораздо проще изменить реализацию в случае необходимости. ```javascript /* рекомендовано */ @@ -1251,9 +1251,9 @@ - Размещайте стартовую начальную логику для контроллера в функции `activate`. - *Почему?*: Размещение стартовой логики в согласованом месте контроллера позволяет ее быстрее находить, более согласованно тестить, и позволить не разбразывать по всему контроллеру логику активации. + *Почему?*: Размещение стартовой логики в согласованном месте контроллера позволяет ее быстрее находить, более согласованно тестировать и позволит не расбрасывать по всему контроллеру логику активации. - *Почему?*: Функция `activate` делает удобным повторное использование логики для обновления контроллера/представления, держит все логику вместе, делает более быстрой работу пользователя с представлением, делает анимацию более простой в директивами `ng-view` и `ui-view`, ну и делает ориентирование разработчика в коде более энергичным и быстрым. + *Почему?*: Функция `activate` делает удобным повторное использование логики для обновления контроллера/представления, держит всю логику вместе, делает более быстрой работу пользователя с представлением, делает анимацию более простой с директивами `ng-view` и `ui-view`, ну и делает ориентирование разработчика в коде более энергичным и быстрым. Замечание: Если вам нужно при каком-то условии отменить маршрут перед началом использования контроллера, используйте для этого [route resolve](#style-y081). @@ -1425,13 +1425,13 @@ ### Определяйте Зависимости Вручную ###### [Style [Y091](#style-y091)] - - Используйте `$inject` для ручного определения ваших зависимостей для AngulaJS. + - Используйте `$inject` для ручного определения ваших зависимостей для AngularJS. - *Почему?*: Этот техника отражает технику использованную в [`ng-annotate`](https://github.com/olov/ng-annotate), которую я рекомендую для автоматического создания зависимостей, безопасных для минификации. Если `ng-annotate` обнаруживает вставку(injection), которая уже была, то она не будет продублирована. + *Почему?*: Этот прием отражает технику использованную в [`ng-annotate`](https://github.com/olov/ng-annotate), которую я рекомендую для автоматического создания зависимостей, безопасных для минификации. Если `ng-annotate` обнаруживает вставку(injection), которая уже была, то она не будет продублирована. *Почему?*: Это гарантирует вашим зависимостям защиту от повреждений, которую может нанести минификация, путем урезания и укорачивания переменных. Например, `common` и `dataservice` превратятся `a` и `b`, и не будут найдены средой AngularJS. - *Почему?*: Избегайте создания однострочных зависимостей в виде длинных списков, которые трудно читаемы в массиве. Еще в вводят в конфуз, то что такие массивы состоят из элементов строк, а последний элемент - это функция. + *Почему?*: Избегайте создания однострочных зависимостей в виде длинных списков, которые трудно читаемы в массиве. Еще вводят в заблуждение то, что такие массивы состоят из элементов строк, а последний элемент - это функция. ```javascript /* избегайте этого */ @@ -1466,7 +1466,7 @@ } ``` - Замечание: Если функция снизу является возращаемым значением, то $inject может быть недостижымым (это может случится в директиве). Это можно решить перемещением $inject выше, чем возращаемое значение, либо использовать альтернативный синтаксис массива вставок. + Замечание: Если функция снизу является возвращаемым значением, то $inject может быть недостижим (это может случится в директиве). Это можно решить перемещением $inject выше, чем возращаемое значение, либо использовать альтернативный синтаксис массива вставок. Замечание: [`ng-annotate 0.10.0`](https://github.com/olov/ng-annotate) ввело возможность, когда `$inject` переносится туда, где оно доступно. @@ -1496,12 +1496,12 @@ } ``` -### Определяйте Маршрутных Обработчиков Зависимостей Вручную +### Определяйте Маршрутные Обработчики Зависимостей Вручную ###### [Style [Y092](#style-y092)] - Используйте $inject, чтобы вручную определить ваш маршрутный обработчик зависимостей для компонентов AngularJS. - *Почему?*: Эта техника убирает анонимные функции для маршрутных обработчиков, делая чтение такого код проще. + *Почему?*: Эта техника убирает анонимные функции для маршрутных обработчиков, делая чтение такого кода проще. *Почему?*: Оператор $inject` может предшествовать обработчику для того, чтобы сделать любую минификацию зависимостей безопасной. @@ -1611,9 +1611,9 @@ - Используйте [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) или [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) для автоматических билдов. Вставляйте `/* @ngInject */` перед любой функцией, которая имеет зависимости. - *Почему?*: ng-annotate будет отлавливать большинство зависимостей, но иногда требуются вставлять подсказки в виде синтаксиса `/* @ngInject */`. + *Почему?*: ng-annotate будет отлавливать большинство зависимостей, но иногда требуется вставлять подсказки в виде синтаксиса `/* @ngInject */`. - Следующий код является примером gulp задания с использованием ngAnnotate. + Следующий код является примером gulp-задания с использованием ngAnnotate. ```javascript gulp.task('js', ['jshint'], function() { @@ -1647,7 +1647,7 @@ *Почему?*: Это дает постоянный надежный способ обработки необработанных исключений Angular во время разработки и во время выполнения. - Замечание: Другим способом является переопределение сервиса, вместо использования декоратора. Это прекрасный способ, но если вы хотите сохранить поведение по умолчанию, и просто дополнить это поведение, то декоратоор крайне рекомендуем. + Замечание: Другим способом является переопределение сервиса, вместо использования декоратора. Это прекрасный способ, но если вы хотите сохранить поведение по умолчанию, и просто дополнить это поведение, то декоратор крайне рекомендуем. ```javascript /* рекомендовано */ @@ -1869,7 +1869,7 @@ ### Суффикс Имени Контроллера ###### [Style [Y124](#style-y124)] - - Добавляйте к имени контроллера суффикс `Controller` или не добавляйте. Выберите одно правило т придерживайтесь его везде. + - Добавляйте к имени контроллера суффикс `Controller` или не добавляйте. Выберите одно правило и придерживайтесь его везде. *Почему?*: Суффикс `Controller` более общеупотребим и наиболее явно описателен. @@ -2735,7 +2735,7 @@ ## Contributing -Сначала откройте issie и объясните вопрос/проблему для того, чтобы обсудить потенциальные изменения/добавления. Если у вас есть вопросы по этому руководству, вы можете свободно задавать их, создавая в хранилище issues. Если вы нашли опечатку, создайте pull request. Идея в том, чтобы содержимое всегда дежать актуальным и используя возможности системы Github, помочь рассказать историю с вопросами или проблемами и распространить ее, чтобы она была доступна через поиск Google. Почему? Потому что, если у вас был такой вопрос, то вполне вероятно, что кто-то тоже ищет решение на этот же вопрос! Вы можете узнать больше здесь о том как можно сотрудничать. +Сначала откройте issue и объясните вопрос/проблему для того, чтобы обсудить потенциальные изменения/добавления. Если у вас есть вопросы по этому руководству, вы можете свободно задавать их, создавая в хранилище issues. Если вы нашли опечатку, создайте pull request. Идея в том, чтобы содержимое всегда дежать актуальным и используя возможности системы Github, помочь рассказать историю с вопросами или проблемами и распространить ее, чтобы она была доступна через поиск Google. Почему? Потому что, если у вас был такой вопрос, то вполне вероятно, что кто-то тоже ищет решение на этот же вопрос! Вы можете узнать больше здесь о том как можно сотрудничать. *Добавляя материал в данное хранилище вы согласны с тем, ваше содержимое будет доступно согласно приведенной ниже лицензии.* From 09d382ea3d0072c8adda1b0941fa937394417d92 Mon Sep 17 00:00:00 2001 From: ReadmeCritic Date: Fri, 5 Feb 2016 14:26:39 -0800 Subject: [PATCH 031/114] Update README URLs based on HTTP redirects --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index df333a39..a3b91d55 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Special thanks to Igor Minar, lead on the Angular team, for reviewing, contribut ## Purpose *Opinionated Angular style guide for teams by [@john_papa](//twitter.com/john_papa)* -If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular applications, then step right in. These styles are based on my development experience with [Angular](//angularjs.org), presentations, [Pluralsight training courses](http://pluralsight.com/training/Authors/Details/john-papa) and working in teams. +If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular applications, then step right in. These styles are based on my development experience with [Angular](//angularjs.org), presentations, [Pluralsight training courses](http://app.pluralsight.com/author/john-papa) and working in teams. The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions I use and, more importantly, why I choose them. @@ -15,9 +15,9 @@ The purpose of this style guide is to provide guidance on building Angular appli [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Community Awesomeness and Credit -Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. As such, a friend and Angular expert Todd Motto and I have collaborated on many styles and conventions. We agree on most, and some we diverge. I encourage you to check out [Todd's guidelines](https://github.com/toddmotto/angularjs-styleguide) to get a sense for his approach and how it compares. +Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. As such, a friend and Angular expert Todd Motto and I have collaborated on many styles and conventions. We agree on most, and some we diverge. I encourage you to check out [Todd's guidelines](https://github.com/toddmotto/angular-styleguide) to get a sense for his approach and how it compares. -Many of my styles have been from the many pair programming sessions [Ward Bell](http://twitter.com/wardbell) and I have had. My friend Ward has certainly helped influence the ultimate evolution of this guide. +Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My friend Ward has certainly helped influence the ultimate evolution of this guide. ## See the Styles in a Sample App While this guide explains the *what*, *why* and *how*, I find it helpful to see them in practice. This guide is accompanied by a sample application that follows these styles and patterns. You can find the [sample application (named modular) here](https://github.com/johnpapa/ng-demos) in the `modular` folder. Feel free to grab it, clone it, or fork it. [Instructions on running it are in its readme](https://github.com/johnpapa/ng-demos/tree/master/modular). @@ -398,7 +398,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see } ``` - Note: You can avoid any [jshint](http://www.jshint.com/) warnings by placing the comment above the line of code. However it is not needed when the function is named using UpperCasing, as this convention means it is a constructor function, which is what a controller is in Angular. + Note: You can avoid any [jshint](http://jshint.com/) warnings by placing the comment above the line of code. However it is not needed when the function is named using UpperCasing, as this convention means it is a constructor function, which is what a controller is in Angular. ```javascript /* jshint validthis: true */ @@ -531,7 +531,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see ### Function Declarations to Hide Implementation Details ###### [Style [Y034](#style-y034)] - - Use function declarations to hide implementation details. Keep your bindable members up top. When you need to bind a function in a controller, point it to a function declaration that appears later in the file. This is tied directly to the section Bindable Members Up Top. For more details see [this post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). + - Use function declarations to hide implementation details. Keep your bindable members up top. When you need to bind a function in a controller, point it to a function declaration that appears later in the file. This is tied directly to the section Bindable Members Up Top. For more details see [this post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code/). *Why?*: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the controller can be bound and used in the View. (Same as above.) @@ -771,7 +771,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see ### Single Responsibility ###### [Style [Y050](#style-y050)] - - Factories should have a [single responsibility](http://en.wikipedia.org/wiki/Single_responsibility_principle), that is encapsulated by its context. Once a factory begins to exceed that singular purpose, a new factory should be created. + - Factories should have a [single responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle), that is encapsulated by its context. Once a factory begins to exceed that singular purpose, a new factory should be created. ### Singletons ###### [Style [Y051](#style-y051)] @@ -783,7 +783,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see ### Accessible Members Up Top ###### [Style [Y052](#style-y052)] - - Expose the callable members of the service (its interface) at the top, using a technique derived from the [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). + - Expose the callable members of the service (its interface) at the top, using a technique derived from the [Revealing Module Pattern](https://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). *Why?*: Placing the callable members at the top makes it easy to read and helps you instantly identify which members of the service can be called and must be unit tested (and/or mocked). @@ -1794,7 +1794,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see ### Use Gulp or Grunt for ng-annotate ###### [Style [Y101](#style-y101)] - - Use [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) or [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) in an automated build task. Inject `/* @ngInject */` prior to any function that has dependencies. + - Use [gulp-ng-annotate](https://www.npmjs.com/package/gulp-ng-annotate) or [grunt-ng-annotate](https://www.npmjs.com/package/grunt-ng-annotate) in an automated build task. Inject `/* @ngInject */` prior to any function that has dependencies. *Why?*: ng-annotate will catch most dependencies, but it sometimes requires hints using the `/* @ngInject */` syntax. @@ -2542,9 +2542,9 @@ Unit testing helps maintain clean code, as such I included some of my recommenda *Why?*: Karma hooks into your Continuous Integration process easily on its own or through Grunt or Gulp. - *Why?*: Some IDE's are beginning to integrate with Karma, such as [WebStorm](http://www.jetbrains.com/webstorm/) and [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). + *Why?*: Some IDE's are beginning to integrate with Karma, such as [WebStorm](http://www.jetbrains.com/webstorm/) and [Visual Studio](https://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). - *Why?*: Karma works well with task automation leaders such as [Grunt](http://www.gruntjs.com) (with [grunt-karma](https://github.com/karma-runner/grunt-karma)) and [Gulp](http://www.gulpjs.com). When using Gulp, use [Karma](https://github.com/karma-runner/karma) directly and not with a plugin as the API can be called directly. + *Why?*: Karma works well with task automation leaders such as [Grunt](http://gruntjs.com/) (with [grunt-karma](https://github.com/karma-runner/grunt-karma)) and [Gulp](http://gulpjs.com/). When using Gulp, use [Karma](https://github.com/karma-runner/karma) directly and not with a plugin as the API can be called directly. ```javascript /* recommended */ @@ -2757,7 +2757,7 @@ Unit testing helps maintain clean code, as such I included some of my recommenda ### Use an Options File ###### [Style [Y230](#style-y230)] - - Use JS Hint for linting your JavaScript and be sure to customize the JS Hint options file and include in source control. See the [JS Hint docs](http://www.jshint.com/docs/) for details on the options. + - Use JS Hint for linting your JavaScript and be sure to customize the JS Hint options file and include in source control. See the [JS Hint docs](http://jshint.com/docs/) for details on the options. *Why?*: Provides a first alert prior to committing any code to source control. @@ -2834,7 +2834,7 @@ Unit testing helps maintain clean code, as such I included some of my recommenda ### Use an Options File ###### [Style [Y235](#style-y235)] - - Use JSCS for checking your coding styles your JavaScript and be sure to customize the JSCS options file and include in source control. See the [JSCS docs](http://www.jscs.info) for details on the options. + - Use JSCS for checking your coding styles your JavaScript and be sure to customize the JSCS options file and include in source control. See the [JSCS docs](http://jscs.info/) for details on the options. *Why?*: Provides a first alert prior to committing any code to source control. @@ -3112,7 +3112,7 @@ Use file templates or snippets to help follow consistent styles and patterns. He ###### [Style [Y256](#style-y256)] - - [Visual Studio Code](http://code.visualstudio.com) snippets that follow these styles and guidelines. + - [Visual Studio Code](https://code.visualstudio.com/) snippets that follow these styles and guidelines. - Download the [VS Code Angular snippets](assets/vscode-snippets/javascript.json?raw=true) - copy snippets to snippet directory, or alternatively copy and paste the snippets into your existing ones From 8669bd94e03258d0cf6efdc0c9aa8730624a0f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?RAMELOT=20Lo=C3=AFc?= Date: Thu, 11 Feb 2016 10:56:40 +0100 Subject: [PATCH 032/114] Fix broken link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix broken link inside of the document - Annotation manuelle pour l'injection de dépendance - Minification et annotation --- i18n/fr-FR.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/fr-FR.md b/i18n/fr-FR.md index d84c4f27..9229d67c 100644 --- a/i18n/fr-FR.md +++ b/i18n/fr-FR.md @@ -1200,7 +1200,7 @@ Bien que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'est ut Note : La directive ci-dessous montre une façon parmi d'autres d'utiliser *scope* à l'intérieur de la fonction `link` et dans un contrôleur de directive, par l'utilisation de `controllerAs`. J'ai *inliné* le template pour tout mettre au même endroit. - Note : Concernant l'injection de dépendances, voir [Annoter manuellement les dépendances à injecter](#annoter-manuellement-les-dépendances-à-injecter). + Note : Concernant l'injection de dépendances, voir [Annoter manuellement les dépendances à injecter](#annotation-manuelle-pour-linjection-de-dépendances). Note : Remarquez que le contrôleur de la directive est à l'extérieur de la *closure* de la directive. Cette façon de faire évite le problème d'indisponibilité des injections après le `return`. @@ -1466,7 +1466,7 @@ Bien que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'est ut vm.movies = moviesPrepService.movies; } ``` - Note : Les dépendances sur `movieService` dans l'exemple ne sont pas directement compatibles avec le processus de minification. Pour plus de détails sur la façon de rendre ce code minifiable sans risques, voir la section sur l'[injection de dépendances](#manual-annotating-for-dependency-injection) et sur [la minification et les annotations](#minification-and-annotation). + Note : Les dépendances sur `movieService` dans l'exemple ne sont pas directement compatibles avec le processus de minification. Pour plus de détails sur la façon de rendre ce code minifiable sans risques, voir la section sur l'[injection de dépendances](#annotation-manuelle-pour-linjection-de-dépendances) et sur [la minification et les annotations](#minification-et-annotation). **[Retour en haut de page](#table-des-matières)** From accf0ad3e55de6d576d4d0bab75ed41cd0481adf Mon Sep 17 00:00:00 2001 From: Irvin Sandoval Date: Sat, 20 Feb 2016 22:18:12 -0600 Subject: [PATCH 033/114] Fix orthography issues --- i18n/es-ES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/es-ES.md b/i18n/es-ES.md index 984274e2..27a47961 100644 --- a/i18n/es-ES.md +++ b/i18n/es-ES.md @@ -793,9 +793,9 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti *¿Por qué?*: Coloca los elementos accesibles en la parte superior para hacerlo más fácil de leer y ayudarte a identificar instantáneamente qué funciones de la fábrica se pueden accesar externamente. - *¿Por qué?*: Colocar los detalles de implementación de una función al final del archivo mueve esa complegidad fuera de la vista, de esta forma puedes dejar lo importante arriba. + *¿Por qué?*: Colocar los detalles de implementación de una función al final del archivo mueve esa complejidad fuera de la vista, de esta forma puedes dejar lo importante arriba. - *¿Por qué?*: Las declaraciones de las funciones son "elevedas" de esta forma no hay problemas en usar una función antes de su definición (como la habría si fueran funciones en forma de expresión). + *¿Por qué?*: Las declaraciones de las funciones son "elevadas" de esta forma no hay problemas en usar una función antes de su definición (como la habría si fueran funciones en forma de expresión). *¿Por qué?*: No tendrás que preocuparte de que si pones `var a` antes de `var b` se rompa el código porque `a` dependa de `b`. From 6e21fe8a4504d351271d23b3d2486336746b9a87 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 21 Feb 2016 11:20:58 -0500 Subject: [PATCH 034/114] moved everything to a1 folder --- README.md | 3281 +--------------- a1/README.md | 3285 +++++++++++++++++ {assets => a1/assets}/above-the-fold-1.png | Bin {assets => a1/assets}/above-the-fold-2.png | Bin .../assets}/brackets-angular-snippets.yaml | 0 {assets => a1/assets}/gde.png | Bin {assets => a1/assets}/modularity-1.png | Bin {assets => a1/assets}/modularity-2.png | Bin .../assets}/ng-clean-code-banner.png | Bin .../angular.controller.sublime-snippet | 0 .../angular.directive.sublime-snippet | 0 .../angular.factory.sublime-snippet | 0 .../angular.filter.sublime-snippet | 0 .../angular.module.sublime-snippet | 0 .../angular.service.sublime-snippet | 0 {assets => a1/assets}/testing-tools.png | Bin .../angular.controller.snip | 0 .../angular.directive.snip | 0 .../vim-angular-snippets/angular.factory.snip | 0 .../vim-angular-snippets/angular.filter.snip | 0 .../vim-angular-snippets/angular.module.snip | 0 .../vim-angular-snippets/angular.service.snip | 0 .../javascript_angular.controller.snippets | 0 .../javascript_angular.directive.snippets | 0 .../javascript_angular.factory.snippets | 0 .../javascript_angular.filter.snippets | 0 .../javascript_angular.module.snippets | 0 .../javascript_angular.service.snippets | 0 .../assets}/vscode-snippets/javascript.json | 0 .../assets}/vscode-snippets/typescript.json | 0 .../angular.app.webstorm-live-template.xml | 0 .../angular.config.webstorm-live-template.xml | 0 ...ular.controller.webstorm-live-template.xml | 0 ...gular.directive.webstorm-live-template.xml | 0 ...angular.factory.webstorm-live-template.xml | 0 .../angular.filter.webstorm-live-template.xml | 0 .../angular.module.webstorm-live-template.xml | 0 .../angular.route.webstorm-live-template.xml | 0 .../angular.run.webstorm-live-template.xml | 0 ...angular.service.webstorm-live-template.xml | 0 .../angular.state.webstorm-live-template.xml | 0 .../webstorm-angular-live-templates.xml | 0 {i18n => a1/i18n}/README.md | 0 {i18n => a1/i18n}/de-DE.md | 0 {i18n => a1/i18n}/es-ES.md | 0 {i18n => a1/i18n}/fr-FR.md | 0 {i18n => a1/i18n}/it-IT.md | 0 {i18n => a1/i18n}/ja-JP.md | 0 {i18n => a1/i18n}/ko_KR.md | 0 {i18n => a1/i18n}/mk-MK.md | 0 {i18n => a1/i18n}/pt-BR.md | 0 {i18n => a1/i18n}/ru-RU.md | 0 {i18n => a1/i18n}/tr-TR.md | 0 {i18n => a1/i18n}/zh-CN.md | 0 54 files changed, 3296 insertions(+), 3270 deletions(-) create mode 100644 a1/README.md rename {assets => a1/assets}/above-the-fold-1.png (100%) rename {assets => a1/assets}/above-the-fold-2.png (100%) rename {assets => a1/assets}/brackets-angular-snippets.yaml (100%) rename {assets => a1/assets}/gde.png (100%) rename {assets => a1/assets}/modularity-1.png (100%) rename {assets => a1/assets}/modularity-2.png (100%) rename {assets => a1/assets}/ng-clean-code-banner.png (100%) rename {assets => a1/assets}/sublime-angular-snippets/angular.controller.sublime-snippet (100%) rename {assets => a1/assets}/sublime-angular-snippets/angular.directive.sublime-snippet (100%) rename {assets => a1/assets}/sublime-angular-snippets/angular.factory.sublime-snippet (100%) rename {assets => a1/assets}/sublime-angular-snippets/angular.filter.sublime-snippet (100%) rename {assets => a1/assets}/sublime-angular-snippets/angular.module.sublime-snippet (100%) rename {assets => a1/assets}/sublime-angular-snippets/angular.service.sublime-snippet (100%) rename {assets => a1/assets}/testing-tools.png (100%) rename {assets => a1/assets}/vim-angular-snippets/angular.controller.snip (100%) rename {assets => a1/assets}/vim-angular-snippets/angular.directive.snip (100%) rename {assets => a1/assets}/vim-angular-snippets/angular.factory.snip (100%) rename {assets => a1/assets}/vim-angular-snippets/angular.filter.snip (100%) rename {assets => a1/assets}/vim-angular-snippets/angular.module.snip (100%) rename {assets => a1/assets}/vim-angular-snippets/angular.service.snip (100%) rename {assets => a1/assets}/vim-angular-ultisnips/javascript_angular.controller.snippets (100%) rename {assets => a1/assets}/vim-angular-ultisnips/javascript_angular.directive.snippets (100%) rename {assets => a1/assets}/vim-angular-ultisnips/javascript_angular.factory.snippets (100%) rename {assets => a1/assets}/vim-angular-ultisnips/javascript_angular.filter.snippets (100%) rename {assets => a1/assets}/vim-angular-ultisnips/javascript_angular.module.snippets (100%) rename {assets => a1/assets}/vim-angular-ultisnips/javascript_angular.service.snippets (100%) rename {assets => a1/assets}/vscode-snippets/javascript.json (100%) rename {assets => a1/assets}/vscode-snippets/typescript.json (100%) rename {assets => a1/assets}/webstorm-angular-live-templates/angular.app.webstorm-live-template.xml (100%) rename {assets => a1/assets}/webstorm-angular-live-templates/angular.config.webstorm-live-template.xml (100%) rename {assets => a1/assets}/webstorm-angular-live-templates/angular.controller.webstorm-live-template.xml (100%) rename {assets => a1/assets}/webstorm-angular-live-templates/angular.directive.webstorm-live-template.xml (100%) rename {assets => a1/assets}/webstorm-angular-live-templates/angular.factory.webstorm-live-template.xml (100%) rename {assets => a1/assets}/webstorm-angular-live-templates/angular.filter.webstorm-live-template.xml (100%) rename {assets => a1/assets}/webstorm-angular-live-templates/angular.module.webstorm-live-template.xml (100%) rename {assets => a1/assets}/webstorm-angular-live-templates/angular.route.webstorm-live-template.xml (100%) rename {assets => a1/assets}/webstorm-angular-live-templates/angular.run.webstorm-live-template.xml (100%) rename {assets => a1/assets}/webstorm-angular-live-templates/angular.service.webstorm-live-template.xml (100%) rename {assets => a1/assets}/webstorm-angular-live-templates/angular.state.webstorm-live-template.xml (100%) rename {assets => a1/assets}/webstorm-angular-live-templates/webstorm-angular-live-templates.xml (100%) rename {i18n => a1/i18n}/README.md (100%) rename {i18n => a1/i18n}/de-DE.md (100%) rename {i18n => a1/i18n}/es-ES.md (100%) rename {i18n => a1/i18n}/fr-FR.md (100%) rename {i18n => a1/i18n}/it-IT.md (100%) rename {i18n => a1/i18n}/ja-JP.md (100%) rename {i18n => a1/i18n}/ko_KR.md (100%) rename {i18n => a1/i18n}/mk-MK.md (100%) rename {i18n => a1/i18n}/pt-BR.md (100%) rename {i18n => a1/i18n}/ru-RU.md (100%) rename {i18n => a1/i18n}/tr-TR.md (100%) rename {i18n => a1/i18n}/zh-CN.md (100%) diff --git a/README.md b/README.md index a3b91d55..1a9ac6ea 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ # Angular Style Guide +## Versions +There are multiple versions of Angular, and thus there are multiple versions of the guide. Choose your guide appropriately. + +### Angular 1 Style Guide +[The Angular 1 Style Guide is located here](https://github.com/johnpapa/angular-styleguide/tree/master/a1/README.md). + +### Angular 2 Style Guide +**COMING SOON** + ## Angular Team Endorsed Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. @@ -15,3277 +24,9 @@ The purpose of this style guide is to provide guidance on building Angular appli [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Community Awesomeness and Credit -Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. As such, a friend and Angular expert Todd Motto and I have collaborated on many styles and conventions. We agree on most, and some we diverge. I encourage you to check out [Todd's guidelines](https://github.com/toddmotto/angular-styleguide) to get a sense for his approach and how it compares. - -Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My friend Ward has certainly helped influence the ultimate evolution of this guide. - -## See the Styles in a Sample App -While this guide explains the *what*, *why* and *how*, I find it helpful to see them in practice. This guide is accompanied by a sample application that follows these styles and patterns. You can find the [sample application (named modular) here](https://github.com/johnpapa/ng-demos) in the `modular` folder. Feel free to grab it, clone it, or fork it. [Instructions on running it are in its readme](https://github.com/johnpapa/ng-demos/tree/master/modular). - -##Translations -[Translations of this Angular style guide](https://github.com/johnpapa/angular-styleguide/tree/master/i18n) are maintained by the community and can be found here. - -## Table of Contents - - 1. [Single Responsibility](#single-responsibility) - 1. [IIFE](#iife) - 1. [Modules](#modules) - 1. [Controllers](#controllers) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Data Services](#data-services) - 1. [Directives](#directives) - 1. [Resolving Promises](#resolving-promises) - 1. [Manual Annotating for Dependency Injection](#manual-annotating-for-dependency-injection) - 1. [Minification and Annotation](#minification-and-annotation) - 1. [Exception Handling](#exception-handling) - 1. [Naming](#naming) - 1. [Application Structure LIFT Principle](#application-structure-lift-principle) - 1. [Application Structure](#application-structure) - 1. [Modularity](#modularity) - 1. [Startup Logic](#startup-logic) - 1. [Angular $ Wrapper Services](#angular--wrapper-services) - 1. [Testing](#testing) - 1. [Animations](#animations) - 1. [Comments](#comments) - 1. [JSHint](#js-hint) - 1. [JSCS](#jscs) - 1. [Constants](#constants) - 1. [File Templates and Snippets](#file-templates-and-snippets) - 1. [Yeoman Generator](#yeoman-generator) - 1. [Routing](#routing) - 1. [Task Automation](#task-automation) - 1. [Filters](#filters) - 1. [Angular Docs](#angular-docs) - 1. [Contributing](#contributing) - 1. [License](#license) - -## Single Responsibility - -### Rule of 1 -###### [Style [Y001](#style-y001)] - - - Define 1 component per file, recommended to be less than 500 lines of code. - - *Why?*: One component per file promotes easier unit testing and mocking. - - *Why?*: One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control. - - *Why?*: One component per file avoids hidden bugs that often arise when combining components in a file where they may share variables, create unwanted closures, or unwanted coupling with dependencies. - - The following example defines the `app` module and its dependencies, defines a controller, and defines a factory all in the same file. - - ```javascript - /* avoid */ - angular - .module('app', ['ngRoute']) - .controller('SomeController', SomeController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - The same components are now separated into their own files. - - ```javascript - /* recommended */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* recommended */ - - // some.controller.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommended */ - - // someFactory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[Back to top](#table-of-contents)** - -### Small Functions -###### [Style [Y002](#style-y002)] - - - Define small functions, no more than 75 LOC (less is better). - - *Why?*: Small functions are easier to test, especially when they do one thing and serve one purpose. - - *Why?*: Small functions promote reuse. - - *Why?*: Small functions are easier to read. - - *Why?*: Small functions are easier to maintain. - - *Why?*: Small functions help avoid hidden bugs that come with large functions that share variables with external scope, create unwanted closures, or unwanted coupling with dependencies. - -**[Back to top](#table-of-contents)** - -## IIFE -### JavaScript Closures -###### [Style [Y010](#style-y010)] - - - Wrap Angular components in an Immediately Invoked Function Expression (IIFE). - - *Why?*: An IIFE removes variables from the global scope. This helps prevent variables and function declarations from living longer than expected in the global scope, which also helps avoid variable collisions. - - *Why?*: When your code is minified and bundled into a single file for deployment to a production server, you could have collisions of variables and many global variables. An IIFE protects you against both of these by providing variable scope for each file. - - ```javascript - /* avoid */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // logger function is added as a global variable - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // storage function is added as a global variable - function storage() { } - ``` - - ```javascript - /** - * recommended - * - * no globals are left behind - */ - - // logger.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('logger', logger); - - function logger() { } - })(); - - // storage.js - (function() { - 'use strict'; - - angular - .module('app') - .factory('storage', storage); - - function storage() { } - })(); - ``` - - - Note: For brevity only, the rest of the examples in this guide may omit the IIFE syntax. - - - Note: IIFE's prevent test code from reaching private members like regular expressions or helper functions which are often good to unit test directly on their own. However you can test these through accessible members or by exposing them through their own component. For example placing helper functions, regular expressions or constants in their own factory or constant. - -**[Back to top](#table-of-contents)** - -## Modules - -### Avoid Naming Collisions -###### [Style [Y020](#style-y020)] - - - Use unique naming conventions with separators for sub-modules. - - *Why?*: Unique names help avoid module name collisions. Separators help define modules and their submodule hierarchy. For example `app` may be your root module while `app.dashboard` and `app.users` may be modules that are used as dependencies of `app`. - -### Definitions (aka Setters) -###### [Style [Y021](#style-y021)] - - - Declare modules without a variable using the setter syntax. - - *Why?*: With 1 component per file, there is rarely a need to introduce a variable for the module. - - ```javascript - /* avoid */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - Instead use the simple setter syntax. - - ```javascript - /* recommended */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### Getters -###### [Style [Y022](#style-y022)] - - - When using a module, avoid using a variable and instead use chaining with the getter syntax. - - *Why?*: This produces more readable code and avoids variable collisions or leaks. - - ```javascript - /* avoid */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommended */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -### Setting vs Getting -###### [Style [Y023](#style-y023)] - - - Only set once and get for all other instances. - - *Why?*: A module should only be created once, then retrieved from that point and after. - - ```javascript - /* recommended */ - - // to set a module - angular.module('app', []); - - // to get a module - angular.module('app'); - ``` - -### Named vs Anonymous Functions -###### [Style [Y024](#style-y024)] - - - Use named functions instead of passing an anonymous function in as a callback. - - *Why?*: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code. - - ```javascript - /* avoid */ - angular - .module('app') - .controller('DashboardController', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* recommended */ - - // dashboard.js - angular - .module('app') - .controller('DashboardController', DashboardController); - - function DashboardController() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[Back to top](#table-of-contents)** - -## Controllers - -### controllerAs View Syntax -###### [Style [Y030](#style-y030)] - - - Use the [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) syntax over the `classic controller with $scope` syntax. - - *Why?*: Controllers are constructed, "newed" up, and provide a single new instance, and the `controllerAs` syntax is closer to that of a JavaScript constructor than the `classic $scope syntax`. - - *Why?*: It promotes the use of binding to a "dotted" object in the View (e.g. `customer.name` instead of `name`), which is more contextual, easier to read, and avoids any reference issues that may occur without "dotting". - - *Why?*: Helps avoid using `$parent` calls in Views with nested controllers. - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### controllerAs Controller Syntax -###### [Style [Y031](#style-y031)] - - - Use the `controllerAs` syntax over the `classic controller with $scope` syntax. - - - The `controllerAs` syntax uses `this` inside controllers which gets bound to `$scope` - - *Why?*: `controllerAs` is syntactic sugar over `$scope`. You can still bind to the View and still access `$scope` methods. - - *Why?*: Helps avoid the temptation of using `$scope` methods inside a controller when it may otherwise be better to avoid them or move the method to a factory, and reference them from the controller. Consider using `$scope` in a controller only when needed. For example when publishing and subscribing events using [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), or [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on). - - ```javascript - /* avoid */ - function CustomerController($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommended - but see next section */ - function CustomerController() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### controllerAs with vm -###### [Style [Y032](#style-y032)] - - - Use a capture variable for `this` when using the `controllerAs` syntax. Choose a consistent variable name such as `vm`, which stands for ViewModel. - - *Why?*: The `this` keyword is contextual and when used within a function inside a controller may change its context. Capturing the context of `this` avoids encountering this problem. - - ```javascript - /* avoid */ - function CustomerController() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommended */ - function CustomerController() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - Note: You can avoid any [jshint](http://jshint.com/) warnings by placing the comment above the line of code. However it is not needed when the function is named using UpperCasing, as this convention means it is a constructor function, which is what a controller is in Angular. - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - Note: When creating watches in a controller using `controller as`, you can watch the `vm.*` member using the following syntax. (Create watches with caution as they add more load to the digest cycle.) - - ```html - - ``` - - ```javascript - function SomeController($scope, $log) { - var vm = this; - vm.title = 'Some Title'; - - $scope.$watch('vm.title', function(current, original) { - $log.info('vm.title was %s', original); - $log.info('vm.title is now %s', current); - }); - } - ``` - - Note: When working with larger codebases, using a more descriptive name can help ease cognitive overhead & searchability. Avoid overly verbose names that are cumbersome to type. - - ```html - - - ``` - - ```html - - - ``` - -### Bindable Members Up Top -###### [Style [Y033](#style-y033)] - - - Place bindable members at the top of the controller, alphabetized, and not spread through the controller code. - - *Why?*: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the controller can be bound and used in the View. - - *Why?*: Setting anonymous functions in-line can be easy, but when those functions are more than 1 line of code they can reduce the readability. Defining the functions below the bindable members (the functions will be hoisted) moves the implementation details down, keeps the bindable members up top, and makes it easier to read. - - ```javascript - /* avoid */ - function SessionsController() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - } - ``` - - ```javascript - /* recommended */ - function SessionsController() { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = refresh; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - - //////////// - - function gotoSession() { - /* */ - } - - function refresh() { - /* */ - } - - function search() { - /* */ - } - } - ``` - - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) - - Note: If the function is a 1 liner consider keeping it right up top, as long as readability is not affected. - - ```javascript - /* avoid */ - function SessionsController(data) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = function() { - /** - * lines - * of - * code - * affects - * readability - */ - }; - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - } - ``` - - ```javascript - /* recommended */ - function SessionsController(sessionDataService) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = sessionDataService.refresh; // 1 liner is OK - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - } - ``` - -### Function Declarations to Hide Implementation Details -###### [Style [Y034](#style-y034)] - - - Use function declarations to hide implementation details. Keep your bindable members up top. When you need to bind a function in a controller, point it to a function declaration that appears later in the file. This is tied directly to the section Bindable Members Up Top. For more details see [this post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code/). - - *Why?*: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the controller can be bound and used in the View. (Same as above.) - - *Why?*: Placing the implementation details of a function later in the file moves that complexity out of view so you can see the important stuff up top. - - *Why?*: Function declaration are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions). - - *Why?*: You never have to worry with function declarations that moving `var a` before `var b` will break your code because `a` depends on `b`. - - *Why?*: Order is critical with function expressions - - ```javascript - /** - * avoid - * Using function expressions. - */ - function AvengersController(avengersService, logger) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - var activate = function() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - var getAvengers = function() { - return avengersService.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - Notice that the important stuff is scattered in the preceding example. In the example below, notice that the important stuff is up top. For example, the members bound to the controller such as `vm.avengers` and `vm.title`. The implementation details are down below. This is just easier to read. - - ```javascript - /* - * recommend - * Using function declarations - * and bindable members up top. - */ - function AvengersController(avengersService, logger) { - var vm = this; - vm.avengers = []; - vm.getAvengers = getAvengers; - vm.title = 'Avengers'; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return avengersService.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Defer Controller Logic to Services -###### [Style [Y035](#style-y035)] - - - Defer logic in a controller by delegating to services and factories. - - *Why?*: Logic may be reused by multiple controllers when placed within a service and exposed via a function. - - *Why?*: Logic in a service can more easily be isolated in a unit test, while the calling logic in the controller can be easily mocked. - - *Why?*: Removes dependencies and hides implementation details from the controller. - - *Why?*: Keeps the controller slim, trim, and focused. - - ```javascript - - /* avoid */ - function OrderController($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - var settings = {}; - // Get the credit service base URL from config - // Set credit service required headers - // Prepare URL query string or data object with request data - // Add user-identifying info so service gets the right credit limit for this user. - // Use JSONP for this browser if it doesn't support CORS - return $http.get(settings) - .then(function(data) { - // Unpack JSON data in the response object - // to find maxRemainingAmount - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Interpret error - // Cope w/ timeout? retry? try alternate service? - // Re-reject with appropriate error for a user to see - }); - }; - } - ``` - - ```javascript - /* recommended */ - function OrderController(creditService) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - return creditService.isOrderTotalOk(vm.total) - .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showError); - }; - } - ``` - -### Keep Controllers Focused -###### [Style [Y037](#style-y037)] - - - Define a controller for a view, and try not to reuse the controller for other views. Instead, move reusable logic to factories and keep the controller simple and focused on its view. - - *Why?*: Reusing controllers with several views is brittle and good end-to-end (e2e) test coverage is required to ensure stability across large applications. - -### Assigning Controllers -###### [Style [Y038](#style-y038)] - - - When a controller must be paired with a view and either component may be re-used by other controllers or views, define controllers along with their routes. - - Note: If a View is loaded via another means besides a route, then use the `ng-controller="Avengers as vm"` syntax. - - *Why?*: Pairing the controller in the route allows different routes to invoke different pairs of controllers and views. When controllers are assigned in the view using [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), that view is always associated with the same controller. - - ```javascript - /* avoid - when using with a route and dynamic pairing is desired */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* recommended */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[Back to top](#table-of-contents)** - -## Services - -### Singletons -###### [Style [Y040](#style-y040)] - - - Services are instantiated with the `new` keyword, use `this` for public methods and variables. Since these are so similar to factories, use a factory instead for consistency. - - Note: [All Angular services are singletons](https://docs.angularjs.org/guide/services). This means that there is only one instance of a given service per injector. - - ```javascript - // service - angular - .module('app') - .service('logger', logger); - - function logger() { - this.logError = function(msg) { - /* */ - }; - } - ``` - - ```javascript - // factory - angular - .module('app') - .factory('logger', logger); - - function logger() { - return { - logError: function(msg) { - /* */ - } - }; - } - ``` - -**[Back to top](#table-of-contents)** - -## Factories - -### Single Responsibility -###### [Style [Y050](#style-y050)] - - - Factories should have a [single responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle), that is encapsulated by its context. Once a factory begins to exceed that singular purpose, a new factory should be created. - -### Singletons -###### [Style [Y051](#style-y051)] - - - Factories are singletons and return an object that contains the members of the service. - - Note: [All Angular services are singletons](https://docs.angularjs.org/guide/services). - -### Accessible Members Up Top -###### [Style [Y052](#style-y052)] - - - Expose the callable members of the service (its interface) at the top, using a technique derived from the [Revealing Module Pattern](https://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - - *Why?*: Placing the callable members at the top makes it easy to read and helps you instantly identify which members of the service can be called and must be unit tested (and/or mocked). - - *Why?*: This is especially helpful when the file gets longer as it helps avoid the need to scroll to see what is exposed. - - *Why?*: Setting functions as you go can be easy, but when those functions are more than 1 line of code they can reduce the readability and cause more scrolling. Defining the callable interface via the returned service moves the implementation details down, keeps the callable interface up top, and makes it easier to read. - - ```javascript - /* avoid */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* recommended */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - - This way bindings are mirrored across the host object, primitive values cannot update alone using the revealing module pattern. - - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-2.png) - -### Function Declarations to Hide Implementation Details -###### [Style [Y053](#style-y053)] - - - Use function declarations to hide implementation details. Keep your accessible members of the factory up top. Point those to function declarations that appears later in the file. For more details see [this post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *Why?*: Placing accessible members at the top makes it easy to read and helps you instantly identify which functions of the factory you can access externally. - - *Why?*: Placing the implementation details of a function later in the file moves that complexity out of view so you can see the important stuff up top. - - *Why?*: Function declaration are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions). - - *Why?*: You never have to worry with function declarations that moving `var a` before `var b` will break your code because `a` depends on `b`. - - *Why?*: Order is critical with function expressions - - ```javascript - /** - * avoid - * Using function expressions - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // implementation details go here - }; - - var getAvengerCount = function() { - // implementation details go here - }; - - var getAvengersCast = function() { - // implementation details go here - }; - - var prime = function() { - // implementation details go here - }; - - var ready = function(nextPromises) { - // implementation details go here - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * recommended - * Using function declarations - * and accessible members up top. - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - - //////////// - - function getAvengers() { - // implementation details go here - } - - function getAvengerCount() { - // implementation details go here - } - - function getAvengersCast() { - // implementation details go here - } - - function prime() { - // implementation details go here - } - - function ready(nextPromises) { - // implementation details go here - } - } - ``` - -**[Back to top](#table-of-contents)** - -## Data Services - -### Separate Data Calls -###### [Style [Y060](#style-y060)] - - - Refactor logic for making data operations and interacting with data to a factory. Make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations. - - *Why?*: The controller's responsibility is for the presentation and gathering of information for the view. It should not care how it gets the data, just that it knows who to ask for it. Separating the data services moves the logic on how to get it to the data service, and lets the controller be simpler and more focused on the view. - - *Why?*: This makes it easier to test (mock or real) the data calls when testing a controller that uses a data service. - - *Why?*: Data service implementation may have very specific code to handle the data repository. This may include headers, how to talk to the data, or other services such as `$http`. Separating the logic into a data service encapsulates this logic in a single place hiding the implementation from the outside consumers (perhaps a controller), also making it easier to change the implementation. - - ```javascript - /* recommended */ - - // dataservice factory - angular - .module('app.core') - .factory('dataservice', dataservice); - - dataservice.$inject = ['$http', 'logger']; - - function dataservice($http, logger) { - return { - getAvengers: getAvengers - }; - - function getAvengers() { - return $http.get('/api/maa') - .then(getAvengersComplete) - .catch(getAvengersFailed); - - function getAvengersComplete(response) { - return response.data.results; - } - - function getAvengersFailed(error) { - logger.error('XHR Failed for getAvengers.' + error.data); - } - } - } - ``` - - Note: The data service is called from consumers, such as a controller, hiding the implementation from the consumers, as shown below. - - ```javascript - /* recommended */ - - // controller calling the dataservice factory - angular - .module('app.avengers') - .controller('AvengersController', AvengersController); - - AvengersController.$inject = ['dataservice', 'logger']; - - function AvengersController(dataservice, logger) { - var vm = this; - vm.avengers = []; - - activate(); - - function activate() { - return getAvengers().then(function() { - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - return dataservice.getAvengers() - .then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Return a Promise from Data Calls -###### [Style [Y061](#style-y061)] - - - When calling a data service that returns a promise such as `$http`, return a promise in your calling function too. - - *Why?*: You can chain the promises together and take further action after the data call completes and resolves or rejects the promise. - - ```javascript - /* recommended */ - - activate(); - - function activate() { - /** - * Step 1 - * Ask the getAvengers function for the - * avenger data and wait for the promise - */ - return getAvengers().then(function() { - /** - * Step 4 - * Perform an action on resolve of final promise - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Step 2 - * Ask the data service for the data and wait - * for the promise - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Step 3 - * set the data and resolve the promise - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - -**[Back to top](#table-of-contents)** - -## Directives -### Limit 1 Per File -###### [Style [Y070](#style-y070)] - - - Create one directive per file. Name the file for the directive. - - *Why?*: It is easy to mash all the directives in one file, but difficult to then break those out so some are shared across apps, some across modules, some just for one module. - - *Why?*: One directive per file is easy to maintain. - - > Note: "**Best Practice**: Directives should clean up after themselves. You can use `element.on('$destroy', ...)` or `scope.$on('$destroy', ...)` to run a clean-up function when the directive is removed" ... from the Angular documentation. - - ```javascript - /* avoid */ - /* directives.js */ - - angular - .module('app.widgets') - - /* order directive that is specific to the order module */ - .directive('orderCalendarRange', orderCalendarRange) - - /* sales directive that can be used anywhere across the sales app */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* spinner directive that can be used anywhere across apps */ - .directive('sharedSpinner', sharedSpinner); - - function orderCalendarRange() { - /* implementation details */ - } - - function salesCustomerInfo() { - /* implementation details */ - } - - function sharedSpinner() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* calendar-range.directive.js */ - - /** - * @desc order directive that is specific to the order module at a company named Acme - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* customer-info.directive.js */ - - /** - * @desc sales directive that can be used anywhere across the sales app at a company named Acme - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* implementation details */ - } - ``` - - ```javascript - /* recommended */ - /* spinner.directive.js */ - - /** - * @desc spinner directive that can be used anywhere across apps at a company named Acme - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* implementation details */ - } - ``` - - Note: There are many naming options for directives, especially since they can be used in narrow or wide scopes. Choose one that makes the directive and its file name distinct and clear. Some examples are below, but see the [Naming](#naming) section for more recommendations. - -### Manipulate DOM in a Directive -###### [Style [Y072](#style-y072)] - - - When manipulating the DOM directly, use a directive. If alternative ways can be used such as using CSS to set styles or the [animation services](https://docs.angularjs.org/api/ngAnimate), Angular templating, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) or [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), then use those instead. For example, if the directive simply hides and shows, use ngHide/ngShow. - - *Why?*: DOM manipulation can be difficult to test, debug, and there are often better ways (e.g. CSS, animations, templates) - -### Provide a Unique Directive Prefix -###### [Style [Y073](#style-y073)] - - - Provide a short, unique and descriptive directive prefix such as `acmeSalesCustomerInfo` which would be declared in HTML as `acme-sales-customer-info`. - - *Why?*: The unique short prefix identifies the directive's context and origin. For example a prefix of `cc-` may indicate that the directive is part of a CodeCamper app while `acme-` may indicate a directive for the Acme company. - - Note: Avoid `ng-` as these are reserved for Angular directives. Research widely used directives to avoid naming conflicts, such as `ion-` for the [Ionic Framework](http://ionicframework.com/). - -### Restrict to Elements and Attributes -###### [Style [Y074](#style-y074)] - - - When creating a directive that makes sense as a stand-alone element, allow restrict `E` (custom element) and optionally restrict `A` (custom attribute). Generally, if it could be its own control, `E` is appropriate. General guideline is allow `EA` but lean towards implementing as an element when it's stand-alone and as an attribute when it enhances its existing DOM element. - - *Why?*: It makes sense. - - *Why?*: While we can allow the directive to be used as a class, if the directive is truly acting as an element it makes more sense as an element or at least as an attribute. - - Note: EA is the default for Angular 1.3 + - - ```html - -
- ``` - - ```javascript - /* avoid */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'C' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - - ```html - - -
- ``` - - ```javascript - /* recommended */ - angular - .module('app.widgets') - .directive('myCalendarRange', myCalendarRange); - - function myCalendarRange() { - var directive = { - link: link, - templateUrl: '/template/is/located/here.html', - restrict: 'EA' - }; - return directive; - - function link(scope, element, attrs) { - /* */ - } - } - ``` - -### Directives and ControllerAs -###### [Style [Y075](#style-y075)] - - - Use `controller as` syntax with a directive to be consistent with using `controller as` with view and controller pairings. - - *Why?*: It makes sense and it's not difficult. - - Note: The directive below demonstrates some of the ways you can use scope inside of link and directive controllers, using controllerAs. I in-lined the template just to keep it all in one place. - - Note: Regarding dependency injection, see [Manually Identify Dependencies](#manual-annotating-for-dependency-injection). - - Note: Note that the directive's controller is outside the directive's closure. This style eliminates issues where the injection gets created as unreachable code after a `return`. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - link: linkFunc, - controller: ExampleController, - // note: This would be 'ExampleController' (the exported controller name, as string) - // if referring to a defined controller in its separate file. - controllerAs: 'vm', - bindToController: true // because the scope is isolated - }; - - return directive; - - function linkFunc(scope, el, attr, ctrl) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: scope.vm.min = %s', scope.vm.min); - console.log('LINK: scope.vm.max = %s', scope.vm.max); - } - } - - ExampleController.$inject = ['$scope']; - - function ExampleController($scope) { - // Injecting $scope just for comparison - var vm = this; - - vm.min = 3; - - console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); - console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - - Note: You can also name the controller when you inject it into the link function and access directive attributes as properties of the controller. - - ```javascript - // Alternative to above example - function linkFunc(scope, el, attr, vm) { - console.log('LINK: scope.min = %s *** should be undefined', scope.min); - console.log('LINK: scope.max = %s *** should be undefined', scope.max); - console.log('LINK: vm.min = %s', vm.min); - console.log('LINK: vm.max = %s', vm.max); - } - ``` - -###### [Style [Y076](#style-y076)] - - - Use `bindToController = true` when using `controller as` syntax with a directive when you want to bind the outer scope to the directive's controller's scope. - - *Why?*: It makes it easy to bind outer scope to the directive's controller scope. - - Note: `bindToController` was introduced in Angular 1.3.0. - - ```html -
- ``` - - ```javascript - angular - .module('app') - .directive('myExample', myExample); - - function myExample() { - var directive = { - restrict: 'EA', - templateUrl: 'app/feature/example.directive.html', - scope: { - max: '=' - }, - controller: ExampleController, - controllerAs: 'vm', - bindToController: true - }; - - return directive; - } - - function ExampleController() { - var vm = this; - vm.min = 3; - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); - } - ``` - - ```html - -
hello world
-
max={{vm.max}}
-
min={{vm.min}}
- ``` - -**[Back to top](#table-of-contents)** - -## Resolving Promises -### Controller Activation Promises -###### [Style [Y080](#style-y080)] - - - Resolve start-up logic for a controller in an `activate` function. - - *Why?*: Placing start-up logic in a consistent place in the controller makes it easier to locate, more consistent to test, and helps avoid spreading out the activation logic across the controller. - - *Why?*: The controller `activate` makes it convenient to re-use the logic for a refresh for the controller/View, keeps the logic together, gets the user to the View faster, makes animations easy on the `ng-view` or `ui-view`, and feels snappier to the user. - - Note: If you need to conditionally cancel the route before you start using the controller, use a [route resolve](#style-y081) instead. - - ```javascript - /* avoid */ - function AvengersController(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* recommended */ - function AvengersController(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - activate(); - - //////////// - - function activate() { - return dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Route Resolve Promises -###### [Style [Y081](#style-y081)] - - - When a controller depends on a promise to be resolved before the controller is activated, resolve those dependencies in the `$routeProvider` before the controller logic is executed. If you need to conditionally cancel a route before the controller is activated, use a route resolver. - - - Use a route resolve when you want to decide to cancel the route before ever transitioning to the View. - - *Why?*: A controller may require data before it loads. That data may come from a promise via a custom factory or [$http](https://docs.angularjs.org/api/ng/service/$http). Using a [route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) allows the promise to resolve before the controller logic executes, so it might take action based on that data from the promise. - - *Why?*: The code executes after the route and in the controller’s activate function. The View starts to load right away. Data binding kicks in when the activate promise resolves. A “busy” animation can be shown during the view transition (via `ng-view` or `ui-view`) - - Note: The code executes before the route via a promise. Rejecting the promise cancels the route. Resolve makes the new view wait for the route to resolve. A “busy” animation can be shown before the resolve and through the view transition. If you want to get to the View faster and do not require a checkpoint to decide if you can get to the View, consider the [controller `activate` technique](#style-y080) instead. - - ```javascript - /* avoid */ - angular - .module('app') - .controller('AvengersController', AvengersController); - - function AvengersController(movieService) { - var vm = this; - // unresolved - vm.movies; - // resolved asynchronously - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* better */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - - // avengers.js - angular - .module('app') - .controller('AvengersController', AvengersController); - - AvengersController.$inject = ['moviesPrepService']; - function AvengersController(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - - Note: The example below shows the route resolve points to a named function, which is easier to debug and easier to handle dependency injection. - - ```javascript - /* even better */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - - // avengers.js - angular - .module('app') - .controller('AvengersController', AvengersController); - - AvengersController.$inject = ['moviesPrepService']; - function AvengersController(moviesPrepService) { - var vm = this; - vm.movies = moviesPrepService.movies; - } - ``` - Note: The code example's dependency on `movieService` is not minification safe on its own. For details on how to make this code minification safe, see the sections on [dependency injection](#manual-annotating-for-dependency-injection) and on [minification and annotation](#minification-and-annotation). - -**[Back to top](#table-of-contents)** - -### Handling Exceptions with Promises -###### [Style [Y082](#style-y082)] - - - The `catch` block of a promise must return a rejected promise to maintain the exception in the promise chain. - - - Always handle exceptions in services/factories. - - *Why?*: If the `catch` block does not return a rejected promise, the caller of the promise will not know an exception occurred. The caller's `then` will execute. Thus, the user may never know what happened. - - *Why?*: To avoid swallowing errors and misinforming the user. - - Note: Consider putting any exception handling in a function in a shared module and service. - - ```javascript - /* avoid */ - - function getCustomer(id) { - return $http.get('/api/customer/' + id) - .then(getCustomerComplete) - .catch(getCustomerFailed); - - function getCustomerComplete(data, status, headers, config) { - return data.data; - } - - function getCustomerFailed(e) { - var newMessage = 'XHR Failed for getCustomer' - if (e.data && e.data.description) { - newMessage = newMessage + '\n' + e.data.description; - } - e.data.description = newMessage; - logger.error(newMessage); - // *** - // Notice there is no return of the rejected promise - // *** - } - } - - /* recommended */ - function getCustomer(id) { - return $http.get('/api/customer/' + id) - .then(getCustomerComplete) - .catch(getCustomerFailed); - - function getCustomerComplete(data, status, headers, config) { - return data.data; - } - - function getCustomerFailed(e) { - var newMessage = 'XHR Failed for getCustomer' - if (e.data && e.data.description) { - newMessage = newMessage + '\n' + e.data.description; - } - e.data.description = newMessage; - logger.error(newMessage); - return $q.reject(e); - } - } - ``` - -**[Back to top](#table-of-contents)** - -## Manual Annotating for Dependency Injection - -### UnSafe from Minification -###### [Style [Y090](#style-y090)] - - - Avoid using the shortcut syntax of declaring dependencies without using a minification-safe approach. - - *Why?*: The parameters to the component (e.g. controller, factory, etc) will be converted to mangled variables. For example, `common` and `dataservice` may become `a` or `b` and not be found by Angular. - - ```javascript - /* avoid - not minification-safe*/ - angular - .module('app') - .controller('DashboardController', DashboardController); - - function DashboardController(common, dataservice) { - } - ``` - - This code may produce mangled variables when minified and thus cause runtime errors. - - ```javascript - /* avoid - not minification-safe*/ - angular.module('app').controller('DashboardController', d);function d(a, b) { } - ``` - -### Manually Identify Dependencies -###### [Style [Y091](#style-y091)] - - - Use `$inject` to manually identify your dependencies for Angular components. - - *Why?*: This technique mirrors the technique used by [`ng-annotate`](https://github.com/olov/ng-annotate), which I recommend for automating the creation of minification safe dependencies. If `ng-annotate` detects injection has already been made, it will not duplicate it. - - *Why?*: This safeguards your dependencies from being vulnerable to minification issues when parameters may be mangled. For example, `common` and `dataservice` may become `a` or `b` and not be found by Angular. - - *Why?*: Avoid creating in-line dependencies as long lists can be difficult to read in the array. Also it can be confusing that the array is a series of strings while the last item is the component's function. - - ```javascript - /* avoid */ - angular - .module('app') - .controller('DashboardController', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* avoid */ - angular - .module('app') - .controller('DashboardController', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* recommended */ - angular - .module('app') - .controller('DashboardController', DashboardController); - - DashboardController.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function DashboardController($location, $routeParams, common, dataservice) { - } - ``` - - Note: When your function is below a return statement the `$inject` may be unreachable (this may happen in a directive). You can solve this by moving the Controller outside of the directive. - - ```javascript - /* avoid */ - // inside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - - DashboardPanelController.$inject = ['logger']; // Unreachable - function DashboardPanelController(logger) { - } - } - ``` - - ```javascript - /* recommended */ - // outside a directive definition - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - } - - DashboardPanelController.$inject = ['logger']; - function DashboardPanelController(logger) { - } - ``` - -### Manually Identify Route Resolver Dependencies -###### [Style [Y092](#style-y092)] - - - Use `$inject` to manually identify your route resolver dependencies for Angular components. - - *Why?*: This technique breaks out the anonymous function for the route resolver, making it easier to read. - - *Why?*: An `$inject` statement can easily precede the resolver to handle making any dependencies minification safe. - - ```javascript - /* recommended */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviesPrepService - } - }); - } - - moviesPrepService.$inject = ['movieService']; - function moviesPrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[Back to top](#table-of-contents)** - -## Minification and Annotation - -### ng-annotate -###### [Style [Y100](#style-y100)] - - - Use [ng-annotate](//github.com/olov/ng-annotate) for [Gulp](http://gulpjs.com) or [Grunt](http://gruntjs.com) and comment functions that need automated dependency injection using `/* @ngInject */` - - *Why?*: This safeguards your code from any dependencies that may not be using minification-safe practices. - - *Why?*: [`ng-min`](https://github.com/btford/ngmin) is deprecated - - >I prefer Gulp as I feel it is easier to write, to read, and to debug. - - The following code is not using minification safe dependencies. - - ```javascript - angular - .module('app') - .controller('AvengersController', AvengersController); - - /* @ngInject */ - function AvengersController(storage, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storage.save(hero.name, hero); - } - } - ``` - - When the above code is run through ng-annotate it will produce the following output with the `$inject` annotation and become minification-safe. - - ```javascript - angular - .module('app') - .controller('AvengersController', AvengersController); - - /* @ngInject */ - function AvengersController(storage, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storage.save(hero.name, hero); - } - } - - AvengersController.$inject = ['storage', 'avengerService']; - ``` - - Note: If `ng-annotate` detects injection has already been made (e.g. `@ngInject` was detected), it will not duplicate the `$inject` code. - - Note: When using a route resolver you can prefix the resolver's function with `/* @ngInject */` and it will produce properly annotated code, keeping any injected dependencies minification safe. - - ```javascript - // Using @ngInject annotations - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > Note: Starting from Angular 1.3 you can use the [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp) directive's `ngStrictDi` parameter to detect any potentially missing minification safe dependencies. When present the injector will be created in "strict-di" mode causing the application to fail to invoke functions which do not use explicit function annotation (these may not be minification safe). Debugging info will be logged to the console to help track down the offending code. I prefer to only use `ng-strict-di` for debugging purposes only. - `` - -### Use Gulp or Grunt for ng-annotate -###### [Style [Y101](#style-y101)] - - - Use [gulp-ng-annotate](https://www.npmjs.com/package/gulp-ng-annotate) or [grunt-ng-annotate](https://www.npmjs.com/package/grunt-ng-annotate) in an automated build task. Inject `/* @ngInject */` prior to any function that has dependencies. - - *Why?*: ng-annotate will catch most dependencies, but it sometimes requires hints using the `/* @ngInject */` syntax. - - The following code is an example of a gulp task using ngAnnotate - - ```javascript - gulp.task('js', ['jshint'], function() { - var source = pkg.paths.js; - - return gulp.src(source) - .pipe(sourcemaps.init()) - .pipe(concat('all.min.js', {newLine: ';'})) - // Annotate before uglify so the code get's min'd properly. - .pipe(ngAnnotate({ - // true helps add where @ngInject is not used. It infers. - // Doesn't work with resolve, so we must be explicit there - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[Back to top](#table-of-contents)** - -## Exception Handling - -### decorators -###### [Style [Y110](#style-y110)] - - - Use a [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator), at config time using the [`$provide`](https://docs.angularjs.org/api/auto/service/$provide) service, on the [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) service to perform custom actions when exceptions occur. - - *Why?*: Provides a consistent way to handle uncaught Angular exceptions for development-time or run-time. - - Note: Another option is to override the service instead of using a decorator. This is a fine option, but if you want to keep the default behavior and extend it a decorator is recommended. - - ```javascript - /* recommended */ - angular - .module('blocks.exception') - .config(exceptionConfig); - - exceptionConfig.$inject = ['$provide']; - - function exceptionConfig($provide) { - $provide.decorator('$exceptionHandler', extendExceptionHandler); - } - - extendExceptionHandler.$inject = ['$delegate', 'toastr']; - - function extendExceptionHandler($delegate, toastr) { - return function(exception, cause) { - $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause - }; - /** - * Could add the error to a service's collection, - * add errors to $rootScope, log errors to remote web server, - * or log locally. Or throw hard. It is entirely up to you. - * throw exception; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Exception Catchers -###### [Style [Y111](#style-y111)] - - - Create a factory that exposes an interface to catch and gracefully handle exceptions. - - *Why?*: Provides a consistent way to catch exceptions that may be thrown in your code (e.g. during XHR calls or promise failures). - - Note: The exception catcher is good for catching and reacting to specific exceptions from calls that you know may throw one. For example, when making an XHR call to retrieve data from a remote web service and you want to catch any exceptions from that service and react uniquely. - - ```javascript - /* recommended */ - angular - .module('blocks.exception') - .factory('exception', exception); - - exception.$inject = ['logger']; - - function exception(logger) { - var service = { - catcher: catcher - }; - return service; - - function catcher(message) { - return function(reason) { - logger.error(message, reason); - }; - } - } - ``` - -### Route Errors -###### [Style [Y112](#style-y112)] - - - Handle and log all routing errors using [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). - - *Why?*: Provides a consistent way to handle all routing errors. - - *Why?*: Potentially provides a better user experience if a routing error occurs and you route them to a friendly screen with more details or recovery options. - - ```javascript - /* recommended */ - var handlingRouteChangeError = false; - - function handleRoutingErrors() { - /** - * Route cancellation: - * On routing error, go to the dashboard. - * Provide an exit clause if it tries to do it twice. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - if (handlingRouteChangeError) { return; } - handlingRouteChangeError = true; - var destination = (current && (current.title || - current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + - (rejection.msg || ''); - - /** - * Optionally log using a custom service or $log. - * (Don't forget to inject custom service) - */ - logger.warning(msg, [current]); - - /** - * On routing error, go to another route/state. - */ - $location.path('/'); - - } - ); - } - ``` - -**[Back to top](#table-of-contents)** - -## Naming - -### Naming Guidelines -###### [Style [Y120](#style-y120)] - - - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.js`. There are 2 names for most assets: - * the file name (`avengers.controller.js`) - * the registered component name with Angular (`AvengersController`) - - *Why?*: Naming conventions help provide a consistent way to find content at a glance. Consistency within the project is vital. Consistency with a team is important. Consistency across a company provides tremendous efficiency. - - *Why?*: The naming conventions should simply help you find your code faster and make it easier to understand. - -### Feature File Names -###### [Style [Y121](#style-y121)] - - - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.js`. - - *Why?*: Provides a consistent way to quickly identify components. - - *Why?*: Provides pattern matching for any automated tasks. - - ```javascript - /** - * common options - */ - - // Controllers - avengers.js - avengers.controller.js - avengersController.js - - // Services/Factories - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * recommended - */ - - // controllers - avengers.controller.js - avengers.controller.spec.js - - // services/factories - logger.service.js - logger.service.spec.js - - // constants - constants.js - - // module definition - avengers.module.js - - // routes - avengers.routes.js - avengers.routes.spec.js - - // configuration - avengers.config.js - - // directives - avenger-profile.directive.js - avenger-profile.directive.spec.js - ``` - - Note: Another common convention is naming controller files without the word `controller` in the file name such as `avengers.js` instead of `avengers.controller.js`. All other conventions still hold using a suffix of the type. Controllers are the most common type of component so this just saves typing and is still easily identifiable. I recommend you choose 1 convention and be consistent for your team. My preference is `avengers.controller.js` identifying the `AvengersController`. - - ```javascript - /** - * recommended - */ - // Controllers - avengers.js - avengers.spec.js - ``` - -### Test File Names -###### [Style [Y122](#style-y122)] - - - Name test specifications similar to the component they test with a suffix of `spec`. - - *Why?*: Provides a consistent way to quickly identify components. - - *Why?*: Provides pattern matching for [karma](http://karma-runner.github.io/) or other test runners. - - ```javascript - /** - * recommended - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Controller Names -###### [Style [Y123](#style-y123)] - - - Use consistent names for all controllers named after their feature. Use UpperCamelCase for controllers, as they are constructors. - - *Why?*: Provides a consistent way to quickly identify and reference controllers. - - *Why?*: UpperCamelCase is conventional for identifying object that can be instantiated using a constructor. - - ```javascript - /** - * recommended - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengersController', HeroAvengersController); - - function HeroAvengersController() { } - ``` - -### Controller Name Suffix -###### [Style [Y124](#style-y124)] - - - Append the controller name with the suffix `Controller`. - - *Why?*: The `Controller` suffix is more commonly used and is more explicitly descriptive. - - ```javascript - /** - * recommended - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController() { } - ``` - -### Factory and Service Names -###### [Style [Y125](#style-y125)] - - - Use consistent names for all factories and services named after their feature. Use camel-casing for services and factories. Avoid prefixing factories and services with `$`. Only suffix service and factories with `Service` when it is not clear what they are (i.e. when they are nouns). - - *Why?*: Provides a consistent way to quickly identify and reference factories. - - *Why?*: Avoids name collisions with built-in factories and services that use the `$` prefix. - - *Why?*: Clear service names such as `logger` do not require a suffix. - - *Why?*: Service names such as `avengers` are nouns and require a suffix and should be named `avengersService`. - - ```javascript - /** - * recommended - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger() { } - ``` - - ```javascript - /** - * recommended - */ - - // credit.service.js - angular - .module - .factory('creditService', creditService); - - function creditService() { } - - // customer.service.js - angular - .module - .service('customerService', customerService); - - function customerService() { } - ``` - -### Directive Component Names -###### [Style [Y126](#style-y126)] - - - Use consistent names for all directives using camel-case. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). - - *Why?*: Provides a consistent way to quickly identify and reference components. - - ```javascript - /** - * recommended - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // usage is - - function xxAvengerProfile() { } - ``` - -### Modules -###### [Style [Y127](#style-y127)] - - - When there are multiple modules, the main module file is named `app.module.js` while other dependent modules are named after what they represent. For example, an admin module is named `admin.module.js`. The respective registered module names would be `app` and `admin`. - - *Why?*: Provides consistency for multiple module apps, and for expanding to large applications. - - *Why?*: Provides easy way to use task automation to load all module definitions first, then all other angular files (for bundling). - -### Configuration -###### [Style [Y128](#style-y128)] - - - Separate configuration for a module into its own file named after the module. A configuration file for the main `app` module is named `app.config.js` (or simply `config.js`). A configuration for a module named `admin.module.js` is named `admin.config.js`. - - *Why?*: Separates configuration from module definition, components, and active code. - - *Why?*: Provides an identifiable place to set configuration for a module. - -### Routes -###### [Style [Y129](#style-y129)] - - - Separate route configuration into its own file. Examples might be `app.route.js` for the main module and `admin.route.js` for the `admin` module. Even in smaller apps I prefer this separation from the rest of the configuration. - -**[Back to top](#table-of-contents)** - -## Application Structure LIFT Principle -### LIFT -###### [Style [Y140](#style-y140)] - - - Structure your app such that you can `L`ocate your code quickly, `I`dentify the code at a glance, keep the `F`lattest structure you can, and `T`ry to stay DRY. The structure should follow these 4 basic guidelines. - - *Why LIFT?*: Provides a consistent structure that scales well, is modular, and makes it easier to increase developer efficiency by finding code quickly. Another way to check your app structure is to ask yourself: How quickly can you open and work in all of the related files for a feature? - - When I find my structure is not feeling comfortable, I go back and revisit these LIFT guidelines - - 1. `L`ocating our code is easy - 2. `I`dentify code at a glance - 3. `F`lat structure as long as we can - 4. `T`ry to stay DRY (Don’t Repeat Yourself) or T-DRY - -### Locate -###### [Style [Y141](#style-y141)] - - - Make locating your code intuitive, simple and fast. - - *Why?*: I find this to be super important for a project. If the team cannot find the files they need to work on quickly, they will not be able to work as efficiently as possible, and the structure needs to change. You may not know the file name or where its related files are, so putting them in the most intuitive locations and near each other saves a ton of time. A descriptive folder structure can help with this. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identify -###### [Style [Y142](#style-y142)] - - - When you look at a file you should instantly know what it contains and represents. - - *Why?*: You spend less time hunting and pecking for code, and become more efficient. If this means you want longer file names, then so be it. Be descriptive with file names and keeping the contents of the file to exactly 1 component. Avoid files with multiple controllers, multiple services, or a mixture. There are deviations of the 1 per file rule when I have a set of very small features that are all related to each other, they are still easily identifiable. - -### Flat -###### [Style [Y143](#style-y143)] - - - Keep a flat folder structure as long as possible. When you get to 7+ files, begin considering separation. - - *Why?*: Nobody wants to search 7 levels of folders to find a file. Think about menus on web sites … anything deeper than 2 should take serious consideration. In a folder structure there is no hard and fast number rule, but when a folder has 7-10 files, that may be time to create subfolders. Base it on your comfort level. Use a flatter structure until there is an obvious value (to help the rest of LIFT) in creating a new folder. - -### T-DRY (Try to Stick to DRY) -###### [Style [Y144](#style-y144)] - - - Be DRY, but don't go nuts and sacrifice readability. - - *Why?*: Being DRY is important, but not crucial if it sacrifices the others in LIFT, which is why I call it T-DRY. I don’t want to type session-view.html for a view because, well, it’s obviously a view. If it is not obvious or by convention, then I name it. - -**[Back to top](#table-of-contents)** - -## Application Structure - -### Overall Guidelines -###### [Style [Y150](#style-y150)] - - - Have a near term view of implementation and a long term vision. In other words, start small but keep in mind on where the app is heading down the road. All of the app's code goes in a root folder named `app`. All content is 1 feature per file. Each controller, service, module, view is in its own file. All 3rd party vendor scripts are stored in another root folder and not in the `app` folder. I didn't write them and I don't want them cluttering my app (`bower_components`, `scripts`, `lib`). - - Note: Find more details and reasoning behind the structure at [this original post on application structure](http://www.johnpapa.net/angular-app-structuring-guidelines/). - -### Layout -###### [Style [Y151](#style-y151)] - - - Place components that define the overall layout of the application in a folder named `layout`. These may include a shell view and controller may act as the container for the app, navigation, menus, content areas, and other regions. - - *Why?*: Organizes all layout in a single place re-used throughout the application. - -### Folders-by-Feature Structure -###### [Style [Y152](#style-y152)] - - - Create folders named for the feature they represent. When a folder grows to contain more than 7 files, start to consider creating a folder for them. Your threshold may be different, so adjust as needed. - - *Why?*: A developer can locate the code, identify what each file represents at a glance, the structure is flat as can be, and there is no repetitive nor redundant names. - - *Why?*: The LIFT guidelines are all covered. - - *Why?*: Helps reduce the app from becoming cluttered through organizing the content and keeping them aligned with the LIFT guidelines. - - *Why?*: When there are a lot of files (10+) locating them is easier with a consistent folder structures and more difficult in flat structures. - - ```javascript - /** - * recommended - */ - - app/ - app.module.js - app.config.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - layout/ - shell.html - shell.controller.js - topnav.html - topnav.controller.js - people/ - attendees.html - attendees.controller.js - people.routes.js - speakers.html - speakers.controller.js - speaker-detail.html - speaker-detail.controller.js - services/ - data.service.js - localstorage.service.js - logger.service.js - spinner.service.js - sessions/ - sessions.html - sessions.controller.js - sessions.routes.js - session-detail.html - session-detail.controller.js - ``` - - ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) - - Note: Do not structure your app using folders-by-type. This requires moving to multiple folders when working on a feature and gets unwieldy quickly as the app grows to 5, 10 or 25+ views and controllers (and other features), which makes it more difficult than folder-by-feature to locate files. - - ```javascript - /* - * avoid - * Alternative folders-by-type. - * I recommend "folders-by-feature", instead. - */ - - app/ - app.module.js - app.config.js - app.routes.js - directives.js - controllers/ - attendees.js - session-detail.js - sessions.js - shell.js - speakers.js - speaker-detail.js - topnav.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js - localstorage.js - logger.js - spinner.js - views/ - attendees.html - session-detail.html - sessions.html - shell.html - speakers.html - speaker-detail.html - topnav.html - ``` - -**[Back to top](#table-of-contents)** - -## Modularity - -### Many Small, Self Contained Modules -###### [Style [Y160](#style-y160)] - - - Create small modules that encapsulate one responsibility. - - *Why?*: Modular applications make it easy to plug and go as they allow the development teams to build vertical slices of the applications and roll out incrementally. This means we can plug in new features as we develop them. - -### Create an App Module -###### [Style [Y161](#style-y161)] - - - Create an application root module whose role is to pull together all of the modules and features of your application. Name this for your application. - - *Why?*: Angular encourages modularity and separation patterns. Creating an application root module whose role is to tie your other modules together provides a very straightforward way to add or remove modules from your application. - -### Keep the App Module Thin -###### [Style [Y162](#style-y162)] - - - Only put logic for pulling together the app in the application module. Leave features in their own modules. - - *Why?*: Adding additional roles to the application root to get remote data, display views, or other logic not related to pulling the app together muddies the app module and make both sets of features harder to reuse or turn off. - - *Why?*: The app module becomes a manifest that describes which modules help define the application. - -### Feature Areas are Modules -###### [Style [Y163](#style-y163)] - - - Create modules that represent feature areas, such as layout, reusable and shared services, dashboards, and app specific features (e.g. customers, admin, sales). - - *Why?*: Self contained modules can be added to the application with little or no friction. - - *Why?*: Sprints or iterations can focus on feature areas and turn them on at the end of the sprint or iteration. - - *Why?*: Separating feature areas into modules makes it easier to test the modules in isolation and reuse code. - -### Reusable Blocks are Modules -###### [Style [Y164](#style-y164)] - - - Create modules that represent reusable application blocks for common services such as exception handling, logging, diagnostics, security, and local data stashing. - - *Why?*: These types of features are needed in many applications, so by keeping them separated in their own modules they can be application generic and be reused across applications. - -### Module Dependencies -###### [Style [Y165](#style-y165)] - - - The application root module depends on the app specific feature modules and any shared or reusable modules. - - ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) - - *Why?*: The main app module contains a quickly identifiable manifest of the application's features. - - *Why?*: Each feature area contains a manifest of what it depends on, so it can be pulled in as a dependency in other applications and still work. - - *Why?*: Intra-App features such as shared data services become easy to locate and share from within `app.core` (choose your favorite name for this module). - - Note: This is a strategy for consistency. There are many good options here. Choose one that is consistent, follows Angular's dependency rules, and is easy to maintain and scale. - - > My structures vary slightly between projects but they all follow these guidelines for structure and modularity. The implementation may vary depending on the features and the team. In other words, don't get hung up on an exact like-for-like structure but do justify your structure using consistency, maintainability, and efficiency in mind. - - > In a small app, you can also consider putting all the shared dependencies in the app module where the feature modules have no direct dependencies. This makes it easier to maintain the smaller application, but makes it harder to reuse modules outside of this application. - -**[Back to top](#table-of-contents)** - -## Startup Logic - -### Configuration -###### [Style [Y170](#style-y170)] - - - Inject code into [module configuration](https://docs.angularjs.org/guide/module#module-loading-dependencies) that must be configured before running the angular app. Ideal candidates include providers and constants. - - *Why?*: This makes it easier to have less places for configuration. - - ```javascript - angular - .module('app') - .config(configure); - - configure.$inject = - ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - - function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { - exceptionHandlerProvider.configure(config.appErrorPrefix); - configureStateHelper(); - - toastr.options.timeOut = 4000; - toastr.options.positionClass = 'toast-bottom-right'; - - //////////////// - - function configureStateHelper() { - routerHelperProvider.configure({ - docTitle: 'NG-Modular: ' - }); - } - } - ``` - -### Run Blocks -###### [Style [Y171](#style-y171)] - - - Any code that needs to run when an application starts should be declared in a factory, exposed via a function, and injected into the [run block](https://docs.angularjs.org/guide/module#module-loading-dependencies). - - *Why?*: Code directly in a run block can be difficult to test. Placing in a factory makes it easier to abstract and mock. - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[Back to top](#table-of-contents)** - -## Angular $ Wrapper Services - -### $document and $window -###### [Style [Y180](#style-y180)] - - - Use [`$document`](https://docs.angularjs.org/api/ng/service/$document) and [`$window`](https://docs.angularjs.org/api/ng/service/$window) instead of `document` and `window`. - - *Why?*: These services are wrapped by Angular and more easily testable than using document and window in tests. This helps you avoid having to mock document and window yourself. - -### $timeout and $interval -###### [Style [Y181](#style-y181)] - - - Use [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) and [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) instead of `setTimeout` and `setInterval` . - - *Why?*: These services are wrapped by Angular and more easily testable and handle Angular's digest cycle thus keeping data binding in sync. - -**[Back to top](#table-of-contents)** - -## Testing -Unit testing helps maintain clean code, as such I included some of my recommendations for unit testing foundations with links for more information. - -### Write Tests with Stories -###### [Style [Y190](#style-y190)] - - - Write a set of tests for every story. Start with an empty test and fill them in as you write the code for the story. - - *Why?*: Writing the test descriptions helps clearly define what your story will do, will not do, and how you can measure success. - - ```javascript - it('should have Avengers controller', function() { - // TODO - }); - - it('should find 1 Avenger when filtered by name', function() { - // TODO - }); - - it('should have 10 Avengers', function() { - // TODO (mock data?) - }); - - it('should return Avengers via XHR', function() { - // TODO ($httpBackend?) - }); - - // and so on - ``` - -### Testing Library -###### [Style [Y191](#style-y191)] - - - Use [Jasmine](http://jasmine.github.io/) or [Mocha](http://mochajs.org) for unit testing. - - *Why?*: Both Jasmine and Mocha are widely used in the Angular community. Both are stable, well maintained, and provide robust testing features. - - Note: When using Mocha, also consider choosing an assert library such as [Chai](http://chaijs.com). I prefer Mocha. - -### Test Runner -###### [Style [Y192](#style-y192)] - - - Use [Karma](http://karma-runner.github.io) as a test runner. - - *Why?*: Karma is easy to configure to run once or automatically when you change your code. - - *Why?*: Karma hooks into your Continuous Integration process easily on its own or through Grunt or Gulp. - - *Why?*: Some IDE's are beginning to integrate with Karma, such as [WebStorm](http://www.jetbrains.com/webstorm/) and [Visual Studio](https://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). - - *Why?*: Karma works well with task automation leaders such as [Grunt](http://gruntjs.com/) (with [grunt-karma](https://github.com/karma-runner/grunt-karma)) and [Gulp](http://gulpjs.com/). When using Gulp, use [Karma](https://github.com/karma-runner/karma) directly and not with a plugin as the API can be called directly. - - ```javascript - /* recommended */ - - // Gulp example with Karma directly - function startTests(singleRun, done) { - var child; - var excludeFiles = []; - var fork = require('child_process').fork; - var karma = require('karma').server; - var serverSpecs = config.serverIntegrationSpecs; - - if (args.startServers) { - log('Starting servers'); - var savedEnv = process.env; - savedEnv.NODE_ENV = 'dev'; - savedEnv.PORT = 8888; - child = fork(config.nodeServer); - } else { - if (serverSpecs && serverSpecs.length) { - excludeFiles = serverSpecs; - } - } - - karma.start({ - configFile: __dirname + '/karma.conf.js', - exclude: excludeFiles, - singleRun: !!singleRun - }, karmaCompleted); - - //////////////// - - function karmaCompleted(karmaResult) { - log('Karma completed'); - if (child) { - log('shutting down the child process'); - child.kill(); - } - if (karmaResult === 1) { - done('karma: tests failed with code ' + karmaResult); - } else { - done(); - } - } - } - ``` - -### Stubbing and Spying -###### [Style [Y193](#style-y193)] - - - Use [Sinon](http://sinonjs.org/) for stubbing and spying. - - *Why?*: Sinon works well with both Jasmine and Mocha and extends the stubbing and spying features they offer. - - *Why?*: Sinon makes it easier to toggle between Jasmine and Mocha, if you want to try both. - - *Why?*: Sinon has descriptive messages when tests fail the assertions. - -### Headless Browser -###### [Style [Y194](#style-y194)] - - - Use [PhantomJS](http://phantomjs.org/) to run your tests on a server. - - *Why?*: PhantomJS is a headless browser that helps run your tests without needing a "visual" browser. So you do not have to install Chrome, Safari, IE, or other browsers on your server. - - Note: You should still test on all browsers in your environment, as appropriate for your target audience. - -### Code Analysis -###### [Style [Y195](#style-y195)] - - - Run JSHint on your tests. - - *Why?*: Tests are code. JSHint can help identify code quality issues that may cause the test to work improperly. - -### Alleviate Globals for JSHint Rules on Tests -###### [Style [Y196](#style-y196)] - - - Relax the rules on your test code to allow for common globals such as `describe` and `expect`. Relax the rules for expressions, as Mocha uses these. - - *Why?*: Your tests are code and require the same attention and code quality rules as all of your production code. However, global variables used by the testing framework, for example, can be relaxed by including this in your test specs. - - ```javascript - /* jshint -W117, -W030 */ - ``` - Or you can add the following to your JSHint Options file. - - ```javascript - "jasmine": true, - "mocha": true, - ``` - - ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) - -### Organizing Tests -###### [Style [Y197](#style-y197)] - - - Place unit test files (specs) side-by-side with your client code. Place specs that cover server integration or test multiple components in a separate `tests` folder. - - *Why?*: Unit tests have a direct correlation to a specific component and file in source code. - - *Why?*: It is easier to keep them up to date since they are always in sight. When coding whether you do TDD or test during development or test after development, the specs are side-by-side and never out of sight nor mind, and thus more likely to be maintained which also helps maintain code coverage. - - *Why?*: When you update source code it is easier to go update the tests at the same time. - - *Why?*: Placing them side-by-side makes it easy to find them and easy to move them with the source code if you move the source. - - *Why?*: Having the spec nearby makes it easier for the source code reader to learn how the component is supposed to be used and to discover its known limitations. - - *Why?*: Separating specs so they are not in a distributed build is easy with grunt or gulp. - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.js - /customers.controller.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[Back to top](#table-of-contents)** - -## Animations - -### Usage -###### [Style [Y210](#style-y210)] - - - Use subtle [animations with Angular](https://docs.angularjs.org/guide/animations) to transition between states for views and primary visual elements. Include the [ngAnimate module](https://docs.angularjs.org/api/ngAnimate). The 3 keys are subtle, smooth, seamless. - - *Why?*: Subtle animations can improve User Experience when used appropriately. - - *Why?*: Subtle animations can improve perceived performance as views transition. - -### Sub Second -###### [Style [Y211](#style-y211)] - - - Use short durations for animations. I generally start with 300ms and adjust until appropriate. - - *Why?*: Long animations can have the reverse effect on User Experience and perceived performance by giving the appearance of a slow application. - -### animate.css -###### [Style [Y212](#style-y212)] - - - Use [animate.css](http://daneden.github.io/animate.css/) for conventional animations. - - *Why?*: The animations that animate.css provides are fast, smooth, and easy to add to your application. - - *Why?*: Provides consistency in your animations. - - *Why?*: animate.css is widely used and tested. - - Note: See this [great post by Matias Niemelä on Angular animations](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) - -**[Back to top](#table-of-contents)** - -## Comments - -### jsDoc -###### [Style [Y220](#style-y220)] - - - If planning to produce documentation, use [`jsDoc`](http://usejsdoc.org/) syntax to document function names, description, params and returns. Use `@namespace` and `@memberOf` to match your app structure. - - *Why?*: You can generate (and regenerate) documentation from your code, instead of writing it from scratch. - - *Why?*: Provides consistency using a common industry tool. - - ```javascript - /** - * Logger Factory - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Application wide logger - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Logs errors - * @param {String} msg Message to log - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[Back to top](#table-of-contents)** - -## JS Hint - -### Use an Options File -###### [Style [Y230](#style-y230)] - - - Use JS Hint for linting your JavaScript and be sure to customize the JS Hint options file and include in source control. See the [JS Hint docs](http://jshint.com/docs/) for details on the options. - - *Why?*: Provides a first alert prior to committing any code to source control. - - *Why?*: Provides consistency across your team. - - ```javascript - { - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "es3": false, - "forin": true, - "freeze": true, - "immed": true, - "indent": 4, - "latedef": "nofunc", - "newcap": true, - "noarg": true, - "noempty": true, - "nonbsp": true, - "nonew": true, - "plusplus": false, - "quotmark": "single", - "undef": true, - "unused": false, - "strict": false, - "maxparams": 10, - "maxdepth": 5, - "maxstatements": 40, - "maxcomplexity": 8, - "maxlen": 120, - - "asi": false, - "boss": false, - "debug": false, - "eqnull": true, - "esnext": false, - "evil": false, - "expr": false, - "funcscope": false, - "globalstrict": false, - "iterator": false, - "lastsemic": false, - "laxbreak": false, - "laxcomma": false, - "loopfunc": true, - "maxerr": false, - "moz": false, - "multistr": false, - "notypeof": false, - "proto": false, - "scripturl": false, - "shadow": false, - "sub": true, - "supernew": false, - "validthis": false, - "noyield": false, - - "browser": true, - "node": true, - - "globals": { - "angular": false, - "$": false - } - } - ``` - -**[Back to top](#table-of-contents)** - -## JSCS - -### Use an Options File -###### [Style [Y235](#style-y235)] - - - Use JSCS for checking your coding styles your JavaScript and be sure to customize the JSCS options file and include in source control. See the [JSCS docs](http://jscs.info/) for details on the options. - - *Why?*: Provides a first alert prior to committing any code to source control. - - *Why?*: Provides consistency across your team. - - ```javascript - { - "excludeFiles": ["node_modules/**", "bower_components/**"], - - "requireCurlyBraces": [ - "if", - "else", - "for", - "while", - "do", - "try", - "catch" - ], - "requireOperatorBeforeLineBreak": true, - "requireCamelCaseOrUpperCaseIdentifiers": true, - "maximumLineLength": { - "value": 100, - "allowComments": true, - "allowRegex": true - }, - "validateIndentation": 4, - "validateQuoteMarks": "'", - - "disallowMultipleLineStrings": true, - "disallowMixedSpacesAndTabs": true, - "disallowTrailingWhitespace": true, - "disallowSpaceAfterPrefixUnaryOperators": true, - "disallowMultipleVarDecl": null, - - "requireSpaceAfterKeywords": [ - "if", - "else", - "for", - "while", - "do", - "switch", - "return", - "try", - "catch" - ], - "requireSpaceBeforeBinaryOperators": [ - "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", - "&=", "|=", "^=", "+=", - - "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", - "|", "^", "&&", "||", "===", "==", ">=", - "<=", "<", ">", "!=", "!==" - ], - "requireSpaceAfterBinaryOperators": true, - "requireSpacesInConditionalExpression": true, - "requireSpaceBeforeBlockStatements": true, - "requireLineFeedAtFileEnd": true, - "disallowSpacesInsideObjectBrackets": "all", - "disallowSpacesInsideArrayBrackets": "all", - "disallowSpacesInsideParentheses": true, - - "jsDoc": { - "checkAnnotations": true, - "checkParamNames": true, - "requireParamTypes": true, - "checkReturnTypes": true, - "checkTypes": true - }, - - "disallowMultipleLineBreaks": true, - - "disallowCommaBeforeLineBreak": null, - "disallowDanglingUnderscores": null, - "disallowEmptyBlocks": null, - "disallowTrailingComma": null, - "requireCommaBeforeLineBreak": null, - "requireDotNotation": null, - "requireMultipleVarDecl": null, - "requireParenthesesAroundIIFE": true - } - ``` - -**[Back to top](#table-of-contents)** - -## Constants - -### Vendor Globals -###### [Style [Y240](#style-y240)] - - - Create an Angular Constant for vendor libraries' global variables. - - *Why?*: Provides a way to inject vendor libraries that otherwise are globals. This improves code testability by allowing you to more easily know what the dependencies of your components are (avoids leaky abstractions). It also allows you to mock these dependencies, where it makes sense. - - ```javascript - // constants.js - - /* global toastr:false, moment:false */ - (function() { - 'use strict'; - - angular - .module('app.core') - .constant('toastr', toastr) - .constant('moment', moment); - })(); - ``` - -###### [Style [Y241](#style-y241)] - - - Use constants for values that do not change and do not come from another service. When constants are used only for a module that may be reused in multiple applications, place constants in a file per module named after the module. Until this is required, keep constants in the main module in a `constants.js` file. - - *Why?*: A value that may change, even infrequently, should be retrieved from a service so you do not have to change the source code. For example, a url for a data service could be placed in a constants but a better place would be to load it from a web service. - - *Why?*: Constants can be injected into any angular component, including providers. - - *Why?*: When an application is separated into modules that may be reused in other applications, each stand-alone module should be able to operate on its own including any dependent constants. - - ```javascript - // Constants used by the entire app - angular - .module('app.core') - .constant('moment', moment); - - // Constants used only by the sales module - angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); - ``` - -**[Back to top](#table-of-contents)** - -## File Templates and Snippets -Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. - -### Sublime Text -###### [Style [Y250](#style-y250)] - - - Angular snippets that follow these styles and guidelines. - - - Download the [Sublime Angular snippets](assets/sublime-angular-snippets?raw=true) - - Place it in your Packages folder - - Restart Sublime - - In a JavaScript file type these commands followed by a `TAB` - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Visual Studio -###### [Style [Y251](#style-y251)] - - - Angular file templates that follow these styles and guidelines can be found at [SideWaffle](http://www.sidewaffle.com) - - - Download the [SideWaffle](http://www.sidewaffle.com) Visual Studio extension (vsix file) - - Run the vsix file - - Restart Visual Studio - -### WebStorm -###### [Style [Y252](#style-y252)] - - - Angular live templates that follow these styles and guidelines. - - - Download the [webstorm-angular-live-templates.xml](assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml?raw=true) - - Place it in your [templates folder](https://www.jetbrains.com/webstorm/help/project-and-ide-settings.html) - - Restart WebStorm - - In a JavaScript file type these commands followed by a `TAB`: - - ```javascript - // These are full file snippets containing an IIFE - ngapp // creates an Angular module setter - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngfilter // creates an Angular filter - ngservice // creates an Angular service - - // These are partial snippets intended to be chained - ngconfig // defines a configuration phase function - ngmodule // creates an Angular module getter - ngroute // defines an Angular ngRoute 'when' definition - ngrun // defines a run phase function - ngstate // creates an Angular UI Router state definition - ``` - - *Individual templates are also available for download within the [webstorm-angular-live-templates](assets/webstorm-angular-live-templates?raw=true) folder* - -### Atom -###### [Style [Y253](#style-y253)] - - - Angular snippets that follow these styles and guidelines. - ``` - apm install angularjs-styleguide-snippets - ``` - or - - Open Atom, then open the Package Manager (Packages -> Settings View -> Install Packages/Themes) - - Search for the package 'angularjs-styleguide-snippets' - - Click 'Install' to install the package - - - In a JavaScript file type these commands followed by a `TAB` - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Brackets -###### [Style [Y254](#style-y254)] - - - Angular snippets that follow these styles and guidelines. - - Download the [Brackets Angular snippets](assets/brackets-angular-snippets.yaml?raw=true) - - Brackets Extension manager ( File > Extension manager ) - - Install ['Brackets Snippets (by edc)'](https://github.com/chuyik/brackets-snippets) - - Click the light bulb in brackets' right gutter - - Click `Settings` and then `Import` - - Choose the file and select to skip or override - - Click `Start Import` - - - In a JavaScript file type these commands followed by a `TAB` - - ```javascript - // These are full file snippets containing an IIFE - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngapp // creates an Angular module setter - ngservice // creates an Angular service - ngfilter // creates an Angular filter - - // These are partial snippets intended to chained - ngmodule // creates an Angular module getter - ngstate // creates an Angular UI Router state definition - ngconfig // defines a configuration phase function - ngrun // defines a run phase function - ngwhen // defines an Angular ngRoute 'when' definition - ngtranslate // uses $translate service with its promise - ``` - -### vim -###### [Style [Y255](#style-y255)] - - - vim snippets that follow these styles and guidelines. - - - Download the [vim Angular snippets](assets/vim-angular-snippets?raw=true) - - set [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) - - copy snippets to snippet directory - - - vim UltiSnips snippets that follow these styles and guidelines. - - - Download the [vim Angular UltiSnips snippets](assets/vim-angular-ultisnips?raw=true) - - set [UltiSnips](https://github.com/SirVer/ultisnips) - - copy snippets to UltiSnips directory - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ngfilter // creates an Angular filter - ``` - -### Visual Studio Code - -###### [Style [Y256](#style-y256)] - - - [Visual Studio Code](https://code.visualstudio.com/) snippets that follow these styles and guidelines. - - - Download the [VS Code Angular snippets](assets/vscode-snippets/javascript.json?raw=true) - - copy snippets to snippet directory, or alternatively copy and paste the snippets into your existing ones - - ```javascript - ngcontroller // creates an Angular controller - ngdirective // creates an Angular directive - ngfactory // creates an Angular factory - ngmodule // creates an Angular module - ngservice // creates an Angular service - ``` - -**[Back to top](#table-of-contents)** - -## Yeoman Generator -###### [Style [Y260](#style-y260)] - -You can use the [HotTowel yeoman generator](http://jpapa.me/yohottowel) to create an app that serves as a starting point for Angular that follows this style guide. - -1. Install generator-hottowel - - ``` - npm install -g generator-hottowel - ``` - -2. Create a new folder and change directory to it - - ``` - mkdir myapp - cd myapp - ``` - -3. Run the generator - - ``` - yo hottowel helloWorld - ``` - -**[Back to top](#table-of-contents)** - -## Routing -Client-side routing is important for creating a navigation flow between views and composing views that are made of many smaller templates and directives. - -###### [Style [Y270](#style-y270)] - - - Use the [AngularUI Router](http://angular-ui.github.io/ui-router/) for client-side routing. - - *Why?*: UI Router offers all the features of the Angular router plus a few additional ones including nested routes and states. - - *Why?*: The syntax is quite similar to the Angular router and is easy to migrate to UI Router. - - - Note: You can use a provider such as the `routerHelperProvider` shown below to help configure states across files, during the run phase. - - ```javascript - // customers.routes.js - angular - .module('app.customers') - .run(appRun); - - /* @ngInject */ - function appRun(routerHelper) { - routerHelper.configureStates(getStates()); - } - - function getStates() { - return [ - { - state: 'customer', - config: { - abstract: true, - template: '', - url: '/customer' - } - } - ]; - } - ``` - - ```javascript - // routerHelperProvider.js - angular - .module('blocks.router') - .provider('routerHelper', routerHelperProvider); - - routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; - /* @ngInject */ - function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { - /* jshint validthis:true */ - this.$get = RouterHelper; - - $locationProvider.html5Mode(true); - - RouterHelper.$inject = ['$state']; - /* @ngInject */ - function RouterHelper($state) { - var hasOtherwise = false; - - var service = { - configureStates: configureStates, - getStates: getStates - }; - - return service; - - /////////////// - - function configureStates(states, otherwisePath) { - states.forEach(function(state) { - $stateProvider.state(state.state, state.config); - }); - if (otherwisePath && !hasOtherwise) { - hasOtherwise = true; - $urlRouterProvider.otherwise(otherwisePath); - } - } - - function getStates() { return $state.get(); } - } - } - ``` - -###### [Style [Y271](#style-y271)] - - - Define routes for views in the module where they exist. Each module should contain the routes for the views in the module. - - *Why?*: Each module should be able to stand on its own. - - *Why?*: When removing a module or adding a module, the app will only contain routes that point to existing views. - - *Why?*: This makes it easy to enable or disable portions of an application without concern over orphaned routes. - -**[Back to top](#table-of-contents)** - -## Task Automation -Use [Gulp](http://gulpjs.com) or [Grunt](http://gruntjs.com) for creating automated tasks. Gulp leans to code over configuration while Grunt leans to configuration over code. I personally prefer Gulp as I feel it is easier to read and write, but both are excellent. - -> Learn more about gulp and patterns for task automation in my [Gulp Pluralsight course](http://jpapa.me/gulpps) - -###### [Style [Y400](#style-y400)] - - - Use task automation to list module definition files `*.module.js` before all other application JavaScript files. - - *Why?*: Angular needs the module definitions to be registered before they are used. - - *Why?*: Naming modules with a specific pattern such as `*.module.js` makes it easy to grab them with a glob and list them first. - - ```javascript - var clientApp = './src/client/app/'; - - // Always grab module files first - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[Back to top](#table-of-contents)** - -## Filters - -###### [Style [Y420](#style-y420)] - - - Avoid using filters for scanning all properties of a complex object graph. Use filters for select properties. - - *Why?*: Filters can easily be abused and negatively affect performance if not used wisely, for example when a filter hits a large and deep object graph. - -**[Back to top](#table-of-contents)** - -## Angular docs -For anything else, API reference, check the [Angular documentation](//docs.angularjs.org/api). +Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My most excellent friend Ward has helped influence the ultimate evolution of these guides. ## Contributing - Open an issue first to discuss potential changes/additions. If you have questions with the guide, feel free to leave them as issues in the repository. If you find a typo, create a pull request. The idea is to keep the content up to date and use github’s native feature to help tell the story with issues and PR’s, which are all searchable via google. Why? Because odds are if you have a question, someone else does too! You can learn more here at about how to contribute. *By contributing to this repository you are agreeing to make your content available subject to the license of this repository.* @@ -3323,4 +64,4 @@ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -**[Back to top](#table-of-contents)** +**[Back to top](#Angular-Style-Guide)** diff --git a/a1/README.md b/a1/README.md new file mode 100644 index 00000000..4b42bd5e --- /dev/null +++ b/a1/README.md @@ -0,0 +1,3285 @@ +# Angular 1 Style Guide + +## Angular Team Endorsed +Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. + +## Purpose +*Opinionated Angular style guide for teams by [@john_papa](//twitter.com/john_papa)* + +If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular applications, then step right in. These styles are based on my development experience with [Angular](//angularjs.org), presentations, [Pluralsight training courses](http://app.pluralsight.com/author/john-papa) and working in teams. + +The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions I use and, more importantly, why I choose them. + +>If you like this guide, check out my [Angular Patterns: Clean Code](http://jpapa.me/ngclean) course at Pluralsight which is a companion to this guide. + + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + +## Community Awesomeness and Credit +Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. As such, Angular expert Todd Motto and I have collaborated on many styles and conventions. We agree on most, and some we diverge. I encourage you to check out [Todd's guidelines](https://github.com/toddmotto/angular-styleguide) to get a sense for his approach and how it compares. + +Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My friend Ward has certainly helped influence the ultimate evolution of this guide. + +## See the Styles in a Sample App +While this guide explains the *what*, *why* and *how*, I find it helpful to see them in practice. This guide is accompanied by a sample application that follows these styles and patterns. You can find the [sample application (named modular) here](https://github.com/johnpapa/ng-demos) in the `modular` folder. Feel free to grab it, clone it, or fork it. [Instructions on running it are in its readme](https://github.com/johnpapa/ng-demos/tree/master/modular). + +##Translations +[Translations of this Angular style guide](https://github.com/johnpapa/angular-styleguide/tree/master/a1/i18n) are maintained by the community and can be found here. + +## Table of Contents + + 1. [Single Responsibility](#single-responsibility) + 1. [IIFE](#iife) + 1. [Modules](#modules) + 1. [Controllers](#controllers) + 1. [Services](#services) + 1. [Factories](#factories) + 1. [Data Services](#data-services) + 1. [Directives](#directives) + 1. [Resolving Promises](#resolving-promises) + 1. [Manual Annotating for Dependency Injection](#manual-annotating-for-dependency-injection) + 1. [Minification and Annotation](#minification-and-annotation) + 1. [Exception Handling](#exception-handling) + 1. [Naming](#naming) + 1. [Application Structure LIFT Principle](#application-structure-lift-principle) + 1. [Application Structure](#application-structure) + 1. [Modularity](#modularity) + 1. [Startup Logic](#startup-logic) + 1. [Angular $ Wrapper Services](#angular--wrapper-services) + 1. [Testing](#testing) + 1. [Animations](#animations) + 1. [Comments](#comments) + 1. [JSHint](#js-hint) + 1. [JSCS](#jscs) + 1. [Constants](#constants) + 1. [File Templates and Snippets](#file-templates-and-snippets) + 1. [Yeoman Generator](#yeoman-generator) + 1. [Routing](#routing) + 1. [Task Automation](#task-automation) + 1. [Filters](#filters) + 1. [Angular Docs](#angular-docs) + +## Single Responsibility + +### Rule of 1 +###### [Style [Y001](#style-y001)] + + - Define 1 component per file, recommended to be less than 500 lines of code. + + *Why?*: One component per file promotes easier unit testing and mocking. + + *Why?*: One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control. + + *Why?*: One component per file avoids hidden bugs that often arise when combining components in a file where they may share variables, create unwanted closures, or unwanted coupling with dependencies. + + The following example defines the `app` module and its dependencies, defines a controller, and defines a factory all in the same file. + + ```javascript + /* avoid */ + angular + .module('app', ['ngRoute']) + .controller('SomeController', SomeController) + .factory('someFactory', someFactory); + + function SomeController() { } + + function someFactory() { } + ``` + + The same components are now separated into their own files. + + ```javascript + /* recommended */ + + // app.module.js + angular + .module('app', ['ngRoute']); + ``` + + ```javascript + /* recommended */ + + // some.controller.js + angular + .module('app') + .controller('SomeController', SomeController); + + function SomeController() { } + ``` + + ```javascript + /* recommended */ + + // someFactory.js + angular + .module('app') + .factory('someFactory', someFactory); + + function someFactory() { } + ``` + +**[Back to top](#table-of-contents)** + +### Small Functions +###### [Style [Y002](#style-y002)] + + - Define small functions, no more than 75 LOC (less is better). + + *Why?*: Small functions are easier to test, especially when they do one thing and serve one purpose. + + *Why?*: Small functions promote reuse. + + *Why?*: Small functions are easier to read. + + *Why?*: Small functions are easier to maintain. + + *Why?*: Small functions help avoid hidden bugs that come with large functions that share variables with external scope, create unwanted closures, or unwanted coupling with dependencies. + +**[Back to top](#table-of-contents)** + +## IIFE +### JavaScript Closures +###### [Style [Y010](#style-y010)] + + - Wrap Angular components in an Immediately Invoked Function Expression (IIFE). + + *Why?*: An IIFE removes variables from the global scope. This helps prevent variables and function declarations from living longer than expected in the global scope, which also helps avoid variable collisions. + + *Why?*: When your code is minified and bundled into a single file for deployment to a production server, you could have collisions of variables and many global variables. An IIFE protects you against both of these by providing variable scope for each file. + + ```javascript + /* avoid */ + // logger.js + angular + .module('app') + .factory('logger', logger); + + // logger function is added as a global variable + function logger() { } + + // storage.js + angular + .module('app') + .factory('storage', storage); + + // storage function is added as a global variable + function storage() { } + ``` + + ```javascript + /** + * recommended + * + * no globals are left behind + */ + + // logger.js + (function() { + 'use strict'; + + angular + .module('app') + .factory('logger', logger); + + function logger() { } + })(); + + // storage.js + (function() { + 'use strict'; + + angular + .module('app') + .factory('storage', storage); + + function storage() { } + })(); + ``` + + - Note: For brevity only, the rest of the examples in this guide may omit the IIFE syntax. + + - Note: IIFE's prevent test code from reaching private members like regular expressions or helper functions which are often good to unit test directly on their own. However you can test these through accessible members or by exposing them through their own component. For example placing helper functions, regular expressions or constants in their own factory or constant. + +**[Back to top](#table-of-contents)** + +## Modules + +### Avoid Naming Collisions +###### [Style [Y020](#style-y020)] + + - Use unique naming conventions with separators for sub-modules. + + *Why?*: Unique names help avoid module name collisions. Separators help define modules and their submodule hierarchy. For example `app` may be your root module while `app.dashboard` and `app.users` may be modules that are used as dependencies of `app`. + +### Definitions (aka Setters) +###### [Style [Y021](#style-y021)] + + - Declare modules without a variable using the setter syntax. + + *Why?*: With 1 component per file, there is rarely a need to introduce a variable for the module. + + ```javascript + /* avoid */ + var app = angular.module('app', [ + 'ngAnimate', + 'ngRoute', + 'app.shared', + 'app.dashboard' + ]); + ``` + + Instead use the simple setter syntax. + + ```javascript + /* recommended */ + angular + .module('app', [ + 'ngAnimate', + 'ngRoute', + 'app.shared', + 'app.dashboard' + ]); + ``` + +### Getters +###### [Style [Y022](#style-y022)] + + - When using a module, avoid using a variable and instead use chaining with the getter syntax. + + *Why?*: This produces more readable code and avoids variable collisions or leaks. + + ```javascript + /* avoid */ + var app = angular.module('app'); + app.controller('SomeController', SomeController); + + function SomeController() { } + ``` + + ```javascript + /* recommended */ + angular + .module('app') + .controller('SomeController', SomeController); + + function SomeController() { } + ``` + +### Setting vs Getting +###### [Style [Y023](#style-y023)] + + - Only set once and get for all other instances. + + *Why?*: A module should only be created once, then retrieved from that point and after. + + ```javascript + /* recommended */ + + // to set a module + angular.module('app', []); + + // to get a module + angular.module('app'); + ``` + +### Named vs Anonymous Functions +###### [Style [Y024](#style-y024)] + + - Use named functions instead of passing an anonymous function in as a callback. + + *Why?*: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code. + + ```javascript + /* avoid */ + angular + .module('app') + .controller('DashboardController', function() { }) + .factory('logger', function() { }); + ``` + + ```javascript + /* recommended */ + + // dashboard.js + angular + .module('app') + .controller('DashboardController', DashboardController); + + function DashboardController() { } + ``` + + ```javascript + // logger.js + angular + .module('app') + .factory('logger', logger); + + function logger() { } + ``` + +**[Back to top](#table-of-contents)** + +## Controllers + +### controllerAs View Syntax +###### [Style [Y030](#style-y030)] + + - Use the [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) syntax over the `classic controller with $scope` syntax. + + *Why?*: Controllers are constructed, "newed" up, and provide a single new instance, and the `controllerAs` syntax is closer to that of a JavaScript constructor than the `classic $scope syntax`. + + *Why?*: It promotes the use of binding to a "dotted" object in the View (e.g. `customer.name` instead of `name`), which is more contextual, easier to read, and avoids any reference issues that may occur without "dotting". + + *Why?*: Helps avoid using `$parent` calls in Views with nested controllers. + + ```html + +
+ {{ name }} +
+ ``` + + ```html + +
+ {{ customer.name }} +
+ ``` + +### controllerAs Controller Syntax +###### [Style [Y031](#style-y031)] + + - Use the `controllerAs` syntax over the `classic controller with $scope` syntax. + + - The `controllerAs` syntax uses `this` inside controllers which gets bound to `$scope` + + *Why?*: `controllerAs` is syntactic sugar over `$scope`. You can still bind to the View and still access `$scope` methods. + + *Why?*: Helps avoid the temptation of using `$scope` methods inside a controller when it may otherwise be better to avoid them or move the method to a factory, and reference them from the controller. Consider using `$scope` in a controller only when needed. For example when publishing and subscribing events using [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), or [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on). + + ```javascript + /* avoid */ + function CustomerController($scope) { + $scope.name = {}; + $scope.sendMessage = function() { }; + } + ``` + + ```javascript + /* recommended - but see next section */ + function CustomerController() { + this.name = {}; + this.sendMessage = function() { }; + } + ``` + +### controllerAs with vm +###### [Style [Y032](#style-y032)] + + - Use a capture variable for `this` when using the `controllerAs` syntax. Choose a consistent variable name such as `vm`, which stands for ViewModel. + + *Why?*: The `this` keyword is contextual and when used within a function inside a controller may change its context. Capturing the context of `this` avoids encountering this problem. + + ```javascript + /* avoid */ + function CustomerController() { + this.name = {}; + this.sendMessage = function() { }; + } + ``` + + ```javascript + /* recommended */ + function CustomerController() { + var vm = this; + vm.name = {}; + vm.sendMessage = function() { }; + } + ``` + + Note: You can avoid any [jshint](http://jshint.com/) warnings by placing the comment above the line of code. However it is not needed when the function is named using UpperCasing, as this convention means it is a constructor function, which is what a controller is in Angular. + + ```javascript + /* jshint validthis: true */ + var vm = this; + ``` + + Note: When creating watches in a controller using `controller as`, you can watch the `vm.*` member using the following syntax. (Create watches with caution as they add more load to the digest cycle.) + + ```html + + ``` + + ```javascript + function SomeController($scope, $log) { + var vm = this; + vm.title = 'Some Title'; + + $scope.$watch('vm.title', function(current, original) { + $log.info('vm.title was %s', original); + $log.info('vm.title is now %s', current); + }); + } + ``` + + Note: When working with larger codebases, using a more descriptive name can help ease cognitive overhead & searchability. Avoid overly verbose names that are cumbersome to type. + + ```html + + + ``` + + ```html + + + ``` + +### Bindable Members Up Top +###### [Style [Y033](#style-y033)] + + - Place bindable members at the top of the controller, alphabetized, and not spread through the controller code. + + *Why?*: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the controller can be bound and used in the View. + + *Why?*: Setting anonymous functions in-line can be easy, but when those functions are more than 1 line of code they can reduce the readability. Defining the functions below the bindable members (the functions will be hoisted) moves the implementation details down, keeps the bindable members up top, and makes it easier to read. + + ```javascript + /* avoid */ + function SessionsController() { + var vm = this; + + vm.gotoSession = function() { + /* ... */ + }; + vm.refresh = function() { + /* ... */ + }; + vm.search = function() { + /* ... */ + }; + vm.sessions = []; + vm.title = 'Sessions'; + } + ``` + + ```javascript + /* recommended */ + function SessionsController() { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = refresh; + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + + //////////// + + function gotoSession() { + /* */ + } + + function refresh() { + /* */ + } + + function search() { + /* */ + } + } + ``` + + ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) + + Note: If the function is a 1 liner consider keeping it right up top, as long as readability is not affected. + + ```javascript + /* avoid */ + function SessionsController(data) { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = function() { + /** + * lines + * of + * code + * affects + * readability + */ + }; + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + } + ``` + + ```javascript + /* recommended */ + function SessionsController(sessionDataService) { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = sessionDataService.refresh; // 1 liner is OK + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + } + ``` + +### Function Declarations to Hide Implementation Details +###### [Style [Y034](#style-y034)] + + - Use function declarations to hide implementation details. Keep your bindable members up top. When you need to bind a function in a controller, point it to a function declaration that appears later in the file. This is tied directly to the section Bindable Members Up Top. For more details see [this post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code/). + + *Why?*: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the controller can be bound and used in the View. (Same as above.) + + *Why?*: Placing the implementation details of a function later in the file moves that complexity out of view so you can see the important stuff up top. + + *Why?*: Function declaration are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions). + + *Why?*: You never have to worry with function declarations that moving `var a` before `var b` will break your code because `a` depends on `b`. + + *Why?*: Order is critical with function expressions + + ```javascript + /** + * avoid + * Using function expressions. + */ + function AvengersController(avengersService, logger) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + var activate = function() { + return getAvengers().then(function() { + logger.info('Activated Avengers View'); + }); + } + + var getAvengers = function() { + return avengersService.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + + vm.getAvengers = getAvengers; + + activate(); + } + ``` + + Notice that the important stuff is scattered in the preceding example. In the example below, notice that the important stuff is up top. For example, the members bound to the controller such as `vm.avengers` and `vm.title`. The implementation details are down below. This is just easier to read. + + ```javascript + /* + * recommend + * Using function declarations + * and bindable members up top. + */ + function AvengersController(avengersService, logger) { + var vm = this; + vm.avengers = []; + vm.getAvengers = getAvengers; + vm.title = 'Avengers'; + + activate(); + + function activate() { + return getAvengers().then(function() { + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + return avengersService.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Defer Controller Logic to Services +###### [Style [Y035](#style-y035)] + + - Defer logic in a controller by delegating to services and factories. + + *Why?*: Logic may be reused by multiple controllers when placed within a service and exposed via a function. + + *Why?*: Logic in a service can more easily be isolated in a unit test, while the calling logic in the controller can be easily mocked. + + *Why?*: Removes dependencies and hides implementation details from the controller. + + *Why?*: Keeps the controller slim, trim, and focused. + + ```javascript + + /* avoid */ + function OrderController($http, $q, config, userInfo) { + var vm = this; + vm.checkCredit = checkCredit; + vm.isCreditOk; + vm.total = 0; + + function checkCredit() { + var settings = {}; + // Get the credit service base URL from config + // Set credit service required headers + // Prepare URL query string or data object with request data + // Add user-identifying info so service gets the right credit limit for this user. + // Use JSONP for this browser if it doesn't support CORS + return $http.get(settings) + .then(function(data) { + // Unpack JSON data in the response object + // to find maxRemainingAmount + vm.isCreditOk = vm.total <= maxRemainingAmount + }) + .catch(function(error) { + // Interpret error + // Cope w/ timeout? retry? try alternate service? + // Re-reject with appropriate error for a user to see + }); + }; + } + ``` + + ```javascript + /* recommended */ + function OrderController(creditService) { + var vm = this; + vm.checkCredit = checkCredit; + vm.isCreditOk; + vm.total = 0; + + function checkCredit() { + return creditService.isOrderTotalOk(vm.total) + .then(function(isOk) { vm.isCreditOk = isOk; }) + .catch(showError); + }; + } + ``` + +### Keep Controllers Focused +###### [Style [Y037](#style-y037)] + + - Define a controller for a view, and try not to reuse the controller for other views. Instead, move reusable logic to factories and keep the controller simple and focused on its view. + + *Why?*: Reusing controllers with several views is brittle and good end-to-end (e2e) test coverage is required to ensure stability across large applications. + +### Assigning Controllers +###### [Style [Y038](#style-y038)] + + - When a controller must be paired with a view and either component may be re-used by other controllers or views, define controllers along with their routes. + + Note: If a View is loaded via another means besides a route, then use the `ng-controller="Avengers as vm"` syntax. + + *Why?*: Pairing the controller in the route allows different routes to invoke different pairs of controllers and views. When controllers are assigned in the view using [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), that view is always associated with the same controller. + + ```javascript + /* avoid - when using with a route and dynamic pairing is desired */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html' + }); + } + ``` + + ```html + +
+
+ ``` + + ```javascript + /* recommended */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'Avengers', + controllerAs: 'vm' + }); + } + ``` + + ```html + +
+
+ ``` + +**[Back to top](#table-of-contents)** + +## Services + +### Singletons +###### [Style [Y040](#style-y040)] + + - Services are instantiated with the `new` keyword, use `this` for public methods and variables. Since these are so similar to factories, use a factory instead for consistency. + + Note: [All Angular services are singletons](https://docs.angularjs.org/guide/services). This means that there is only one instance of a given service per injector. + + ```javascript + // service + angular + .module('app') + .service('logger', logger); + + function logger() { + this.logError = function(msg) { + /* */ + }; + } + ``` + + ```javascript + // factory + angular + .module('app') + .factory('logger', logger); + + function logger() { + return { + logError: function(msg) { + /* */ + } + }; + } + ``` + +**[Back to top](#table-of-contents)** + +## Factories + +### Single Responsibility +###### [Style [Y050](#style-y050)] + + - Factories should have a [single responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle), that is encapsulated by its context. Once a factory begins to exceed that singular purpose, a new factory should be created. + +### Singletons +###### [Style [Y051](#style-y051)] + + - Factories are singletons and return an object that contains the members of the service. + + Note: [All Angular services are singletons](https://docs.angularjs.org/guide/services). + +### Accessible Members Up Top +###### [Style [Y052](#style-y052)] + + - Expose the callable members of the service (its interface) at the top, using a technique derived from the [Revealing Module Pattern](https://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). + + *Why?*: Placing the callable members at the top makes it easy to read and helps you instantly identify which members of the service can be called and must be unit tested (and/or mocked). + + *Why?*: This is especially helpful when the file gets longer as it helps avoid the need to scroll to see what is exposed. + + *Why?*: Setting functions as you go can be easy, but when those functions are more than 1 line of code they can reduce the readability and cause more scrolling. Defining the callable interface via the returned service moves the implementation details down, keeps the callable interface up top, and makes it easier to read. + + ```javascript + /* avoid */ + function dataService() { + var someValue = ''; + function save() { + /* */ + }; + function validate() { + /* */ + }; + + return { + save: save, + someValue: someValue, + validate: validate + }; + } + ``` + + ```javascript + /* recommended */ + function dataService() { + var someValue = ''; + var service = { + save: save, + someValue: someValue, + validate: validate + }; + return service; + + //////////// + + function save() { + /* */ + }; + + function validate() { + /* */ + }; + } + ``` + + This way bindings are mirrored across the host object, primitive values cannot update alone using the revealing module pattern. + + ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-2.png) + +### Function Declarations to Hide Implementation Details +###### [Style [Y053](#style-y053)] + + - Use function declarations to hide implementation details. Keep your accessible members of the factory up top. Point those to function declarations that appears later in the file. For more details see [this post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). + + *Why?*: Placing accessible members at the top makes it easy to read and helps you instantly identify which functions of the factory you can access externally. + + *Why?*: Placing the implementation details of a function later in the file moves that complexity out of view so you can see the important stuff up top. + + *Why?*: Function declaration are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions). + + *Why?*: You never have to worry with function declarations that moving `var a` before `var b` will break your code because `a` depends on `b`. + + *Why?*: Order is critical with function expressions + + ```javascript + /** + * avoid + * Using function expressions + */ + function dataservice($http, $location, $q, exception, logger) { + var isPrimed = false; + var primePromise; + + var getAvengers = function() { + // implementation details go here + }; + + var getAvengerCount = function() { + // implementation details go here + }; + + var getAvengersCast = function() { + // implementation details go here + }; + + var prime = function() { + // implementation details go here + }; + + var ready = function(nextPromises) { + // implementation details go here + }; + + var service = { + getAvengersCast: getAvengersCast, + getAvengerCount: getAvengerCount, + getAvengers: getAvengers, + ready: ready + }; + + return service; + } + ``` + + ```javascript + /** + * recommended + * Using function declarations + * and accessible members up top. + */ + function dataservice($http, $location, $q, exception, logger) { + var isPrimed = false; + var primePromise; + + var service = { + getAvengersCast: getAvengersCast, + getAvengerCount: getAvengerCount, + getAvengers: getAvengers, + ready: ready + }; + + return service; + + //////////// + + function getAvengers() { + // implementation details go here + } + + function getAvengerCount() { + // implementation details go here + } + + function getAvengersCast() { + // implementation details go here + } + + function prime() { + // implementation details go here + } + + function ready(nextPromises) { + // implementation details go here + } + } + ``` + +**[Back to top](#table-of-contents)** + +## Data Services + +### Separate Data Calls +###### [Style [Y060](#style-y060)] + + - Refactor logic for making data operations and interacting with data to a factory. Make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations. + + *Why?*: The controller's responsibility is for the presentation and gathering of information for the view. It should not care how it gets the data, just that it knows who to ask for it. Separating the data services moves the logic on how to get it to the data service, and lets the controller be simpler and more focused on the view. + + *Why?*: This makes it easier to test (mock or real) the data calls when testing a controller that uses a data service. + + *Why?*: Data service implementation may have very specific code to handle the data repository. This may include headers, how to talk to the data, or other services such as `$http`. Separating the logic into a data service encapsulates this logic in a single place hiding the implementation from the outside consumers (perhaps a controller), also making it easier to change the implementation. + + ```javascript + /* recommended */ + + // dataservice factory + angular + .module('app.core') + .factory('dataservice', dataservice); + + dataservice.$inject = ['$http', 'logger']; + + function dataservice($http, logger) { + return { + getAvengers: getAvengers + }; + + function getAvengers() { + return $http.get('/api/maa') + .then(getAvengersComplete) + .catch(getAvengersFailed); + + function getAvengersComplete(response) { + return response.data.results; + } + + function getAvengersFailed(error) { + logger.error('XHR Failed for getAvengers.' + error.data); + } + } + } + ``` + + Note: The data service is called from consumers, such as a controller, hiding the implementation from the consumers, as shown below. + + ```javascript + /* recommended */ + + // controller calling the dataservice factory + angular + .module('app.avengers') + .controller('AvengersController', AvengersController); + + AvengersController.$inject = ['dataservice', 'logger']; + + function AvengersController(dataservice, logger) { + var vm = this; + vm.avengers = []; + + activate(); + + function activate() { + return getAvengers().then(function() { + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + return dataservice.getAvengers() + .then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Return a Promise from Data Calls +###### [Style [Y061](#style-y061)] + + - When calling a data service that returns a promise such as `$http`, return a promise in your calling function too. + + *Why?*: You can chain the promises together and take further action after the data call completes and resolves or rejects the promise. + + ```javascript + /* recommended */ + + activate(); + + function activate() { + /** + * Step 1 + * Ask the getAvengers function for the + * avenger data and wait for the promise + */ + return getAvengers().then(function() { + /** + * Step 4 + * Perform an action on resolve of final promise + */ + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + /** + * Step 2 + * Ask the data service for the data and wait + * for the promise + */ + return dataservice.getAvengers() + .then(function(data) { + /** + * Step 3 + * set the data and resolve the promise + */ + vm.avengers = data; + return vm.avengers; + }); + } + ``` + +**[Back to top](#table-of-contents)** + +## Directives +### Limit 1 Per File +###### [Style [Y070](#style-y070)] + + - Create one directive per file. Name the file for the directive. + + *Why?*: It is easy to mash all the directives in one file, but difficult to then break those out so some are shared across apps, some across modules, some just for one module. + + *Why?*: One directive per file is easy to maintain. + + > Note: "**Best Practice**: Directives should clean up after themselves. You can use `element.on('$destroy', ...)` or `scope.$on('$destroy', ...)` to run a clean-up function when the directive is removed" ... from the Angular documentation. + + ```javascript + /* avoid */ + /* directives.js */ + + angular + .module('app.widgets') + + /* order directive that is specific to the order module */ + .directive('orderCalendarRange', orderCalendarRange) + + /* sales directive that can be used anywhere across the sales app */ + .directive('salesCustomerInfo', salesCustomerInfo) + + /* spinner directive that can be used anywhere across apps */ + .directive('sharedSpinner', sharedSpinner); + + function orderCalendarRange() { + /* implementation details */ + } + + function salesCustomerInfo() { + /* implementation details */ + } + + function sharedSpinner() { + /* implementation details */ + } + ``` + + ```javascript + /* recommended */ + /* calendar-range.directive.js */ + + /** + * @desc order directive that is specific to the order module at a company named Acme + * @example
+ */ + angular + .module('sales.order') + .directive('acmeOrderCalendarRange', orderCalendarRange); + + function orderCalendarRange() { + /* implementation details */ + } + ``` + + ```javascript + /* recommended */ + /* customer-info.directive.js */ + + /** + * @desc sales directive that can be used anywhere across the sales app at a company named Acme + * @example
+ */ + angular + .module('sales.widgets') + .directive('acmeSalesCustomerInfo', salesCustomerInfo); + + function salesCustomerInfo() { + /* implementation details */ + } + ``` + + ```javascript + /* recommended */ + /* spinner.directive.js */ + + /** + * @desc spinner directive that can be used anywhere across apps at a company named Acme + * @example
+ */ + angular + .module('shared.widgets') + .directive('acmeSharedSpinner', sharedSpinner); + + function sharedSpinner() { + /* implementation details */ + } + ``` + + Note: There are many naming options for directives, especially since they can be used in narrow or wide scopes. Choose one that makes the directive and its file name distinct and clear. Some examples are below, but see the [Naming](#naming) section for more recommendations. + +### Manipulate DOM in a Directive +###### [Style [Y072](#style-y072)] + + - When manipulating the DOM directly, use a directive. If alternative ways can be used such as using CSS to set styles or the [animation services](https://docs.angularjs.org/api/ngAnimate), Angular templating, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) or [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), then use those instead. For example, if the directive simply hides and shows, use ngHide/ngShow. + + *Why?*: DOM manipulation can be difficult to test, debug, and there are often better ways (e.g. CSS, animations, templates) + +### Provide a Unique Directive Prefix +###### [Style [Y073](#style-y073)] + + - Provide a short, unique and descriptive directive prefix such as `acmeSalesCustomerInfo` which would be declared in HTML as `acme-sales-customer-info`. + + *Why?*: The unique short prefix identifies the directive's context and origin. For example a prefix of `cc-` may indicate that the directive is part of a CodeCamper app while `acme-` may indicate a directive for the Acme company. + + Note: Avoid `ng-` as these are reserved for Angular directives. Research widely used directives to avoid naming conflicts, such as `ion-` for the [Ionic Framework](http://ionicframework.com/). + +### Restrict to Elements and Attributes +###### [Style [Y074](#style-y074)] + + - When creating a directive that makes sense as a stand-alone element, allow restrict `E` (custom element) and optionally restrict `A` (custom attribute). Generally, if it could be its own control, `E` is appropriate. General guideline is allow `EA` but lean towards implementing as an element when it's stand-alone and as an attribute when it enhances its existing DOM element. + + *Why?*: It makes sense. + + *Why?*: While we can allow the directive to be used as a class, if the directive is truly acting as an element it makes more sense as an element or at least as an attribute. + + Note: EA is the default for Angular 1.3 + + + ```html + +
+ ``` + + ```javascript + /* avoid */ + angular + .module('app.widgets') + .directive('myCalendarRange', myCalendarRange); + + function myCalendarRange() { + var directive = { + link: link, + templateUrl: '/template/is/located/here.html', + restrict: 'C' + }; + return directive; + + function link(scope, element, attrs) { + /* */ + } + } + ``` + + ```html + + +
+ ``` + + ```javascript + /* recommended */ + angular + .module('app.widgets') + .directive('myCalendarRange', myCalendarRange); + + function myCalendarRange() { + var directive = { + link: link, + templateUrl: '/template/is/located/here.html', + restrict: 'EA' + }; + return directive; + + function link(scope, element, attrs) { + /* */ + } + } + ``` + +### Directives and ControllerAs +###### [Style [Y075](#style-y075)] + + - Use `controller as` syntax with a directive to be consistent with using `controller as` with view and controller pairings. + + *Why?*: It makes sense and it's not difficult. + + Note: The directive below demonstrates some of the ways you can use scope inside of link and directive controllers, using controllerAs. I in-lined the template just to keep it all in one place. + + Note: Regarding dependency injection, see [Manually Identify Dependencies](#manual-annotating-for-dependency-injection). + + Note: Note that the directive's controller is outside the directive's closure. This style eliminates issues where the injection gets created as unreachable code after a `return`. + + ```html +
+ ``` + + ```javascript + angular + .module('app') + .directive('myExample', myExample); + + function myExample() { + var directive = { + restrict: 'EA', + templateUrl: 'app/feature/example.directive.html', + scope: { + max: '=' + }, + link: linkFunc, + controller: ExampleController, + // note: This would be 'ExampleController' (the exported controller name, as string) + // if referring to a defined controller in its separate file. + controllerAs: 'vm', + bindToController: true // because the scope is isolated + }; + + return directive; + + function linkFunc(scope, el, attr, ctrl) { + console.log('LINK: scope.min = %s *** should be undefined', scope.min); + console.log('LINK: scope.max = %s *** should be undefined', scope.max); + console.log('LINK: scope.vm.min = %s', scope.vm.min); + console.log('LINK: scope.vm.max = %s', scope.vm.max); + } + } + + ExampleController.$inject = ['$scope']; + + function ExampleController($scope) { + // Injecting $scope just for comparison + var vm = this; + + vm.min = 3; + + console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); + console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); + console.log('CTRL: vm.min = %s', vm.min); + console.log('CTRL: vm.max = %s', vm.max); + } + ``` + + ```html + +
hello world
+
max={{vm.max}}
+
min={{vm.min}}
+ ``` + + Note: You can also name the controller when you inject it into the link function and access directive attributes as properties of the controller. + + ```javascript + // Alternative to above example + function linkFunc(scope, el, attr, vm) { + console.log('LINK: scope.min = %s *** should be undefined', scope.min); + console.log('LINK: scope.max = %s *** should be undefined', scope.max); + console.log('LINK: vm.min = %s', vm.min); + console.log('LINK: vm.max = %s', vm.max); + } + ``` + +###### [Style [Y076](#style-y076)] + + - Use `bindToController = true` when using `controller as` syntax with a directive when you want to bind the outer scope to the directive's controller's scope. + + *Why?*: It makes it easy to bind outer scope to the directive's controller scope. + + Note: `bindToController` was introduced in Angular 1.3.0. + + ```html +
+ ``` + + ```javascript + angular + .module('app') + .directive('myExample', myExample); + + function myExample() { + var directive = { + restrict: 'EA', + templateUrl: 'app/feature/example.directive.html', + scope: { + max: '=' + }, + controller: ExampleController, + controllerAs: 'vm', + bindToController: true + }; + + return directive; + } + + function ExampleController() { + var vm = this; + vm.min = 3; + console.log('CTRL: vm.min = %s', vm.min); + console.log('CTRL: vm.max = %s', vm.max); + } + ``` + + ```html + +
hello world
+
max={{vm.max}}
+
min={{vm.min}}
+ ``` + +**[Back to top](#table-of-contents)** + +## Resolving Promises +### Controller Activation Promises +###### [Style [Y080](#style-y080)] + + - Resolve start-up logic for a controller in an `activate` function. + + *Why?*: Placing start-up logic in a consistent place in the controller makes it easier to locate, more consistent to test, and helps avoid spreading out the activation logic across the controller. + + *Why?*: The controller `activate` makes it convenient to re-use the logic for a refresh for the controller/View, keeps the logic together, gets the user to the View faster, makes animations easy on the `ng-view` or `ui-view`, and feels snappier to the user. + + Note: If you need to conditionally cancel the route before you start using the controller, use a [route resolve](#style-y081) instead. + + ```javascript + /* avoid */ + function AvengersController(dataservice) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + dataservice.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + ``` + + ```javascript + /* recommended */ + function AvengersController(dataservice) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + activate(); + + //////////// + + function activate() { + return dataservice.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Route Resolve Promises +###### [Style [Y081](#style-y081)] + + - When a controller depends on a promise to be resolved before the controller is activated, resolve those dependencies in the `$routeProvider` before the controller logic is executed. If you need to conditionally cancel a route before the controller is activated, use a route resolver. + + - Use a route resolve when you want to decide to cancel the route before ever transitioning to the View. + + *Why?*: A controller may require data before it loads. That data may come from a promise via a custom factory or [$http](https://docs.angularjs.org/api/ng/service/$http). Using a [route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) allows the promise to resolve before the controller logic executes, so it might take action based on that data from the promise. + + *Why?*: The code executes after the route and in the controller’s activate function. The View starts to load right away. Data binding kicks in when the activate promise resolves. A “busy” animation can be shown during the view transition (via `ng-view` or `ui-view`) + + Note: The code executes before the route via a promise. Rejecting the promise cancels the route. Resolve makes the new view wait for the route to resolve. A “busy” animation can be shown before the resolve and through the view transition. If you want to get to the View faster and do not require a checkpoint to decide if you can get to the View, consider the [controller `activate` technique](#style-y080) instead. + + ```javascript + /* avoid */ + angular + .module('app') + .controller('AvengersController', AvengersController); + + function AvengersController(movieService) { + var vm = this; + // unresolved + vm.movies; + // resolved asynchronously + movieService.getMovies().then(function(response) { + vm.movies = response.movies; + }); + } + ``` + + ```javascript + /* better */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { + moviesPrepService: function(movieService) { + return movieService.getMovies(); + } + } + }); + } + + // avengers.js + angular + .module('app') + .controller('AvengersController', AvengersController); + + AvengersController.$inject = ['moviesPrepService']; + function AvengersController(moviesPrepService) { + var vm = this; + vm.movies = moviesPrepService.movies; + } + ``` + + Note: The example below shows the route resolve points to a named function, which is easier to debug and easier to handle dependency injection. + + ```javascript + /* even better */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { + moviesPrepService: moviesPrepService + } + }); + } + + function moviesPrepService(movieService) { + return movieService.getMovies(); + } + + // avengers.js + angular + .module('app') + .controller('AvengersController', AvengersController); + + AvengersController.$inject = ['moviesPrepService']; + function AvengersController(moviesPrepService) { + var vm = this; + vm.movies = moviesPrepService.movies; + } + ``` + Note: The code example's dependency on `movieService` is not minification safe on its own. For details on how to make this code minification safe, see the sections on [dependency injection](#manual-annotating-for-dependency-injection) and on [minification and annotation](#minification-and-annotation). + +**[Back to top](#table-of-contents)** + +### Handling Exceptions with Promises +###### [Style [Y082](#style-y082)] + + - The `catch` block of a promise must return a rejected promise to maintain the exception in the promise chain. + + - Always handle exceptions in services/factories. + + *Why?*: If the `catch` block does not return a rejected promise, the caller of the promise will not know an exception occurred. The caller's `then` will execute. Thus, the user may never know what happened. + + *Why?*: To avoid swallowing errors and misinforming the user. + + Note: Consider putting any exception handling in a function in a shared module and service. + + ```javascript + /* avoid */ + + function getCustomer(id) { + return $http.get('/api/customer/' + id) + .then(getCustomerComplete) + .catch(getCustomerFailed); + + function getCustomerComplete(data, status, headers, config) { + return data.data; + } + + function getCustomerFailed(e) { + var newMessage = 'XHR Failed for getCustomer' + if (e.data && e.data.description) { + newMessage = newMessage + '\n' + e.data.description; + } + e.data.description = newMessage; + logger.error(newMessage); + // *** + // Notice there is no return of the rejected promise + // *** + } + } + + /* recommended */ + function getCustomer(id) { + return $http.get('/api/customer/' + id) + .then(getCustomerComplete) + .catch(getCustomerFailed); + + function getCustomerComplete(data, status, headers, config) { + return data.data; + } + + function getCustomerFailed(e) { + var newMessage = 'XHR Failed for getCustomer' + if (e.data && e.data.description) { + newMessage = newMessage + '\n' + e.data.description; + } + e.data.description = newMessage; + logger.error(newMessage); + return $q.reject(e); + } + } + ``` + +**[Back to top](#table-of-contents)** + +## Manual Annotating for Dependency Injection + +### UnSafe from Minification +###### [Style [Y090](#style-y090)] + + - Avoid using the shortcut syntax of declaring dependencies without using a minification-safe approach. + + *Why?*: The parameters to the component (e.g. controller, factory, etc) will be converted to mangled variables. For example, `common` and `dataservice` may become `a` or `b` and not be found by Angular. + + ```javascript + /* avoid - not minification-safe*/ + angular + .module('app') + .controller('DashboardController', DashboardController); + + function DashboardController(common, dataservice) { + } + ``` + + This code may produce mangled variables when minified and thus cause runtime errors. + + ```javascript + /* avoid - not minification-safe*/ + angular.module('app').controller('DashboardController', d);function d(a, b) { } + ``` + +### Manually Identify Dependencies +###### [Style [Y091](#style-y091)] + + - Use `$inject` to manually identify your dependencies for Angular components. + + *Why?*: This technique mirrors the technique used by [`ng-annotate`](https://github.com/olov/ng-annotate), which I recommend for automating the creation of minification safe dependencies. If `ng-annotate` detects injection has already been made, it will not duplicate it. + + *Why?*: This safeguards your dependencies from being vulnerable to minification issues when parameters may be mangled. For example, `common` and `dataservice` may become `a` or `b` and not be found by Angular. + + *Why?*: Avoid creating in-line dependencies as long lists can be difficult to read in the array. Also it can be confusing that the array is a series of strings while the last item is the component's function. + + ```javascript + /* avoid */ + angular + .module('app') + .controller('DashboardController', + ['$location', '$routeParams', 'common', 'dataservice', + function Dashboard($location, $routeParams, common, dataservice) {} + ]); + ``` + + ```javascript + /* avoid */ + angular + .module('app') + .controller('DashboardController', + ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); + + function Dashboard($location, $routeParams, common, dataservice) { + } + ``` + + ```javascript + /* recommended */ + angular + .module('app') + .controller('DashboardController', DashboardController); + + DashboardController.$inject = ['$location', '$routeParams', 'common', 'dataservice']; + + function DashboardController($location, $routeParams, common, dataservice) { + } + ``` + + Note: When your function is below a return statement the `$inject` may be unreachable (this may happen in a directive). You can solve this by moving the Controller outside of the directive. + + ```javascript + /* avoid */ + // inside a directive definition + function outer() { + var ddo = { + controller: DashboardPanelController, + controllerAs: 'vm' + }; + return ddo; + + DashboardPanelController.$inject = ['logger']; // Unreachable + function DashboardPanelController(logger) { + } + } + ``` + + ```javascript + /* recommended */ + // outside a directive definition + function outer() { + var ddo = { + controller: DashboardPanelController, + controllerAs: 'vm' + }; + return ddo; + } + + DashboardPanelController.$inject = ['logger']; + function DashboardPanelController(logger) { + } + ``` + +### Manually Identify Route Resolver Dependencies +###### [Style [Y092](#style-y092)] + + - Use `$inject` to manually identify your route resolver dependencies for Angular components. + + *Why?*: This technique breaks out the anonymous function for the route resolver, making it easier to read. + + *Why?*: An `$inject` statement can easily precede the resolver to handle making any dependencies minification safe. + + ```javascript + /* recommended */ + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { + moviesPrepService: moviesPrepService + } + }); + } + + moviesPrepService.$inject = ['movieService']; + function moviesPrepService(movieService) { + return movieService.getMovies(); + } + ``` + +**[Back to top](#table-of-contents)** + +## Minification and Annotation + +### ng-annotate +###### [Style [Y100](#style-y100)] + + - Use [ng-annotate](//github.com/olov/ng-annotate) for [Gulp](http://gulpjs.com) or [Grunt](http://gruntjs.com) and comment functions that need automated dependency injection using `/* @ngInject */` + + *Why?*: This safeguards your code from any dependencies that may not be using minification-safe practices. + + *Why?*: [`ng-min`](https://github.com/btford/ngmin) is deprecated + + >I prefer Gulp as I feel it is easier to write, to read, and to debug. + + The following code is not using minification safe dependencies. + + ```javascript + angular + .module('app') + .controller('AvengersController', AvengersController); + + /* @ngInject */ + function AvengersController(storage, avengerService) { + var vm = this; + vm.heroSearch = ''; + vm.storeHero = storeHero; + + function storeHero() { + var hero = avengerService.find(vm.heroSearch); + storage.save(hero.name, hero); + } + } + ``` + + When the above code is run through ng-annotate it will produce the following output with the `$inject` annotation and become minification-safe. + + ```javascript + angular + .module('app') + .controller('AvengersController', AvengersController); + + /* @ngInject */ + function AvengersController(storage, avengerService) { + var vm = this; + vm.heroSearch = ''; + vm.storeHero = storeHero; + + function storeHero() { + var hero = avengerService.find(vm.heroSearch); + storage.save(hero.name, hero); + } + } + + AvengersController.$inject = ['storage', 'avengerService']; + ``` + + Note: If `ng-annotate` detects injection has already been made (e.g. `@ngInject` was detected), it will not duplicate the `$inject` code. + + Note: When using a route resolver you can prefix the resolver's function with `/* @ngInject */` and it will produce properly annotated code, keeping any injected dependencies minification safe. + + ```javascript + // Using @ngInject annotations + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { /* @ngInject */ + moviesPrepService: function(movieService) { + return movieService.getMovies(); + } + } + }); + } + ``` + + > Note: Starting from Angular 1.3 you can use the [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp) directive's `ngStrictDi` parameter to detect any potentially missing minification safe dependencies. When present the injector will be created in "strict-di" mode causing the application to fail to invoke functions which do not use explicit function annotation (these may not be minification safe). Debugging info will be logged to the console to help track down the offending code. I prefer to only use `ng-strict-di` for debugging purposes only. + `` + +### Use Gulp or Grunt for ng-annotate +###### [Style [Y101](#style-y101)] + + - Use [gulp-ng-annotate](https://www.npmjs.com/package/gulp-ng-annotate) or [grunt-ng-annotate](https://www.npmjs.com/package/grunt-ng-annotate) in an automated build task. Inject `/* @ngInject */` prior to any function that has dependencies. + + *Why?*: ng-annotate will catch most dependencies, but it sometimes requires hints using the `/* @ngInject */` syntax. + + The following code is an example of a gulp task using ngAnnotate + + ```javascript + gulp.task('js', ['jshint'], function() { + var source = pkg.paths.js; + + return gulp.src(source) + .pipe(sourcemaps.init()) + .pipe(concat('all.min.js', {newLine: ';'})) + // Annotate before uglify so the code get's min'd properly. + .pipe(ngAnnotate({ + // true helps add where @ngInject is not used. It infers. + // Doesn't work with resolve, so we must be explicit there + add: true + })) + .pipe(bytediff.start()) + .pipe(uglify({mangle: true})) + .pipe(bytediff.stop()) + .pipe(sourcemaps.write('./')) + .pipe(gulp.dest(pkg.paths.dev)); + }); + + ``` + +**[Back to top](#table-of-contents)** + +## Exception Handling + +### decorators +###### [Style [Y110](#style-y110)] + + - Use a [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator), at config time using the [`$provide`](https://docs.angularjs.org/api/auto/service/$provide) service, on the [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) service to perform custom actions when exceptions occur. + + *Why?*: Provides a consistent way to handle uncaught Angular exceptions for development-time or run-time. + + Note: Another option is to override the service instead of using a decorator. This is a fine option, but if you want to keep the default behavior and extend it a decorator is recommended. + + ```javascript + /* recommended */ + angular + .module('blocks.exception') + .config(exceptionConfig); + + exceptionConfig.$inject = ['$provide']; + + function exceptionConfig($provide) { + $provide.decorator('$exceptionHandler', extendExceptionHandler); + } + + extendExceptionHandler.$inject = ['$delegate', 'toastr']; + + function extendExceptionHandler($delegate, toastr) { + return function(exception, cause) { + $delegate(exception, cause); + var errorData = { + exception: exception, + cause: cause + }; + /** + * Could add the error to a service's collection, + * add errors to $rootScope, log errors to remote web server, + * or log locally. Or throw hard. It is entirely up to you. + * throw exception; + */ + toastr.error(exception.msg, errorData); + }; + } + ``` + +### Exception Catchers +###### [Style [Y111](#style-y111)] + + - Create a factory that exposes an interface to catch and gracefully handle exceptions. + + *Why?*: Provides a consistent way to catch exceptions that may be thrown in your code (e.g. during XHR calls or promise failures). + + Note: The exception catcher is good for catching and reacting to specific exceptions from calls that you know may throw one. For example, when making an XHR call to retrieve data from a remote web service and you want to catch any exceptions from that service and react uniquely. + + ```javascript + /* recommended */ + angular + .module('blocks.exception') + .factory('exception', exception); + + exception.$inject = ['logger']; + + function exception(logger) { + var service = { + catcher: catcher + }; + return service; + + function catcher(message) { + return function(reason) { + logger.error(message, reason); + }; + } + } + ``` + +### Route Errors +###### [Style [Y112](#style-y112)] + + - Handle and log all routing errors using [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). + + *Why?*: Provides a consistent way to handle all routing errors. + + *Why?*: Potentially provides a better user experience if a routing error occurs and you route them to a friendly screen with more details or recovery options. + + ```javascript + /* recommended */ + var handlingRouteChangeError = false; + + function handleRoutingErrors() { + /** + * Route cancellation: + * On routing error, go to the dashboard. + * Provide an exit clause if it tries to do it twice. + */ + $rootScope.$on('$routeChangeError', + function(event, current, previous, rejection) { + if (handlingRouteChangeError) { return; } + handlingRouteChangeError = true; + var destination = (current && (current.title || + current.name || current.loadedTemplateUrl)) || + 'unknown target'; + var msg = 'Error routing to ' + destination + '. ' + + (rejection.msg || ''); + + /** + * Optionally log using a custom service or $log. + * (Don't forget to inject custom service) + */ + logger.warning(msg, [current]); + + /** + * On routing error, go to another route/state. + */ + $location.path('/'); + + } + ); + } + ``` + +**[Back to top](#table-of-contents)** + +## Naming + +### Naming Guidelines +###### [Style [Y120](#style-y120)] + + - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.js`. There are 2 names for most assets: + * the file name (`avengers.controller.js`) + * the registered component name with Angular (`AvengersController`) + + *Why?*: Naming conventions help provide a consistent way to find content at a glance. Consistency within the project is vital. Consistency with a team is important. Consistency across a company provides tremendous efficiency. + + *Why?*: The naming conventions should simply help you find your code faster and make it easier to understand. + +### Feature File Names +###### [Style [Y121](#style-y121)] + + - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.js`. + + *Why?*: Provides a consistent way to quickly identify components. + + *Why?*: Provides pattern matching for any automated tasks. + + ```javascript + /** + * common options + */ + + // Controllers + avengers.js + avengers.controller.js + avengersController.js + + // Services/Factories + logger.js + logger.service.js + loggerService.js + ``` + + ```javascript + /** + * recommended + */ + + // controllers + avengers.controller.js + avengers.controller.spec.js + + // services/factories + logger.service.js + logger.service.spec.js + + // constants + constants.js + + // module definition + avengers.module.js + + // routes + avengers.routes.js + avengers.routes.spec.js + + // configuration + avengers.config.js + + // directives + avenger-profile.directive.js + avenger-profile.directive.spec.js + ``` + + Note: Another common convention is naming controller files without the word `controller` in the file name such as `avengers.js` instead of `avengers.controller.js`. All other conventions still hold using a suffix of the type. Controllers are the most common type of component so this just saves typing and is still easily identifiable. I recommend you choose 1 convention and be consistent for your team. My preference is `avengers.controller.js` identifying the `AvengersController`. + + ```javascript + /** + * recommended + */ + // Controllers + avengers.js + avengers.spec.js + ``` + +### Test File Names +###### [Style [Y122](#style-y122)] + + - Name test specifications similar to the component they test with a suffix of `spec`. + + *Why?*: Provides a consistent way to quickly identify components. + + *Why?*: Provides pattern matching for [karma](http://karma-runner.github.io/) or other test runners. + + ```javascript + /** + * recommended + */ + avengers.controller.spec.js + logger.service.spec.js + avengers.routes.spec.js + avenger-profile.directive.spec.js + ``` + +### Controller Names +###### [Style [Y123](#style-y123)] + + - Use consistent names for all controllers named after their feature. Use UpperCamelCase for controllers, as they are constructors. + + *Why?*: Provides a consistent way to quickly identify and reference controllers. + + *Why?*: UpperCamelCase is conventional for identifying object that can be instantiated using a constructor. + + ```javascript + /** + * recommended + */ + + // avengers.controller.js + angular + .module + .controller('HeroAvengersController', HeroAvengersController); + + function HeroAvengersController() { } + ``` + +### Controller Name Suffix +###### [Style [Y124](#style-y124)] + + - Append the controller name with the suffix `Controller`. + + *Why?*: The `Controller` suffix is more commonly used and is more explicitly descriptive. + + ```javascript + /** + * recommended + */ + + // avengers.controller.js + angular + .module + .controller('AvengersController', AvengersController); + + function AvengersController() { } + ``` + +### Factory and Service Names +###### [Style [Y125](#style-y125)] + + - Use consistent names for all factories and services named after their feature. Use camel-casing for services and factories. Avoid prefixing factories and services with `$`. Only suffix service and factories with `Service` when it is not clear what they are (i.e. when they are nouns). + + *Why?*: Provides a consistent way to quickly identify and reference factories. + + *Why?*: Avoids name collisions with built-in factories and services that use the `$` prefix. + + *Why?*: Clear service names such as `logger` do not require a suffix. + + *Why?*: Service names such as `avengers` are nouns and require a suffix and should be named `avengersService`. + + ```javascript + /** + * recommended + */ + + // logger.service.js + angular + .module + .factory('logger', logger); + + function logger() { } + ``` + + ```javascript + /** + * recommended + */ + + // credit.service.js + angular + .module + .factory('creditService', creditService); + + function creditService() { } + + // customer.service.js + angular + .module + .service('customerService', customerService); + + function customerService() { } + ``` + +### Directive Component Names +###### [Style [Y126](#style-y126)] + + - Use consistent names for all directives using camel-case. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). + + *Why?*: Provides a consistent way to quickly identify and reference components. + + ```javascript + /** + * recommended + */ + + // avenger-profile.directive.js + angular + .module + .directive('xxAvengerProfile', xxAvengerProfile); + + // usage is + + function xxAvengerProfile() { } + ``` + +### Modules +###### [Style [Y127](#style-y127)] + + - When there are multiple modules, the main module file is named `app.module.js` while other dependent modules are named after what they represent. For example, an admin module is named `admin.module.js`. The respective registered module names would be `app` and `admin`. + + *Why?*: Provides consistency for multiple module apps, and for expanding to large applications. + + *Why?*: Provides easy way to use task automation to load all module definitions first, then all other angular files (for bundling). + +### Configuration +###### [Style [Y128](#style-y128)] + + - Separate configuration for a module into its own file named after the module. A configuration file for the main `app` module is named `app.config.js` (or simply `config.js`). A configuration for a module named `admin.module.js` is named `admin.config.js`. + + *Why?*: Separates configuration from module definition, components, and active code. + + *Why?*: Provides an identifiable place to set configuration for a module. + +### Routes +###### [Style [Y129](#style-y129)] + + - Separate route configuration into its own file. Examples might be `app.route.js` for the main module and `admin.route.js` for the `admin` module. Even in smaller apps I prefer this separation from the rest of the configuration. + +**[Back to top](#table-of-contents)** + +## Application Structure LIFT Principle +### LIFT +###### [Style [Y140](#style-y140)] + + - Structure your app such that you can `L`ocate your code quickly, `I`dentify the code at a glance, keep the `F`lattest structure you can, and `T`ry to stay DRY. The structure should follow these 4 basic guidelines. + + *Why LIFT?*: Provides a consistent structure that scales well, is modular, and makes it easier to increase developer efficiency by finding code quickly. Another way to check your app structure is to ask yourself: How quickly can you open and work in all of the related files for a feature? + + When I find my structure is not feeling comfortable, I go back and revisit these LIFT guidelines + + 1. `L`ocating our code is easy + 2. `I`dentify code at a glance + 3. `F`lat structure as long as we can + 4. `T`ry to stay DRY (Don’t Repeat Yourself) or T-DRY + +### Locate +###### [Style [Y141](#style-y141)] + + - Make locating your code intuitive, simple and fast. + + *Why?*: I find this to be super important for a project. If the team cannot find the files they need to work on quickly, they will not be able to work as efficiently as possible, and the structure needs to change. You may not know the file name or where its related files are, so putting them in the most intuitive locations and near each other saves a ton of time. A descriptive folder structure can help with this. + + ``` + /bower_components + /client + /app + /avengers + /blocks + /exception + /logger + /core + /dashboard + /data + /layout + /widgets + /content + index.html + .bower.json + ``` + +### Identify +###### [Style [Y142](#style-y142)] + + - When you look at a file you should instantly know what it contains and represents. + + *Why?*: You spend less time hunting and pecking for code, and become more efficient. If this means you want longer file names, then so be it. Be descriptive with file names and keeping the contents of the file to exactly 1 component. Avoid files with multiple controllers, multiple services, or a mixture. There are deviations of the 1 per file rule when I have a set of very small features that are all related to each other, they are still easily identifiable. + +### Flat +###### [Style [Y143](#style-y143)] + + - Keep a flat folder structure as long as possible. When you get to 7+ files, begin considering separation. + + *Why?*: Nobody wants to search 7 levels of folders to find a file. Think about menus on web sites … anything deeper than 2 should take serious consideration. In a folder structure there is no hard and fast number rule, but when a folder has 7-10 files, that may be time to create subfolders. Base it on your comfort level. Use a flatter structure until there is an obvious value (to help the rest of LIFT) in creating a new folder. + +### T-DRY (Try to Stick to DRY) +###### [Style [Y144](#style-y144)] + + - Be DRY, but don't go nuts and sacrifice readability. + + *Why?*: Being DRY is important, but not crucial if it sacrifices the others in LIFT, which is why I call it T-DRY. I don’t want to type session-view.html for a view because, well, it’s obviously a view. If it is not obvious or by convention, then I name it. + +**[Back to top](#table-of-contents)** + +## Application Structure + +### Overall Guidelines +###### [Style [Y150](#style-y150)] + + - Have a near term view of implementation and a long term vision. In other words, start small but keep in mind on where the app is heading down the road. All of the app's code goes in a root folder named `app`. All content is 1 feature per file. Each controller, service, module, view is in its own file. All 3rd party vendor scripts are stored in another root folder and not in the `app` folder. I didn't write them and I don't want them cluttering my app (`bower_components`, `scripts`, `lib`). + + Note: Find more details and reasoning behind the structure at [this original post on application structure](http://www.johnpapa.net/angular-app-structuring-guidelines/). + +### Layout +###### [Style [Y151](#style-y151)] + + - Place components that define the overall layout of the application in a folder named `layout`. These may include a shell view and controller may act as the container for the app, navigation, menus, content areas, and other regions. + + *Why?*: Organizes all layout in a single place re-used throughout the application. + +### Folders-by-Feature Structure +###### [Style [Y152](#style-y152)] + + - Create folders named for the feature they represent. When a folder grows to contain more than 7 files, start to consider creating a folder for them. Your threshold may be different, so adjust as needed. + + *Why?*: A developer can locate the code, identify what each file represents at a glance, the structure is flat as can be, and there is no repetitive nor redundant names. + + *Why?*: The LIFT guidelines are all covered. + + *Why?*: Helps reduce the app from becoming cluttered through organizing the content and keeping them aligned with the LIFT guidelines. + + *Why?*: When there are a lot of files (10+) locating them is easier with a consistent folder structures and more difficult in flat structures. + + ```javascript + /** + * recommended + */ + + app/ + app.module.js + app.config.js + components/ + calendar.directive.js + calendar.directive.html + user-profile.directive.js + user-profile.directive.html + layout/ + shell.html + shell.controller.js + topnav.html + topnav.controller.js + people/ + attendees.html + attendees.controller.js + people.routes.js + speakers.html + speakers.controller.js + speaker-detail.html + speaker-detail.controller.js + services/ + data.service.js + localstorage.service.js + logger.service.js + spinner.service.js + sessions/ + sessions.html + sessions.controller.js + sessions.routes.js + session-detail.html + session-detail.controller.js + ``` + + ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) + + Note: Do not structure your app using folders-by-type. This requires moving to multiple folders when working on a feature and gets unwieldy quickly as the app grows to 5, 10 or 25+ views and controllers (and other features), which makes it more difficult than folder-by-feature to locate files. + + ```javascript + /* + * avoid + * Alternative folders-by-type. + * I recommend "folders-by-feature", instead. + */ + + app/ + app.module.js + app.config.js + app.routes.js + directives.js + controllers/ + attendees.js + session-detail.js + sessions.js + shell.js + speakers.js + speaker-detail.js + topnav.js + directives/ + calendar.directive.js + calendar.directive.html + user-profile.directive.js + user-profile.directive.html + services/ + dataservice.js + localstorage.js + logger.js + spinner.js + views/ + attendees.html + session-detail.html + sessions.html + shell.html + speakers.html + speaker-detail.html + topnav.html + ``` + +**[Back to top](#table-of-contents)** + +## Modularity + +### Many Small, Self Contained Modules +###### [Style [Y160](#style-y160)] + + - Create small modules that encapsulate one responsibility. + + *Why?*: Modular applications make it easy to plug and go as they allow the development teams to build vertical slices of the applications and roll out incrementally. This means we can plug in new features as we develop them. + +### Create an App Module +###### [Style [Y161](#style-y161)] + + - Create an application root module whose role is to pull together all of the modules and features of your application. Name this for your application. + + *Why?*: Angular encourages modularity and separation patterns. Creating an application root module whose role is to tie your other modules together provides a very straightforward way to add or remove modules from your application. + +### Keep the App Module Thin +###### [Style [Y162](#style-y162)] + + - Only put logic for pulling together the app in the application module. Leave features in their own modules. + + *Why?*: Adding additional roles to the application root to get remote data, display views, or other logic not related to pulling the app together muddies the app module and make both sets of features harder to reuse or turn off. + + *Why?*: The app module becomes a manifest that describes which modules help define the application. + +### Feature Areas are Modules +###### [Style [Y163](#style-y163)] + + - Create modules that represent feature areas, such as layout, reusable and shared services, dashboards, and app specific features (e.g. customers, admin, sales). + + *Why?*: Self contained modules can be added to the application with little or no friction. + + *Why?*: Sprints or iterations can focus on feature areas and turn them on at the end of the sprint or iteration. + + *Why?*: Separating feature areas into modules makes it easier to test the modules in isolation and reuse code. + +### Reusable Blocks are Modules +###### [Style [Y164](#style-y164)] + + - Create modules that represent reusable application blocks for common services such as exception handling, logging, diagnostics, security, and local data stashing. + + *Why?*: These types of features are needed in many applications, so by keeping them separated in their own modules they can be application generic and be reused across applications. + +### Module Dependencies +###### [Style [Y165](#style-y165)] + + - The application root module depends on the app specific feature modules and any shared or reusable modules. + + ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) + + *Why?*: The main app module contains a quickly identifiable manifest of the application's features. + + *Why?*: Each feature area contains a manifest of what it depends on, so it can be pulled in as a dependency in other applications and still work. + + *Why?*: Intra-App features such as shared data services become easy to locate and share from within `app.core` (choose your favorite name for this module). + + Note: This is a strategy for consistency. There are many good options here. Choose one that is consistent, follows Angular's dependency rules, and is easy to maintain and scale. + + > My structures vary slightly between projects but they all follow these guidelines for structure and modularity. The implementation may vary depending on the features and the team. In other words, don't get hung up on an exact like-for-like structure but do justify your structure using consistency, maintainability, and efficiency in mind. + + > In a small app, you can also consider putting all the shared dependencies in the app module where the feature modules have no direct dependencies. This makes it easier to maintain the smaller application, but makes it harder to reuse modules outside of this application. + +**[Back to top](#table-of-contents)** + +## Startup Logic + +### Configuration +###### [Style [Y170](#style-y170)] + + - Inject code into [module configuration](https://docs.angularjs.org/guide/module#module-loading-dependencies) that must be configured before running the angular app. Ideal candidates include providers and constants. + + *Why?*: This makes it easier to have less places for configuration. + + ```javascript + angular + .module('app') + .config(configure); + + configure.$inject = + ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; + + function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { + exceptionHandlerProvider.configure(config.appErrorPrefix); + configureStateHelper(); + + toastr.options.timeOut = 4000; + toastr.options.positionClass = 'toast-bottom-right'; + + //////////////// + + function configureStateHelper() { + routerHelperProvider.configure({ + docTitle: 'NG-Modular: ' + }); + } + } + ``` + +### Run Blocks +###### [Style [Y171](#style-y171)] + + - Any code that needs to run when an application starts should be declared in a factory, exposed via a function, and injected into the [run block](https://docs.angularjs.org/guide/module#module-loading-dependencies). + + *Why?*: Code directly in a run block can be difficult to test. Placing in a factory makes it easier to abstract and mock. + + ```javascript + angular + .module('app') + .run(runBlock); + + runBlock.$inject = ['authenticator', 'translator']; + + function runBlock(authenticator, translator) { + authenticator.initialize(); + translator.initialize(); + } + ``` + +**[Back to top](#table-of-contents)** + +## Angular $ Wrapper Services + +### $document and $window +###### [Style [Y180](#style-y180)] + + - Use [`$document`](https://docs.angularjs.org/api/ng/service/$document) and [`$window`](https://docs.angularjs.org/api/ng/service/$window) instead of `document` and `window`. + + *Why?*: These services are wrapped by Angular and more easily testable than using document and window in tests. This helps you avoid having to mock document and window yourself. + +### $timeout and $interval +###### [Style [Y181](#style-y181)] + + - Use [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) and [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) instead of `setTimeout` and `setInterval` . + + *Why?*: These services are wrapped by Angular and more easily testable and handle Angular's digest cycle thus keeping data binding in sync. + +**[Back to top](#table-of-contents)** + +## Testing +Unit testing helps maintain clean code, as such I included some of my recommendations for unit testing foundations with links for more information. + +### Write Tests with Stories +###### [Style [Y190](#style-y190)] + + - Write a set of tests for every story. Start with an empty test and fill them in as you write the code for the story. + + *Why?*: Writing the test descriptions helps clearly define what your story will do, will not do, and how you can measure success. + + ```javascript + it('should have Avengers controller', function() { + // TODO + }); + + it('should find 1 Avenger when filtered by name', function() { + // TODO + }); + + it('should have 10 Avengers', function() { + // TODO (mock data?) + }); + + it('should return Avengers via XHR', function() { + // TODO ($httpBackend?) + }); + + // and so on + ``` + +### Testing Library +###### [Style [Y191](#style-y191)] + + - Use [Jasmine](http://jasmine.github.io/) or [Mocha](http://mochajs.org) for unit testing. + + *Why?*: Both Jasmine and Mocha are widely used in the Angular community. Both are stable, well maintained, and provide robust testing features. + + Note: When using Mocha, also consider choosing an assert library such as [Chai](http://chaijs.com). I prefer Mocha. + +### Test Runner +###### [Style [Y192](#style-y192)] + + - Use [Karma](http://karma-runner.github.io) as a test runner. + + *Why?*: Karma is easy to configure to run once or automatically when you change your code. + + *Why?*: Karma hooks into your Continuous Integration process easily on its own or through Grunt or Gulp. + + *Why?*: Some IDE's are beginning to integrate with Karma, such as [WebStorm](http://www.jetbrains.com/webstorm/) and [Visual Studio](https://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). + + *Why?*: Karma works well with task automation leaders such as [Grunt](http://gruntjs.com/) (with [grunt-karma](https://github.com/karma-runner/grunt-karma)) and [Gulp](http://gulpjs.com/). When using Gulp, use [Karma](https://github.com/karma-runner/karma) directly and not with a plugin as the API can be called directly. + + ```javascript + /* recommended */ + + // Gulp example with Karma directly + function startTests(singleRun, done) { + var child; + var excludeFiles = []; + var fork = require('child_process').fork; + var karma = require('karma').server; + var serverSpecs = config.serverIntegrationSpecs; + + if (args.startServers) { + log('Starting servers'); + var savedEnv = process.env; + savedEnv.NODE_ENV = 'dev'; + savedEnv.PORT = 8888; + child = fork(config.nodeServer); + } else { + if (serverSpecs && serverSpecs.length) { + excludeFiles = serverSpecs; + } + } + + karma.start({ + configFile: __dirname + '/karma.conf.js', + exclude: excludeFiles, + singleRun: !!singleRun + }, karmaCompleted); + + //////////////// + + function karmaCompleted(karmaResult) { + log('Karma completed'); + if (child) { + log('shutting down the child process'); + child.kill(); + } + if (karmaResult === 1) { + done('karma: tests failed with code ' + karmaResult); + } else { + done(); + } + } + } + ``` + +### Stubbing and Spying +###### [Style [Y193](#style-y193)] + + - Use [Sinon](http://sinonjs.org/) for stubbing and spying. + + *Why?*: Sinon works well with both Jasmine and Mocha and extends the stubbing and spying features they offer. + + *Why?*: Sinon makes it easier to toggle between Jasmine and Mocha, if you want to try both. + + *Why?*: Sinon has descriptive messages when tests fail the assertions. + +### Headless Browser +###### [Style [Y194](#style-y194)] + + - Use [PhantomJS](http://phantomjs.org/) to run your tests on a server. + + *Why?*: PhantomJS is a headless browser that helps run your tests without needing a "visual" browser. So you do not have to install Chrome, Safari, IE, or other browsers on your server. + + Note: You should still test on all browsers in your environment, as appropriate for your target audience. + +### Code Analysis +###### [Style [Y195](#style-y195)] + + - Run JSHint on your tests. + + *Why?*: Tests are code. JSHint can help identify code quality issues that may cause the test to work improperly. + +### Alleviate Globals for JSHint Rules on Tests +###### [Style [Y196](#style-y196)] + + - Relax the rules on your test code to allow for common globals such as `describe` and `expect`. Relax the rules for expressions, as Mocha uses these. + + *Why?*: Your tests are code and require the same attention and code quality rules as all of your production code. However, global variables used by the testing framework, for example, can be relaxed by including this in your test specs. + + ```javascript + /* jshint -W117, -W030 */ + ``` + Or you can add the following to your JSHint Options file. + + ```javascript + "jasmine": true, + "mocha": true, + ``` + + ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) + +### Organizing Tests +###### [Style [Y197](#style-y197)] + + - Place unit test files (specs) side-by-side with your client code. Place specs that cover server integration or test multiple components in a separate `tests` folder. + + *Why?*: Unit tests have a direct correlation to a specific component and file in source code. + + *Why?*: It is easier to keep them up to date since they are always in sight. When coding whether you do TDD or test during development or test after development, the specs are side-by-side and never out of sight nor mind, and thus more likely to be maintained which also helps maintain code coverage. + + *Why?*: When you update source code it is easier to go update the tests at the same time. + + *Why?*: Placing them side-by-side makes it easy to find them and easy to move them with the source code if you move the source. + + *Why?*: Having the spec nearby makes it easier for the source code reader to learn how the component is supposed to be used and to discover its known limitations. + + *Why?*: Separating specs so they are not in a distributed build is easy with grunt or gulp. + + ``` + /src/client/app/customers/customer-detail.controller.js + /customer-detail.controller.spec.js + /customers.controller.js + /customers.controller.spec.js + /customers.module.js + /customers.route.js + /customers.route.spec.js + ``` + +**[Back to top](#table-of-contents)** + +## Animations + +### Usage +###### [Style [Y210](#style-y210)] + + - Use subtle [animations with Angular](https://docs.angularjs.org/guide/animations) to transition between states for views and primary visual elements. Include the [ngAnimate module](https://docs.angularjs.org/api/ngAnimate). The 3 keys are subtle, smooth, seamless. + + *Why?*: Subtle animations can improve User Experience when used appropriately. + + *Why?*: Subtle animations can improve perceived performance as views transition. + +### Sub Second +###### [Style [Y211](#style-y211)] + + - Use short durations for animations. I generally start with 300ms and adjust until appropriate. + + *Why?*: Long animations can have the reverse effect on User Experience and perceived performance by giving the appearance of a slow application. + +### animate.css +###### [Style [Y212](#style-y212)] + + - Use [animate.css](http://daneden.github.io/animate.css/) for conventional animations. + + *Why?*: The animations that animate.css provides are fast, smooth, and easy to add to your application. + + *Why?*: Provides consistency in your animations. + + *Why?*: animate.css is widely used and tested. + + Note: See this [great post by Matias Niemelä on Angular animations](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) + +**[Back to top](#table-of-contents)** + +## Comments + +### jsDoc +###### [Style [Y220](#style-y220)] + + - If planning to produce documentation, use [`jsDoc`](http://usejsdoc.org/) syntax to document function names, description, params and returns. Use `@namespace` and `@memberOf` to match your app structure. + + *Why?*: You can generate (and regenerate) documentation from your code, instead of writing it from scratch. + + *Why?*: Provides consistency using a common industry tool. + + ```javascript + /** + * Logger Factory + * @namespace Factories + */ + (function() { + angular + .module('app') + .factory('logger', logger); + + /** + * @namespace Logger + * @desc Application wide logger + * @memberOf Factories + */ + function logger($log) { + var service = { + logError: logError + }; + return service; + + //////////// + + /** + * @name logError + * @desc Logs errors + * @param {String} msg Message to log + * @returns {String} + * @memberOf Factories.Logger + */ + function logError(msg) { + var loggedMsg = 'Error: ' + msg; + $log.error(loggedMsg); + return loggedMsg; + }; + } + })(); + ``` + +**[Back to top](#table-of-contents)** + +## JS Hint + +### Use an Options File +###### [Style [Y230](#style-y230)] + + - Use JS Hint for linting your JavaScript and be sure to customize the JS Hint options file and include in source control. See the [JS Hint docs](http://jshint.com/docs/) for details on the options. + + *Why?*: Provides a first alert prior to committing any code to source control. + + *Why?*: Provides consistency across your team. + + ```javascript + { + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "es3": false, + "forin": true, + "freeze": true, + "immed": true, + "indent": 4, + "latedef": "nofunc", + "newcap": true, + "noarg": true, + "noempty": true, + "nonbsp": true, + "nonew": true, + "plusplus": false, + "quotmark": "single", + "undef": true, + "unused": false, + "strict": false, + "maxparams": 10, + "maxdepth": 5, + "maxstatements": 40, + "maxcomplexity": 8, + "maxlen": 120, + + "asi": false, + "boss": false, + "debug": false, + "eqnull": true, + "esnext": false, + "evil": false, + "expr": false, + "funcscope": false, + "globalstrict": false, + "iterator": false, + "lastsemic": false, + "laxbreak": false, + "laxcomma": false, + "loopfunc": true, + "maxerr": false, + "moz": false, + "multistr": false, + "notypeof": false, + "proto": false, + "scripturl": false, + "shadow": false, + "sub": true, + "supernew": false, + "validthis": false, + "noyield": false, + + "browser": true, + "node": true, + + "globals": { + "angular": false, + "$": false + } + } + ``` + +**[Back to top](#table-of-contents)** + +## JSCS + +### Use an Options File +###### [Style [Y235](#style-y235)] + + - Use JSCS for checking your coding styles your JavaScript and be sure to customize the JSCS options file and include in source control. See the [JSCS docs](http://jscs.info/) for details on the options. + + *Why?*: Provides a first alert prior to committing any code to source control. + + *Why?*: Provides consistency across your team. + + ```javascript + { + "excludeFiles": ["node_modules/**", "bower_components/**"], + + "requireCurlyBraces": [ + "if", + "else", + "for", + "while", + "do", + "try", + "catch" + ], + "requireOperatorBeforeLineBreak": true, + "requireCamelCaseOrUpperCaseIdentifiers": true, + "maximumLineLength": { + "value": 100, + "allowComments": true, + "allowRegex": true + }, + "validateIndentation": 4, + "validateQuoteMarks": "'", + + "disallowMultipleLineStrings": true, + "disallowMixedSpacesAndTabs": true, + "disallowTrailingWhitespace": true, + "disallowSpaceAfterPrefixUnaryOperators": true, + "disallowMultipleVarDecl": null, + + "requireSpaceAfterKeywords": [ + "if", + "else", + "for", + "while", + "do", + "switch", + "return", + "try", + "catch" + ], + "requireSpaceBeforeBinaryOperators": [ + "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", + "&=", "|=", "^=", "+=", + + "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", + "|", "^", "&&", "||", "===", "==", ">=", + "<=", "<", ">", "!=", "!==" + ], + "requireSpaceAfterBinaryOperators": true, + "requireSpacesInConditionalExpression": true, + "requireSpaceBeforeBlockStatements": true, + "requireLineFeedAtFileEnd": true, + "disallowSpacesInsideObjectBrackets": "all", + "disallowSpacesInsideArrayBrackets": "all", + "disallowSpacesInsideParentheses": true, + + "jsDoc": { + "checkAnnotations": true, + "checkParamNames": true, + "requireParamTypes": true, + "checkReturnTypes": true, + "checkTypes": true + }, + + "disallowMultipleLineBreaks": true, + + "disallowCommaBeforeLineBreak": null, + "disallowDanglingUnderscores": null, + "disallowEmptyBlocks": null, + "disallowTrailingComma": null, + "requireCommaBeforeLineBreak": null, + "requireDotNotation": null, + "requireMultipleVarDecl": null, + "requireParenthesesAroundIIFE": true + } + ``` + +**[Back to top](#table-of-contents)** + +## Constants + +### Vendor Globals +###### [Style [Y240](#style-y240)] + + - Create an Angular Constant for vendor libraries' global variables. + + *Why?*: Provides a way to inject vendor libraries that otherwise are globals. This improves code testability by allowing you to more easily know what the dependencies of your components are (avoids leaky abstractions). It also allows you to mock these dependencies, where it makes sense. + + ```javascript + // constants.js + + /* global toastr:false, moment:false */ + (function() { + 'use strict'; + + angular + .module('app.core') + .constant('toastr', toastr) + .constant('moment', moment); + })(); + ``` + +###### [Style [Y241](#style-y241)] + + - Use constants for values that do not change and do not come from another service. When constants are used only for a module that may be reused in multiple applications, place constants in a file per module named after the module. Until this is required, keep constants in the main module in a `constants.js` file. + + *Why?*: A value that may change, even infrequently, should be retrieved from a service so you do not have to change the source code. For example, a url for a data service could be placed in a constants but a better place would be to load it from a web service. + + *Why?*: Constants can be injected into any angular component, including providers. + + *Why?*: When an application is separated into modules that may be reused in other applications, each stand-alone module should be able to operate on its own including any dependent constants. + + ```javascript + // Constants used by the entire app + angular + .module('app.core') + .constant('moment', moment); + + // Constants used only by the sales module + angular + .module('app.sales') + .constant('events', { + ORDER_CREATED: 'event_order_created', + INVENTORY_DEPLETED: 'event_inventory_depleted' + }); + ``` + +**[Back to top](#table-of-contents)** + +## File Templates and Snippets +Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. + +### Sublime Text +###### [Style [Y250](#style-y250)] + + - Angular snippets that follow these styles and guidelines. + + - Download the [Sublime Angular snippets](assets/sublime-angular-snippets?raw=true) + - Place it in your Packages folder + - Restart Sublime + - In a JavaScript file type these commands followed by a `TAB` + + ```javascript + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngmodule // creates an Angular module + ngservice // creates an Angular service + ngfilter // creates an Angular filter + ``` + +### Visual Studio +###### [Style [Y251](#style-y251)] + + - Angular file templates that follow these styles and guidelines can be found at [SideWaffle](http://www.sidewaffle.com) + + - Download the [SideWaffle](http://www.sidewaffle.com) Visual Studio extension (vsix file) + - Run the vsix file + - Restart Visual Studio + +### WebStorm +###### [Style [Y252](#style-y252)] + + - Angular live templates that follow these styles and guidelines. + + - Download the [webstorm-angular-live-templates.xml](assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml?raw=true) + - Place it in your [templates folder](https://www.jetbrains.com/webstorm/help/project-and-ide-settings.html) + - Restart WebStorm + - In a JavaScript file type these commands followed by a `TAB`: + + ```javascript + // These are full file snippets containing an IIFE + ngapp // creates an Angular module setter + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngfilter // creates an Angular filter + ngservice // creates an Angular service + + // These are partial snippets intended to be chained + ngconfig // defines a configuration phase function + ngmodule // creates an Angular module getter + ngroute // defines an Angular ngRoute 'when' definition + ngrun // defines a run phase function + ngstate // creates an Angular UI Router state definition + ``` + + *Individual templates are also available for download within the [webstorm-angular-live-templates](assets/webstorm-angular-live-templates?raw=true) folder* + +### Atom +###### [Style [Y253](#style-y253)] + + - Angular snippets that follow these styles and guidelines. + ``` + apm install angularjs-styleguide-snippets + ``` + or + - Open Atom, then open the Package Manager (Packages -> Settings View -> Install Packages/Themes) + - Search for the package 'angularjs-styleguide-snippets' + - Click 'Install' to install the package + + - In a JavaScript file type these commands followed by a `TAB` + + ```javascript + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngmodule // creates an Angular module + ngservice // creates an Angular service + ngfilter // creates an Angular filter + ``` + +### Brackets +###### [Style [Y254](#style-y254)] + + - Angular snippets that follow these styles and guidelines. + - Download the [Brackets Angular snippets](assets/brackets-angular-snippets.yaml?raw=true) + - Brackets Extension manager ( File > Extension manager ) + - Install ['Brackets Snippets (by edc)'](https://github.com/chuyik/brackets-snippets) + - Click the light bulb in brackets' right gutter + - Click `Settings` and then `Import` + - Choose the file and select to skip or override + - Click `Start Import` + + - In a JavaScript file type these commands followed by a `TAB` + + ```javascript + // These are full file snippets containing an IIFE + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngapp // creates an Angular module setter + ngservice // creates an Angular service + ngfilter // creates an Angular filter + + // These are partial snippets intended to chained + ngmodule // creates an Angular module getter + ngstate // creates an Angular UI Router state definition + ngconfig // defines a configuration phase function + ngrun // defines a run phase function + ngwhen // defines an Angular ngRoute 'when' definition + ngtranslate // uses $translate service with its promise + ``` + +### vim +###### [Style [Y255](#style-y255)] + + - vim snippets that follow these styles and guidelines. + + - Download the [vim Angular snippets](assets/vim-angular-snippets?raw=true) + - set [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) + - copy snippets to snippet directory + + - vim UltiSnips snippets that follow these styles and guidelines. + + - Download the [vim Angular UltiSnips snippets](assets/vim-angular-ultisnips?raw=true) + - set [UltiSnips](https://github.com/SirVer/ultisnips) + - copy snippets to UltiSnips directory + + ```javascript + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngmodule // creates an Angular module + ngservice // creates an Angular service + ngfilter // creates an Angular filter + ``` + +### Visual Studio Code + +###### [Style [Y256](#style-y256)] + + - [Visual Studio Code](https://code.visualstudio.com/) snippets that follow these styles and guidelines. + + - Download the [VS Code Angular snippets](assets/vscode-snippets/javascript.json?raw=true) + - copy snippets to snippet directory, or alternatively copy and paste the snippets into your existing ones + + ```javascript + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngmodule // creates an Angular module + ngservice // creates an Angular service + ``` + +**[Back to top](#table-of-contents)** + +## Yeoman Generator +###### [Style [Y260](#style-y260)] + +You can use the [HotTowel yeoman generator](http://jpapa.me/yohottowel) to create an app that serves as a starting point for Angular that follows this style guide. + +1. Install generator-hottowel + + ``` + npm install -g generator-hottowel + ``` + +2. Create a new folder and change directory to it + + ``` + mkdir myapp + cd myapp + ``` + +3. Run the generator + + ``` + yo hottowel helloWorld + ``` + +**[Back to top](#table-of-contents)** + +## Routing +Client-side routing is important for creating a navigation flow between views and composing views that are made of many smaller templates and directives. + +###### [Style [Y270](#style-y270)] + + - Use the [AngularUI Router](http://angular-ui.github.io/ui-router/) for client-side routing. + + *Why?*: UI Router offers all the features of the Angular router plus a few additional ones including nested routes and states. + + *Why?*: The syntax is quite similar to the Angular router and is easy to migrate to UI Router. + + - Note: You can use a provider such as the `routerHelperProvider` shown below to help configure states across files, during the run phase. + + ```javascript + // customers.routes.js + angular + .module('app.customers') + .run(appRun); + + /* @ngInject */ + function appRun(routerHelper) { + routerHelper.configureStates(getStates()); + } + + function getStates() { + return [ + { + state: 'customer', + config: { + abstract: true, + template: '', + url: '/customer' + } + } + ]; + } + ``` + + ```javascript + // routerHelperProvider.js + angular + .module('blocks.router') + .provider('routerHelper', routerHelperProvider); + + routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; + /* @ngInject */ + function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { + /* jshint validthis:true */ + this.$get = RouterHelper; + + $locationProvider.html5Mode(true); + + RouterHelper.$inject = ['$state']; + /* @ngInject */ + function RouterHelper($state) { + var hasOtherwise = false; + + var service = { + configureStates: configureStates, + getStates: getStates + }; + + return service; + + /////////////// + + function configureStates(states, otherwisePath) { + states.forEach(function(state) { + $stateProvider.state(state.state, state.config); + }); + if (otherwisePath && !hasOtherwise) { + hasOtherwise = true; + $urlRouterProvider.otherwise(otherwisePath); + } + } + + function getStates() { return $state.get(); } + } + } + ``` + +###### [Style [Y271](#style-y271)] + + - Define routes for views in the module where they exist. Each module should contain the routes for the views in the module. + + *Why?*: Each module should be able to stand on its own. + + *Why?*: When removing a module or adding a module, the app will only contain routes that point to existing views. + + *Why?*: This makes it easy to enable or disable portions of an application without concern over orphaned routes. + +**[Back to top](#table-of-contents)** + +## Task Automation +Use [Gulp](http://gulpjs.com) or [Grunt](http://gruntjs.com) for creating automated tasks. Gulp leans to code over configuration while Grunt leans to configuration over code. I personally prefer Gulp as I feel it is easier to read and write, but both are excellent. + +> Learn more about gulp and patterns for task automation in my [Gulp Pluralsight course](http://jpapa.me/gulpps) + +###### [Style [Y400](#style-y400)] + + - Use task automation to list module definition files `*.module.js` before all other application JavaScript files. + + *Why?*: Angular needs the module definitions to be registered before they are used. + + *Why?*: Naming modules with a specific pattern such as `*.module.js` makes it easy to grab them with a glob and list them first. + + ```javascript + var clientApp = './src/client/app/'; + + // Always grab module files first + var files = [ + clientApp + '**/*.module.js', + clientApp + '**/*.js' + ]; + ``` + +**[Back to top](#table-of-contents)** + +## Filters + +###### [Style [Y420](#style-y420)] + + - Avoid using filters for scanning all properties of a complex object graph. Use filters for select properties. + + *Why?*: Filters can easily be abused and negatively affect performance if not used wisely, for example when a filter hits a large and deep object graph. + +**[Back to top](#table-of-contents)** + +## Angular docs +For anything else, API reference, check the [Angular documentation](//docs.angularjs.org/api). + +**[Back to top](#table-of-contents)** diff --git a/assets/above-the-fold-1.png b/a1/assets/above-the-fold-1.png similarity index 100% rename from assets/above-the-fold-1.png rename to a1/assets/above-the-fold-1.png diff --git a/assets/above-the-fold-2.png b/a1/assets/above-the-fold-2.png similarity index 100% rename from assets/above-the-fold-2.png rename to a1/assets/above-the-fold-2.png diff --git a/assets/brackets-angular-snippets.yaml b/a1/assets/brackets-angular-snippets.yaml similarity index 100% rename from assets/brackets-angular-snippets.yaml rename to a1/assets/brackets-angular-snippets.yaml diff --git a/assets/gde.png b/a1/assets/gde.png similarity index 100% rename from assets/gde.png rename to a1/assets/gde.png diff --git a/assets/modularity-1.png b/a1/assets/modularity-1.png similarity index 100% rename from assets/modularity-1.png rename to a1/assets/modularity-1.png diff --git a/assets/modularity-2.png b/a1/assets/modularity-2.png similarity index 100% rename from assets/modularity-2.png rename to a1/assets/modularity-2.png diff --git a/assets/ng-clean-code-banner.png b/a1/assets/ng-clean-code-banner.png similarity index 100% rename from assets/ng-clean-code-banner.png rename to a1/assets/ng-clean-code-banner.png diff --git a/assets/sublime-angular-snippets/angular.controller.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.controller.sublime-snippet similarity index 100% rename from assets/sublime-angular-snippets/angular.controller.sublime-snippet rename to a1/assets/sublime-angular-snippets/angular.controller.sublime-snippet diff --git a/assets/sublime-angular-snippets/angular.directive.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.directive.sublime-snippet similarity index 100% rename from assets/sublime-angular-snippets/angular.directive.sublime-snippet rename to a1/assets/sublime-angular-snippets/angular.directive.sublime-snippet diff --git a/assets/sublime-angular-snippets/angular.factory.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.factory.sublime-snippet similarity index 100% rename from assets/sublime-angular-snippets/angular.factory.sublime-snippet rename to a1/assets/sublime-angular-snippets/angular.factory.sublime-snippet diff --git a/assets/sublime-angular-snippets/angular.filter.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.filter.sublime-snippet similarity index 100% rename from assets/sublime-angular-snippets/angular.filter.sublime-snippet rename to a1/assets/sublime-angular-snippets/angular.filter.sublime-snippet diff --git a/assets/sublime-angular-snippets/angular.module.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.module.sublime-snippet similarity index 100% rename from assets/sublime-angular-snippets/angular.module.sublime-snippet rename to a1/assets/sublime-angular-snippets/angular.module.sublime-snippet diff --git a/assets/sublime-angular-snippets/angular.service.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.service.sublime-snippet similarity index 100% rename from assets/sublime-angular-snippets/angular.service.sublime-snippet rename to a1/assets/sublime-angular-snippets/angular.service.sublime-snippet diff --git a/assets/testing-tools.png b/a1/assets/testing-tools.png similarity index 100% rename from assets/testing-tools.png rename to a1/assets/testing-tools.png diff --git a/assets/vim-angular-snippets/angular.controller.snip b/a1/assets/vim-angular-snippets/angular.controller.snip similarity index 100% rename from assets/vim-angular-snippets/angular.controller.snip rename to a1/assets/vim-angular-snippets/angular.controller.snip diff --git a/assets/vim-angular-snippets/angular.directive.snip b/a1/assets/vim-angular-snippets/angular.directive.snip similarity index 100% rename from assets/vim-angular-snippets/angular.directive.snip rename to a1/assets/vim-angular-snippets/angular.directive.snip diff --git a/assets/vim-angular-snippets/angular.factory.snip b/a1/assets/vim-angular-snippets/angular.factory.snip similarity index 100% rename from assets/vim-angular-snippets/angular.factory.snip rename to a1/assets/vim-angular-snippets/angular.factory.snip diff --git a/assets/vim-angular-snippets/angular.filter.snip b/a1/assets/vim-angular-snippets/angular.filter.snip similarity index 100% rename from assets/vim-angular-snippets/angular.filter.snip rename to a1/assets/vim-angular-snippets/angular.filter.snip diff --git a/assets/vim-angular-snippets/angular.module.snip b/a1/assets/vim-angular-snippets/angular.module.snip similarity index 100% rename from assets/vim-angular-snippets/angular.module.snip rename to a1/assets/vim-angular-snippets/angular.module.snip diff --git a/assets/vim-angular-snippets/angular.service.snip b/a1/assets/vim-angular-snippets/angular.service.snip similarity index 100% rename from assets/vim-angular-snippets/angular.service.snip rename to a1/assets/vim-angular-snippets/angular.service.snip diff --git a/assets/vim-angular-ultisnips/javascript_angular.controller.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.controller.snippets similarity index 100% rename from assets/vim-angular-ultisnips/javascript_angular.controller.snippets rename to a1/assets/vim-angular-ultisnips/javascript_angular.controller.snippets diff --git a/assets/vim-angular-ultisnips/javascript_angular.directive.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.directive.snippets similarity index 100% rename from assets/vim-angular-ultisnips/javascript_angular.directive.snippets rename to a1/assets/vim-angular-ultisnips/javascript_angular.directive.snippets diff --git a/assets/vim-angular-ultisnips/javascript_angular.factory.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.factory.snippets similarity index 100% rename from assets/vim-angular-ultisnips/javascript_angular.factory.snippets rename to a1/assets/vim-angular-ultisnips/javascript_angular.factory.snippets diff --git a/assets/vim-angular-ultisnips/javascript_angular.filter.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.filter.snippets similarity index 100% rename from assets/vim-angular-ultisnips/javascript_angular.filter.snippets rename to a1/assets/vim-angular-ultisnips/javascript_angular.filter.snippets diff --git a/assets/vim-angular-ultisnips/javascript_angular.module.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.module.snippets similarity index 100% rename from assets/vim-angular-ultisnips/javascript_angular.module.snippets rename to a1/assets/vim-angular-ultisnips/javascript_angular.module.snippets diff --git a/assets/vim-angular-ultisnips/javascript_angular.service.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.service.snippets similarity index 100% rename from assets/vim-angular-ultisnips/javascript_angular.service.snippets rename to a1/assets/vim-angular-ultisnips/javascript_angular.service.snippets diff --git a/assets/vscode-snippets/javascript.json b/a1/assets/vscode-snippets/javascript.json similarity index 100% rename from assets/vscode-snippets/javascript.json rename to a1/assets/vscode-snippets/javascript.json diff --git a/assets/vscode-snippets/typescript.json b/a1/assets/vscode-snippets/typescript.json similarity index 100% rename from assets/vscode-snippets/typescript.json rename to a1/assets/vscode-snippets/typescript.json diff --git a/assets/webstorm-angular-live-templates/angular.app.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.app.webstorm-live-template.xml similarity index 100% rename from assets/webstorm-angular-live-templates/angular.app.webstorm-live-template.xml rename to a1/assets/webstorm-angular-live-templates/angular.app.webstorm-live-template.xml diff --git a/assets/webstorm-angular-live-templates/angular.config.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.config.webstorm-live-template.xml similarity index 100% rename from assets/webstorm-angular-live-templates/angular.config.webstorm-live-template.xml rename to a1/assets/webstorm-angular-live-templates/angular.config.webstorm-live-template.xml diff --git a/assets/webstorm-angular-live-templates/angular.controller.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.controller.webstorm-live-template.xml similarity index 100% rename from assets/webstorm-angular-live-templates/angular.controller.webstorm-live-template.xml rename to a1/assets/webstorm-angular-live-templates/angular.controller.webstorm-live-template.xml diff --git a/assets/webstorm-angular-live-templates/angular.directive.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.directive.webstorm-live-template.xml similarity index 100% rename from assets/webstorm-angular-live-templates/angular.directive.webstorm-live-template.xml rename to a1/assets/webstorm-angular-live-templates/angular.directive.webstorm-live-template.xml diff --git a/assets/webstorm-angular-live-templates/angular.factory.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.factory.webstorm-live-template.xml similarity index 100% rename from assets/webstorm-angular-live-templates/angular.factory.webstorm-live-template.xml rename to a1/assets/webstorm-angular-live-templates/angular.factory.webstorm-live-template.xml diff --git a/assets/webstorm-angular-live-templates/angular.filter.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.filter.webstorm-live-template.xml similarity index 100% rename from assets/webstorm-angular-live-templates/angular.filter.webstorm-live-template.xml rename to a1/assets/webstorm-angular-live-templates/angular.filter.webstorm-live-template.xml diff --git a/assets/webstorm-angular-live-templates/angular.module.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.module.webstorm-live-template.xml similarity index 100% rename from assets/webstorm-angular-live-templates/angular.module.webstorm-live-template.xml rename to a1/assets/webstorm-angular-live-templates/angular.module.webstorm-live-template.xml diff --git a/assets/webstorm-angular-live-templates/angular.route.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.route.webstorm-live-template.xml similarity index 100% rename from assets/webstorm-angular-live-templates/angular.route.webstorm-live-template.xml rename to a1/assets/webstorm-angular-live-templates/angular.route.webstorm-live-template.xml diff --git a/assets/webstorm-angular-live-templates/angular.run.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.run.webstorm-live-template.xml similarity index 100% rename from assets/webstorm-angular-live-templates/angular.run.webstorm-live-template.xml rename to a1/assets/webstorm-angular-live-templates/angular.run.webstorm-live-template.xml diff --git a/assets/webstorm-angular-live-templates/angular.service.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.service.webstorm-live-template.xml similarity index 100% rename from assets/webstorm-angular-live-templates/angular.service.webstorm-live-template.xml rename to a1/assets/webstorm-angular-live-templates/angular.service.webstorm-live-template.xml diff --git a/assets/webstorm-angular-live-templates/angular.state.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.state.webstorm-live-template.xml similarity index 100% rename from assets/webstorm-angular-live-templates/angular.state.webstorm-live-template.xml rename to a1/assets/webstorm-angular-live-templates/angular.state.webstorm-live-template.xml diff --git a/assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml b/a1/assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml similarity index 100% rename from assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml rename to a1/assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml diff --git a/i18n/README.md b/a1/i18n/README.md similarity index 100% rename from i18n/README.md rename to a1/i18n/README.md diff --git a/i18n/de-DE.md b/a1/i18n/de-DE.md similarity index 100% rename from i18n/de-DE.md rename to a1/i18n/de-DE.md diff --git a/i18n/es-ES.md b/a1/i18n/es-ES.md similarity index 100% rename from i18n/es-ES.md rename to a1/i18n/es-ES.md diff --git a/i18n/fr-FR.md b/a1/i18n/fr-FR.md similarity index 100% rename from i18n/fr-FR.md rename to a1/i18n/fr-FR.md diff --git a/i18n/it-IT.md b/a1/i18n/it-IT.md similarity index 100% rename from i18n/it-IT.md rename to a1/i18n/it-IT.md diff --git a/i18n/ja-JP.md b/a1/i18n/ja-JP.md similarity index 100% rename from i18n/ja-JP.md rename to a1/i18n/ja-JP.md diff --git a/i18n/ko_KR.md b/a1/i18n/ko_KR.md similarity index 100% rename from i18n/ko_KR.md rename to a1/i18n/ko_KR.md diff --git a/i18n/mk-MK.md b/a1/i18n/mk-MK.md similarity index 100% rename from i18n/mk-MK.md rename to a1/i18n/mk-MK.md diff --git a/i18n/pt-BR.md b/a1/i18n/pt-BR.md similarity index 100% rename from i18n/pt-BR.md rename to a1/i18n/pt-BR.md diff --git a/i18n/ru-RU.md b/a1/i18n/ru-RU.md similarity index 100% rename from i18n/ru-RU.md rename to a1/i18n/ru-RU.md diff --git a/i18n/tr-TR.md b/a1/i18n/tr-TR.md similarity index 100% rename from i18n/tr-TR.md rename to a1/i18n/tr-TR.md diff --git a/i18n/zh-CN.md b/a1/i18n/zh-CN.md similarity index 100% rename from i18n/zh-CN.md rename to a1/i18n/zh-CN.md From 30db93f67466d71b878a250c1f0b167001234490 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 21 Feb 2016 11:21:52 -0500 Subject: [PATCH 035/114] fixed img --- a1/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/README.md b/a1/README.md index 4b42bd5e..24d5baea 100644 --- a/a1/README.md +++ b/a1/README.md @@ -12,7 +12,7 @@ The purpose of this style guide is to provide guidance on building Angular appli >If you like this guide, check out my [Angular Patterns: Clean Code](http://jpapa.me/ngclean) course at Pluralsight which is a companion to this guide. - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Community Awesomeness and Credit Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. As such, Angular expert Todd Motto and I have collaborated on many styles and conventions. We agree on most, and some we diverge. I encourage you to check out [Todd's guidelines](https://github.com/toddmotto/angular-styleguide) to get a sense for his approach and how it compares. From e0f4aba0e62143e5d474e131f017edcdbe8d0ad6 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 21 Feb 2016 11:22:20 -0500 Subject: [PATCH 036/114] fixed images --- a1/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/a1/README.md b/a1/README.md index 24d5baea..d6c6d7a0 100644 --- a/a1/README.md +++ b/a1/README.md @@ -488,7 +488,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see } ``` - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) + ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) Note: If the function is a 1 liner consider keeping it right up top, as long as readability is not affected. @@ -833,7 +833,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see This way bindings are mirrored across the host object, primitive values cannot update alone using the revealing module pattern. - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-2.png) + ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) ### Function Declarations to Hide Implementation Details ###### [Style [Y053](#style-y053)] @@ -2308,7 +2308,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see session-detail.controller.js ``` - ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) + ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) Note: Do not structure your app using folders-by-type. This requires moving to multiple folders when working on a feature and gets unwieldy quickly as the app grows to 5, 10 or 25+ views and controllers (and other features), which makes it more difficult than folder-by-feature to locate files. @@ -2402,7 +2402,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see - The application root module depends on the app specific feature modules and any shared or reusable modules. - ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) + ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) *Why?*: The main app module contains a quickly identifiable manifest of the application's features. @@ -2634,7 +2634,7 @@ Unit testing helps maintain clean code, as such I included some of my recommenda "mocha": true, ``` - ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) + ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) ### Organizing Tests ###### [Style [Y197](#style-y197)] From 867f50cd98f836e24c8edcd2b146a51a6850dd77 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 21 Feb 2016 11:54:12 -0500 Subject: [PATCH 037/114] a2 draft --- a2/README.md | 423 ++++++++++++++++++++++++++++++++++ a2/notes.md | 632 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1055 insertions(+) create mode 100644 a2/README.md create mode 100644 a2/notes.md diff --git a/a2/README.md b/a2/README.md new file mode 100644 index 00000000..710400c8 --- /dev/null +++ b/a2/README.md @@ -0,0 +1,423 @@ +# Angular 2 Style Guide **D R A F T** +Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and conributions from the Angular core team. Nobody has the same massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. + +My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. By wary and definately ask questions when someone, including me, publishes a guide :) + +## Angular Team Endorsed +Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. + +## Purpose +*Opinionated Angular style guide for teams by [@john_papa](//twitter.com/john_papa)* + +If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular applications, then step right in. These styles are based on my development experience with [Angular](//angularjs.org), presentations, [Pluralsight training courses](http://app.pluralsight.com/author/john-papa) and working in teams. + +The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions I use and, more importantly, why I choose them. + +>If you like this guide, check out my Angular 2 First Look course **coming soon** to Pluralsight. + +## Community Awesomeness and Credit +Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My friend Ward has certainly helped influence the ultimate evolution of this guide. + +##Translations +Translations of this Angular 2 style guide are maintained by the community. Due to the in flux nature of this guide, I will hold off on accepting contributions for the time being. But I hope to get the same amazing contributions we had for the v1 of the guide! + +## Table of Contents + + 1. [Single Responsibility](#single-responsibility) + 1. [Modules](#modules) + 1. [Components](#components) + 1. [Services](#services) + 1. [Factories](#factories) + 1. [Data Services](#data-services) + 1. [Naming](#naming) + 1. [Application Structure LIFT Principle](#application-structure-lift-principle) + 1. [Application Structure](#application-structure) + 1. [File Templates and Snippets](#file-templates-and-snippets) + 1. [Angular CLI](#angular-cli) + 1. [Routing](#routing) + 1. [Angular Docs](#angular-docs) + +## Single Responsibility + +### Rule of 1 +###### [Style [A2-001](#style-a2-001)] + + - Define 1 component per file, recommended to be less than 500 lines of code. + + *Why?*: One component per file promotes easier unit testing and mocking. + + *Why?*: One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control. + + *Why?*: One component per file avoids hidden bugs that often arise when combining components in a file where they may share variables, create unwanted closures, or unwanted coupling with dependencies. + + The following example defines the `app` module and its dependencies, defines a component, and defines a factory all in the same file. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Small Functions +###### [Style [A2-002](#style-a2-002)] + + - Define small functions, no more than 75 LOC (less is better). + + *Why?*: Small functions are easier to test, especially when they do one thing and serve one purpose. + + *Why?*: Small functions promote reuse. + + *Why?*: Small functions are easier to read. + + *Why?*: Small functions are easier to maintain. + + *Why?*: Small functions help avoid hidden bugs that come with large functions that share variables with external scope, create unwanted closures, or unwanted coupling with dependencies. + +**[Back to top](#table-of-contents)** + +## Modules + +### Avoid Naming Collisions +###### [Style [A2-020](#style-a2-020)] + + - Use unique naming conventions with separators for sub-modules. + + *Why?*: Unique names help avoid module name collisions. Separators help define modules and their submodule hierarchy. For example `app` may be your root module while `app.dashboard` and `app.users` may be modules that are used as dependencies of `app`. + +### Definitions (aka Setters) +###### [Style [A2-021](#style-a2-021)] + +## Components + +### Bindable Members Up Top +###### [Style [A2-033](#style-a2-033)] + + - Place bindable members at the top of the component, alphabetized, and not spread through the component code. + + *Why?*: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the component can be bound and used in the View. + + *Why?*: Setting anonymous functions in-line can be easy, but when those functions are more than 1 line of code they can reduce the readability. Defining the functions below the bindable members (the functions will be hoisted) moves the implementation details down, keeps the bindable members up top, and makes it easier to read. + + **example coming soon** + +### Defer Logic to Services +###### [Style [A2-035](#style-a2-035)] + + - Defer logic in a component by delegating to services. + + *Why?*: Logic may be reused by multiple components when placed within a service and exposed via a function. + + *Why?*: Logic in a service can more easily be isolated in a unit test, while the calling logic in the component can be easily mocked. + + *Why?*: Removes dependencies and hides implementation details from the component. + + *Why?*: Keeps the component slim, trim, and focused. + + **example coming soon** + +### Keep Components Focused +###### [Style [A2-037](#style-a2-037)] + + - Define a component for a view, and try not to reuse the component for other views. Instead, move reusable logic to factories and keep the component simple and focused on its view. + + *Why?*: Reusing components with several views is brittle and good end-to-end (e2e) test coverage is required to ensure stability across large applications. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Services + +### Singletons +###### [Style [A2-040](#style-a2-040)] + + - Services are singletons and should be used for sharing data and functionality. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Single Responsibility +###### [Style [A2-050](#style-a2-050)] + + - Services should have a [single responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle), that is encapsulated by its context. Once a service begins to exceed that singular purpose, a new one should be created. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Singletons +###### [Style [A2-051](#style-a2-051)] + + - Factories are singletons and return an object that contains the members of the service. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Accessible Members Up Top +###### [Style [A2-052](#style-a2-052)] + + - Expose the callable members of the service (its interface) at the top + + *Why?*: Placing the callable members at the top makes it easy to read and helps you instantly identify which members of the service can be called and must be unit tested (and/or mocked). + + *Why?*: This is especially helpful when the file gets longer as it helps avoid the need to scroll to see what is exposed. + + *Why?*: Setting functions as you go can be easy, but when those functions are more than 1 line of code they can reduce the readability and cause more scrolling. Defining the callable interface via the returned service moves the implementation details down, keeps the callable interface up top, and makes it easier to read. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Data Services + +### Separate Data Calls +###### [Style [A2-060](#style-a2-060)] + + - Refactor logic for making data operations and interacting with data to a service. Make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations. + + *Why?*: The component's responsibility is for the presentation and gathering of information for the view. It should not care how it gets the data, just that it knows who to ask for it. Separating the data services moves the logic on how to get it to the data service, and lets the component be simpler and more focused on the view. + + *Why?*: This makes it easier to test (mock or real) the data calls when testing a component that uses a data service. + + *Why?*: Data service implementation may have very specific code to handle the data repository. This may include headers, how to talk to the data, or other services such as `$http`. Separating the logic into a data service encapsulates this logic in a single place hiding the implementation from the outside consumers (perhaps a component), also making it easier to change the implementation. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Naming + +### Naming Guidelines +###### [Style [A2-120](#style-a2-120)] + + - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.js`. There are 2 names for most assets: + * the file name (`avengers.component.ts`) + * the registered component name with Angular (`Component`) + + *Why?*: Naming conventions help provide a consistent way to find content at a glance. Consistency within the project is vital. Consistency with a team is important. Consistency across a company provides tremendous efficiency. + + *Why?*: The naming conventions should simply help you find your code faster and make it easier to understand. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Feature File Names +###### [Style [A2-121](#style-a2-121)] + + - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.ts`. + + *Why?*: Provides a consistent way to quickly identify components. + + *Why?*: Provides pattern matching for any automated tasks. + +### Test File Names +###### [Style [A2-122](#style-a2-122)] + + - Name test specifications similar to the component they test with a suffix of `spec`. + + *Why?*: Provides a consistent way to quickly identify components. + + *Why?*: Provides pattern matching for [karma](http://karma-runner.github.io/) or other test runners. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Component Names +###### [Style [A2-123](#style-a2-123)] + + - Use consistent names for all components named after their feature. Use UpperCamelCase for components, as they are constructors. + + *Why?*: Provides a consistent way to quickly identify and reference components. + + *Why?*: UpperCamelCase is conventional for identifying object that can be instantiated using a constructor. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Component Name Suffix +###### [Style [A2-124](#style-a2-124)] + + - Append the component name with the suffix `Component`. + + *Why?*: The `Component` suffix is more commonly used and is more explicitly descriptive. + + ```typescript + /** + * recommended + */ + + // avengers.component.ts + export class AvengersComponent { } + ``` + +### Service Names +###### [Style [A2-125](#style-a2-125)] + + - Use consistent names for all services named after their feature. Use UpperCamelCase for services. Only suffix service and factories with `Service` when it is not clear what they are (i.e. when they are nouns). + + *Why?*: Provides a consistent way to quickly identify and reference services. + + *Why?*: Clear service names such as `logger` do not require a suffix. + + *Why?*: Service names such as `avengers` are nouns and require a suffix and should be named `AvengersService`. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Directive Component Names +###### [Style [A2-126](#style-a2-126)] + + - Use consistent names for all directives using camel-case. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). + + *Why?*: Provides a consistent way to quickly identify and reference components. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Routes +###### [Style [A2-129](#style-a2-129)] + + - Separate route configuration into a routing component file. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Application Structure LIFT Principle +### LIFT +###### [Style [Y140](#style-y140)] + + - Structure your app such that you can `L`ocate your code quickly, `I`dentify the code at a glance, keep the `F`lattest structure you can, and `T`ry to stay DRY. The structure should follow these 4 basic guidelines. + + *Why LIFT?*: Provides a consistent structure that scales well, is modular, and makes it easier to increase developer efficiency by finding code quickly. Another way to check your app structure is to ask yourself: How quickly can you open and work in all of the related files for a feature? + + When I find my structure is not feeling comfortable, I go back and revisit these LIFT guidelines + + 1. `L`ocating our code is easy + 2. `I`dentify code at a glance + 3. `F`lat structure as long as we can + 4. `T`ry to stay DRY (Don’t Repeat Yourself) or T-DRY + +### Locate +###### [Style [Y141](#style-y141)] + + - Make locating your code intuitive, simple and fast. + + *Why?*: I find this to be super important for a project. If the team cannot find the files they need to work on quickly, they will not be able to work as efficiently as possible, and the structure needs to change. You may not know the file name or where its related files are, so putting them in the most intuitive locations and near each other saves a ton of time. A descriptive folder structure can help with this. + + ``` + /bower_components + /client + /app + /avengers + /blocks + /exception + /logger + /core + /dashboard + /data + /layout + /widgets + /content + index.html + .bower.json + ``` + +### Identify +###### [Style [Y142](#style-y142)] + + - When you look at a file you should instantly know what it contains and represents. + + *Why?*: You spend less time hunting and pecking for code, and become more efficient. If this means you want longer file names, then so be it. Be descriptive with file names and keeping the contents of the file to exactly 1 component. Avoid files with multiple controllers, multiple services, or a mixture. There are deviations of the 1 per file rule when I have a set of very small features that are all related to each other, they are still easily identifiable. + +### Flat +###### [Style [Y143](#style-y143)] + + - Keep a flat folder structure as long as possible. When you get to 7+ files, begin considering separation. + + *Why?*: Nobody wants to search 7 levels of folders to find a file. Think about menus on web sites … anything deeper than 2 should take serious consideration. In a folder structure there is no hard and fast number rule, but when a folder has 7-10 files, that may be time to create subfolders. Base it on your comfort level. Use a flatter structure until there is an obvious value (to help the rest of LIFT) in creating a new folder. + +### T-DRY (Try to Stick to DRY) +###### [Style [Y144](#style-y144)] + + - Be DRY, but don't go nuts and sacrifice readability. + + *Why?*: Being DRY is important, but not crucial if it sacrifices the others in LIFT, which is why I call it T-DRY. I don’t want to type session-view.html for a view because, well, it’s obviously a view. If it is not obvious or by convention, then I name it. + +**[Back to top](#table-of-contents)** + +## Application Structure + +### Overall Guidelines +###### [Style [A2-150](#style-a2-150)] + + - Have a near term view of implementation and a long term vision. In other words, start small but keep in mind on where the app is heading down the road. All of the app's code goes in a root folder named `app`. All content is 1 feature per file. Each component, service, pipe is in its own file. All 3rd party vendor scripts are stored in another root folder and not in the `app` folder. I didn't write them and I don't want them cluttering my app. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Layout +###### [Style [A2-151](#style-a2-151)] + + - Place components that define the overall layout of the application in a folder named `layout`. These may include a shell view and component may act as the container for the app, navigation, menus, content areas, and other regions. + + *Why?*: Organizes all layout in a single place re-used throughout the application. + +### Folders-by-Feature Structure +###### [Style [A2-152](#style-a2-152)] + + - Create folders named for the feature they represent. When a folder grows to contain more than 7 files, start to consider creating a folder for them. Your threshold may be different, so adjust as needed. + + *Why?*: A developer can locate the code, identify what each file represents at a glance, the structure is flat as can be, and there is no repetitive nor redundant names. + + *Why?*: The LIFT guidelines are all covered. + + *Why?*: Helps reduce the app from becoming cluttered through organizing the content and keeping them aligned with the LIFT guidelines. + + *Why?*: When there are a lot of files (10+) locating them is easier with a consistent folder structures and more difficult in flat structures. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Modularity + +### Many Small, Self Contained Modules +###### [Style [A2-160](#style-a2-160)] + + - Create small modules that encapsulate one responsibility. + + *Why?*: Modular applications make it easy to plug and go as they allow the development teams to build vertical slices of the applications and roll out incrementally. This means we can plug in new features as we develop them. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## File Templates and Snippets +Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. + +### Visual Studio Code + + - [Visual Studio Code](https://code.visualstudio.com/) snippets that follow these styles and guidelines. + +**[Back to top](#table-of-contents)** + +## Angular CLI + +**[Back to top](#table-of-contents)** + +## Routing +Client-side routing is important for creating a navigation flow between views and composing views that are made of many smaller templates and directives. + +**[Back to top](#table-of-contents)** + +## Angular docs +For anything else, API reference, check the [Angular 2 documentation](//angular.io). + +**[Back to top](#table-of-contents)** diff --git a/a2/notes.md b/a2/notes.md new file mode 100644 index 00000000..345ade8d --- /dev/null +++ b/a2/notes.md @@ -0,0 +1,632 @@ +# Angular 2 Style Guide +Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and conributions from the Angular core team. Nobody has the same massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. + +My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. By wary and definately ask questions when someone, including me, publishes a guide :) + +## Angular Team Endorsed +Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. + +## Purpose +*Opinionated Angular style guide for teams by [@john_papa](//twitter.com/john_papa)* + +If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular applications, then step right in. These styles are based on my development experience with [Angular](//angularjs.org), presentations, [Pluralsight training courses](http://app.pluralsight.com/author/john-papa) and working in teams. + +The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions I use and, more importantly, why I choose them. + +>If you like this guide, check out my Angular 2 First Look course **coming soon** to Pluralsight. + +## Community Awesomeness and Credit +Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My friend Ward has certainly helped influence the ultimate evolution of this guide. + +##Translations +Translations of this Angular 2 style guide are maintained by the community. Due to the in flux nature of this guide, I will hold off on accepting contributions for the time being. But I hope to get the same amazing contributions we had for the v1 of the guide! + +## Table of Contents + + 1. [Single Responsibility](#single-responsibility) + 1. [IIFE](#iife) + 1. [Modules](#modules) + 1. [Components](#components) + 1. [Services](#services) + 1. [Factories](#factories) + 1. [Data Services](#data-services) + 1. [Directives](#directives) + 1. [Resolving Promises](#resolving-promises) + 1. [Manual Annotating for Dependency Injection](#manual-annotating-for-dependency-injection) + 1. [Minification and Annotation](#minification-and-annotation) + 1. [Exception Handling](#exception-handling) + 1. [Naming](#naming) + 1. [Application Structure LIFT Principle](#application-structure-lift-principle) + 1. [Application Structure](#application-structure) + 1. [Modularity](#modularity) + 1. [Startup Logic](#startup-logic) + 1. [Angular $ Wrapper Services](#angular--wrapper-services) + 1. [Testing](#testing) + 1. [Animations](#animations) + 1. [Comments](#comments) + 1. [JSHint](#js-hint) + 1. [JSCS](#jscs) + 1. [Constants](#constants) + 1. [File Templates and Snippets](#file-templates-and-snippets) + 1. [Yeoman Generator](#yeoman-generator) + 1. [Routing](#routing) + 1. [Task Automation](#task-automation) + 1. [Filters](#filters) + 1. [Angular Docs](#angular-docs) + 1. [Contributing](#contributing) + 1. [License](#license) + +## Single Responsibility + +### Rule of 1 +###### [Style [A2-001](#style-a2-001)] + + - Define 1 component per file, recommended to be less than 500 lines of code. + + *Why?*: One component per file promotes easier unit testing and mocking. + + *Why?*: One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control. + + *Why?*: One component per file avoids hidden bugs that often arise when combining components in a file where they may share variables, create unwanted closures, or unwanted coupling with dependencies. + + The following example defines the `app` module and its dependencies, defines a component, and defines a factory all in the same file. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Small Functions +###### [Style [A2-002](#style-a2-002)] + + - Define small functions, no more than 75 LOC (less is better). + + *Why?*: Small functions are easier to test, especially when they do one thing and serve one purpose. + + *Why?*: Small functions promote reuse. + + *Why?*: Small functions are easier to read. + + *Why?*: Small functions are easier to maintain. + + *Why?*: Small functions help avoid hidden bugs that come with large functions that share variables with external scope, create unwanted closures, or unwanted coupling with dependencies. + +**[Back to top](#table-of-contents)** + +## Modules + +### Avoid Naming Collisions +###### [Style [A2-020](#style-a2-020)] + + - Use unique naming conventions with separators for sub-modules. + + *Why?*: Unique names help avoid module name collisions. Separators help define modules and their submodule hierarchy. For example `app` may be your root module while `app.dashboard` and `app.users` may be modules that are used as dependencies of `app`. + +### Definitions (aka Setters) +###### [Style [A2-021](#style-a2-021)] + +## Components + +### Bindable Members Up Top +###### [Style [A2-033](#style-a2-033)] + + - Place bindable members at the top of the component, alphabetized, and not spread through the component code. + + *Why?*: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the component can be bound and used in the View. + + *Why?*: Setting anonymous functions in-line can be easy, but when those functions are more than 1 line of code they can reduce the readability. Defining the functions below the bindable members (the functions will be hoisted) moves the implementation details down, keeps the bindable members up top, and makes it easier to read. + + **example coming soon** + +### Defer Logic to Services +###### [Style [A2-035](#style-a2-035)] + + - Defer logic in a component by delegating to services. + + *Why?*: Logic may be reused by multiple components when placed within a service and exposed via a function. + + *Why?*: Logic in a service can more easily be isolated in a unit test, while the calling logic in the component can be easily mocked. + + *Why?*: Removes dependencies and hides implementation details from the component. + + *Why?*: Keeps the component slim, trim, and focused. + + **example coming soon** + +### Keep Components Focused +###### [Style [A2-037](#style-a2-037)] + + - Define a component for a view, and try not to reuse the component for other views. Instead, move reusable logic to factories and keep the component simple and focused on its view. + + *Why?*: Reusing components with several views is brittle and good end-to-end (e2e) test coverage is required to ensure stability across large applications. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Services + +### Singletons +###### [Style [A2-040](#style-a2-040)] + + - Services are singletons and should be used for sharing data and functionality. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Single Responsibility +###### [Style [A2-050](#style-a2-050)] + + - Services should have a [single responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle), that is encapsulated by its context. Once a service begins to exceed that singular purpose, a new one should be created. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Singletons +###### [Style [A2-051](#style-a2-051)] + + - Factories are singletons and return an object that contains the members of the service. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Accessible Members Up Top +###### [Style [A2-052](#style-a2-052)] + + - Expose the callable members of the service (its interface) at the top + + *Why?*: Placing the callable members at the top makes it easy to read and helps you instantly identify which members of the service can be called and must be unit tested (and/or mocked). + + *Why?*: This is especially helpful when the file gets longer as it helps avoid the need to scroll to see what is exposed. + + *Why?*: Setting functions as you go can be easy, but when those functions are more than 1 line of code they can reduce the readability and cause more scrolling. Defining the callable interface via the returned service moves the implementation details down, keeps the callable interface up top, and makes it easier to read. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Data Services + +### Separate Data Calls +###### [Style [A2-060](#style-a2-060)] + + - Refactor logic for making data operations and interacting with data to a service. Make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations. + + *Why?*: The component's responsibility is for the presentation and gathering of information for the view. It should not care how it gets the data, just that it knows who to ask for it. Separating the data services moves the logic on how to get it to the data service, and lets the component be simpler and more focused on the view. + + *Why?*: This makes it easier to test (mock or real) the data calls when testing a component that uses a data service. + + *Why?*: Data service implementation may have very specific code to handle the data repository. This may include headers, how to talk to the data, or other services such as `$http`. Separating the logic into a data service encapsulates this logic in a single place hiding the implementation from the outside consumers (perhaps a component), also making it easier to change the implementation. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Return a Promise or Observable from Data Calls +###### [Style [A2-061](#style-a2-061)] + + - When calling a data service that returns a promise such as `http`, return a promise or Observable in your calling function too. + + *Why?*: You can chain the promises together and take further action after the data call completes and resolves or rejects the promise. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Directives +### Limit 1 Per File +###### [Style [A2-070](#style-a2-070)] + + - Create one directive per file. Name the file for the directive. + + *Why?*: It is easy to mash all the directives in one file, but difficult to then break those out so some are shared across apps, some across modules, some just for one module. + + *Why?*: One directive per file is easy to maintain. + +### Manipulate DOM in a Structural Directive +###### [Style [A2-072](#style-a2-072)] + + - When manipulating the DOM directly, use a directive. If alternative ways can be used such as using CSS to set styles or the [animation services](https://docs.angularjs.org/api/ngAnimate), Angular templating, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) or [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), then use those instead. For example, if the directive simply hides and shows, use ngHide/ngShow. + + *Why?*: DOM manipulation can be difficult to test, debug, and there are often better ways (e.g. CSS, animations, templates) + +### Provide a Unique Directive Prefix +###### [Style [A2-073](#style-a2-073)] + + - Provide a short, unique and descriptive directive prefix such as `acmeSalesCustomerInfo` which would be declared in HTML as `acme-sales-customer-info`. + + *Why?*: The unique short prefix identifies the directive's context and origin. For example a prefix of `cc-` may indicate that the directive is part of a CodeCamper app while `acme-` may indicate a directive for the Acme company. + + Note: Avoid `ng-` as these are reserved for Angular directives. Research widely used directives to avoid naming conflicts, such as `ion-` for the [Ionic Framework](http://ionicframework.com/). + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Resolving Promises +### Component Activation Promises +###### [Style [A2-080](#style-a2-080)] + + - Resolve start-up logic for a component in an `activate` function. + + *Why?*: Placing start-up logic in a consistent place in the component makes it easier to locate, more consistent to test, and helps avoid spreading out the activation logic across the component. + + *Why?*: The component `activate` makes it convenient to re-use the logic for a refresh for the component/View, keeps the logic together, gets the user to the View faster, makes animations easy on the `ng-view` or `ui-view`, and feels snappier to the user. + + Note: If you need to conditionally cancel the route before you start using the component, use a [route resolve](A2-style-a2-081) instead. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Route Resolve Promises +###### [Style [A2-081](#style-a2-081)] + + - When a component depends on a promise to be resolved before the component is activated, resolve those dependencies in the `$routeProvider` before the component logic is executed. If you need to conditionally cancel a route before the component is activated, use a route resolver. + + - Use a route resolve when you want to decide to cancel the route before ever transitioning to the View. + + *Why?*: A component may require data before it loads. That data may come from a promise via a custom factory or [$http](https://docs.angularjs.org/api/ng/service/$http). Using a [route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) allows the promise to resolve before the component logic executes, so it might take action based on that data from the promise. + + *Why?*: The code executes after the route and in the component’s activate function. The View starts to load right away. Data binding kicks in when the activate promise resolves. A “busy” animation can be shown during the view transition (via `ng-view` or `ui-view`) + + Note: The code executes before the route via a promise. Rejecting the promise cancels the route. Resolve makes the new view wait for the route to resolve. A “busy” animation can be shown before the resolve and through the view transition. If you want to get to the View faster and do not require a checkpoint to decide if you can get to the View, consider the [component `activate` technique](#style-aA2--080) instead. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Handling Exceptions with Promises +###### [Style [A2-082](#style-a2-082)] + + - The `catch` block of a promise must return a rejected promise to maintain the exception in the promise chain. + + - Always handle exceptions in services/factories. + + *Why?*: If the `catch` block does not return a rejected promise, the caller of the promise will not know an exception occurred. The caller's `then` will execute. Thus, the user may never know what happened. + + *Why?*: To avoid swallowing errors and misinforming the user. + + Note: Consider putting any exception handling in a function in a shared module and service. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Exception Handling + +### Exception Catchers +###### [Style [A2-111](#style-a2-111)] + + - Create a service that exposes an interface to catch and gracefully handle exceptions. + + *Why?*: Provides a consistent way to catch exceptions that may be thrown in your code (e.g. during XHR calls or promise failures). + + Note: The exception catcher is good for catching and reacting to specific exceptions from calls that you know may throw one. For example, when making an XHR call to retrieve data from a remote web service and you want to catch any exceptions from that service and react uniquely. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Naming + +### Naming Guidelines +###### [Style [A2-120](#style-a2-120)] + + - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.js`. There are 2 names for most assets: + * the file name (`avengers.component.ts`) + * the registered component name with Angular (`Component`) + + *Why?*: Naming conventions help provide a consistent way to find content at a glance. Consistency within the project is vital. Consistency with a team is important. Consistency across a company provides tremendous efficiency. + + *Why?*: The naming conventions should simply help you find your code faster and make it easier to understand. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Feature File Names +###### [Style [A2-121](#style-a2-121)] + + - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.ts`. + + *Why?*: Provides a consistent way to quickly identify components. + + *Why?*: Provides pattern matching for any automated tasks. + +### Test File Names +###### [Style [A2-122](#style-a2-122)] + + - Name test specifications similar to the component they test with a suffix of `spec`. + + *Why?*: Provides a consistent way to quickly identify components. + + *Why?*: Provides pattern matching for [karma](http://karma-runner.github.io/) or other test runners. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Component Names +###### [Style [A2-123](#style-a2-123)] + + - Use consistent names for all components named after their feature. Use UpperCamelCase for components, as they are constructors. + + *Why?*: Provides a consistent way to quickly identify and reference components. + + *Why?*: UpperCamelCase is conventional for identifying object that can be instantiated using a constructor. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Component Name Suffix +###### [Style [A2-124](#style-a2-124)] + + - Append the component name with the suffix `Component`. + + *Why?*: The `Component` suffix is more commonly used and is more explicitly descriptive. + + ```typescript + /** + * recommended + */ + + // avengers.component.ts + export class AvengersComponent { } + ``` + +### Service Names +###### [Style [A2-125](#style-a2-125)] + + - Use consistent names for all services named after their feature. Use UpperCamelCase for services. Only suffix service and factories with `Service` when it is not clear what they are (i.e. when they are nouns). + + *Why?*: Provides a consistent way to quickly identify and reference services. + + *Why?*: Clear service names such as `logger` do not require a suffix. + + *Why?*: Service names such as `avengers` are nouns and require a suffix and should be named `AvengersService`. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Directive Component Names +###### [Style [A2-126](#style-a2-126)] + + - Use consistent names for all directives using camel-case. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). + + *Why?*: Provides a consistent way to quickly identify and reference components. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Routes +###### [Style [A2-129](#style-a2-129)] + + - Separate route configuration into a routing component file. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Application Structure LIFT Principle +### LIFT +###### [Style [Y140](#style-y140)] + + - Structure your app such that you can `L`ocate your code quickly, `I`dentify the code at a glance, keep the `F`lattest structure you can, and `T`ry to stay DRY. The structure should follow these 4 basic guidelines. + + *Why LIFT?*: Provides a consistent structure that scales well, is modular, and makes it easier to increase developer efficiency by finding code quickly. Another way to check your app structure is to ask yourself: How quickly can you open and work in all of the related files for a feature? + + When I find my structure is not feeling comfortable, I go back and revisit these LIFT guidelines + + 1. `L`ocating our code is easy + 2. `I`dentify code at a glance + 3. `F`lat structure as long as we can + 4. `T`ry to stay DRY (Don’t Repeat Yourself) or T-DRY + +### Locate +###### [Style [Y141](#style-y141)] + + - Make locating your code intuitive, simple and fast. + + *Why?*: I find this to be super important for a project. If the team cannot find the files they need to work on quickly, they will not be able to work as efficiently as possible, and the structure needs to change. You may not know the file name or where its related files are, so putting them in the most intuitive locations and near each other saves a ton of time. A descriptive folder structure can help with this. + + ``` + /bower_components + /client + /app + /avengers + /blocks + /exception + /logger + /core + /dashboard + /data + /layout + /widgets + /content + index.html + .bower.json + ``` + +### Identify +###### [Style [Y142](#style-y142)] + + - When you look at a file you should instantly know what it contains and represents. + + *Why?*: You spend less time hunting and pecking for code, and become more efficient. If this means you want longer file names, then so be it. Be descriptive with file names and keeping the contents of the file to exactly 1 component. Avoid files with multiple controllers, multiple services, or a mixture. There are deviations of the 1 per file rule when I have a set of very small features that are all related to each other, they are still easily identifiable. + +### Flat +###### [Style [Y143](#style-y143)] + + - Keep a flat folder structure as long as possible. When you get to 7+ files, begin considering separation. + + *Why?*: Nobody wants to search 7 levels of folders to find a file. Think about menus on web sites … anything deeper than 2 should take serious consideration. In a folder structure there is no hard and fast number rule, but when a folder has 7-10 files, that may be time to create subfolders. Base it on your comfort level. Use a flatter structure until there is an obvious value (to help the rest of LIFT) in creating a new folder. + +### T-DRY (Try to Stick to DRY) +###### [Style [Y144](#style-y144)] + + - Be DRY, but don't go nuts and sacrifice readability. + + *Why?*: Being DRY is important, but not crucial if it sacrifices the others in LIFT, which is why I call it T-DRY. I don’t want to type session-view.html for a view because, well, it’s obviously a view. If it is not obvious or by convention, then I name it. + +**[Back to top](#table-of-contents)** + +## Application Structure + +### Overall Guidelines +###### [Style [A2-150](#style-a2-150)] + + - Have a near term view of implementation and a long term vision. In other words, start small but keep in mind on where the app is heading down the road. All of the app's code goes in a root folder named `app`. All content is 1 feature per file. Each component, service, pipe is in its own file. All 3rd party vendor scripts are stored in another root folder and not in the `app` folder. I didn't write them and I don't want them cluttering my app. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Layout +###### [Style [A2-151](#style-a2-151)] + + - Place components that define the overall layout of the application in a folder named `layout`. These may include a shell view and component may act as the container for the app, navigation, menus, content areas, and other regions. + + *Why?*: Organizes all layout in a single place re-used throughout the application. + +### Folders-by-Feature Structure +###### [Style [A2-152](#style-a2-152)] + + - Create folders named for the feature they represent. When a folder grows to contain more than 7 files, start to consider creating a folder for them. Your threshold may be different, so adjust as needed. + + *Why?*: A developer can locate the code, identify what each file represents at a glance, the structure is flat as can be, and there is no repetitive nor redundant names. + + *Why?*: The LIFT guidelines are all covered. + + *Why?*: Helps reduce the app from becoming cluttered through organizing the content and keeping them aligned with the LIFT guidelines. + + *Why?*: When there are a lot of files (10+) locating them is easier with a consistent folder structures and more difficult in flat structures. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Modularity + +### Many Small, Self Contained Modules +###### [Style [A2-160](#style-a2-160)] + + - Create small modules that encapsulate one responsibility. + + *Why?*: Modular applications make it easy to plug and go as they allow the development teams to build vertical slices of the applications and roll out incrementally. This means we can plug in new features as we develop them. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Startup Logic + +### Bootstrapping +###### [Style [A2-170](#style-a2-170)] + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## Testing +Unit testing helps maintain clean code, as such I included some of my recommendations for unit testing foundations with links for more information. + +### Write Tests with Stories +###### [Style [A2-190](#style-a2-190)] + + - Write a set of tests for every story. Start with an empty test and fill them in as you write the code for the story. + + *Why?*: Writing the test descriptions helps clearly define what your story will do, will not do, and how you can measure success. + + ```javascript + it('should have Avengers component', function() { + // TODO + }); + + it('should find 1 Avenger when filtered by name', function() { + // TODO + }); + + it('should have 10 Avengers', function() { + // TODO (mock data?) + }); + + it('should return Avengers via XHR', function() { + // TODO ($httpBackend?) + }); + + // and so on + ``` + +### Testing Library +###### [Style [A2-191](#style-a2-191)] + + - Use [Jasmine](http://jasmine.github.io/) or [Mocha](http://mochajs.org) for unit testing. + + *Why?*: Both Jasmine and Mocha are widely used in the Angular community. Both are stable, well maintained, and provide robust testing features. + +### Test Runner +###### [Style [A2-192](#style-a2-192)] + + **example coming soon** + +**[Back to top](#table-of-contents)** + +### Organizing Tests +###### [Style [A2-197](#style-a2-197)] + + - Place unit test files (specs) side-by-side with your client code. Place specs that cover server integration or test multiple components in a separate `tests` folder. + + *Why?*: Unit tests have a direct correlation to a specific component and file in source code. + + *Why?*: It is easier to keep them up to date since they are always in sight. When coding whether you do TDD or test during development or test after development, the specs are side-by-side and never out of sight nor mind, and thus more likely to be maintained which also helps maintain code coverage. + + *Why?*: When you update source code it is easier to go update the tests at the same time. + + *Why?*: Placing them side-by-side makes it easy to find them and easy to move them with the source code if you move the source. + + *Why?*: Having the spec nearby makes it easier for the source code reader to learn how the component is supposed to be used and to discover its known limitations. + + *Why?*: Separating specs so they are not in a distributed build is easy with grunt or gulp. + + **example coming soon** + +**[Back to top](#table-of-contents)** + +## File Templates and Snippets +Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. + +### Visual Studio Code + + - [Visual Studio Code](https://code.visualstudio.com/) snippets that follow these styles and guidelines. + +**[Back to top](#table-of-contents)** + +## Angular CLI + +**[Back to top](#table-of-contents)** + +## Routing +Client-side routing is important for creating a navigation flow between views and composing views that are made of many smaller templates and directives. + +**[Back to top](#table-of-contents)** + +## Task Automation + +**[Back to top](#table-of-contents)** + +## Pipes + +###### [Style [A2-420](#style-a2-420)] + +**[Back to top](#table-of-contents)** + +## Angular docs +For anything else, API reference, check the [Angular 2 documentation](//angular.io). + +**[Back to top](#table-of-contents)** From 8430ec087affeb9f2bf8e4e3f875e835391fdb42 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 21 Feb 2016 11:54:41 -0500 Subject: [PATCH 038/114] draft link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1a9ac6ea..61432adc 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ There are multiple versions of Angular, and thus there are multiple versions of [The Angular 1 Style Guide is located here](https://github.com/johnpapa/angular-styleguide/tree/master/a1/README.md). ### Angular 2 Style Guide -**COMING SOON** +[The **D R A F T** Angular 1 Style Guide is located here](https://github.com/johnpapa/angular-styleguide/tree/master/a2/README.md). ## Angular Team Endorsed Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. From e65d33978cf6c43a61c672b604bbd6208975486a Mon Sep 17 00:00:00 2001 From: alialamine Date: Sun, 21 Feb 2016 23:09:26 +0200 Subject: [PATCH 039/114] Fix a typo in Readme.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61432adc..5e593e88 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ There are multiple versions of Angular, and thus there are multiple versions of [The Angular 1 Style Guide is located here](https://github.com/johnpapa/angular-styleguide/tree/master/a1/README.md). ### Angular 2 Style Guide -[The **D R A F T** Angular 1 Style Guide is located here](https://github.com/johnpapa/angular-styleguide/tree/master/a2/README.md). +[The **D R A F T** Angular 2 Style Guide is located here](https://github.com/johnpapa/angular-styleguide/tree/master/a2/README.md). ## Angular Team Endorsed Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. From fde98402f79ac97f2e218de97be596adfe051784 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 21 Feb 2016 16:46:40 -0500 Subject: [PATCH 040/114] img fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e593e88..a076cfe3 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ The purpose of this style guide is to provide guidance on building Angular appli >If you like this guide, check out my [Angular Patterns: Clean Code](http://jpapa.me/ngclean) course at Pluralsight which is a companion to this guide. - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Community Awesomeness and Credit Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My most excellent friend Ward has helped influence the ultimate evolution of these guides. From 0a3faeaa98643557a915dd47b4b66623643b6ff5 Mon Sep 17 00:00:00 2001 From: Cycododge Date: Tue, 23 Feb 2016 09:59:33 -0700 Subject: [PATCH 041/114] Update README.md fix type --- a2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a2/README.md b/a2/README.md index 710400c8..8c36fbf1 100644 --- a/a2/README.md +++ b/a2/README.md @@ -1,7 +1,7 @@ # Angular 2 Style Guide **D R A F T** Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and conributions from the Angular core team. Nobody has the same massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. -My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. By wary and definately ask questions when someone, including me, publishes a guide :) +My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. Be wary and definately ask questions when someone, including me, publishes a guide :) ## Angular Team Endorsed Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. From 9cf6d664d801081793c2b8daec3b7c1865b010a4 Mon Sep 17 00:00:00 2001 From: brian schermerhorn Date: Thu, 25 Feb 2016 22:27:44 -0700 Subject: [PATCH 042/114] Update README.md typo --- a2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a2/README.md b/a2/README.md index 710400c8..b80c67b2 100644 --- a/a2/README.md +++ b/a2/README.md @@ -1,5 +1,5 @@ # Angular 2 Style Guide **D R A F T** -Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and conributions from the Angular core team. Nobody has the same massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. +Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and contributions from the Angular core team. Nobody has the same massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. By wary and definately ask questions when someone, including me, publishes a guide :) From 45f19c9d154d7274e9efd6148127d5f3869e13b5 Mon Sep 17 00:00:00 2001 From: Yauhen Saroka Date: Fri, 26 Feb 2016 17:02:10 +0300 Subject: [PATCH 043/114] Fix banner link. --- a1/i18n/de-DE.md | 2 +- a1/i18n/es-ES.md | 2 +- a1/i18n/fr-FR.md | 2 +- a1/i18n/it-IT.md | 2 +- a1/i18n/ja-JP.md | 2 +- a1/i18n/ko_KR.md | 2 +- a1/i18n/mk-MK.md | 2 +- a1/i18n/ru-RU.md | 2 +- a1/i18n/tr-TR.md | 2 +- a1/i18n/zh-CN.md | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/a1/i18n/de-DE.md b/a1/i18n/de-DE.md index 818d2f37..d6961fd1 100644 --- a/a1/i18n/de-DE.md +++ b/a1/i18n/de-DE.md @@ -8,7 +8,7 @@ Der Zweck dieses Styleguides ist es, eine Anleitung für die Erstellung von Angu >Wenn Sie diese Anleitung mögen, dann besuchen Sie meinen Kurs [Angular Patterns: Clean Code] (http://jpapa.me/ngclean) auf Pluralsight, der eine Begleitung zu dieser Anleitung darstellt. - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Außergewöhnliche Community und Anerkennung Arbeite niemals im leeren Raum. Ich finde, dass die Angular-Community eine unglaubliche Gruppe ist, die ihre Erfahrung mit Leidenschaft teilt. Also haben ein Freund und Angular-Experte, Todd Motto, und ich viele Vorlagen und Konventionen zusammengetragen. Bei den meisten sind wir uns einig, und bei ein paar sind wir verschiedener Meinung. Ich möchte Sie ermutigen, sich [Todd's Guidelines](https://github.com/toddmotto/angularjs-styleguide) anzusehen, um ein Gespür für seinen Ansatz zu entwickeln und ihn vergleichen zu können. diff --git a/a1/i18n/es-ES.md b/a1/i18n/es-ES.md index 27a47961..7a6c5e0c 100644 --- a/a1/i18n/es-ES.md +++ b/a1/i18n/es-ES.md @@ -8,7 +8,7 @@ El propósito de esta guía de estilos es proporcionar una guía de cómo constr >Si te gusta esta guía, echa un vistazo al curso de Pluralsight [Angular Patterns: Clean Code](http://jpapa.me/ngclean). - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Asombrosa comunidad y créditos Nunca trabajes solo. Personalmente, la comunidad de Angular es un increíble grupo apasionado por compartir experiencias. Como por ejemplo, mi amigo y experto en Angular Todd Motto, con el que he colaborado en muchos de los estilos y convenciones. Estamos de acuerdo en la mayoría, y en otras no. Te recomiendo que le eches un vistazo a [Todd's guidelines](https://github.com/toddmotto/angularjs-styleguide) para que le des sentido a esta guía y la compares. diff --git a/a1/i18n/fr-FR.md b/a1/i18n/fr-FR.md index 9229d67c..b0f42aa8 100644 --- a/a1/i18n/fr-FR.md +++ b/a1/i18n/fr-FR.md @@ -8,7 +8,7 @@ Le but de ce guide de style est de proposer des conseils sur le développement d >Si vous appréciez ce guide, visitez mon cours [Angular Patterns: Clean Code](http://jpapa.me/ngclean) sur Pluralsight qui va de pair avec ce guide. - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Remerciements individuels et à la communauté Ne jamais travailler en vase clos. J'ai trouvé que la communauté Angular est une incroyable communauté dont les membres ont à cœur de partager leurs expériences. Ainsi, avec mon ami et expert d'Angular, Todd Motto, nous avons collaboré sur de nombreux styles et conventions. Nous sommes d'accord sur la plupart, et nous divergeons sur d'autres. Je vous encourage à visiter [le guide de style de Todd](https://github.com/toddmotto/angularjs-styleguide) pour vous faire votre propre avis sur son approche et en quoi elle diverge. diff --git a/a1/i18n/it-IT.md b/a1/i18n/it-IT.md index 7505155f..88efae36 100644 --- a/a1/i18n/it-IT.md +++ b/a1/i18n/it-IT.md @@ -12,7 +12,7 @@ L'obbiettivo di questa guida stilistica è di fare da vademecum alla costruzione >Se ti piace questa guida, dai un'occhiata al mio corso [Angular Patterns: Clean Code](http://jpapa.me/ngclean) (in inglese) su Pluralsight come complemento a questa guida. - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Eccezionalità della comunità e riconoscimenti Mai lavorare nel vuoto. Ritengo che la comunità intorno ad Angular sia un gruppo incredibile con la passione di condividere le esperienze. Perciò, Todd Motto, un amico ed un esperto di Angular, ed io abbiamo collaborato su molti stili e convenzioni. Su molto siamo d'accordo, su altro meno. Ti invito a controllare le [linee guida di Todd](https://github.com/toddmotto/angularjs-styleguide) per avere cognizione del suo approccio e di come paragonarle. diff --git a/a1/i18n/ja-JP.md b/a1/i18n/ja-JP.md index 064fbd51..6cde81fe 100644 --- a/a1/i18n/ja-JP.md +++ b/a1/i18n/ja-JP.md @@ -8,7 +8,7 @@ >もしあなたがこのガイドを気に入ったのなら、Pluralsightにある [Angular Patterns: Clean Code](http://jpapa.me/ngclean) の私のコースもチェックして下さい。 -[![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) +[![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Community Awesomeness and Credit あなたは決して1人でありません!Angularのコミュニティは、自身の経験を共有することに情熱的な素晴らしい集団です。実際、友人でありAngularのエキスパートでもある Todd Motto と私は、共同で多くのスタイルや規約をまとめました。一部意見が分かれましたが、概ね合意できるものでした。彼のアプローチと本スタイルとの比較のため、是非 [Todd's guidelines](https://github.com/toddmotto/angularjs-styleguide) をチェックすることをお勧めします。 diff --git a/a1/i18n/ko_KR.md b/a1/i18n/ko_KR.md index 3b71c616..df9de666 100644 --- a/a1/i18n/ko_KR.md +++ b/a1/i18n/ko_KR.md @@ -11,7 +11,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 이 스타일 가이드의 목적은 Angular 어플리케이션을 만드는 길잡이 역할을 하기 위함이며 더 나아가 왜 내가 이런 것들을 선택했는지 보여주기 위함입니다. >만약 이 가이드가 마음에 든다면 Pluralsight 에 올려놓은 저의 강의를 참고하시기 바랍니다. [Angular Patterns: Clean Code](http://jpapa.me/ngclean) - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Community Awesomeness and Credit 저는 Angular 커뮤니티의 대단함을 알게 되었습니다. 그들은 자신들의 경험을 공유하는데 열정적이기 때문입니다. 나의 친구이자 Angular 전문가인 Todd Motto 와 나는 많은 스타일과 컨벤션을 위해 공동작업을 하였습니다. 대부분 우리는 서로 동의하였지만 어떤 부분에서는 의견이 갈렸습니다. Todd의 접근방법이 궁금하고 이를 비교해보고 싶으신 분들은 다음 링크에 가서 확인해보시면 좋을 것 같습니다 [Todd's guidelines](https://github.com/toddmotto/angularjs-styleguide). diff --git a/a1/i18n/mk-MK.md b/a1/i18n/mk-MK.md index 076188ce..69ed2751 100644 --- a/a1/i18n/mk-MK.md +++ b/a1/i18n/mk-MK.md @@ -8,7 +8,7 @@ >Ако ви се допаѓа овој водич, тогаш проверете ми го курсот [Angular Patterns: Clean Code](http://jpapa.me/ngclean) на Pluralsight кој е придружник на овој водич. - [![Angular Шаблон: Чист Код](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + [![Angular Шаблон: Чист Код](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Величествена заедница и заслуга Никогаш не работи во вакуум. Јас сметам дека Angular заедницата е неверојатна група кои се страсни за споделување искуство. Како резултат, јас и мојот пријател кој е Angular експерт, Todd Motto соработувавме со многу стилови и конвенции. Се согласуваме на повеќето, додека на останатите се разликуваме. Ве охрабрувам да ги погледнете на [Todd's guidelines](https://github.com/toddmotto/angular-styleguide) со цел да добиете осет за неговиот пристап и како се споредува. diff --git a/a1/i18n/ru-RU.md b/a1/i18n/ru-RU.md index 54133476..709ee11b 100644 --- a/a1/i18n/ru-RU.md +++ b/a1/i18n/ru-RU.md @@ -8,7 +8,7 @@ >Если это руководство вам понравится, то вы можете также оценить мой курс [Angular Patterns: Clean Code](http://jpapa.me/ngclean), который размещен на сайте Pluralsight. - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Признательность сообществу и коллегам Никогда не работайте в вакууме. Я считаю AngularJS-сообщество невероятно открытым, которое активно обменивается опытом и заботится об этом. Также как и мой друг Todd Motto (отличный Angular эксперт), я работал со многими стилями и соглашениями. Мы с ним сходимся во многом, но иногда и противоречим друг другу. Я предлагаю вам ознакомиться с курсом [Todd's guidelines](https://github.com/toddmotto/angularjs-styleguide) дабы почувствовать разницу подходов. diff --git a/a1/i18n/tr-TR.md b/a1/i18n/tr-TR.md index 3a22b9fe..23a4f8fd 100644 --- a/a1/i18n/tr-TR.md +++ b/a1/i18n/tr-TR.md @@ -12,7 +12,7 @@ Bu rehberin amacı, kullandığım yöntemleri göstererek, hatta daha önemlisi >Eğer bu rehberi beğendiyseniz, [Angular Patterns: Clean Code](http://jpapa.me/ngclean) isimli kursuma Pluralsight sitesinden bir bakın. Bu rehberle pekiltirici olacaktır. - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Topluluğun Aşmışlığı ve Referanslar Asla izole olarak çalışmayın. Angular topluluğunu, deneyimlerini paylaşma konusunda tutukulu buluyorum. Örneğin, arkadaşım ve bir Angular uzmanı Todd Motto ile birçok stil ve yöntem üzerinde işbirliği yaptık. Birçoğunda hemfikir olduk, birkaçında farklı yollar izledik. [Todd'un rehberi'ni](https://github.com/toddmotto/angularjs-styleguide) de onun yaklaşımını anlamak ve karşılaştırma yapmak için incelemenizi öneririm diff --git a/a1/i18n/zh-CN.md b/a1/i18n/zh-CN.md index ef65b6d2..b897d640 100644 --- a/a1/i18n/zh-CN.md +++ b/a1/i18n/zh-CN.md @@ -12,7 +12,7 @@ >如果你喜欢这个规范,请在Pluralsight看看[Angular Patterns: Clean Code](http://jpapa.me/ngclean)。 - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Community Awesomeness and Credit Angular社区是一个热衷于分享经验的令人难以置信的社区,尽管Todd Motto(他是我的一个朋友,也是Angular专家)和我合作了多种规范和惯例,但是我们也存在着一些分歧。我鼓励你去看看[Todd的指南](https://github.com/toddmotto/angularjs-styleguide),在那里你能看到我们之间的区别。 From 1319a3fe410d3379ac922e04cf79225fe7f12c4e Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 27 Feb 2016 01:00:39 -0800 Subject: [PATCH 044/114] Change my name on credit list --- a1/i18n/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/i18n/README.md b/a1/i18n/README.md index 709fc26d..40e24863 100644 --- a/a1/i18n/README.md +++ b/a1/i18n/README.md @@ -13,7 +13,7 @@ The [original English version](http://jpapa.me/ngstyles) is the source of truth, 1. [Russian](ru-RU.md) by [Vasiliy Mazhekin](https://github.com/mazhekin) 1. [Simplified Chinese](zh-CN.md) by [Zhao Ke](https://github.com/natee) 1. [Spanish](es-ES.md) by [Alberto Calleja](https://github.com/AlbertoImpl) and [Gilberto](https://github.com/ingilniero) - 1. [Korean](ko-KR.md) by [Joshua Ji](https://github.com/zirho) + 1. [Korean](ko-KR.md) by [Youngjae Ji](https://github.com/zirho) 1. [Turkish](tr-TR.md) by [Uğur Korfalı](https://github.com/kel-sakal-biyik) ## Contributing From 2db9a14d01db95171f443b7fdde7549f59f6ee00 Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 27 Feb 2016 01:15:40 -0800 Subject: [PATCH 045/114] Fix image links in markdown moved to 'a1/assets' --- a1/i18n/de-DE.md | 10 +++++----- a1/i18n/es-ES.md | 10 +++++----- a1/i18n/fr-FR.md | 10 +++++----- a1/i18n/it-IT.md | 10 +++++----- a1/i18n/ja-JP.md | 8 ++++---- a1/i18n/{ko_KR.md => ko-KR.md} | 10 +++++----- a1/i18n/mk-MK.md | 12 ++++++------ a1/i18n/pt-BR.md | 2 +- a1/i18n/ru-RU.md | 10 +++++----- a1/i18n/tr-TR.md | 10 +++++----- a1/i18n/zh-CN.md | 10 +++++----- 11 files changed, 51 insertions(+), 51 deletions(-) rename a1/i18n/{ko_KR.md => ko-KR.md} (99%) diff --git a/a1/i18n/de-DE.md b/a1/i18n/de-DE.md index d6961fd1..99be9755 100644 --- a/a1/i18n/de-DE.md +++ b/a1/i18n/de-DE.md @@ -442,7 +442,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe } ``` - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) + ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) Anmerkung: Falls eine Funktion aus nur einer Zeile bestehen sollte, können Sie sich überlegen, diese nach oben zu verlagern, so lange die Lesbarkeit nicht betroffen ist. @@ -786,7 +786,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe So spiegeln sich die Bindungen im gesamten (beinhaltenden) Objekt wieder. Werte können nicht selbständig durch die Offenlegung des Modulmusters geändert werden. - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-2.png) + ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) ### Funktionsdeklarationen, um die Details der Implementierung zu verbergen ###### [Style [Y053](#style-y053)] @@ -2177,7 +2177,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe session-detail.controller.js ``` - ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) + ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) Anmerkung: Strukturieren Sie Ihre Verzeichnisse nicht nach Typ. Das hat zur Folge, dass Sie sich in vielen Verzeichnissen bewegen müssen, um ein einziges Feature bearbeiten zu wollen. Vergrößert sich die Anwendung auf fünf, zehn oder gar mehr als 25 Views und Controller (und andere Features), wird es sehr schnell unhandlich, im Gegensatz zur Organisation der Verzeichnisse nach Features. @@ -2271,7 +2271,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Das Hauptmodul einer Applikation ist abhängig von den applikationsspezifischen Funktionsmodulen und den allgemeingültigen oder wiederverwendbaren Modulen. - ![Modularität und Abhängigkeiten](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) + ![Modularität und Abhängigkeiten](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) *Warum?*: Das Hauptmodul der Applikation enthält ein schnell ersichtliches Manifest der Anwendungsfunktionen. @@ -2457,7 +2457,7 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei "mocha": true, ``` - ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) + ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) ### Tests organisieren ###### [Style [Y197](#style-y197)] diff --git a/a1/i18n/es-ES.md b/a1/i18n/es-ES.md index 7a6c5e0c..1b1f7072 100644 --- a/a1/i18n/es-ES.md +++ b/a1/i18n/es-ES.md @@ -441,7 +441,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti } ``` - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-1.png) + ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) Nota: Si la función es de una línea, déjala arriba, siempre y cuando no afecte en la legibilidad. @@ -784,7 +784,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti De esta forma se asocian los bindeos desde el objeto que lo mantiene, los valores primitivos no se pueden modificar por si solos usando este patrón - ![Fábricas Usando "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-2.png) + ![Fábricas Usando "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) ### Declaración de Funciones para Esconder los Detalles de Implementación ###### [Style [Y053](#style-y053)] @@ -2160,7 +2160,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti session-detail.controller.js ``` - ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/modularity-2.png) + ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) Nota: No estructures tu aplicación usando directorios-por-tipo. Esto requiere mover múltiples directorios cuando se está trabajando en una característica y se vuelve difícil de manejar conforme la aplicación crece a 5, 10 o 25+ vistas y controladores (y otras características), lo que lo hace más difícil que localizar archivos en una aplicación estructura en directorios-por-característica. @@ -2253,7 +2253,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti - El módulo raíz de la aplicación depende de módulos de características específicas y cualquier módulo compartido o reusable. - ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/modularity-1.png) + ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) *¿Por qué?*: El módulo principal de la aplicación contiene un manifiesto rápidamente identificable de las características de la aplicación. @@ -2431,7 +2431,7 @@ Las pruebas unitarias ayudan a mantener el código limpio, así que incluyo algu /* global sinon, describe, it, afterEach, beforeEach, expect, inject */ ``` - ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/testing-tools.png) + ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) ### Organizando las Pruebas ###### [Style [Y197](#style-y197)] diff --git a/a1/i18n/fr-FR.md b/a1/i18n/fr-FR.md index b0f42aa8..b3fae0d8 100644 --- a/a1/i18n/fr-FR.md +++ b/a1/i18n/fr-FR.md @@ -449,7 +449,7 @@ Bien que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'est ut } ``` - ![Contrôleur utilisant la syntaxe avec les membres bindables au dessus](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) + ![Contrôleur utilisant la syntaxe avec les membres bindables au dessus](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) Note : Si la fonction est un *oneliner*, vous pouvez la garder en haut du contrôleur, tant que la lisibilité n'est pas affectée. @@ -792,7 +792,7 @@ Bien que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'est ut De cette façon, les *bindings* sont répliqués à travers l'objet de capture, les valeurs primitives ne peuvent pas se mettre à jour toutes seules grâce au *revealing module pattern*. - ![Factories utilisant la syntaxe avec les membres bindables au dessus](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-2.png) + ![Factories utilisant la syntaxe avec les membres bindables au dessus](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) ### Déclaration des fonctions pour cacher les détails d'implémentation ###### [Style [Y053](#style-y053)] @@ -2203,7 +2203,7 @@ Bien que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'est ut session-detail.controller.js ``` - ![Image d'un exemple de structure d'application](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) + ![Image d'un exemple de structure d'application](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) Note : N'utilisez pas une structuration « répertoire-type ». Cela requiert de se déplacer entre de multiples répertoires lors du travail sur une fonctionnalité et cela devient rapidement difficile à manier lorsque l'application passe de à cinq, dix ou plus de vingt-cinq vues et contrôleurs (et autres fonctionnalités), ce qui complique la localisation par rapport à une structure en « répertoire-fonctionnalité ». @@ -2297,7 +2297,7 @@ Bien que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'est ut - Le module racine de l'application dépend des modules des fonctionnalités spécifiques et de certains modules partagés et réutilisables. - ![Modularité et Dépendences](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) + ![Modularité et Dépendences](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) *Pourquoi?* : Le module principal de l'application continent manifeste des fonctionnalités de l'application. @@ -2529,7 +2529,7 @@ Les tests unitaires aident à maintenir un code source propre, ainsi j'ai inclus "mocha": true, ``` - ![Outils de test](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) + ![Outils de test](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) ### Organisation des tests ###### [Style [Y197](#style-y197)] diff --git a/a1/i18n/it-IT.md b/a1/i18n/it-IT.md index 88efae36..fa87b1b5 100644 --- a/a1/i18n/it-IT.md +++ b/a1/i18n/it-IT.md @@ -466,7 +466,7 @@ Invece usa la più semplice sintassi setter. } ``` - ![Controller che usa "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-1.png) + ![Controller che usa "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) Nota: Se la funzione è di 1 linea considera di poterla lasciare in alto fino a che la leggibilità non ne è compromessa. @@ -811,7 +811,7 @@ Invece usa la più semplice sintassi setter. In questo modo i binding si riflettono in tutto l'oggetto host, i valori di base non possono essere solamente aggiornati usando il revealing module pattern. - ![Factory che usano "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-2.png) + ![Factory che usano "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) ### Dichiarazioni di funzione per nascondere i dettagli di implementazione ###### [Stile [Y053](#stile-y053)] @@ -2223,7 +2223,7 @@ Invece usa la più semplice sintassi setter. session-detail.controller.js ``` - ![Struttura dell'App di Esempio](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/modularity-2.png) + ![Struttura dell'App di Esempio](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) Nota: Non usare una strutturazione del tipo cartella-per-tipo per la tua app. Questo richiede spostarsi tra molte cartelle quando si lavora su una funzionalità e diventa rapidamente scomodo quando l'app cresce di 5, 10 o più di 25 tra view e controller (ed altre funzionalità), per cui è più difficile rispetto alla localizzazione basata su cartella-per-funzionalità. @@ -2317,7 +2317,7 @@ Invece usa la più semplice sintassi setter. - Il modulo principale dell'applicazione dipende dai moduli di funzionalità specifiche dell'app e da qualunque altro modulo che sia condiviso o riusabile. - ![Modularità e Dipendenze](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/modularity-1.png) + ![Modularità e Dipendenze](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) *Perché?*: Il modulo principale dell'app contiene un manifesto che sia facilmente identificabile con le funzionalità dell'applicazione. @@ -2547,7 +2547,7 @@ Gli unit test aiutano a mantenere il codice più chiaro, perciò ho incluso alcu "mocha": true, ``` - ![Strumenti per i test](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/testing-tools.png) + ![Strumenti per i test](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) ### Organizzazione dei test ###### [Stile [Y197](#stile-y197)] diff --git a/a1/i18n/ja-JP.md b/a1/i18n/ja-JP.md index 6cde81fe..4b034ae4 100644 --- a/a1/i18n/ja-JP.md +++ b/a1/i18n/ja-JP.md @@ -442,7 +442,7 @@ } ``` - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) + ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) Note: もし関数がワンライナーであれば、可読性に影響が無い限り上に置いたままにすることを検討して下さい。 @@ -2172,7 +2172,7 @@ session-detail.controller.js ``` - ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) + ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) Note: folders-by-typeを使って構造化をしてはいけません。一つの機能が、5、10、25以上のビューやコントローラ(また他の機能)からなるときにアプリが肥大化してきます。そのとき複数のフォルダに移動する必要がありますが、ファイルを配置するのはfolder-by-featureよりも難しいでしょう。 @@ -2266,7 +2266,7 @@ - ルートのモジュールは、アプリケーションスペシフィックなモジュールや共有または再利用されるモジュールに依存します。 - ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) + ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) *なぜ ?*: メインのアプリケーションのモジュールは、アプリケーションの機能を素早く特定可能なマニュフェストを含みます。 @@ -2452,7 +2452,7 @@ "mocha": true, ``` - ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) + ![Testing Tools](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) ### Organizing Tests ###### [Style [Y197](#style-y197)] diff --git a/a1/i18n/ko_KR.md b/a1/i18n/ko-KR.md similarity index 99% rename from a1/i18n/ko_KR.md rename to a1/i18n/ko-KR.md index df9de666..5bd5ea2e 100644 --- a/a1/i18n/ko_KR.md +++ b/a1/i18n/ko-KR.md @@ -472,7 +472,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 } ``` - !["Above the Fold"를 사용하는 컨트롤러](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) + !["Above the Fold"를 사용하는 컨트롤러](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) 주의: 만약 코드 가독성에 영향을 주지않고 1줄이라면 그냥 위쪽에 두어도 됩니다. @@ -817,7 +817,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 호스트 오브젝트 내에서 이런 식의 바인딩이 반영이 되어서, 원시 값들은 모듈 패턴 노출 방식으로 업데이트 되지 않습니다. - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-2.png) + ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) ### Function Declarations to Hide Implementation Details ###### [Style [Y053](#style-y053)] @@ -2234,7 +2234,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 session-detail.controller.js ``` - ![심플한 앱 구조](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) + ![심플한 앱 구조](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) 주의: 타입별로 묶어서 폴더를 만드는 식으로 앱 구조를 만들지 마세요. 한 가지 기능에 대해서 일하려면 여러 폴더를 옮겨 다녀야 합니다. 이는 앱이 5, 10 또는 25 이상의 뷰와 컨트롤러(또는 다른 기능들)를 갖게되면 기능으로 나뉜 폴더일 경우보다 금방 거추장스러워 집니다. @@ -2329,7 +2329,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 - 어플리케이션의 루트 모듈은 앱 특화된 기능모듈 그리고 재사용되고 공유된 모듈들에 의존하게 됩니다. - ![모듈화 그리고 의존성](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) + ![모듈화 그리고 의존성](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) *이유*: 주요 앱 모듈은 어플리케이션의 기능들을 빠르게 구분할 수 있는 목록을 가지고 있습니다. @@ -2562,7 +2562,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 "mocha": true, ``` - ![테스팅 도구들](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) + ![테스팅 도구들](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) ### Organizing Tests diff --git a/a1/i18n/mk-MK.md b/a1/i18n/mk-MK.md index 69ed2751..3ad49ea8 100644 --- a/a1/i18n/mk-MK.md +++ b/a1/i18n/mk-MK.md @@ -423,7 +423,7 @@ } ``` - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) + ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) Забелешка: Ако функцијата е 1 линија код, можете да ја поставите горе се додека читливоста не се наруши. @@ -756,7 +756,7 @@ На овој начин поврзувањата се пресликуваат низ објектот, примитивните вредности не можат да се ажурираат самостојно со употреба на Revealing шаблонот на модули. - ![Фабрики искористуваат "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-2.png) + ![Фабрики искористуваат "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) ### Декларации на функции ја кријат имплементацијата ###### [Style [Y053](#style-Y053)] @@ -2105,7 +2105,7 @@ session-detail.controller.js ``` - ![Пробна апликација пример](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) + ![Пробна апликација пример](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) Забелешка: Не употребувајте структура со папка-по-тип. Со ова ќе се движите низ повеќе папки кога работите на функционалност што станува потешко како што апликацијата има повеќе од 5, 10 или 25 прегледи и контролери (за други функционалности), а со тоа и потешко за лоцирање на датотеките на таа функционалност. @@ -2192,7 +2192,7 @@ ###### [Style [Y165](#style-Y165)] - Корен модулот на апликацијата зависи од функционалните модули на апликацијата како и било кои заеднички или реискористливи модули. - ![Модуларност и Зависности](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) + ![Модуларност и Зависности](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) *Зошто?*: Главниот модул на апликацијата содржи манифест од брзо идентифицирани функционалности на апликацијата. @@ -2367,7 +2367,7 @@ "mocha": true, ``` - ![Алатки за тестирање](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) + ![Алатки за тестирање](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) ### Организирање на тестови ###### [Style [Y197](#style-y197)] @@ -2926,4 +2926,4 @@ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -**[Назад кон содржината](#table-of-contents)** \ No newline at end of file +**[Назад кон содржината](#table-of-contents)** diff --git a/a1/i18n/pt-BR.md b/a1/i18n/pt-BR.md index db9539a2..2f961aeb 100644 --- a/a1/i18n/pt-BR.md +++ b/a1/i18n/pt-BR.md @@ -2121,7 +2121,7 @@ ou *Dependências do módulo* - O módulo raiz da aplicação depende de módulos de recursos específicos do aplicativo e de qualquer módulo compartilhado ou reutilizado. - ![Moduluaridade e Dependências](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) + ![Moduluaridade e Dependências](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) **Por que?** O módulo principal do aplicativo contém um rápido manifesto para identificar os recursos da aplicação. diff --git a/a1/i18n/ru-RU.md b/a1/i18n/ru-RU.md index 709ee11b..bd8a3f50 100644 --- a/a1/i18n/ru-RU.md +++ b/a1/i18n/ru-RU.md @@ -440,7 +440,7 @@ } ``` - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-1.png) + ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) Замечание: Если функция однострочная, то разместите и ее наверху, но так чтобы это не усложняло читабельность. @@ -781,7 +781,7 @@ Такой способ привязки реализовывается во всем объекте, и приватные члены не моут быть изменены из-за примененного паттерна Revealing Module. - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-2.png) + ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) ### Определения Функций для Скрытия Деталей Реализации ###### [Style [Y053](#style-y053)] @@ -2099,7 +2099,7 @@ session-detail.controller.js ``` - ![Пример Структуры Приложения](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/modularity-2.png) + ![Пример Структуры Приложения](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) Замечание: Не используйте структуру папки-по-типу. Так файлы одной функциональности разбрасываются по нескольким папкам, и далее все быстро становится очень громоздким. Как только в приложении создаются 5, 10, или 25+ представлений и контроллеров (и других компонентов), то работа становится очень сложной, в отличиии от структуры папки-по-функциональностям. @@ -2192,7 +2192,7 @@ - Корневой модуль приложения зависит от специфичных функциональных модулей и от любых общих или повторно используемых модулей. - ![Модульность и Зависимости](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/modularity-1.png) + ![Модульность и Зависимости](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) *Почему?*: Главный модуль приложения содержит хорошо читаемый манифест функциональностей приложения. @@ -2370,7 +2370,7 @@ /* global sinon, describe, it, afterEach, beforeEach, expect, inject */ ``` - ![Средства Тестирования](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/testing-tools.png) + ![Средства Тестирования](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) ### Организация Тестов ###### [Style [Y197](#style-y197)] diff --git a/a1/i18n/tr-TR.md b/a1/i18n/tr-TR.md index 23a4f8fd..bcc73b64 100644 --- a/a1/i18n/tr-TR.md +++ b/a1/i18n/tr-TR.md @@ -452,7 +452,7 @@ Bu rehber *ne*, *neden* ve *nasıl* sorularına odaklanırken, yöntemleri deney } ``` - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) + ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) Not: Eğer fonksiyon bir satıra sığıyorsa, okunabilirlik etkilenmez ve bağlanacak değişkenlerle beraber tutulabilir. @@ -794,7 +794,7 @@ Bu rehber *ne*, *neden* ve *nasıl* sorularına odaklanırken, yöntemleri deney Primitif değerler revealing module pattern yöntemi kullanıldığında güncellenemezler. - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-2.png) + ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) ### Fonksiyon Tanımlamaları ve İmplementasyon Detaylarının Saklanması ###### [Stil [Y053](#style-y053)] @@ -2220,7 +2220,7 @@ Bu rehber *ne*, *neden* ve *nasıl* sorularına odaklanırken, yöntemleri deney session-detail.controller.js ``` - ![Örnek Uygulama Yapısı](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) + ![Örnek Uygulama Yapısı](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) Not: Uygulamanızı tipe göre klasör yapısı ile oluşturmayın. Bu yüzden bir özellik üzerinde çalışırken birden çok klasör içerisinde çalışmanız gerekir ve dosya sayısı arttıkça sizi hantallaştırır. Özelliğe göre klasör yapısına göre dosyaları bulmak daha zordur. @@ -2315,7 +2315,7 @@ Bu rehber *ne*, *neden* ve *nasıl* sorularına odaklanırken, yöntemleri deney - Ana uygulama modülü uygulamaya özel modüllere ve paylaşılan, tekrar kullanılabilen modüllere bağlıdır. - ![Modülerlik ve Bağımlılık](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) + ![Modülerlik ve Bağımlılık](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) *Neden?*: Uygulama ana modülü uygulamanın özelliklerini içeren kolayca tanımlanabilen bir manifestodur. @@ -2550,7 +2550,7 @@ Unit Test yapmak temiz kodu yönetmeye yardımcı olur, bu yüzden benim unit te "mocha": true, ``` - ![Test Araçları](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) + ![Test Araçları](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) ### Testleri Organize Etmek ###### [Stil [Y197](#style-y197)] diff --git a/a1/i18n/zh-CN.md b/a1/i18n/zh-CN.md index b897d640..e369cb42 100644 --- a/a1/i18n/zh-CN.md +++ b/a1/i18n/zh-CN.md @@ -440,7 +440,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 } ``` - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) + ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) 注:如果一个函数就是一行,那么只要不影响可读性就把它放到顶部。 @@ -783,7 +783,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 这种绑定方式复制了宿主对象,原始值不会随着暴露模块模式的使用而更新。 - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-2.png) + ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) ###函数声明隐藏实现细节 ###### [Style [Y053](#style-y053)] @@ -2170,7 +2170,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 session-detail.controller.js ``` - ![实例App结构](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) + ![实例App结构](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) 注意:不要使用按类型划分文件夹结构,因为如果这样的话,当做一个功能时,需要在多个文件夹中来回切换。当应用程序有5个、10个,甚至是25个以上的view、controller(或其他feature)时,这种方式将迅速变得不实用,这就使得它定位文件比按功能分文件夹的方式要困难的多。 @@ -2264,7 +2264,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 - 应用程序根模块依赖于应用程序特定的功能模块、共享的和可复用的模块。 - ![模块化和依赖](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) + ![模块化和依赖](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) *为什么?*:主程序模块包含一个能快速识别应用程序功能的清单。 @@ -2450,7 +2450,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 "mocha": true, ``` - ![测试工具](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) + ![测试工具](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) ### 组织测试 ###### [Style [Y197](#style-y197)] From 58d9f611fa9148afec2af9f57b584c1a1fab5838 Mon Sep 17 00:00:00 2001 From: Baris Date: Mon, 29 Feb 2016 14:05:55 +0200 Subject: [PATCH 046/114] minor change * tutukulu = tutkulu --- a1/i18n/tr-TR.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/i18n/tr-TR.md b/a1/i18n/tr-TR.md index bcc73b64..7d2b973c 100644 --- a/a1/i18n/tr-TR.md +++ b/a1/i18n/tr-TR.md @@ -15,7 +15,7 @@ Bu rehberin amacı, kullandığım yöntemleri göstererek, hatta daha önemlisi [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) ## Topluluğun Aşmışlığı ve Referanslar -Asla izole olarak çalışmayın. Angular topluluğunu, deneyimlerini paylaşma konusunda tutukulu buluyorum. Örneğin, arkadaşım ve bir Angular uzmanı Todd Motto ile birçok stil ve yöntem üzerinde işbirliği yaptık. Birçoğunda hemfikir olduk, birkaçında farklı yollar izledik. [Todd'un rehberi'ni](https://github.com/toddmotto/angularjs-styleguide) de onun yaklaşımını anlamak ve karşılaştırma yapmak için incelemenizi öneririm +Asla izole olarak çalışmayın. Angular topluluğunu, deneyimlerini paylaşma konusunda tutkulu buluyorum. Örneğin, arkadaşım ve bir Angular uzmanı Todd Motto ile birçok stil ve yöntem üzerinde işbirliği yaptık. Birçoğunda hemfikir olduk, birkaçında farklı yollar izledik. [Todd'un rehberi'ni](https://github.com/toddmotto/angularjs-styleguide) de onun yaklaşımını anlamak ve karşılaştırma yapmak için incelemenizi öneririm Bir çok yöntem [Ward Bell](http://twitter.com/wardbell) ile yaptığımız eşli programlama seanslarında ortaya çıktı. Arkadaşım Ward bu rehberin nihai evrimine büyük katkılarda bulundu. From 396bc63a18e27304fe75db497e81a00fbb3aced3 Mon Sep 17 00:00:00 2001 From: Nicolas del Valle Date: Thu, 10 Mar 2016 15:17:49 +0100 Subject: [PATCH 047/114] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a076cfe3..f8d72e76 100644 --- a/README.md +++ b/README.md @@ -64,4 +64,4 @@ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -**[Back to top](#Angular-Style-Guide)** +**[Back to top](#angular-style-guide)** From 4e643cc073c9c884d0d0626c26d6801a35fd67ed Mon Sep 17 00:00:00 2001 From: Raging Goblin Date: Wed, 16 Mar 2016 15:04:50 +0100 Subject: [PATCH 048/114] Naming of someFactory.js to some.factory.js --- a1/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/README.md b/a1/README.md index d6c6d7a0..a4275686 100644 --- a/a1/README.md +++ b/a1/README.md @@ -109,7 +109,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see ```javascript /* recommended */ - // someFactory.js + // some.factory.js angular .module('app') .factory('someFactory', someFactory); From 55ce417a81f91258a75d82ed46cfad8743ee0ec5 Mon Sep 17 00:00:00 2001 From: Eric King Date: Fri, 18 Mar 2016 13:54:49 -0400 Subject: [PATCH 049/114] Update README.md Fixed spelling error 'definitely' --- a2/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/a2/README.md b/a2/README.md index 2dd5747c..a2635b56 100644 --- a/a2/README.md +++ b/a2/README.md @@ -1,8 +1,8 @@ # Angular 2 Style Guide **D R A F T** Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and contributions from the Angular core team. Nobody has the same massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. -My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. Be wary and definately ask questions when someone, including me, publishes a guide :) - +My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. Be wary and definitely ask questions when someone, including me, publishes a guide :) +d ## Angular Team Endorsed Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. From 310e4f254e4ad19dc36335f4fa7d8de30ad73acb Mon Sep 17 00:00:00 2001 From: Eric King Date: Fri, 18 Mar 2016 14:11:22 -0400 Subject: [PATCH 050/114] Update notes.md --- a2/notes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/a2/notes.md b/a2/notes.md index 345ade8d..393606b5 100644 --- a/a2/notes.md +++ b/a2/notes.md @@ -1,7 +1,7 @@ # Angular 2 Style Guide -Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and conributions from the Angular core team. Nobody has the same massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. +Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and contributions from the Angular core team. Nobody has the same massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. -My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. By wary and definately ask questions when someone, including me, publishes a guide :) +My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. By wary and definitely ask questions when someone, including me, publishes a guide :) ## Angular Team Endorsed Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. From 60736dbdee4e5158d31b3c3ad9f26042270fe597 Mon Sep 17 00:00:00 2001 From: luixaviles Date: Mon, 21 Mar 2016 16:12:08 -0400 Subject: [PATCH 051/114] Update README.md --- a1/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/a1/README.md b/a1/README.md index a4275686..35e6d804 100644 --- a/a1/README.md +++ b/a1/README.md @@ -425,12 +425,12 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see ```html - + ``` ```html - + ``` ### Bindable Members Up Top From f8f4c0b2f44d64131098824f9f54b0d36a4da424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Herrera=20Garc=C3=ADa?= Date: Thu, 24 Mar 2016 19:55:42 -0500 Subject: [PATCH 052/114] Update es-ES.md The reasons "why" weren't written in "Single Responsibility". --- a1/i18n/es-ES.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/a1/i18n/es-ES.md b/a1/i18n/es-ES.md index 1b1f7072..c332073c 100644 --- a/a1/i18n/es-ES.md +++ b/a1/i18n/es-ES.md @@ -23,8 +23,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti ## Tabla de contenidos - 1. [Responsabilidad - Única](#single-responsibility-o-responsabilidad-única) + 1. [Responsabilidad Única](#single-responsibility-o-responsabilidad-Única) 1. [IIFE](#iife) 1. [Módulos](#módulos) 1. [Controladores](#controladores) @@ -61,6 +60,12 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti ###### [Style [Y001](#style-y001)] - Define 1 componente por archivo. + + *¿Por qué?*: Un componente por archivo promueve pruebas unitarias más fáciles. + + *¿Por qué?*: Un componente por archivo hace que sea mucho más fácil de leer, mantener, y evita colisiones con los equipos en el control de código. + + *¿Por qué?*: Un componente por archivo evita errores ocultos que a menudo surgen cuando se combinan componentes en un archivo donde pueden compartir variables, crear closures (clausuras) no deseadas, o acoplamiento indeseado de dependencias. El siguiente ejemplo define el módulo `app` y sus dependencias, define un controlador, y defines una fábrica todo en el mismo archivo. From a2e65efb81ceeac3ee0709deab5b57386917ff84 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 15:23:01 -0400 Subject: [PATCH 053/114] closes #700 --- a1/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/README.md b/a1/README.md index 35e6d804..e8668e6d 100644 --- a/a1/README.md +++ b/a1/README.md @@ -137,7 +137,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see **[Back to top](#table-of-contents)** ## IIFE -### JavaScript Closures +### JavaScript Scopes ###### [Style [Y010](#style-y010)] - Wrap Angular components in an Immediately Invoked Function Expression (IIFE). From 0f3934a3288a0ce9de55e463d399a4ebe904c14a Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 15:46:26 -0400 Subject: [PATCH 054/114] Y001 --- a1/README.md | 2 +- a2/README.md | 111 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 103 insertions(+), 10 deletions(-) diff --git a/a1/README.md b/a1/README.md index e8668e6d..1c12bb12 100644 --- a/a1/README.md +++ b/a1/README.md @@ -63,7 +63,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see ### Rule of 1 ###### [Style [Y001](#style-y001)] - - Define 1 component per file, recommended to be less than 500 lines of code. + - Define 1 component per file, recommended to be less than 400 lines of code. *Why?*: One component per file promotes easier unit testing and mocking. diff --git a/a2/README.md b/a2/README.md index a2635b56..8827a370 100644 --- a/a2/README.md +++ b/a2/README.md @@ -1,8 +1,8 @@ # Angular 2 Style Guide **D R A F T** -Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and contributions from the Angular core team. Nobody has the same massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. +Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and contributions from the Angular core team. Nobody has the equivalent massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. Be wary and definitely ask questions when someone, including me, publishes a guide :) -d + ## Angular Team Endorsed Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. @@ -42,7 +42,7 @@ Translations of this Angular 2 style guide are maintained by the community. Due ### Rule of 1 ###### [Style [A2-001](#style-a2-001)] - - Define 1 component per file, recommended to be less than 500 lines of code. + - Define 1 component per file, recommended to be less than 400 lines of code. *Why?*: One component per file promotes easier unit testing and mocking. @@ -50,9 +50,105 @@ Translations of this Angular 2 style guide are maintained by the community. Due *Why?*: One component per file avoids hidden bugs that often arise when combining components in a file where they may share variables, create unwanted closures, or unwanted coupling with dependencies. - The following example defines the `app` module and its dependencies, defines a component, and defines a factory all in the same file. - - **example coming soon** + The following example defines the `AppComponent`, handles the bootstrapping, and shared functions all in the same file. + + The key is to make the code more reusable, easier to read, and less mistake prone. + + ```javascript + /* avoid */ + import { bootstrap } from 'angular2/platform/browser'; + import { Component, OnInit } from 'angular2/core'; + + @Component({ + selector: 'my-app', + template: ` +

{{title}}

+
{{heroes | json}}
+ `, + styleUrls: ['app/app.component.css'] + }) + export class AppComponent implements OnInit{ + public title = 'Tour of Heroes'; + + public heroes: Hero[] = []; + + ngOnInit() { + getHeroes().then(heroes => this.heroes = heroes); + } + } + + bootstrap(AppComponent, []); + + function getHeroes() { + return // some promise of data; + } + ``` + + The same components are now separated into their own files. + + ```javascript + /* recommended */ + + // main.ts + import { bootstrap } from 'angular2/platform/browser'; + import { AppComponent } from './app.component'; + + bootstrap(AppComponent, []); + ``` + + ```javascript + /* recommended */ + + // app.component.ts + import { Component, OnInit } from 'angular2/core'; + + import { Hero } from './hero'; + import { HeroService } from './hero.service'; + + @Component({ + selector: 'my-app', + template: ` +

{{title}}

+
{{heroes | json}}
+ `, + styleUrls: ['app/app.component.css'], + providers: [HeroService] + }) + export class AppComponent implements OnInit{ + public title = 'Tour of Heroes'; + + public heroes: Hero[] = []; + + ngOnInit() { + this._heroService.getHeroes().then(heroes => this.heroes = heroes); + } + } + ``` + + ```javascript + /* recommended */ + + // hero.service.ts + import { Injectable } from 'angular2/core'; + import { HEROES } from './mock-heroes'; + + @Injectable() + export class HeroService { + getHeroes() { + return Promise.resolve(HEROES); + } + } + ``` + + ```javascript + /* recommended */ + + // hero.ts + export class Hero { + id: number; + name: string; + } + ``` **[Back to top](#table-of-contents)** @@ -82,9 +178,6 @@ Translations of this Angular 2 style guide are maintained by the community. Due *Why?*: Unique names help avoid module name collisions. Separators help define modules and their submodule hierarchy. For example `app` may be your root module while `app.dashboard` and `app.users` may be modules that are used as dependencies of `app`. -### Definitions (aka Setters) -###### [Style [A2-021](#style-a2-021)] - ## Components ### Bindable Members Up Top From 8b26f1f9a7921a8c7079a0aca17358573ad88ff8 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 15:49:48 -0400 Subject: [PATCH 055/114] ps course --- a2/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/a2/README.md b/a2/README.md index 8827a370..52cee52f 100644 --- a/a2/README.md +++ b/a2/README.md @@ -13,7 +13,9 @@ If you are looking for an opinionated style guide for syntax, conventions, and s The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions I use and, more importantly, why I choose them. ->If you like this guide, check out my Angular 2 First Look course **coming soon** to Pluralsight. +>If you like this guide, check out my [Angular 2 First Look course on Pluralsight](http://jpapa.me/a2ps1stlook). + +![Angular 2 First Look](https://s3-us-west-2.amazonaws.com/johnpapa-blog-images/a2-first-look-app.gif) ## Community Awesomeness and Credit Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My friend Ward has certainly helped influence the ultimate evolution of this guide. From bacff7dad7ed564dad42afd2270c87040e1837a3 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 15:50:32 -0400 Subject: [PATCH 056/114] spacing --- a2/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/a2/README.md b/a2/README.md index 52cee52f..0b6c18b2 100644 --- a/a2/README.md +++ b/a2/README.md @@ -70,9 +70,9 @@ Translations of this Angular 2 style guide are maintained by the community. Due styleUrls: ['app/app.component.css'] }) export class AppComponent implements OnInit{ - public title = 'Tour of Heroes'; + title = 'Tour of Heroes'; - public heroes: Hero[] = []; + heroes: Hero[] = []; ngOnInit() { getHeroes().then(heroes => this.heroes = heroes); From 00eae58d4f288adc7f8e26267c0809c150bec00b Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 16:01:59 -0400 Subject: [PATCH 057/114] y002 --- a2/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/a2/README.md b/a2/README.md index 0b6c18b2..b0a639bd 100644 --- a/a2/README.md +++ b/a2/README.md @@ -151,6 +151,8 @@ Translations of this Angular 2 style guide are maintained by the community. Due name: string; } ``` + + As the app grows, this rule becomes even more important. **[Back to top](#table-of-contents)** @@ -176,9 +178,11 @@ Translations of this Angular 2 style guide are maintained by the community. Due ### Avoid Naming Collisions ###### [Style [A2-020](#style-a2-020)] - - Use unique naming conventions with separators for sub-modules. + - Use unique naming conventions. + + *Why?*: Unique names help avoid module name collisions. We import and export modules by the file in which they are contained. Thus the name of the files and their folders is important. - *Why?*: Unique names help avoid module name collisions. Separators help define modules and their submodule hierarchy. For example `app` may be your root module while `app.dashboard` and `app.users` may be modules that are used as dependencies of `app`. + *Why?*: Names of folders and files should clear convey their intent. For example, `app/speakers/speaker-list.component.ts` may contain a component that manages a list of speakers. The folder and file names clearly convey the meaning, and thus the module import is clear as `import { SpeakerComponent } from './speakers/speaker-list.component'`. ## Components From 04e19aa3f448127104b03efb056fd85d5bfb074b Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 16:08:27 -0400 Subject: [PATCH 058/114] member sequence --- a2/README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/a2/README.md b/a2/README.md index b0a639bd..323efa31 100644 --- a/a2/README.md +++ b/a2/README.md @@ -127,7 +127,7 @@ Translations of this Angular 2 style guide are maintained by the community. Due } ``` - ```javascript + ```typescript /* recommended */ // hero.service.ts @@ -186,16 +186,58 @@ Translations of this Angular 2 style guide are maintained by the community. Due ## Components -### Bindable Members Up Top +### Member Sequence ###### [Style [A2-033](#style-a2-033)] - - Place bindable members at the top of the component, alphabetized, and not spread through the component code. + - Place properties up top, then public functions, then private functions, alphabetized, and not spread through the component code. - *Why?*: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the component can be bound and used in the View. + *Why?*: Placing members in a consistent sequence makes it easy to read and helps you instantly identify which members of the component serve which purpose. - *Why?*: Setting anonymous functions in-line can be easy, but when those functions are more than 1 line of code they can reduce the readability. Defining the functions below the bindable members (the functions will be hoisted) moves the implementation details down, keeps the bindable members up top, and makes it easier to read. + ```typescript + /* recommended */ + export class ToastComponent implements OnInit { + // private fields + private _defaults = { + title: '', + message: 'May the Force be with You' + }; + private _toastElement: any; + + // public properties + title: string; + message: string; + + // ctor + constructor(toastService: ToastService) { + toastService.activate = this.activate.bind(this); + } - **example coming soon** + // public functions + activate(message = this._defaults.message, title = this._defaults.title) { + this.title = title; + this.message = message; + this._show(); + } + + ngOnInit() { + this._toastElement = document.getElementById('toast'); + } + + // private functions + private _show() { + console.log(this.message); + this._toastElement.style.opacity = 1; + this._toastElement.style.zIndex = 9999; + + window.setTimeout(() => this._hide(), 2500); + } + + private _hide() { + this._toastElement.style.opacity = 0; + window.setTimeout(() => this._toastElement.style.zIndex = 0, 400); + } + } + ``` ### Defer Logic to Services ###### [Style [A2-035](#style-a2-035)] From 38cad2814e8b2a5a0ee8261556e92091b6f8e8e4 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 16:39:48 -0400 Subject: [PATCH 059/114] 060 services --- a2/README.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/a2/README.md b/a2/README.md index 323efa31..a4b4ad0d 100644 --- a/a2/README.md +++ b/a2/README.md @@ -322,7 +322,34 @@ Translations of this Angular 2 style guide are maintained by the community. Due *Why?*: Data service implementation may have very specific code to handle the data repository. This may include headers, how to talk to the data, or other services such as `$http`. Separating the logic into a data service encapsulates this logic in a single place hiding the implementation from the outside consumers (perhaps a component), also making it easier to change the implementation. - **example coming soon** + ```typescript + // recommended + export class SessionListComponent implements OnInit { + sessions: Session[]; + filteredSessions = this.sessions; + + constructor(private _sessionService: SessionService) { } + + getSessions() { + this.sessions = []; + this._sessionService.getSessions() + .subscribe(sessions => { + this.sessions = this.filteredSessions = sessions; + }, + error => { + console.log('error occurred here'); + console.log(error); + }, + () => { + console.log('completed'); + }); + } + + ngOnInit() { + this.getSessions(); + } + } + ``` **[Back to top](#table-of-contents)** From ca0a5658e4b90e956d4d14a93ac3a2f19726fe3e Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 16:44:43 -0400 Subject: [PATCH 060/114] 035 - defer logic to services --- a2/README.md | 57 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/a2/README.md b/a2/README.md index a4b4ad0d..705b160b 100644 --- a/a2/README.md +++ b/a2/README.md @@ -252,7 +252,47 @@ Translations of this Angular 2 style guide are maintained by the community. Due *Why?*: Keeps the component slim, trim, and focused. - **example coming soon** + ```typescript + // avoid + export class SessionListComponent implements OnInit { + sessions: Session[]; + + constructor(private _http: Http) { } + + getSessions() { + this.sessions = []; + this._http.get(sessionsUrl) + .map((response: Response) => response.json().data) + .catch(this._exceptionService.catchBadResponse) + .finally(() => this._spinnerService.hide()) + .subscribe(sessions => this.sessions = sessions); + } + + + ngOnInit() { + this.getSessions(); + } + } + ``` + + ```typescript + // recommended + export class SessionListComponent implements OnInit { + sessions: Session[]; + + constructor(private _sessionService: SessionService) { } + + getSessions() { + this.sessions = []; + this._sessionService.getSessions() + .subscribe(sessions => this.sessions = sessions); + } + + ngOnInit() { + this.getSessions(); + } + } + ``` ### Keep Components Focused ###### [Style [A2-037](#style-a2-037)] @@ -294,21 +334,6 @@ Translations of this Angular 2 style guide are maintained by the community. Due **[Back to top](#table-of-contents)** -### Accessible Members Up Top -###### [Style [A2-052](#style-a2-052)] - - - Expose the callable members of the service (its interface) at the top - - *Why?*: Placing the callable members at the top makes it easy to read and helps you instantly identify which members of the service can be called and must be unit tested (and/or mocked). - - *Why?*: This is especially helpful when the file gets longer as it helps avoid the need to scroll to see what is exposed. - - *Why?*: Setting functions as you go can be easy, but when those functions are more than 1 line of code they can reduce the readability and cause more scrolling. Defining the callable interface via the returned service moves the implementation details down, keeps the callable interface up top, and makes it easier to read. - - **example coming soon** - -**[Back to top](#table-of-contents)** - ## Data Services ### Separate Data Calls From 954a0e883a51bface8b5f0d1d243947c78e3f77b Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 16:55:32 -0400 Subject: [PATCH 061/114] singletons --- a2/README.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/a2/README.md b/a2/README.md index 705b160b..cbe23cb0 100644 --- a/a2/README.md +++ b/a2/README.md @@ -244,6 +244,8 @@ Translations of this Angular 2 style guide are maintained by the community. Due - Defer logic in a component by delegating to services. + - Define a component for a view, and try not to reuse the component for other views. Instead, move reusable logic to factories and keep the component simple and focused on its view. + *Why?*: Logic may be reused by multiple components when placed within a service and exposed via a function. *Why?*: Logic in a service can more easily be isolated in a unit test, while the calling logic in the component can be easily mocked. @@ -294,15 +296,6 @@ Translations of this Angular 2 style guide are maintained by the community. Due } ``` -### Keep Components Focused -###### [Style [A2-037](#style-a2-037)] - - - Define a component for a view, and try not to reuse the component for other views. Instead, move reusable logic to factories and keep the component simple and focused on its view. - - *Why?*: Reusing components with several views is brittle and good end-to-end (e2e) test coverage is required to ensure stability across large applications. - - **example coming soon** - **[Back to top](#table-of-contents)** ## Services @@ -312,8 +305,20 @@ Translations of this Angular 2 style guide are maintained by the community. Due - Services are singletons and should be used for sharing data and functionality. - **example coming soon** + ```typescript + // service + import { Injectable } from 'angular2/core'; + import { Session } from './session'; + @Injectable() + export class HeroService { + getHeroes() { + return this._http.get('api/sessions') + .map((response: Response) => response.json().data) + } + } + ``` + **[Back to top](#table-of-contents)** ### Single Responsibility From 2be9597de8ce4fb0ffb502eeb96cc90e5681ce07 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 17:05:13 -0400 Subject: [PATCH 062/114] 041 - shared service provider --- a2/README.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/a2/README.md b/a2/README.md index cbe23cb0..032f5e1d 100644 --- a/a2/README.md +++ b/a2/README.md @@ -110,17 +110,16 @@ Translations of this Angular 2 style guide are maintained by the community. Due @Component({ selector: 'my-app', template: ` -

{{title}}

{{heroes | json}}
`, styleUrls: ['app/app.component.css'], providers: [HeroService] }) export class AppComponent implements OnInit{ - public title = 'Tour of Heroes'; - - public heroes: Hero[] = []; + heroes: Hero[] = []; + constructor(private _heroService: HeroService) {} + ngOnInit() { this._heroService.getHeroes().then(heroes => this.heroes = heroes); } @@ -318,7 +317,64 @@ Translations of this Angular 2 style guide are maintained by the community. Due } } ``` + +### Providing a Shared Service +###### [Style [A2-041](#style-a2-041)] + + - Services should be provided to the Angular 2 injector at the top-most component where they will be shared. + + *Why?*: The Angular 2 injector is hierarchical. + + *Why?*: When providing the service to a top level component, that instance is shared and available to all child components of that top level component. + + *Why?*: When providing the service to a top level component, that instance is shared and available to all child components of that top level component. + + ```typescript + /* recommended */ + + // app.component.ts + import { Component } from 'angular2/core'; + + import { SpeakerListComponent } from './speakers/speaker-list.component'; + import { SpeakerService } from './common/speaker.service'; + + @Component({ + selector: 'my-app', + template: ` + + `, + directives: [SpeakerListComponent], + providers: [SpeakerService] + }) + export class AppComponent { } + ``` + ```typescript + /* recommended */ + + // speaker-list.component.ts + import { Component, OnInit } from 'angular2/core'; + + import { SpeakerService } from './common/speaker.service'; + import { Speaker } from './common/speaker'; + + @Component({ + selector: 'my-speakers', + template: ` +
{{speakers | json}}
+ ` + }) + export class SpeakerListComponent implements OnInit{ + speakers: Speaker[] = []; + + constructor(private _speakerService: SpeakerService) {} + + ngOnInit() { + this._speakerService.getSpeakers().then(speakers => this.speakers = speakers); + } + } + ``` + **[Back to top](#table-of-contents)** ### Single Responsibility From 01106f09cdf3fca94d6fe3fbfc7c673c286a58aa Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 17:06:31 -0400 Subject: [PATCH 063/114] state --- a2/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/a2/README.md b/a2/README.md index 032f5e1d..b393fca1 100644 --- a/a2/README.md +++ b/a2/README.md @@ -327,8 +327,8 @@ Translations of this Angular 2 style guide are maintained by the community. Due *Why?*: When providing the service to a top level component, that instance is shared and available to all child components of that top level component. - *Why?*: When providing the service to a top level component, that instance is shared and available to all child components of that top level component. - + *Why?*: This is ideal when a service is sharing methods and has no state, or state that must be shared. + ```typescript /* recommended */ From 13c60dfa9daf9d83f961fc84baeabb0a082492e4 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 17:08:54 -0400 Subject: [PATCH 064/114] injector and instances --- a2/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/a2/README.md b/a2/README.md index b393fca1..25cdc70d 100644 --- a/a2/README.md +++ b/a2/README.md @@ -318,7 +318,7 @@ Translations of this Angular 2 style guide are maintained by the community. Due } ``` -### Providing a Shared Service +### Providing a Service ###### [Style [A2-041](#style-a2-041)] - Services should be provided to the Angular 2 injector at the top-most component where they will be shared. @@ -329,6 +329,8 @@ Translations of this Angular 2 style guide are maintained by the community. Due *Why?*: This is ideal when a service is sharing methods and has no state, or state that must be shared. + *Why?*: This is not ideal when two different components need different instances of a service. In this scenario it would be better to provide the service at the component level that needs the new and separate instance. + ```typescript /* recommended */ @@ -374,7 +376,7 @@ Translations of this Angular 2 style guide are maintained by the community. Due } } ``` - + **[Back to top](#table-of-contents)** ### Single Responsibility From a05e89a40d215b3cfb0cb4636394948ecba85f20 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 17:14:09 -0400 Subject: [PATCH 065/114] shortening --- a2/README.md | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/a2/README.md b/a2/README.md index 25cdc70d..a462d223 100644 --- a/a2/README.md +++ b/a2/README.md @@ -380,27 +380,16 @@ Translations of this Angular 2 style guide are maintained by the community. Due **[Back to top](#table-of-contents)** ### Single Responsibility -###### [Style [A2-050](#style-a2-050)] +###### [Style [A2-042](#style-a2-050)] - Services should have a [single responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle), that is encapsulated by its context. Once a service begins to exceed that singular purpose, a new one should be created. - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Singletons -###### [Style [A2-051](#style-a2-051)] - - - Factories are singletons and return an object that contains the members of the service. - - **example coming soon** - **[Back to top](#table-of-contents)** ## Data Services ### Separate Data Calls -###### [Style [A2-060](#style-a2-060)] +###### [Style [A2-050](#style-a2-050)] - Refactor logic for making data operations and interacting with data to a service. Make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations. From 93dee6e66647311f7176187910aa259889364480 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 17:41:10 -0400 Subject: [PATCH 066/114] naming --- a2/README.md | 96 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 23 deletions(-) diff --git a/a2/README.md b/a2/README.md index a462d223..a9319de7 100644 --- a/a2/README.md +++ b/a2/README.md @@ -433,7 +433,7 @@ Translations of this Angular 2 style guide are maintained by the community. Due ## Naming ### Naming Guidelines -###### [Style [A2-120](#style-a2-120)] +###### [Style [A2-100](#style-a2-100)] - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.js`. There are 2 names for most assets: * the file name (`avengers.component.ts`) @@ -443,21 +443,47 @@ Translations of this Angular 2 style guide are maintained by the community. Due *Why?*: The naming conventions should simply help you find your code faster and make it easier to understand. - **example coming soon** - **[Back to top](#table-of-contents)** -### Feature File Names -###### [Style [A2-121](#style-a2-121)] +### File Names +###### [Style [A2-101](#style-a2-101)] + + - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. A recommended pattern is `feature.type.ts`. - - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.ts`. + - use dashes to separate words and dots to separate the descriptive name from the type. *Why?*: Provides a consistent way to quickly identify components. *Why?*: Provides pattern matching for any automated tasks. + ``` + // recommended + + // Bootstrapping file and main entry point + main.ts + + // Components + speakers.component.ts + speaker-list.component.ts + speaker-detail.component.ts + + // Services + logger.service.ts + speaker.service.ts + exception.service.ts + filter-text.service.ts + + // Models + session.ts + speaker.ts + + // Pipes + ellipsis.pipe.ts + init-caps.pipe.ts + ``` + ### Test File Names -###### [Style [A2-122](#style-a2-122)] +###### [Style [A2-102](#style-a2-102)] - Name test specifications similar to the component they test with a suffix of `spec`. @@ -465,52 +491,76 @@ Translations of this Angular 2 style guide are maintained by the community. Due *Why?*: Provides pattern matching for [karma](http://karma-runner.github.io/) or other test runners. - **example coming soon** + ``` + // recommended + + // Components + speakers.component.spec.ts + speaker-list.component.spec.ts + speaker-detail.component.spec.ts + + // Services + logger.service.spec.ts + speaker.service.spec.ts + exception.service.spec.ts + filter-text.service.spec.ts + + // Pipes + ellipsis.pipe.spec.ts + init-caps.pipe.spec.ts + ``` **[Back to top](#table-of-contents)** ### Component Names -###### [Style [A2-123](#style-a2-123)] +###### [Style [A2-103](#style-a2-103)] - - Use consistent names for all components named after their feature. Use UpperCamelCase for components, as they are constructors. + - Use consistent names for all components named after their feature. Use UpperCamelCase for components' symbols. Match the name of the component to the naming of the file *Why?*: Provides a consistent way to quickly identify and reference components. *Why?*: UpperCamelCase is conventional for identifying object that can be instantiated using a constructor. - **example coming soon** + ```typescript + AppComponent //app.component.ts + SpeakersComponent //speakers.component.ts + SpeakerListComponent //speaker-list.component.ts + SpeakerDetailComponent //speaker-detail.component.ts + ``` **[Back to top](#table-of-contents)** -### Component Name Suffix -###### [Style [A2-124](#style-a2-124)] +### Suffixes +###### [Style [A2-104](#style-a2-104)] - Append the component name with the suffix `Component`. *Why?*: The `Component` suffix is more commonly used and is more explicitly descriptive. ```typescript - /** - * recommended - */ + // recommended - // avengers.component.ts - export class AvengersComponent { } + // speaker-list.component.ts + export class SpeakerListComponent { } ``` ### Service Names -###### [Style [A2-125](#style-a2-125)] +###### [Style [A2-110](#style-a2-110)] - - Use consistent names for all services named after their feature. Use UpperCamelCase for services. Only suffix service and factories with `Service` when it is not clear what they are (i.e. when they are nouns). + - Use consistent names for all services named after their feature. Use UpperCamelCase for services. Only suffix service and factories with `Service` when it is not clear what they are (e.g. when they are nouns). *Why?*: Provides a consistent way to quickly identify and reference services. *Why?*: Clear service names such as `logger` do not require a suffix. - *Why?*: Service names such as `avengers` are nouns and require a suffix and should be named `AvengersService`. - - **example coming soon** + *Why?*: Service names such as `Credit` are nouns and require a suffix and should be named with a suffix when it is not obvious if it is a service or something else. + ```typescript + SpeakerService // speaker.service.ts + CreditService // credit.service.ts + Logger // logger.service.ts + ``` + **[Back to top](#table-of-contents)** ### Directive Component Names From e404bdda349ddce8f0be24fae5eb6b1c45d76afa Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 17:48:24 -0400 Subject: [PATCH 067/114] component router --- a2/README.md | 50 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/a2/README.md b/a2/README.md index a9319de7..72f4543d 100644 --- a/a2/README.md +++ b/a2/README.md @@ -563,23 +563,45 @@ Translations of this Angular 2 style guide are maintained by the community. Due **[Back to top](#table-of-contents)** -### Directive Component Names -###### [Style [A2-126](#style-a2-126)] +### Component Router +###### [Style [A2-120](#style-a2-120)] - - Use consistent names for all directives using camel-case. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). + - Separate route configuration into a routing component file, also known as a component router. - *Why?*: Provides a consistent way to quickly identify and reference components. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Routes -###### [Style [A2-129](#style-a2-129)] - - - Separate route configuration into a routing component file. + - Use a `` in the component router, where the routes will have their component targets display their templates. + + - Focus the logic in the component router to the routing aspects and its target components. Extract other logic to services and other components. + + *Why?*: A component that handles routing is known as the component router, thus this follows the Angular 2 routing pattern. + + *Why?*: A component that handles routing is known as the componenter router. - **example coming soon** + *Why?*: The `` indicates where the tempalte should be displayed for the target route. + + ```typescript + import { Component } from 'angular2/core'; + import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router'; + + import { SpeakersComponent, SpeakerService } from './^speakers'; + import { DashboardComponent } from './^dashboard'; + import { NavComponent } from './layout/nav.component'; + + @Component({ + selector: 'my-app', + templateUrl: 'app/app.component.html', + styleUrls: ['app/app.component.css'], + directives: [ROUTER_DIRECTIVES, NavComponent], + providers: [ + ROUTER_PROVIDERS, + SpeakerService + ] + }) + @RouteConfig([ + { path: '/dashboard', name: 'Dashboard', component: DashboardComponent, useAsDefault: true }, + { path: '/speakers/...', name: 'Speakers', component: SpeakersComponent }, + ]) + export class AppComponent { } + ``` **[Back to top](#table-of-contents)** From 08611e9af180dd8921f67284c8949abf5d161a89 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 17:50:22 -0400 Subject: [PATCH 068/114] removal --- a2/README.md | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/a2/README.md b/a2/README.md index 72f4543d..318f0a1b 100644 --- a/a2/README.md +++ b/a2/README.md @@ -563,8 +563,13 @@ Translations of this Angular 2 style guide are maintained by the community. Due **[Back to top](#table-of-contents)** +## Routing +Client-side routing is important for creating a navigation flow between a component tree hierarchy, and composing components that are made of many other child components. + +**[Back to top](#table-of-contents)** + ### Component Router -###### [Style [A2-120](#style-a2-120)] +###### [Style [A2-130](#style-a2-130)] - Separate route configuration into a routing component file, also known as a component router. @@ -703,19 +708,6 @@ Translations of this Angular 2 style guide are maintained by the community. Due **[Back to top](#table-of-contents)** -## Modularity - -### Many Small, Self Contained Modules -###### [Style [A2-160](#style-a2-160)] - - - Create small modules that encapsulate one responsibility. - - *Why?*: Modular applications make it easy to plug and go as they allow the development teams to build vertical slices of the applications and roll out incrementally. This means we can plug in new features as we develop them. - - **example coming soon** - -**[Back to top](#table-of-contents)** - ## File Templates and Snippets Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. @@ -729,11 +721,6 @@ Use file templates or snippets to help follow consistent styles and patterns. He **[Back to top](#table-of-contents)** -## Routing -Client-side routing is important for creating a navigation flow between views and composing views that are made of many smaller templates and directives. - -**[Back to top](#table-of-contents)** - ## Angular docs For anything else, API reference, check the [Angular 2 documentation](//angular.io). From d5272b53b27caf1d317543051edaea1949380152 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 27 Mar 2016 18:12:10 -0400 Subject: [PATCH 069/114] snippets --- a2/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/a2/README.md b/a2/README.md index 318f0a1b..c804d61b 100644 --- a/a2/README.md +++ b/a2/README.md @@ -714,6 +714,11 @@ Use file templates or snippets to help follow consistent styles and patterns. He ### Visual Studio Code - [Visual Studio Code](https://code.visualstudio.com/) snippets that follow these styles and guidelines. + + - [Snippets for VS Code](https://marketplace.visualstudio.com/items?itemName=johnpapa.Angular2) + + [![Use Extension](https://github.com/johnpapa/vscode-angular2-snippets/raw/master/images/use-extension.gif)](https://marketplace.visualstudio.com/items?itemName=johnpapa.Angular2) + **[Back to top](#table-of-contents)** From 247d19487d4bf8f8f772d66594481895591e85e5 Mon Sep 17 00:00:00 2001 From: Lior Saadon Date: Thu, 31 Mar 2016 11:10:41 +0300 Subject: [PATCH 070/114] Update README.md "es3" is deprecated we can replace it with: "esversion": 6, "maxerr": false is wrong, we can just put the default "50" --- a1/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/a1/README.md b/a1/README.md index 1c12bb12..a02bce82 100644 --- a/a1/README.md +++ b/a1/README.md @@ -2767,7 +2767,7 @@ Unit testing helps maintain clean code, as such I included some of my recommenda "camelcase": true, "curly": true, "eqeqeq": true, - "es3": false, + "esversion": 6, "forin": true, "freeze": true, "immed": true, @@ -2788,7 +2788,6 @@ Unit testing helps maintain clean code, as such I included some of my recommenda "maxstatements": 40, "maxcomplexity": 8, "maxlen": 120, - "asi": false, "boss": false, "debug": false, @@ -2803,7 +2802,7 @@ Unit testing helps maintain clean code, as such I included some of my recommenda "laxbreak": false, "laxcomma": false, "loopfunc": true, - "maxerr": false, + "maxerr": 50, "moz": false, "multistr": false, "notypeof": false, From 8f1c7b42b5d43789dd37cabc42ddf8a41a3194dd Mon Sep 17 00:00:00 2001 From: HyunSeob Date: Sun, 10 Apr 2016 17:19:26 +0900 Subject: [PATCH 071/114] Remove original English text --- a1/i18n/ko-KR.md | 1 - 1 file changed, 1 deletion(-) diff --git a/a1/i18n/ko-KR.md b/a1/i18n/ko-KR.md index 5bd5ea2e..90b4de96 100644 --- a/a1/i18n/ko-KR.md +++ b/a1/i18n/ko-KR.md @@ -2897,7 +2897,6 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 **[Back to top](#table-of-contents)** ## File Templates and Snippets -Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. 파일 템플릿이나 스니펫을 사용하면 일관적인 스타일과 패턴을 지킬수 있습니다. 웹 개발용 에디터와 IDE들에서 사용 가능한 템플릿과 스니펫을 알려 드리겠습니다. ### Sublime Text From 311ac1c7a151cd3bf814959645e2be070e4f3b69 Mon Sep 17 00:00:00 2001 From: Antony Budianto Date: Wed, 13 Apr 2016 21:53:46 +0700 Subject: [PATCH 072/114] Add http injection --- a2/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/a2/README.md b/a2/README.md index c804d61b..296d7ed5 100644 --- a/a2/README.md +++ b/a2/README.md @@ -311,6 +311,8 @@ Translations of this Angular 2 style guide are maintained by the community. Due @Injectable() export class HeroService { + constructor(private _http: Http) { } + getHeroes() { return this._http.get('api/sessions') .map((response: Response) => response.json().data) From 4761c33431b20f3dad3d7e12cad4f5c610210432 Mon Sep 17 00:00:00 2001 From: Irvin Waldman Date: Fri, 15 Apr 2016 09:55:35 -0500 Subject: [PATCH 073/114] Update README.md SpeakerComponent better named asSpeakerListComponent since the corresponding files are named speaker-list.component.* --- a2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a2/README.md b/a2/README.md index c804d61b..8edd61f5 100644 --- a/a2/README.md +++ b/a2/README.md @@ -181,7 +181,7 @@ Translations of this Angular 2 style guide are maintained by the community. Due *Why?*: Unique names help avoid module name collisions. We import and export modules by the file in which they are contained. Thus the name of the files and their folders is important. - *Why?*: Names of folders and files should clear convey their intent. For example, `app/speakers/speaker-list.component.ts` may contain a component that manages a list of speakers. The folder and file names clearly convey the meaning, and thus the module import is clear as `import { SpeakerComponent } from './speakers/speaker-list.component'`. + *Why?*: Names of folders and files should clear convey their intent. For example, `app/speakers/speaker-list.component.ts` may contain a component that manages a list of speakers. The folder and file names clearly convey the meaning, and thus the module import is clear as `import { SpeakerListComponent } from './speakers/speaker-list.component'`. ## Components From b5592d42fe870ad619b4a54afe5c5625c427a2b2 Mon Sep 17 00:00:00 2001 From: Raju Gandhi Date: Sun, 24 Apr 2016 02:16:33 -0400 Subject: [PATCH 074/114] alphabetize private functions --- a2/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/a2/README.md b/a2/README.md index c804d61b..515a3ddc 100644 --- a/a2/README.md +++ b/a2/README.md @@ -223,6 +223,11 @@ Translations of this Angular 2 style guide are maintained by the community. Due } // private functions + private _hide() { + this._toastElement.style.opacity = 0; + window.setTimeout(() => this._toastElement.style.zIndex = 0, 400); + } + private _show() { console.log(this.message); this._toastElement.style.opacity = 1; @@ -230,11 +235,6 @@ Translations of this Angular 2 style guide are maintained by the community. Due window.setTimeout(() => this._hide(), 2500); } - - private _hide() { - this._toastElement.style.opacity = 0; - window.setTimeout(() => this._toastElement.style.zIndex = 0, 400); - } } ``` From 5027899c97d4bda53b3c6aa98e40d4d582f683a5 Mon Sep 17 00:00:00 2001 From: John Papa Date: Mon, 25 Apr 2016 18:46:38 -0400 Subject: [PATCH 075/114] relocation --- a2/README.md | 721 +-------------------------------------------------- 1 file changed, 7 insertions(+), 714 deletions(-) diff --git a/a2/README.md b/a2/README.md index 5d8bd422..113a9dc0 100644 --- a/a2/README.md +++ b/a2/README.md @@ -1,4 +1,4 @@ -# Angular 2 Style Guide **D R A F T** +# Angular 2 Style Guide Style Guides require experience building applications with the tools. My style guide for Angular 1 was based on years of experience with Angular 1, collaboration with other Angular experts, and contributions from the Angular core team. Nobody has the equivalent massive experience with Angular 2, and thus the Angular 2 Style Guide is a work in progress. My intent is to release the guide as a living document. Guidelines that we are comfortable recommending will be included. Be wary and definitely ask questions when someone, including me, publishes a guide :) @@ -9,726 +9,19 @@ Special thanks to Igor Minar, lead on the Angular team, for reviewing, contribut ## Purpose *Opinionated Angular style guide for teams by [@john_papa](//twitter.com/john_papa)* -If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular applications, then step right in. These styles are based on my development experience with [Angular](//angularjs.org), presentations, [Pluralsight training courses](http://app.pluralsight.com/author/john-papa) and working in teams. +If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular applications, then step right in. These styles are based on development experience with [Angular](//angularjs.org), presentations, [Pluralsight training courses](http://app.pluralsight.com/author/john-papa) and working in teams. The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions I use and, more importantly, why I choose them. ->If you like this guide, check out my [Angular 2 First Look course on Pluralsight](http://jpapa.me/a2ps1stlook). - -![Angular 2 First Look](https://s3-us-west-2.amazonaws.com/johnpapa-blog-images/a2-first-look-app.gif) - -## Community Awesomeness and Credit -Never work in a vacuum. I find that the Angular community is an incredible group who are passionate about sharing experiences. Many of my styles have been from the many pair programming sessions [Ward Bell](https://twitter.com/wardbell) and I have had. My friend Ward has certainly helped influence the ultimate evolution of this guide. - -##Translations -Translations of this Angular 2 style guide are maintained by the community. Due to the in flux nature of this guide, I will hold off on accepting contributions for the time being. But I hope to get the same amazing contributions we had for the v1 of the guide! - -## Table of Contents - - 1. [Single Responsibility](#single-responsibility) - 1. [Modules](#modules) - 1. [Components](#components) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Data Services](#data-services) - 1. [Naming](#naming) - 1. [Application Structure LIFT Principle](#application-structure-lift-principle) - 1. [Application Structure](#application-structure) - 1. [File Templates and Snippets](#file-templates-and-snippets) - 1. [Angular CLI](#angular-cli) - 1. [Routing](#routing) - 1. [Angular Docs](#angular-docs) - -## Single Responsibility - -### Rule of 1 -###### [Style [A2-001](#style-a2-001)] - - - Define 1 component per file, recommended to be less than 400 lines of code. - - *Why?*: One component per file promotes easier unit testing and mocking. - - *Why?*: One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control. - - *Why?*: One component per file avoids hidden bugs that often arise when combining components in a file where they may share variables, create unwanted closures, or unwanted coupling with dependencies. - - The following example defines the `AppComponent`, handles the bootstrapping, and shared functions all in the same file. - - The key is to make the code more reusable, easier to read, and less mistake prone. - - ```javascript - /* avoid */ - import { bootstrap } from 'angular2/platform/browser'; - import { Component, OnInit } from 'angular2/core'; - - @Component({ - selector: 'my-app', - template: ` -

{{title}}

-
{{heroes | json}}
- `, - styleUrls: ['app/app.component.css'] - }) - export class AppComponent implements OnInit{ - title = 'Tour of Heroes'; - - heroes: Hero[] = []; - - ngOnInit() { - getHeroes().then(heroes => this.heroes = heroes); - } - } - - bootstrap(AppComponent, []); - - function getHeroes() { - return // some promise of data; - } - ``` - - The same components are now separated into their own files. - - ```javascript - /* recommended */ - - // main.ts - import { bootstrap } from 'angular2/platform/browser'; - import { AppComponent } from './app.component'; - - bootstrap(AppComponent, []); - ``` - - ```javascript - /* recommended */ - - // app.component.ts - import { Component, OnInit } from 'angular2/core'; - - import { Hero } from './hero'; - import { HeroService } from './hero.service'; - - @Component({ - selector: 'my-app', - template: ` -
{{heroes | json}}
- `, - styleUrls: ['app/app.component.css'], - providers: [HeroService] - }) - export class AppComponent implements OnInit{ - heroes: Hero[] = []; - - constructor(private _heroService: HeroService) {} - - ngOnInit() { - this._heroService.getHeroes().then(heroes => this.heroes = heroes); - } - } - ``` - - ```typescript - /* recommended */ - - // hero.service.ts - import { Injectable } from 'angular2/core'; - import { HEROES } from './mock-heroes'; - - @Injectable() - export class HeroService { - getHeroes() { - return Promise.resolve(HEROES); - } - } - ``` - - ```javascript - /* recommended */ - - // hero.ts - export class Hero { - id: number; - name: string; - } - ``` - - As the app grows, this rule becomes even more important. - -**[Back to top](#table-of-contents)** - -### Small Functions -###### [Style [A2-002](#style-a2-002)] - - - Define small functions, no more than 75 LOC (less is better). - - *Why?*: Small functions are easier to test, especially when they do one thing and serve one purpose. - - *Why?*: Small functions promote reuse. - - *Why?*: Small functions are easier to read. - - *Why?*: Small functions are easier to maintain. - - *Why?*: Small functions help avoid hidden bugs that come with large functions that share variables with external scope, create unwanted closures, or unwanted coupling with dependencies. - -**[Back to top](#table-of-contents)** - -## Modules - -### Avoid Naming Collisions -###### [Style [A2-020](#style-a2-020)] - - - Use unique naming conventions. - - *Why?*: Unique names help avoid module name collisions. We import and export modules by the file in which they are contained. Thus the name of the files and their folders is important. - - *Why?*: Names of folders and files should clear convey their intent. For example, `app/speakers/speaker-list.component.ts` may contain a component that manages a list of speakers. The folder and file names clearly convey the meaning, and thus the module import is clear as `import { SpeakerListComponent } from './speakers/speaker-list.component'`. - -## Components - -### Member Sequence -###### [Style [A2-033](#style-a2-033)] - - - Place properties up top, then public functions, then private functions, alphabetized, and not spread through the component code. - - *Why?*: Placing members in a consistent sequence makes it easy to read and helps you instantly identify which members of the component serve which purpose. - - ```typescript - /* recommended */ - export class ToastComponent implements OnInit { - // private fields - private _defaults = { - title: '', - message: 'May the Force be with You' - }; - private _toastElement: any; - - // public properties - title: string; - message: string; - - // ctor - constructor(toastService: ToastService) { - toastService.activate = this.activate.bind(this); - } - - // public functions - activate(message = this._defaults.message, title = this._defaults.title) { - this.title = title; - this.message = message; - this._show(); - } - - ngOnInit() { - this._toastElement = document.getElementById('toast'); - } - - // private functions - private _hide() { - this._toastElement.style.opacity = 0; - window.setTimeout(() => this._toastElement.style.zIndex = 0, 400); - } - - private _show() { - console.log(this.message); - this._toastElement.style.opacity = 1; - this._toastElement.style.zIndex = 9999; - - window.setTimeout(() => this._hide(), 2500); - } - } - ``` - -### Defer Logic to Services -###### [Style [A2-035](#style-a2-035)] - - - Defer logic in a component by delegating to services. - - - Define a component for a view, and try not to reuse the component for other views. Instead, move reusable logic to factories and keep the component simple and focused on its view. - - *Why?*: Logic may be reused by multiple components when placed within a service and exposed via a function. - - *Why?*: Logic in a service can more easily be isolated in a unit test, while the calling logic in the component can be easily mocked. - - *Why?*: Removes dependencies and hides implementation details from the component. - - *Why?*: Keeps the component slim, trim, and focused. - - ```typescript - // avoid - export class SessionListComponent implements OnInit { - sessions: Session[]; - - constructor(private _http: Http) { } - - getSessions() { - this.sessions = []; - this._http.get(sessionsUrl) - .map((response: Response) => response.json().data) - .catch(this._exceptionService.catchBadResponse) - .finally(() => this._spinnerService.hide()) - .subscribe(sessions => this.sessions = sessions); - } - - - ngOnInit() { - this.getSessions(); - } - } - ``` - - ```typescript - // recommended - export class SessionListComponent implements OnInit { - sessions: Session[]; - - constructor(private _sessionService: SessionService) { } - - getSessions() { - this.sessions = []; - this._sessionService.getSessions() - .subscribe(sessions => this.sessions = sessions); - } - - ngOnInit() { - this.getSessions(); - } - } - ``` - -**[Back to top](#table-of-contents)** - -## Services - -### Singletons -###### [Style [A2-040](#style-a2-040)] - - - Services are singletons and should be used for sharing data and functionality. - - ```typescript - // service - import { Injectable } from 'angular2/core'; - import { Session } from './session'; - - @Injectable() - export class HeroService { - constructor(private _http: Http) { } - - getHeroes() { - return this._http.get('api/sessions') - .map((response: Response) => response.json().data) - } - } - ``` - -### Providing a Service -###### [Style [A2-041](#style-a2-041)] - - - Services should be provided to the Angular 2 injector at the top-most component where they will be shared. - - *Why?*: The Angular 2 injector is hierarchical. - - *Why?*: When providing the service to a top level component, that instance is shared and available to all child components of that top level component. - - *Why?*: This is ideal when a service is sharing methods and has no state, or state that must be shared. - - *Why?*: This is not ideal when two different components need different instances of a service. In this scenario it would be better to provide the service at the component level that needs the new and separate instance. - - ```typescript - /* recommended */ - - // app.component.ts - import { Component } from 'angular2/core'; - - import { SpeakerListComponent } from './speakers/speaker-list.component'; - import { SpeakerService } from './common/speaker.service'; - - @Component({ - selector: 'my-app', - template: ` - - `, - directives: [SpeakerListComponent], - providers: [SpeakerService] - }) - export class AppComponent { } - ``` - - ```typescript - /* recommended */ - - // speaker-list.component.ts - import { Component, OnInit } from 'angular2/core'; - - import { SpeakerService } from './common/speaker.service'; - import { Speaker } from './common/speaker'; - - @Component({ - selector: 'my-speakers', - template: ` -
{{speakers | json}}
- ` - }) - export class SpeakerListComponent implements OnInit{ - speakers: Speaker[] = []; - - constructor(private _speakerService: SpeakerService) {} - - ngOnInit() { - this._speakerService.getSpeakers().then(speakers => this.speakers = speakers); - } - } - ``` - -**[Back to top](#table-of-contents)** - -### Single Responsibility -###### [Style [A2-042](#style-a2-050)] - - - Services should have a [single responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle), that is encapsulated by its context. Once a service begins to exceed that singular purpose, a new one should be created. - -**[Back to top](#table-of-contents)** - -## Data Services - -### Separate Data Calls -###### [Style [A2-050](#style-a2-050)] - - - Refactor logic for making data operations and interacting with data to a service. Make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations. - - *Why?*: The component's responsibility is for the presentation and gathering of information for the view. It should not care how it gets the data, just that it knows who to ask for it. Separating the data services moves the logic on how to get it to the data service, and lets the component be simpler and more focused on the view. - - *Why?*: This makes it easier to test (mock or real) the data calls when testing a component that uses a data service. - - *Why?*: Data service implementation may have very specific code to handle the data repository. This may include headers, how to talk to the data, or other services such as `$http`. Separating the logic into a data service encapsulates this logic in a single place hiding the implementation from the outside consumers (perhaps a component), also making it easier to change the implementation. - - ```typescript - // recommended - export class SessionListComponent implements OnInit { - sessions: Session[]; - filteredSessions = this.sessions; +## Relocating the Angular 2 Style Guide +The Angular 2 Style Guide is being moved to the [Official Angular 2 docs](http://angular.io/docs). I will still be shepherding the guide there, and we will release it very soon! - constructor(private _sessionService: SessionService) { } +## Appendix - getSessions() { - this.sessions = []; - this._sessionService.getSessions() - .subscribe(sessions => { - this.sessions = this.filteredSessions = sessions; - }, - error => { - console.log('error occurred here'); - console.log(error); - }, - () => { - console.log('completed'); - }); - } - - ngOnInit() { - this.getSessions(); - } - } - ``` - -**[Back to top](#table-of-contents)** - -## Naming - -### Naming Guidelines -###### [Style [A2-100](#style-a2-100)] - - - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. My recommended pattern is `feature.type.js`. There are 2 names for most assets: - * the file name (`avengers.component.ts`) - * the registered component name with Angular (`Component`) - - *Why?*: Naming conventions help provide a consistent way to find content at a glance. Consistency within the project is vital. Consistency with a team is important. Consistency across a company provides tremendous efficiency. - - *Why?*: The naming conventions should simply help you find your code faster and make it easier to understand. - -**[Back to top](#table-of-contents)** - -### File Names -###### [Style [A2-101](#style-a2-101)] - - - Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. A recommended pattern is `feature.type.ts`. - - - use dashes to separate words and dots to separate the descriptive name from the type. - - *Why?*: Provides a consistent way to quickly identify components. - - *Why?*: Provides pattern matching for any automated tasks. - - ``` - // recommended - - // Bootstrapping file and main entry point - main.ts - - // Components - speakers.component.ts - speaker-list.component.ts - speaker-detail.component.ts - - // Services - logger.service.ts - speaker.service.ts - exception.service.ts - filter-text.service.ts - - // Models - session.ts - speaker.ts - - // Pipes - ellipsis.pipe.ts - init-caps.pipe.ts - ``` - -### Test File Names -###### [Style [A2-102](#style-a2-102)] - - - Name test specifications similar to the component they test with a suffix of `spec`. - - *Why?*: Provides a consistent way to quickly identify components. - - *Why?*: Provides pattern matching for [karma](http://karma-runner.github.io/) or other test runners. - - ``` - // recommended - - // Components - speakers.component.spec.ts - speaker-list.component.spec.ts - speaker-detail.component.spec.ts - - // Services - logger.service.spec.ts - speaker.service.spec.ts - exception.service.spec.ts - filter-text.service.spec.ts - - // Pipes - ellipsis.pipe.spec.ts - init-caps.pipe.spec.ts - ``` - -**[Back to top](#table-of-contents)** - -### Component Names -###### [Style [A2-103](#style-a2-103)] - - - Use consistent names for all components named after their feature. Use UpperCamelCase for components' symbols. Match the name of the component to the naming of the file - - *Why?*: Provides a consistent way to quickly identify and reference components. - - *Why?*: UpperCamelCase is conventional for identifying object that can be instantiated using a constructor. - - ```typescript - AppComponent //app.component.ts - SpeakersComponent //speakers.component.ts - SpeakerListComponent //speaker-list.component.ts - SpeakerDetailComponent //speaker-detail.component.ts - ``` - -**[Back to top](#table-of-contents)** - -### Suffixes -###### [Style [A2-104](#style-a2-104)] - - - Append the component name with the suffix `Component`. - - *Why?*: The `Component` suffix is more commonly used and is more explicitly descriptive. - - ```typescript - // recommended - - // speaker-list.component.ts - export class SpeakerListComponent { } - ``` - -### Service Names -###### [Style [A2-110](#style-a2-110)] - - - Use consistent names for all services named after their feature. Use UpperCamelCase for services. Only suffix service and factories with `Service` when it is not clear what they are (e.g. when they are nouns). - - *Why?*: Provides a consistent way to quickly identify and reference services. - - *Why?*: Clear service names such as `logger` do not require a suffix. - - *Why?*: Service names such as `Credit` are nouns and require a suffix and should be named with a suffix when it is not obvious if it is a service or something else. - - ```typescript - SpeakerService // speaker.service.ts - CreditService // credit.service.ts - Logger // logger.service.ts - ``` - -**[Back to top](#table-of-contents)** - -## Routing -Client-side routing is important for creating a navigation flow between a component tree hierarchy, and composing components that are made of many other child components. - -**[Back to top](#table-of-contents)** - -### Component Router -###### [Style [A2-130](#style-a2-130)] - - - Separate route configuration into a routing component file, also known as a component router. - - - Use a `` in the component router, where the routes will have their component targets display their templates. - - - Focus the logic in the component router to the routing aspects and its target components. Extract other logic to services and other components. - - *Why?*: A component that handles routing is known as the component router, thus this follows the Angular 2 routing pattern. - - *Why?*: A component that handles routing is known as the componenter router. - - *Why?*: The `` indicates where the tempalte should be displayed for the target route. - - ```typescript - import { Component } from 'angular2/core'; - import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router'; - - import { SpeakersComponent, SpeakerService } from './^speakers'; - import { DashboardComponent } from './^dashboard'; - import { NavComponent } from './layout/nav.component'; - - @Component({ - selector: 'my-app', - templateUrl: 'app/app.component.html', - styleUrls: ['app/app.component.css'], - directives: [ROUTER_DIRECTIVES, NavComponent], - providers: [ - ROUTER_PROVIDERS, - SpeakerService - ] - }) - @RouteConfig([ - { path: '/dashboard', name: 'Dashboard', component: DashboardComponent, useAsDefault: true }, - { path: '/speakers/...', name: 'Speakers', component: SpeakersComponent }, - ]) - export class AppComponent { } - ``` - -**[Back to top](#table-of-contents)** - -## Application Structure LIFT Principle -### LIFT -###### [Style [Y140](#style-y140)] - - - Structure your app such that you can `L`ocate your code quickly, `I`dentify the code at a glance, keep the `F`lattest structure you can, and `T`ry to stay DRY. The structure should follow these 4 basic guidelines. - - *Why LIFT?*: Provides a consistent structure that scales well, is modular, and makes it easier to increase developer efficiency by finding code quickly. Another way to check your app structure is to ask yourself: How quickly can you open and work in all of the related files for a feature? - - When I find my structure is not feeling comfortable, I go back and revisit these LIFT guidelines - - 1. `L`ocating our code is easy - 2. `I`dentify code at a glance - 3. `F`lat structure as long as we can - 4. `T`ry to stay DRY (Don’t Repeat Yourself) or T-DRY - -### Locate -###### [Style [Y141](#style-y141)] - - - Make locating your code intuitive, simple and fast. - - *Why?*: I find this to be super important for a project. If the team cannot find the files they need to work on quickly, they will not be able to work as efficiently as possible, and the structure needs to change. You may not know the file name or where its related files are, so putting them in the most intuitive locations and near each other saves a ton of time. A descriptive folder structure can help with this. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identify -###### [Style [Y142](#style-y142)] - - - When you look at a file you should instantly know what it contains and represents. - - *Why?*: You spend less time hunting and pecking for code, and become more efficient. If this means you want longer file names, then so be it. Be descriptive with file names and keeping the contents of the file to exactly 1 component. Avoid files with multiple controllers, multiple services, or a mixture. There are deviations of the 1 per file rule when I have a set of very small features that are all related to each other, they are still easily identifiable. - -### Flat -###### [Style [Y143](#style-y143)] - - - Keep a flat folder structure as long as possible. When you get to 7+ files, begin considering separation. - - *Why?*: Nobody wants to search 7 levels of folders to find a file. Think about menus on web sites … anything deeper than 2 should take serious consideration. In a folder structure there is no hard and fast number rule, but when a folder has 7-10 files, that may be time to create subfolders. Base it on your comfort level. Use a flatter structure until there is an obvious value (to help the rest of LIFT) in creating a new folder. - -### T-DRY (Try to Stick to DRY) -###### [Style [Y144](#style-y144)] - - - Be DRY, but don't go nuts and sacrifice readability. - - *Why?*: Being DRY is important, but not crucial if it sacrifices the others in LIFT, which is why I call it T-DRY. I don’t want to type session-view.html for a view because, well, it’s obviously a view. If it is not obvious or by convention, then I name it. - -**[Back to top](#table-of-contents)** - -## Application Structure - -### Overall Guidelines -###### [Style [A2-150](#style-a2-150)] - - - Have a near term view of implementation and a long term vision. In other words, start small but keep in mind on where the app is heading down the road. All of the app's code goes in a root folder named `app`. All content is 1 feature per file. Each component, service, pipe is in its own file. All 3rd party vendor scripts are stored in another root folder and not in the `app` folder. I didn't write them and I don't want them cluttering my app. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -### Layout -###### [Style [A2-151](#style-a2-151)] - - - Place components that define the overall layout of the application in a folder named `layout`. These may include a shell view and component may act as the container for the app, navigation, menus, content areas, and other regions. - - *Why?*: Organizes all layout in a single place re-used throughout the application. - -### Folders-by-Feature Structure -###### [Style [A2-152](#style-a2-152)] - - - Create folders named for the feature they represent. When a folder grows to contain more than 7 files, start to consider creating a folder for them. Your threshold may be different, so adjust as needed. - - *Why?*: A developer can locate the code, identify what each file represents at a glance, the structure is flat as can be, and there is no repetitive nor redundant names. - - *Why?*: The LIFT guidelines are all covered. - - *Why?*: Helps reduce the app from becoming cluttered through organizing the content and keeping them aligned with the LIFT guidelines. - - *Why?*: When there are a lot of files (10+) locating them is easier with a consistent folder structures and more difficult in flat structures. - - **example coming soon** - -**[Back to top](#table-of-contents)** - -## File Templates and Snippets -Use file templates or snippets to help follow consistent styles and patterns. Here are templates and/or snippets for some of the web development editors and IDEs. - -### Visual Studio Code - - - [Visual Studio Code](https://code.visualstudio.com/) snippets that follow these styles and guidelines. - - - [Snippets for VS Code](https://marketplace.visualstudio.com/items?itemName=johnpapa.Angular2) - - [![Use Extension](https://github.com/johnpapa/vscode-angular2-snippets/raw/master/images/use-extension.gif)](https://marketplace.visualstudio.com/items?itemName=johnpapa.Angular2) - - -**[Back to top](#table-of-contents)** - -## Angular CLI +>If you like this guide, check out my [Angular 2 First Look course on Pluralsight](http://jpapa.me/a2ps1stlook). -**[Back to top](#table-of-contents)** +![Angular 2 First Look](https://s3-us-west-2.amazonaws.com/johnpapa-blog-images/a2-first-look-app.gif) -## Angular docs For anything else, API reference, check the [Angular 2 documentation](//angular.io). **[Back to top](#table-of-contents)** From f891ee6c7006bd01e55099435c3979f302d7bc9f Mon Sep 17 00:00:00 2001 From: John Papa Date: Mon, 25 Apr 2016 18:46:54 -0400 Subject: [PATCH 076/114] link --- a2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a2/README.md b/a2/README.md index 113a9dc0..b75134fc 100644 --- a/a2/README.md +++ b/a2/README.md @@ -14,7 +14,7 @@ If you are looking for an opinionated style guide for syntax, conventions, and s The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions I use and, more importantly, why I choose them. ## Relocating the Angular 2 Style Guide -The Angular 2 Style Guide is being moved to the [Official Angular 2 docs](http://angular.io/docs). I will still be shepherding the guide there, and we will release it very soon! +The Angular 2 Style Guide is being moved to the [Official Angular 2 docs](https://angular.io/docs/ts/latest/). I will still be shepherding the guide there, and we will release it very soon! ## Appendix From 6df3fe0a49da4c1f108e247463b136e5fe468087 Mon Sep 17 00:00:00 2001 From: John Papa Date: Mon, 25 Apr 2016 18:47:16 -0400 Subject: [PATCH 077/114] links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f8d72e76..2c92c15b 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ There are multiple versions of Angular, and thus there are multiple versions of the guide. Choose your guide appropriately. ### Angular 1 Style Guide -[The Angular 1 Style Guide is located here](https://github.com/johnpapa/angular-styleguide/tree/master/a1/README.md). +[The Angular 1 Style Guide](https://github.com/johnpapa/angular-styleguide/tree/master/a1/README.md). ### Angular 2 Style Guide -[The **D R A F T** Angular 2 Style Guide is located here](https://github.com/johnpapa/angular-styleguide/tree/master/a2/README.md). +[The Angular 2 Style Guide](https://github.com/johnpapa/angular-styleguide/tree/master/a2/README.md). ## Angular Team Endorsed Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. From 507600bb50c52096528d878fdbf7eaadd4f8d65f Mon Sep 17 00:00:00 2001 From: Eric Lee Carraway Date: Wed, 27 Apr 2016 12:42:00 -0500 Subject: [PATCH 078/114] fix typo: declaration => declarations --- a1/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/a1/README.md b/a1/README.md index a02bce82..6e98aa0d 100644 --- a/a1/README.md +++ b/a1/README.md @@ -535,7 +535,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see *Why?*: Placing the implementation details of a function later in the file moves that complexity out of view so you can see the important stuff up top. - *Why?*: Function declaration are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions). + *Why?*: Function declarations are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions). *Why?*: You never have to worry with function declarations that moving `var a` before `var b` will break your code because `a` depends on `b`. @@ -844,7 +844,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see *Why?*: Placing the implementation details of a function later in the file moves that complexity out of view so you can see the important stuff up top. - *Why?*: Function declaration are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions). + *Why?*: Function declarations are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions). *Why?*: You never have to worry with function declarations that moving `var a` before `var b` will break your code because `a` depends on `b`. From c3ec7351b9c0588fb2b6eac82bab6892073f44fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=A9rio=20Vieira?= Date: Thu, 28 Apr 2016 18:05:49 -0300 Subject: [PATCH 079/114] Original text and redundant removed --- a1/i18n/pt-BR.md | 1 - 1 file changed, 1 deletion(-) diff --git a/a1/i18n/pt-BR.md b/a1/i18n/pt-BR.md index 2f961aeb..1a9f9a4a 100644 --- a/a1/i18n/pt-BR.md +++ b/a1/i18n/pt-BR.md @@ -2093,7 +2093,6 @@ ou *Mantenha o módulo da aplicação leve* - Somente coloque a lógica para reunir o aplicativo no módulo da aplicação. Deixe os recursos em seus próprios módulos. - **Por que?** Adding additional roles to the application root to get remote data, display views, or other logic not related to pulling the app together muddies the app module and make both sets of features harder to reuse or turn off. **Por que?** Colocar funções adicionais na raiz da aplicação para obter dados remoto, modos de exibição, ou outra lógica não relacionada com o acoplamento do aplicativo, torna mais difícil reutilizar os recursos ou mesmo, desligá-los. **Por que?** O módulo da aplicação torna-se um manifesto que descreve os módulos que ajudam a definir a aplicação. From e7d69e0f8eb0d28c7c04ff821c78551fbfe4de8e Mon Sep 17 00:00:00 2001 From: John Papa Date: Fri, 6 May 2016 11:05:13 -0600 Subject: [PATCH 080/114] guide is in the docs now --- a2/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/a2/README.md b/a2/README.md index b75134fc..dbda1e90 100644 --- a/a2/README.md +++ b/a2/README.md @@ -14,7 +14,7 @@ If you are looking for an opinionated style guide for syntax, conventions, and s The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions I use and, more importantly, why I choose them. ## Relocating the Angular 2 Style Guide -The Angular 2 Style Guide is being moved to the [Official Angular 2 docs](https://angular.io/docs/ts/latest/). I will still be shepherding the guide there, and we will release it very soon! +The Angular 2 Style Guide has been moved to the [Official Angular 2 docs](http://jpapa.me/ng2styleguide). I will still be shepherding the guide there. It is a living guide ... we'll keep evolving the guide. ## Appendix @@ -22,6 +22,4 @@ The Angular 2 Style Guide is being moved to the [Official Angular 2 docs](https: ![Angular 2 First Look](https://s3-us-west-2.amazonaws.com/johnpapa-blog-images/a2-first-look-app.gif) -For anything else, API reference, check the [Angular 2 documentation](//angular.io). - **[Back to top](#table-of-contents)** From 9234c26183398199790ab0e13ce8b15299ed5bcb Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 29 Jun 2016 02:01:16 -0500 Subject: [PATCH 081/114] Fixed typo --- a1/i18n/ru-RU.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/i18n/ru-RU.md b/a1/i18n/ru-RU.md index bd8a3f50..83b2d033 100644 --- a/a1/i18n/ru-RU.md +++ b/a1/i18n/ru-RU.md @@ -2735,7 +2735,7 @@ ## Contributing -Сначала откройте issue и объясните вопрос/проблему для того, чтобы обсудить потенциальные изменения/добавления. Если у вас есть вопросы по этому руководству, вы можете свободно задавать их, создавая в хранилище issues. Если вы нашли опечатку, создайте pull request. Идея в том, чтобы содержимое всегда дежать актуальным и используя возможности системы Github, помочь рассказать историю с вопросами или проблемами и распространить ее, чтобы она была доступна через поиск Google. Почему? Потому что, если у вас был такой вопрос, то вполне вероятно, что кто-то тоже ищет решение на этот же вопрос! Вы можете узнать больше здесь о том как можно сотрудничать. +Сначала откройте issue и объясните вопрос/проблему для того, чтобы обсудить потенциальные изменения/добавления. Если у вас есть вопросы по этому руководству, вы можете свободно задавать их, создавая в хранилище issues. Если вы нашли опечатку, создайте pull request. Идея в том, чтобы содержимое всегда держать актуальным и используя возможности системы Github, помочь рассказать историю с вопросами или проблемами и распространить ее, чтобы она была доступна через поиск Google. Почему? Потому что, если у вас был такой вопрос, то вполне вероятно, что кто-то тоже ищет решение на этот же вопрос! Вы можете узнать больше здесь о том как можно сотрудничать. *Добавляя материал в данное хранилище вы согласны с тем, ваше содержимое будет доступно согласно приведенной ниже лицензии.* From b10311fe36b2646805cd13be932ee5522bf697ed Mon Sep 17 00:00:00 2001 From: Amo Wu Date: Tue, 6 Sep 2016 02:25:07 +0800 Subject: [PATCH 082/114] fix: wrong URL (#763) thanks! --- a1/i18n/zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/i18n/zh-CN.md b/a1/i18n/zh-CN.md index e369cb42..df554f8e 100644 --- a/a1/i18n/zh-CN.md +++ b/a1/i18n/zh-CN.md @@ -23,7 +23,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 看示例代码有助于你更好地理解,你可以在`modular`文件夹下找到[命名为modular的示例应用程序](https://github.com/johnpapa/ng-demos),随便克隆。 ##翻译 -[Angular规范翻译版本](https://github.com/johnpapa/angular-styleguide/tree/master/i18n)。 +[Angular规范翻译版本](https://github.com/johnpapa/angular-styleguide/tree/master/a1/i18n)。 ##目录 1. [单一职责](#单一职责) From 8e1cd5605f4c49ba0143718794104e8b24c30c44 Mon Sep 17 00:00:00 2001 From: Donghyun Seo Date: Tue, 6 Sep 2016 03:25:19 +0900 Subject: [PATCH 083/114] Fixed typo (#762) thanks! --- a1/i18n/ko-KR.md | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/a1/i18n/ko-KR.md b/a1/i18n/ko-KR.md index 90b4de96..62f6f6b3 100644 --- a/a1/i18n/ko-KR.md +++ b/a1/i18n/ko-KR.md @@ -6,7 +6,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 ## Purpose *팀환경을 위한 방향을 제시하는 Angular 스타일 가이드 by [@john_papa](//twitter.com/john_papa)* -만약 Angular [Angular](//angularjs.org) 어플리케이션의 문법, 컨벤션, 구조화를 위한 스타일 가이드를 찾고 있다면 제대로 오셨습니다. 여기 제시된 스타일들은 제 팀 단위 개발 경험, 프레젠테이션, [Pluralsight training courses](http://pluralsight.com/training/Authors/Details/john-papa)를 토대로 만들어졌습니다. +만약 [Angular](//angularjs.org) 어플리케이션의 문법, 컨벤션, 구조화를 위한 스타일 가이드를 찾고 있다면 제대로 오셨습니다. 여기 제시된 스타일들은 제 팀 단위 개발 경험, 프레젠테이션, [Pluralsight training courses](http://pluralsight.com/training/Authors/Details/john-papa)를 토대로 만들어졌습니다. 이 스타일 가이드의 목적은 Angular 어플리케이션을 만드는 길잡이 역할을 하기 위함이며 더 나아가 왜 내가 이런 것들을 선택했는지 보여주기 위함입니다. >만약 이 가이드가 마음에 든다면 Pluralsight 에 올려놓은 저의 강의를 참고하시기 바랍니다. [Angular Patterns: Clean Code](http://jpapa.me/ngclean) @@ -206,7 +206,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 ]); ``` - 대신 간단한 세터 구무을 사용하고, 체인으로 나머지 부분을 처리하세요. + 대신 간단한 세터 구문을 사용하고, 체인으로 나머지 부분을 처리하세요. ```javascript /* recommended */ @@ -330,7 +330,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 ### controllerAs Controller Syntax ###### [Style [Y031](#style-y031)] - - `전형적인 컨트롤러 `$scope` 구문 대신 `controllerAs` 구문을 사용하세요. + - 전형적인 컨트롤러 `$scope` 구문 대신 `controllerAs` 구문을 사용하세요. - `controllerAs` 구문은 `$scope` 에 바인딩 하기위해 컨트롤러 안에서 `this`를 사용합니다. @@ -1141,7 +1141,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 ### Manipulate DOM in a Directive ###### [Style [Y072](#style-y072)] - - DOM을 직접 다루게 되었다면, 디렉티브를 사용하세요. 만약 CSS나 [animation services](https://docs.angularjs.org/api/ngAnimate), Angular templating, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) or [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide) 등의 방식을 사용할 수 있다면 이를 사용하세요. 예를 들, 만약 디렉티브의 기능이 간단히 보여줌, 숨김 기능만 있다면 ngHide/ngShow를 사용하세요. + - DOM을 직접 다루게 되었다면, 디렉티브를 사용하세요. 만약 CSS나 [animation services](https://docs.angularjs.org/api/ngAnimate), Angular templating, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) or [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide) 등의 방식을 사용할 수 있다면 이를 사용하세요. 예를 들어, 만약 디렉티브의 기능이 간단히 보여줌, 숨김 기능만 있다면 ngHide/ngShow를 사용하세요. *이유*: DOM을 다루는 코드는 테스트, 수정이 어렵고 대부분 더 나은 구현 방법이 존재합니다. (e.g. CSS, 애니매이션, 템플릿) @@ -1667,7 +1667,6 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 } ``` - When the above code is run through ng-annotate it will produce the following output with the `$inject` annotation and become minification-safe. 위의 코드가 포함된 코드가 ng-annotate를 거치게 되면 `$inject` 부분을 생성하게 되어 최소화 안전 코드가 됩니다. ```javascript @@ -1797,7 +1796,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 *이유*: 예를 들어 XHR 호출이나 프라미스 실패 시, 좀 더 일관적인 방식으로 코드에서 발생한 예외를 잡아줍니다. - 주의: 예외 캐쳐는 당신이 예상했던 호출에서 특정 예외가 발생했을 때 그것을 잡아내고 대처하는데 좋습ㄴ디ㅏ. 예를 들어 원격 웹 서비스에 접속해서 데이터를 가져오는 XHR 호출을 만들 때 그 서비스로 부터 예외를 받아서 특정한 방식으로 대처할 수 있습니다. + 주의: 예외 캐쳐는 당신이 예상했던 호출에서 특정 예외가 발생했을 때 그것을 잡아내고 대처하는데 좋습니다. 예를 들어 원격 웹 서비스에 접속해서 데이터를 가져오는 XHR 호출을 만들 때 그 서비스로 부터 예외를 받아서 특정한 방식으로 대처할 수 있습니다. ```javascript /* recommended */ @@ -2059,7 +2058,6 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 ### Directive Component Names ###### [Style [Y126](#style-y126)] - - Use consistent names for all directives using camel-case. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). - 캐멀 캐이스를 이용해서 디렉티브 이름을 일관적으로 지어주세요. 짧은 접두어를 사용하여 이 디렉티브가 어떤 프로젝트 혹은 회사에 소속되어 있는지 알려주세요. *이유*: 일관된 방식으로 컴포넌트를 찾아내고 참조할 수 있도록 합니다. @@ -2114,10 +2112,10 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 만약 다음 가이드라인에 적합하지 않는 부분이 있다고 느껴지면, 위로 돌아가서 LIFT 가이드라인을 다시 살펴보세요. - 1. `L`코드를 쉽게 찾아낼 수 있음 - 2. `I`첫눈에 구분할 수 있음 - 3. `F`단순한 구조를 유지할 수 있음 - 4. `T`반복작업을 피할 수 있음 + 1. `L`코드를 쉽게 찾아낼 수 있음 (Locate) + 2. `I`첫눈에 구분할 수 있음 (Identify) + 3. `F`단순한 구조를 유지할 수 있음 (Flat) + 4. `T`반복작업을 피할 수 있음 (T-DRY) ### Locate ###### [Style [Y141](#style-y141)] @@ -2311,7 +2309,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 - 레이아웃 같은 기능 구역을 나타내는 모듈들, 재사용 가능하고 공유가 가능한 서비스, 대시보드, 특화된 기능들을 모듈로 만드세요 (예를 들어 고객, 어드민, 판매). - *이유*: 자극 자족적인 모듈들은 어플리케이션에 아주 작은 충돌 혹은 아무 충돌 없이 추가될 수 있습니다. + *이유*: 자급자족적인 모듈들은 어플리케이션에 아주 작은 충돌 혹은 아무 충돌 없이 추가될 수 있습니다. *이유*: 스프린트나 이터레이션은 기능 구역에 집중될 수 있고 마지막 부분에 켜질 수 있습니다. @@ -2425,7 +2423,6 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 ### Write Tests with Stories ###### [Style [Y190](#style-y190)] - - Write a set of tests for every story. Start with an empty test and fill them in as you write the code for the story. - 모든 각각의 스토리를 위해서 테스트 세트를 작성하세요. 빈 테스트로 시작을 하고 스토리에 맞추어 코드를 작성하면서 채워나가세요. *이유*: 테스트 설명을 작성함으로 스스로의 스토리가 어떻게 작동해야 하고 어떻게 작동하지 말아야 하는지 투명하게 정의할 수 있습니다. 그리고 성공을 어떻게 측정해야 하는지도 포함됩니다. @@ -2572,7 +2569,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 *이유*: 단위 테스트는 소스 코드의 특정 컴포넌트와 파일들과 직접적인 상호작용을 합니다. - *이유*: 항상 눈에 보여지게 됨으로 최신으로 유지하기가 쉽습ㄴ디ㅏ. TDD 또는 개발중 테스트 또는 개발 후 테스트 중 어떤 것을 사용하든 테스트 스팩은 나란히 보여지고 눈에서 멀어지기 어렵고 마음에서도 멀어지기 어렵습니다. 그러니 코드를 테스팅하는 코드도 유지보수하기 쉬워집니다. + *이유*: 항상 눈에 보여지게 됨으로 최신으로 유지하기가 쉽습니다. TDD 또는 개발중 테스트 또는 개발 후 테스트 중 어떤 것을 사용하든 테스트 스팩은 나란히 보여지고 눈에서 멀어지기 어렵고 마음에서도 멀어지기 어렵습니다. 그러니 코드를 테스팅하는 코드도 유지보수하기 쉬워집니다. *이유*: 소스코드를 수정하게 될 때 테스트도 동시에 수정하기가 매우 쉽습니다. 한폴더에 있고 보여지니까요. @@ -2871,7 +2868,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 ###### [Style [Y241](#style-y241)] - - 변하지 않고 다른 서비스로부터 오지 않는 값들은 불변 상수를 이용하세요. 다양한 어플리케이션에서 재사용될 수 있는 모듈 내에서 사용되는 불변상 수들은 모듈의 이름을 딴 상수 파일을 만들어서 넣어두세요. 이 작업이 필요하기 전까지는 불변 상수는 메인 모듈의 `constants.js` 파일에 넣어두면 됩니다. + - 변하지 않고 다른 서비스로부터 오지 않는 값들은 불변 상수를 이용하세요. 다양한 어플리케이션에서 재사용될 수 있는 모듈 내에서 사용되는 불변 상수들은 모듈의 이름을 딴 상수 파일을 만들어서 넣어두세요. 이 작업이 필요하기 전까지는 불변 상수는 메인 모듈의 `constants.js` 파일에 넣어두면 됩니다. *이유*: 자주 변하지 않더라도 변할 가능성이 있는 값들은 서비스로부터 받아서 사용해야 소스코드를 변경하지 않아도 되게 됩니다. 예를 들어 데이터를 받아오는 url 값은 상수로 저장해서 사용할 수도 있지만, 더 좋은 곳은 웹서비스로 부터 받아오는 것입니다. @@ -3175,7 +3172,7 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 **[Back to top](#table-of-contents)** ## Task Automation -[Gulp](http://gulpjs.com) 또는 [Grunt](http://gruntjs.com)를 사용하여 자동화 처리를 사용하세요. Gult는 설정보다는 코드 자체에 무게를 더 주는 반면 Grunt는 설정을 더 중요하게 생각합니다. 개인적으로는 읽고 작성하기가 쉬워서 Gulp를 선호합니다. 하지만 둘다 정말 멋집니다. +[Gulp](http://gulpjs.com) 또는 [Grunt](http://gruntjs.com)를 사용하여 자동화 처리를 사용하세요. Gulp는 설정보다는 코드 자체에 무게를 더 주는 반면 Grunt는 설정을 더 중요하게 생각합니다. 개인적으로는 읽고 작성하기가 쉬워서 Gulp를 선호합니다. 하지만 둘다 정말 멋집니다. > 여기를 참고하여 gulp 자동화 업무 패턴을 배우세요 [Gulp Pluralsight course](http://jpapa.me/gulpps). @@ -3210,13 +3207,13 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 **[Back to top](#table-of-contents)** ## Angular docs -나머지 부분, API 참고는 [Angular 문서](//docs.angularjs.org/api)여기로 가시면 됩니다. +나머지 부분, API 참고는 [Angular 문서](//docs.angularjs.org/api)로 가시면 됩니다. ## Contributing 수정과 추가를 위해서는 이슈를 먼저 발행하시기 바랍니다. 이 가이드에 질문이 있으면 리파지토리에 이슈를 남겨주세요. 오타를 발견하면 pull request를 만들어주세요. 이렇게 하는 이유는 github의 기능을 최대한 사용해서 이슈와 PR이 어떻게 이루어 졌는지를 알려주기 위함입니다. 이런 정보는 구글로 검색도 가능합니다. 왜냐구요? 이상하게도 당신이 질문이 있다면 다른사람들도 그런 질문을 가지기 때문입니다. 여기서 어떻게 기여할 수 있는지 배울 수 있습니다. -*이 저장소에 기여함으로서 당신의 콘텐츠가 이 저장소의 라이센트의 대상이 됨을 동의합니다.* +*이 저장소에 기여함으로서 당신의 콘텐츠가 이 저장소의 라이센스의 대상이 됨을 동의합니다.* ### Process 1. Discuss the changes in a GitHub issue. From 13a38c7f255b8e4eb03387a6a153e19e021ac01a Mon Sep 17 00:00:00 2001 From: Anton Sitnikov Date: Mon, 5 Sep 2016 22:25:46 +0400 Subject: [PATCH 084/114] Fix link to sublime snippets, add new commands (#761) thanks! --- a1/i18n/ru-RU.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/a1/i18n/ru-RU.md b/a1/i18n/ru-RU.md index 83b2d033..5e2b3da8 100644 --- a/a1/i18n/ru-RU.md +++ b/a1/i18n/ru-RU.md @@ -2618,16 +2618,18 @@ - Angular сниппеты, которые соблюдают приведенные здесь стили и руководства. - - Скачайте [Sublime Angular сниппеты](assets/sublime-angular-snippets.zip?raw=true) + - Скачайте [Sublime Angular сниппеты](https://github.com/johnpapa/angular-styleguide/tree/master/a1/assets/sublime-angular-snippets) - Поместите все в вашу папку Packages - Перезапустите Sublime - В файле JavaScript напечатайте следующие команды после клавиши `TAB` ```javascript + ngmodule // создает модуль Angular ngcontroller // создает контроллер Angular - ngdirective // создает директиву Angular + ngservice // создает сервис Angular ngfactory // создает фабрику Angular - ngmodule // создает модуль Angular + ngdirective // создает директиву Angular + ngfilter // создает фильтр Angular ``` ### Visual Studio From 04608f1bb26bbdce919b3faacbcf87a00eb51eb2 Mon Sep 17 00:00:00 2001 From: Irvin Sandoval Date: Mon, 5 Sep 2016 12:25:55 -0600 Subject: [PATCH 085/114] Replace "AngulRJS" to "AngularJS" (#757) thanks! --- a1/i18n/es-ES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/i18n/es-ES.md b/a1/i18n/es-ES.md index c332073c..f0d2f6c2 100644 --- a/a1/i18n/es-ES.md +++ b/a1/i18n/es-ES.md @@ -2,7 +2,7 @@ *Guía de estilos colaborativa de Angular para equipos por [@john_papa](//twitter.com/john_papa)* -Si estás buscando una guía colaborativa sobre sintaxis, convenciones y estructura de aplicaciones con AngulRJS, este es el sitio. Estos estilos están basados en mi experiencia desarrollando con [AngularJS](//angularjs.org), persentaciones, [Cursos en Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) y trabajando en equipos. +Si estás buscando una guía colaborativa sobre sintaxis, convenciones y estructura de aplicaciones con AngularJS, este es el sitio. Estos estilos están basados en mi experiencia desarrollando con [AngularJS](//angularjs.org), persentaciones, [Cursos en Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) y trabajando en equipos. El propósito de esta guía de estilos es proporcionar una guía de cómo construir aplicaciones con Angular enseñando convenciones que uso y, lo más importante, el porqué. From 25a1077633906423df888e7e648643151f4e2de5 Mon Sep 17 00:00:00 2001 From: Patrick Shaughnessy Date: Mon, 5 Sep 2016 11:26:12 -0700 Subject: [PATCH 086/114] update angular 1 karma test runner code snippet to following updated karma api (#756) thanks! --- a1/README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/a1/README.md b/a1/README.md index 6e98aa0d..42082fde 100644 --- a/a1/README.md +++ b/a1/README.md @@ -2552,7 +2552,7 @@ Unit testing helps maintain clean code, as such I included some of my recommenda var child; var excludeFiles = []; var fork = require('child_process').fork; - var karma = require('karma').server; + var Server = require('karma').Server; var serverSpecs = config.serverIntegrationSpecs; if (args.startServers) { @@ -2567,11 +2567,14 @@ Unit testing helps maintain clean code, as such I included some of my recommenda } } - karma.start({ - configFile: __dirname + '/karma.conf.js', - exclude: excludeFiles, - singleRun: !!singleRun - }, karmaCompleted); + var karmaOptions = { + configFile: __dirname + '/karma.conf.js', + exclude: excludeFiles, + singleRun: !!singleRun + }; + + let server = new Server(karmaOptions, karmaCompleted); + server.start(); //////////////// From ccbddf098bf3471a0966ed6e486c55e7a584b1b2 Mon Sep 17 00:00:00 2001 From: Andrey Tkachenko Date: Mon, 5 Sep 2016 22:27:35 +0400 Subject: [PATCH 087/114] Update ru-RU.md (#734) --- a1/i18n/ru-RU.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/i18n/ru-RU.md b/a1/i18n/ru-RU.md index 5e2b3da8..f007a796 100644 --- a/a1/i18n/ru-RU.md +++ b/a1/i18n/ru-RU.md @@ -369,7 +369,7 @@ var vm = this; ``` - Замечание: Когда создаете наблюдающие функции(watcher) в контроллерах типа `controller as`, вы можете наблюдать за переменной следующего вида `vm.*`. (Создавайте наблюдающие функции с предупреждением, что они создают дополнительную нагрузку на цикл digest.) + Замечание: Когда создаете наблюдающие функции(watcher) в контроллерах типа `controller as`, вы можете наблюдать за переменной следующего вида `vm.*`. (Создавайте наблюдающие функции с осторожностью, т.к. они добавляют дополнительную нагрузку на цикл digest.) ```html From 0dafdc87af99ab89777c1770953c25c80e11622d Mon Sep 17 00:00:00 2001 From: Aaron Miller Date: Mon, 5 Sep 2016 14:27:54 -0400 Subject: [PATCH 088/114] add Emacs snippets (#684) thanks! --- a1/README.md | 21 ++++++++++ .../javascript-mode/.yas-parents | 1 + .../js-mode/.yas-parents | 1 + .../js2-mode/angular.controller.snip | 26 +++++++++++++ .../js2-mode/angular.directive.snip | 39 +++++++++++++++++++ .../js2-mode/angular.factory.snip | 26 +++++++++++++ .../js2-mode/angular.filter.snip | 23 +++++++++++ .../js2-mode/angular.module.snip | 14 +++++++ .../js2-mode/angular.service.snip | 23 +++++++++++ .../js3-mode/.yas-parents | 1 + 10 files changed, 175 insertions(+) create mode 100644 a1/assets/emacs-angular-snippets/javascript-mode/.yas-parents create mode 100644 a1/assets/emacs-angular-snippets/js-mode/.yas-parents create mode 100644 a1/assets/emacs-angular-snippets/js2-mode/angular.controller.snip create mode 100644 a1/assets/emacs-angular-snippets/js2-mode/angular.directive.snip create mode 100644 a1/assets/emacs-angular-snippets/js2-mode/angular.factory.snip create mode 100644 a1/assets/emacs-angular-snippets/js2-mode/angular.filter.snip create mode 100644 a1/assets/emacs-angular-snippets/js2-mode/angular.module.snip create mode 100644 a1/assets/emacs-angular-snippets/js2-mode/angular.service.snip create mode 100644 a1/assets/emacs-angular-snippets/js3-mode/.yas-parents diff --git a/a1/README.md b/a1/README.md index 42082fde..edbbef19 100644 --- a/a1/README.md +++ b/a1/README.md @@ -3125,6 +3125,27 @@ Use file templates or snippets to help follow consistent styles and patterns. He ngservice // creates an Angular service ``` +### Emacs +###### [Style [Y257](#style-y257)] + + - [Emacs](https://www.gnu.org/software/emacs/) snippets that follow these styles and guidelines. + + - Download the [Emacs Angular snippets](assets/emacs-angular-snippets?raw=true) + + Note that yasnippet categorizes snippets by major mode, and there are several Emacs major modes for editing Javascript code. The snippets are in `js2-mode`, and the other directories contain only a dotfile to reference them there. + + - install [yasnippet](https://github.com/capitaomorte/yasnippet) (`M-x package-install RET yasnippet RET`) + - copy snippets to snippet directory, or modify your Emacs init to add snippet directory to `yas-snippet-dirs` + + ```javascript + ngcontroller // creates an Angular controller + ngdirective // creates an Angular directive + ngfactory // creates an Angular factory + ngmodule // creates an Angular module + ngservice // creates an Angular service + ngfilter // creates an Angular filter + ``` + **[Back to top](#table-of-contents)** ## Yeoman Generator diff --git a/a1/assets/emacs-angular-snippets/javascript-mode/.yas-parents b/a1/assets/emacs-angular-snippets/javascript-mode/.yas-parents new file mode 100644 index 00000000..8ed60a53 --- /dev/null +++ b/a1/assets/emacs-angular-snippets/javascript-mode/.yas-parents @@ -0,0 +1 @@ +js2-mode diff --git a/a1/assets/emacs-angular-snippets/js-mode/.yas-parents b/a1/assets/emacs-angular-snippets/js-mode/.yas-parents new file mode 100644 index 00000000..8ed60a53 --- /dev/null +++ b/a1/assets/emacs-angular-snippets/js-mode/.yas-parents @@ -0,0 +1 @@ +js2-mode diff --git a/a1/assets/emacs-angular-snippets/js2-mode/angular.controller.snip b/a1/assets/emacs-angular-snippets/js2-mode/angular.controller.snip new file mode 100644 index 00000000..63a7eecb --- /dev/null +++ b/a1/assets/emacs-angular-snippets/js2-mode/angular.controller.snip @@ -0,0 +1,26 @@ +# -*- mode: snippet; require-final-newline: nil -*- +# name: ngcontroller +# key: ngcontroller +# binding: direct-keybinding +# -- + +(function() { + 'use strict'; + + angular + .module('${1:module}') + .controller('${2:Controller}Controller', $2Controller); + + /* @ngInject */ + function $2Controller(${3:dependencies}) { + var vm = this; + vm.title = '$2Controller'; + + activate(); + + //////////////// + + function activate() { + } + } +})(); diff --git a/a1/assets/emacs-angular-snippets/js2-mode/angular.directive.snip b/a1/assets/emacs-angular-snippets/js2-mode/angular.directive.snip new file mode 100644 index 00000000..bb1f8de2 --- /dev/null +++ b/a1/assets/emacs-angular-snippets/js2-mode/angular.directive.snip @@ -0,0 +1,39 @@ +# -*- mode: snippet; require-final-newline: nil -*- +# name: ngdirective +# key: ngdirective +# binding: direct-keybinding +# -- + +(function() { + 'use strict'; + + angular + .module('${1:module}') + .directive('${2:directive}', $2); + + /* @ngInject */ + function $2(${3:dependencies}) { + // Usage: + // + // Creates: + // + var directive = { + bindToController: true, + controller: ${4:Controller}, + controllerAs: '${5:vm}', + link: link, + restrict: 'A', + scope: { + } + }; + return directive; + + function link(scope, element, attrs) { + } + } + + /* @ngInject */ + function $4() { + + } +})(); diff --git a/a1/assets/emacs-angular-snippets/js2-mode/angular.factory.snip b/a1/assets/emacs-angular-snippets/js2-mode/angular.factory.snip new file mode 100644 index 00000000..a08719db --- /dev/null +++ b/a1/assets/emacs-angular-snippets/js2-mode/angular.factory.snip @@ -0,0 +1,26 @@ +# -*- mode: snippet; require-final-newline: nil -*- +# name: ngfactory +# key: ngfactory +# binding: direct-keybinding +# -- + +(function() { + 'use strict'; + + angular + .module('${1:module}') + .factory('${2:factory}', $2); + + /* @ngInject */ + function $2(${3:dependencies}) { + var service = { + ${4:func}: $4 + }; + return service; + + //////////////// + + function $4() { + } + } +})(); diff --git a/a1/assets/emacs-angular-snippets/js2-mode/angular.filter.snip b/a1/assets/emacs-angular-snippets/js2-mode/angular.filter.snip new file mode 100644 index 00000000..14e5e115 --- /dev/null +++ b/a1/assets/emacs-angular-snippets/js2-mode/angular.filter.snip @@ -0,0 +1,23 @@ +# -*- mode: snippet; require-final-newline: nil -*- +# name: ngfilter +# key: ngfilter +# binding: direct-keybinding +# -- + +(function() { + 'use strict'; + + angular + .module('${1:module}') + .filter('${2:filter}', $2); + + function $2() { + return $2Filter; + + //////////////// + function $2Filter(${3:params}) { + return $3; + }; + } + +})(); diff --git a/a1/assets/emacs-angular-snippets/js2-mode/angular.module.snip b/a1/assets/emacs-angular-snippets/js2-mode/angular.module.snip new file mode 100644 index 00000000..fd8eaef8 --- /dev/null +++ b/a1/assets/emacs-angular-snippets/js2-mode/angular.module.snip @@ -0,0 +1,14 @@ +# -*- mode: snippet; require-final-newline: nil -*- +# name: ngmodule +# key: ngmodule +# binding: direct-keybinding +# -- + +(function() { + 'use strict'; + + angular + .module('${1:module}', [ + '${2:dependencies}' + ]); +})(); diff --git a/a1/assets/emacs-angular-snippets/js2-mode/angular.service.snip b/a1/assets/emacs-angular-snippets/js2-mode/angular.service.snip new file mode 100644 index 00000000..c121d682 --- /dev/null +++ b/a1/assets/emacs-angular-snippets/js2-mode/angular.service.snip @@ -0,0 +1,23 @@ +# -*- mode: snippet; require-final-newline: nil -*- +# name: ngservice +# key: ngservice +# binding: direct-keybinding +# -- + +(function() { + 'use strict'; + + angular + .module('${1:module}') + .service('${2:Service}', $2); + + /* @ngInject */ + function $2(${3:dependencies}) { + this.${4:func} = $4; + + //////////////// + + function $4() { + } + } +})(); diff --git a/a1/assets/emacs-angular-snippets/js3-mode/.yas-parents b/a1/assets/emacs-angular-snippets/js3-mode/.yas-parents new file mode 100644 index 00000000..8ed60a53 --- /dev/null +++ b/a1/assets/emacs-angular-snippets/js3-mode/.yas-parents @@ -0,0 +1 @@ +js2-mode From b17676ae26889e9124d52d460cc73730ac2ea99a Mon Sep 17 00:00:00 2001 From: Matt Grande Date: Tue, 13 Sep 2016 22:05:18 -0400 Subject: [PATCH 089/114] Update screenshot to include 'Controller' suffix (#776) --- a1/assets/above-the-fold-1.png | Bin 126268 -> 82710 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/a1/assets/above-the-fold-1.png b/a1/assets/above-the-fold-1.png index 71b977803f7df282926fa2e6c659f4d354cf40f6..e6052bd11a94b4a8c2f7d3dedfc10bfe8b12e30f 100644 GIT binary patch literal 82710 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 literal 126268 zcmZ_01yGb>+XgBnh;%GScS(v!H%qrP2uOE#N{O&^cXvv6iFCKr(w)-HdGY)H|D2hB z&I~iVv+u(0`^0@;^$?;cFM*0ofc)y!D^w{-G38gU;P76(g7ray1zwRKwUB!Sa|9

|zM?g4SWkx* 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Ðu1|KdR*!R`uGI=mT7;~ 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%0AH5$zm$yvAl{n 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 pv&KQUPet4Q$}jJ3Y1q!r$(a#cut()brbQZX= Date: Thu, 26 Jan 2017 03:16:10 +0100 Subject: [PATCH 090/114] doc(fr-FR): fix typo in Y160 (#807) thanks! --- a1/i18n/fr-FR.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/i18n/fr-FR.md b/a1/i18n/fr-FR.md index b3fae0d8..6e781736 100644 --- a/a1/i18n/fr-FR.md +++ b/a1/i18n/fr-FR.md @@ -2256,7 +2256,7 @@ Bien que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'est ut - Créez de petits modules qui encapsulent une seule responsabilité. - *Pourquoi ?* : Les applications modulaires rendent facile la compossibilité rapide puisqu'elles permettent aux équipes de développement de construire verticalement des sections de l'application et de livrer incrémentalement. Cela signifie que nous pouvons brancher de nouvelles fonctionnalités au fur et à mesure de leur développement. + *Pourquoi ?* : Les applications modulaires rendent facile la composabilité rapide puisqu'elles permettent aux équipes de développement de construire verticalement des sections de l'application et de livrer incrémentalement. Cela signifie que nous pouvons brancher de nouvelles fonctionnalités au fur et à mesure de leur développement. ### Création d'un module applicatif ###### [Style [Y161](#style-y161)] From ae94cf359151c53605bf5a2728ade4ded4d27feb Mon Sep 17 00:00:00 2001 From: Taiyib Ashraf Date: Tue, 21 Feb 2017 14:38:48 +0000 Subject: [PATCH 091/114] Added separation between avoid and recommended code for resolving-promises section (#817) --- a1/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/a1/README.md b/a1/README.md index edbbef19..2a80cbf1 100644 --- a/a1/README.md +++ b/a1/README.md @@ -1550,7 +1550,9 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see // *** } } + ``` + ```javascript /* recommended */ function getCustomer(id) { return $http.get('/api/customer/' + id) From c8839dcb3e4658e6972b75752d1b8c75600565a5 Mon Sep 17 00:00:00 2001 From: John Papa Date: Thu, 2 Mar 2017 11:19:19 -0500 Subject: [PATCH 092/114] revising 171 for race conditioning (#818) --- a1/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/a1/README.md b/a1/README.md index 2a80cbf1..3242f6ae 100644 --- a/a1/README.md +++ b/a1/README.md @@ -2457,10 +2457,14 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see ### Run Blocks ###### [Style [Y171](#style-y171)] - - Any code that needs to run when an application starts should be declared in a factory, exposed via a function, and injected into the [run block](https://docs.angularjs.org/guide/module#module-loading-dependencies). + - Any code that needs to run when an application starts should be declared in a factory, exposed via a function, and injected into the [run block](https://docs.angularjs.org/guide/module#module-loading-dependencies). + + - Consider using manual bootstrapping techniques, as an alternative for logic that must run prior to running the Angular app. *Why?*: Code directly in a run block can be difficult to test. Placing in a factory makes it easier to abstract and mock. + *Why?*: Code directly in a run block can cause race conditions for startup logic, as it does not have a way to communicate when asynchronous code in the run block has completed. + ```javascript angular .module('app') From 87c26806518658c28a896d4d6351ab7de386fae9 Mon Sep 17 00:00:00 2001 From: Danilo Velasquez Urrutia Date: Thu, 2 Mar 2017 13:19:42 -0300 Subject: [PATCH 093/114] Update es-ES.md (#816) Fix typo. --- a1/i18n/es-ES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/i18n/es-ES.md b/a1/i18n/es-ES.md index f0d2f6c2..766d23bb 100644 --- a/a1/i18n/es-ES.md +++ b/a1/i18n/es-ES.md @@ -2711,7 +2711,7 @@ Usa Plantillas o snippets para ayudarte a seguir estilos consistentes o patrones ### WebStorm ###### [Style [Y252](#style-y252)] - - Snippets y arhicos de Angular que siguen estos estilos y directrices. Puedes importarlos en tus configuraciones de WebStorm: + - Snippets y arhivos de Angular que siguen estos estilos y directrices. Puedes importarlos en tus configuraciones de WebStorm: - Descarga los [snippets y plantillas de Angular para WebStorm](assets/webstorm-angular-file-template.settings.jar?raw=true) - Abre WebStorm y ve al menú `File` From e02442d265e86d65306ecf8850d6d86b10caf556 Mon Sep 17 00:00:00 2001 From: Raldo CHEA Date: Thu, 2 Mar 2017 17:20:35 +0100 Subject: [PATCH 094/114] Fix i18n link (#809) I fixed the i18n link. --- a1/i18n/fr-FR.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/i18n/fr-FR.md b/a1/i18n/fr-FR.md index 6e781736..60aa6923 100644 --- a/a1/i18n/fr-FR.md +++ b/a1/i18n/fr-FR.md @@ -19,7 +19,7 @@ Beaucoup de mes styles proviennent des nombreuses séances de pair programming a Bien que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'est utile de pouvoir les visualiser dans la pratique. Ce guide est accompagné par une application d'exemple qui suit ces styles et ces modèles. Vous pouvez trouver l'[application d'exemple (intitulée modular) ici](https://github.com/johnpapa/ng-demos) dans le répertoire `modular`. Vous pouvez librement le récupérer, le cloner, ou le *forker*. [Les instructions pour l’exécuter sont contenues dans ce readme](https://github.com/johnpapa/ng-demos/tree/master/modular). ## Traductions -[Les traductions de ce guide stylistique pour Angular](https://github.com/johnpapa/angular-styleguide/tree/master/i18n) sont maintenues par la communauté et peuvent être trouvées ici. +[Les traductions de ce guide stylistique pour Angular](https://github.com/johnpapa/angular-styleguide/tree/master/a1/i18n) sont maintenues par la communauté et peuvent être trouvées ici. ## Table des matières From 352f025122b064c66ebbd7bffeb890a7d54584bc Mon Sep 17 00:00:00 2001 From: Wassim Chegham Date: Thu, 2 Mar 2017 17:20:54 +0100 Subject: [PATCH 095/114] docs(fr-FR): fix typo in french doc (#806) --- a1/i18n/fr-FR.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/i18n/fr-FR.md b/a1/i18n/fr-FR.md index 60aa6923..389ea8b7 100644 --- a/a1/i18n/fr-FR.md +++ b/a1/i18n/fr-FR.md @@ -2050,7 +2050,7 @@ Bien que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'est ut ### Modules ###### [Style [Y127](#style-y127)] - - Lorqu'il y a de multiples modules, nommez le fichier du module principal ˋapp.module.jsˋ et nommez les autres modules qui en sont dépendants d'après ce qu'ils représentent. Par exemple, nommez un module d'administration ˋadmin.module.jsˋ. Les noms des modules déclarés seraient alors respectivement ˋappˋ et ˋadminˋ. + - Lorsqu'il y a de multiples modules, nommez le fichier du module principal ˋapp.module.jsˋ et nommez les autres modules qui en sont dépendants d'après ce qu'ils représentent. Par exemple, nommez un module d'administration ˋadmin.module.jsˋ. Les noms des modules déclarés seraient alors respectivement ˋappˋ et ˋadminˋ. *Pourquoi ?* : Fournit de la cohérence pour les applications modulaires, et pour prévoir l'extension des applications. From 5f1296c9699962ca67ae79919bc4a59f635c782a Mon Sep 17 00:00:00 2001 From: Fabio Silva Lima Date: Thu, 2 Mar 2017 13:21:09 -0300 Subject: [PATCH 096/114] =?UTF-8?q?Melhor=20tradu=C3=A7=C3=A3o=20no=20cont?= =?UTF-8?q?exto=20e=20corre=C3=A7=C3=A3o=20(#801)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A palavra "Teams" = Times (mais usado no RJ) e Equipes (mais usados no resto do Brasil). Porém acredito que poderia ser "desenvolvedores" pois cada desenvolvedor pode seguir sem necessariamente estar em uma "Team". A outra mudança é na regra do 1, "Rule of 1. Não significa "Regra nº 1" como se fosse uma ordem, primeira regra. É na verdade um nome: "Regra do 1" em analogia ao Single Responsability, onde algo de ver apenas uma responsabilidade. --- a1/i18n/pt-BR.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/a1/i18n/pt-BR.md b/a1/i18n/pt-BR.md index 1a9f9a4a..2c5d8fb5 100644 --- a/a1/i18n/pt-BR.md +++ b/a1/i18n/pt-BR.md @@ -1,6 +1,6 @@ # Guia de Estilo AngularJS -*Guia de Estilo opinativo de Angular para times. Por [@john_papa](//twitter.com/john_papa)* +*Guia de Estilo opinativo de Angular para desenvolvedores. Por [@john_papa](//twitter.com/john_papa)* Se você procura por um guia de estilo opinativo para sintaxe, convenções e estruturação de aplicações AngularJS, então siga em frente! Estes estilos são baseados em minha experiência com desenvolvimento com [AngularJS](//angularjs.org), apresentações, [cursos de treinamento na Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) e trabalhando em equipe. @@ -58,9 +58,9 @@ Embora este guia explique o **o quê**, **porque** e **como**, acho útil ver tu ## Single Responsibility ou *Responsabilidade Única* -### Regra nº 1 +### Regra do 1 - - Defina um componente por arquivo. + - Defina 1 componente por arquivo onde contenha menos de 400 linhas de código. O exemplo seguinte define um módulo `app` e suas dependências, define um controller e define uma factory, todos no mesmo arquivo. From 9c9dac5ac1716cb1210a0d4e3dee1a3896f25513 Mon Sep 17 00:00:00 2001 From: blafcnav Date: Thu, 2 Mar 2017 17:21:21 +0100 Subject: [PATCH 097/114] Nom d'une fonction moviesPrepService (#808) Correction sur le nom de la fonction. moviePrepService => moviesPrepService --- a1/i18n/fr-FR.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/i18n/fr-FR.md b/a1/i18n/fr-FR.md index 389ea8b7..6e7981a3 100644 --- a/a1/i18n/fr-FR.md +++ b/a1/i18n/fr-FR.md @@ -1451,7 +1451,7 @@ Bien que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'est ut }); } - function moviePrepService(movieService) { + function moviesPrepService(movieService) { return movieService.getMovies(); } From 2127cceca294988f0934ba08ba3ff33387e678af Mon Sep 17 00:00:00 2001 From: Endi Pons Date: Thu, 2 Mar 2017 17:21:39 +0100 Subject: [PATCH 098/114] Update fr-FR.md (#793) --- a1/i18n/fr-FR.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/i18n/fr-FR.md b/a1/i18n/fr-FR.md index 6e7981a3..637f7673 100644 --- a/a1/i18n/fr-FR.md +++ b/a1/i18n/fr-FR.md @@ -2017,7 +2017,7 @@ Bien que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'est ut function creditService() { } - // credit.service.js + // customers.service.js angular .module .service('customersService', customersService); From 7db2c6f5575ccab94879b29dc5f31ea6eb95a9d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnny=20Calvi=C3=B1o?= Date: Thu, 2 Mar 2017 17:21:50 +0100 Subject: [PATCH 099/114] =?UTF-8?q?Reemplace=20"f=C3=A1brica"=20to=20"fact?= =?UTF-8?q?or=C3=ADa"=20(#794)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The correct translation should be "factoria" --- a1/i18n/es-ES.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/a1/i18n/es-ES.md b/a1/i18n/es-ES.md index 766d23bb..f8edf6d6 100644 --- a/a1/i18n/es-ES.md +++ b/a1/i18n/es-ES.md @@ -28,7 +28,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti 1. [Módulos](#módulos) 1. [Controladores](#controladores) 1. [Servicios](#servicios) - 1. [Fábricas](#fábricas) + 1. [Factorías](#factorías) 1. [Servicios de Datos](#servicios-de-datos) 1. [Directivas](#directivas) 1. [Resolviendo Promesas en un Controlador](#resolviendo-promesas-en-un-controlador) @@ -67,7 +67,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti *¿Por qué?*: Un componente por archivo evita errores ocultos que a menudo surgen cuando se combinan componentes en un archivo donde pueden compartir variables, crear closures (clausuras) no deseadas, o acoplamiento indeseado de dependencias. - El siguiente ejemplo define el módulo `app` y sus dependencias, define un controlador, y defines una fábrica todo en el mismo archivo. + El siguiente ejemplo define el módulo `app` y sus dependencias, define un controlador, y defines una factoría todo en el mismo archivo. ```javascript /* evitar */ @@ -176,7 +176,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti - Nota: Para acortar únicamente, el resto de los ejemplos de esta guía podrían omitir la sintaxis IIFE. - - Nota: IIFE previente que el código de los tests llegue a sus variables privadas, como expresiones regulares o funciones de ayuda que normalmente vienen bien para hacer pruebas por sí solas. Sin embargo, puedes acceder a ellas creando accesorios o accediendo a través de sus componentes. Por ejemplo, poniendo las funciones de ayuda, expresiones regulares o constantes en su propia fábrica. + - Nota: IIFE previente que el código de los tests llegue a sus variables privadas, como expresiones regulares o funciones de ayuda que normalmente vienen bien para hacer pruebas por sí solas. Sin embargo, puedes acceder a ellas creando accesorios o accediendo a través de sus componentes. Por ejemplo, poniendo las funciones de ayuda, expresiones regulares o constantes en su propia factoría. **[Volver arriba](#tabla-de-contenidos)** @@ -326,7 +326,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti *¿Por qué?*: `controllerAs` es azúcar sintáctico sobre el `$scope`. Puedes enlazar a la vista y acceder a los métodos del `$scope`. - *¿Por qué?*: Ayuda a evitar la tentación de usar los métodos del `$scope` dentro de un controller cuando debería ser mejor evitar usarlos o moverlos a una fábrica. Considera usar `$scope` en una factory, o en un controlador sólo cuando sea necesario. Por ejemplo cuando publicas y te suscribes a eventos usando [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), o [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) considera mover estos usos a una fábrica e invocarlos desde el controlador. + *¿Por qué?*: Ayuda a evitar la tentación de usar los métodos del `$scope` dentro de un controller cuando debería ser mejor evitar usarlos o moverlos a una factoría. Considera usar `$scope` en una factory, o en un controlador sólo cuando sea necesario. Por ejemplo cuando publicas y te suscribes a eventos usando [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), o [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) considera mover estos usos a una factoría e invocarlos desde el controlador. ```javascript /* evitar */ @@ -562,7 +562,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti ### Diferir la Lógica del Controlador ###### [Style [Y035](#style-y035)] - - Difiera la lógica dentro de un controlador delegándola a servicios y fábricas. + - Difiera la lógica dentro de un controlador delegándola a servicios y factorías. *¿Por qué?*: La lógica podría ser reutilizada por varios controladores cuando la colocas en un servicio y la expones como una función. @@ -620,7 +620,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti ### Mantén tus Controladores Enfocados ###### [Style [Y037](#style-y037)] - - Define un controlador para una vista, no intentes reutilizar el controlador para otras vistas. En lugar de eso, mueve la lógica que se pueda reutilizar a fábricas y deja el controlador simple y enfocado en su vista. + - Define un controlador para una vista, no intentes reutilizar el controlador para otras vistas. En lugar de eso, mueve la lógica que se pueda reutilizar a factorías y deja el controlador simple y enfocado en su vista. *¿Por qué?*: Reutilizar controladores con varias vistas es arriesgado y necesitarías buena cobertura de tests end to end (e2e) para asegurar que todo funciona bien en la aplicación. @@ -720,17 +720,17 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti **[Volver arriba](#tabla-de-contenidos)** -## Fábricas +## Factorías ### Responsabilidad Única ###### [Style [Y050](#style-y050)] - - Las fábricas deben tener una [responsabilidad única](http://en.wikipedia.org/wiki/Single_responsibility_principle), que es encapsulada por su contexto. Cuando una fábrica empiece a exceder el principio de responsabilidad única, una nueva fábrica debe ser creada. + - Las factorías deben tener una [responsabilidad única](http://en.wikipedia.org/wiki/Single_responsibility_principle), que es encapsulada por su contexto. Cuando una factoría empiece a exceder el principio de responsabilidad única, una nueva factoría debe ser creada. ### Singletons ###### [Style [Y051](#style-y051)] - - Las Fábricas son singleton y devuelven un objeto que contiene las variables del servicio. + - Las Factorías son singleton y devuelven un objeto que contiene las variables del servicio. Nota: [Todos los servicios Angular son singletons](https://docs.angularjs.org/guide/services). @@ -789,14 +789,14 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti De esta forma se asocian los bindeos desde el objeto que lo mantiene, los valores primitivos no se pueden modificar por si solos usando este patrón - ![Fábricas Usando "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) + ![Factorías Usando "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) ### Declaración de Funciones para Esconder los Detalles de Implementación ###### [Style [Y053](#style-y053)] - - Declara funciones para esconder detalles de implementación. Manten los elementos accesibles en la parte superior de la fábrica. Referencia a los que aparezcan después en el archivo. Para más detalles visita [este post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). + - Declara funciones para esconder detalles de implementación. Manten los elementos accesibles en la parte superior de la factoría. Referencia a los que aparezcan después en el archivo. Para más detalles visita [este post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - *¿Por qué?*: Coloca los elementos accesibles en la parte superior para hacerlo más fácil de leer y ayudarte a identificar instantáneamente qué funciones de la fábrica se pueden accesar externamente. + *¿Por qué?*: Coloca los elementos accesibles en la parte superior para hacerlo más fácil de leer y ayudarte a identificar instantáneamente qué funciones de la factoría se pueden accesar externamente. *¿Por qué?*: Colocar los detalles de implementación de una función al final del archivo mueve esa complejidad fuera de la vista, de esta forma puedes dejar lo importante arriba. @@ -1358,7 +1358,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti - Usa un route resolver cuando decidas cancelar la ruta antes de hacer la transición a la Vista. - *¿Por qué?*: Un controlador puede requerir datos antes de que se cargue. Esos datos deben venir desde una promesa a través de una fábrica o de [$http](https://docs.angularjs.org/api/ng/service/$http). Usando un [route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) permite que la promesa se resuelva antes de que la lógica del controlador se ejecute, así puedes tomar decisiones basándote en los datos de la promesa. + *¿Por qué?*: Un controlador puede requerir datos antes de que se cargue. Esos datos deben venir desde una promesa a través de una factoría o de [$http](https://docs.angularjs.org/api/ng/service/$http). Usando un [route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) permite que la promesa se resuelva antes de que la lógica del controlador se ejecute, así puedes tomar decisiones basándote en los datos de la promesa. *¿Por qué?*: El código se ejecuta después de la ruta y la función activate del controlador. La Vista empieza a cargar al instante. El bindeo de los datos se ejecutan cuando la promesa del activate se resuelva. Una animación de "Cargando" se puede mostrar durante la transición de la vista (via ng-view o ui-view) @@ -1746,7 +1746,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti ### Cachadores de Excepciones ###### [Style [Y111](#style-y111)] - - Crea una fábrica que exponga una interfaz para cachar y manejar excepciones elegantemente. + - Crea una factoría que exponga una interfaz para cachar y manejar excepciones elegantemente. *¿Por qué?*: Provee de una manera consistente de cachar excepciones que puedan ser arrojadas en tu código (e.g. durante llamadas XHR o promesas que fallaron). @@ -1968,12 +1968,12 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti function AvengersController() { } ``` -### Nombres de Fábricas +### Nombres de Factorías ###### [Style [Y125](#style-y125)] - - Usa nombres consistentes para todas las fábricas nombradas a partir de lo que hacen. Usa camel-casing para los servicios y las fábricas. + - Usa nombres consistentes para todas las factorías nombradas a partir de lo que hacen. Usa camel-casing para los servicios y las factorías. - *¿Por qué?*: Provee una manera consistente de identificar y referenciar fábricas rápidamente. + *¿Por qué?*: Provee una manera consistente de identificar y referenciar factorías rápidamente. ```javascript /** @@ -2311,9 +2311,9 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti ### Bloques Run ###### [Style [Y171](#style-y171)] - - Cualquier código que necesite ser ejecutado cuando una aplicación arranca debe ser declarado en una fábrica, ser expuesto a través de una función, o inyectado en el [bloque run](https://docs.angularjs.org/guide/module#module-loading-dependencies). + - Cualquier código que necesite ser ejecutado cuando una aplicación arranca debe ser declarado en una factoría, ser expuesto a través de una función, o inyectado en el [bloque run](https://docs.angularjs.org/guide/module#module-loading-dependencies). - *¿Por qué?*: Código que está directamente en un bloque run puede ser difícil de testear. Colocarlo en una fábrica lo hace fácil de abstraer y mockear. + *¿Por qué?*: Código que está directamente en un bloque run puede ser difícil de testear. Colocarlo en una factoría lo hace fácil de abstraer y mockear. ```javascript angular From bb8c7e64edc00f0b2454df9a269c6cc437aadaf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iuliu=20Vi=C8=99ovan?= Date: Thu, 2 Mar 2017 18:22:05 +0200 Subject: [PATCH 100/114] Added translation for romanian (ro-RO) (#799) * half of RO translation done * At 60%! * RO Translation >70% * >80%!! Woo! * RO translation finished * Turns out it wasnt done. Found some untranslated paragraphs. >99% * Finished, before review * RO translation Review over * Review #2 * Review #3 * Review #4 --- a1/i18n/ro-RO.md | 3313 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3313 insertions(+) create mode 100644 a1/i18n/ro-RO.md diff --git a/a1/i18n/ro-RO.md b/a1/i18n/ro-RO.md new file mode 100644 index 00000000..1d6518d5 --- /dev/null +++ b/a1/i18n/ro-RO.md @@ -0,0 +1,3313 @@ +# Ghid stilistic pentru Angular 1 + +## Aprobat de Echipa Angular +Mulțumiri speciale lui Igor Minar, liderul echipei Angular, pentru revizuire, dare de feedback, și pentru încrederea pe care mi-a acordat-o în păstorirea acestui ghid. + +## Scop +*Ghid stilistic dogmatic de Angular pentru echipe de [@john_papa](//twitter.com/john_papa)* + +Dacă cauți un ghid stilistic dogmatic pentru sintaxă, convenții și structurarea aplicațiilor Angular, pornește de aici. Aceste stiluri sunt bazate pe experiența mea de dezvoltare cu [Angular](//angularjs.org), prezentări, [cursuri de training Pluralsight](http://app.pluralsight.com/author/john-papa) și lucrul în echipe. + +Scopul acestui ghid stilistic este acela de a oferi îndrumare pentru creerea de aplicații Angular prin expunereea convențiilor pe care le folosesc și, mai important, motivelor pentru care le-am ales. + +> Dacă îți place acest ghid, vezi și cursul meu [Angular Patterns: Clean Code](http://jpapa.me/ngclean) de la Pluralsight, care este un companion pentru acest ghid. + + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) + +## Minunăția Comunității și Acreditare +Nu lucra niciodată într-un vid. Observ că comunitatea Angular este un grup incredibil care este pasionat de împărtășirea experiențelor. Din motivul acesta, expertul Angular Todd Motto și eu am colaborat la multe stiluri și convenții. Suntem de-acord la multe dintre ele, iar la unele nu. Te încurajez să vezi [Ghidurile lui Todd](https://github.com/toddmotto/angular-styleguide) ca să-ți faci o părere despre abordarea lui și cum diferă de acesta. + +Multe dintre stilurile mele sunt din multitudinea de sesiuni de pair programming pe care eu și [Ward Bell](https://twitter.com/wardbell) le-am făcut. Prietenul meu Ward a influențat cu siguranță evoluția finală a acestui ghid. + + +## Vezi stilurile într-o Aplicație-Model +Chiar dacă acest ghid explică *ce*, *de ce* și *cum*, mi se pare folositor ca acestea să fie văzute în practică. Acest ghid este acompaniat de o aplicație-model ce folosește aceste stiluri și structuri. Poți găsi [aplicația-model (numită 'modular') aici](https://github.com/johnpapa/ng-demos) în folderul `modular`. Simte-te liber să o iei și să-i dai 'clone' sau 'fork'. [Instrucțiuni pentru rularea ei găsești în readme](https://github.com/johnpapa/ng-demos/tree/master/modular). + + +##Traduceri +[Traduceri ale acestui ghid stilistic pentru Angular](https://github.com/johnpapa/angular-styleguide/tree/master/a1/i18n) sunt gestionate de către comunitate și pot fi găsite aici. + +## Cuprins + + 1. [Responsibilitate unică](#single-responsibility) + 1. [IIFE](#iife) + 1. [Module](#modules) + 1. [Controllere](#controllers) + 1. [Servicii](#services) + 1. [Factory-uri](#factories) + 1. [Servicii de date](#data-services) + 1. [Directive](#directives) + 1. [Rezolvarea Promise-urilor](#resolving-promises) + 1. [Adnotarea manuală a Injecției de Dependințe](#manual-annotating-for-dependency-injection) + 1. [Minificare și Adnotare](#minification-and-annotation) + 1. [Tratarea Excepțiilor](#exception-handling) + 1. [Denumire](#naming) + 1. [Structura aplicației - Principiul LIFT](#application-structure-lift-principle) + 1. [Structura aplicației](#application-structure) + 1. [Modularitate](#modularity) + 1. [Logica de lansare](#startup-logic) + 1. [Servicii $Wrapper](#angular--wrapper-services) + 1. [Testare](#testing) + 1. [Animații](#animations) + 1. [Comentarii](#comments) + 1. [JSHint](#js-hint) + 1. [JSCS](#jscs) + 1. [Constante](#constants) + 1. [Șabloane de fișier și snippeturi](#file-templates-and-snippets) + 1. [Generatorul Yeoman](#yeoman-generator) + 1. [Rutare](#routing) + 1. [Automatizarea taskurilor](#task-automation) + 1. [Filtre](#filters) + 1. [Documentația Angular](#angular-docs) + +## Responsabilitatea Unică + +### Regula 1 +###### [Style [Y001](#style-y001)] + + - Definește 1 component per fișier, recomandat cu mai puțin de 400 de linii de cod. + + *De ce?*: Un component per fișier ușurează atât unit testing-ul cât și creerea de date de test mai ușor. + + *De ce?*: Un component per fișier face totul mult mai ușor de citit, gestionat, și de evitat conflictele cu echipa în source control. + + *De ce?*: Un component per fișier evită buguri ascunse care apar frecvent când se combină componentele în fișiere în care se folosesc aceleași variabile, se creează closure-uri nedorite, sau se leagă, în mod nedorit, de dependințe. + + Următorul exemplu definește modulul `app` și dependințele sale, definește un controller și un factory, toate în același fișier. + + ```javascript + /* evită */ + angular + .module('app', ['ngRoute']) + .controller('SomeController', SomeController) + .factory('someFactory', someFactory); + + function SomeController() { } + + function someFactory() { } + ``` + + Aceleași componente sunt acum separate în propriile fișiere. + + ```javascript + /* recomandat */ + + // app.module.js + angular + .module('app', ['ngRoute']); + ``` + + ```javascript + /* recomandat */ + + // some.controller.js + angular + .module('app') + .controller('SomeController', SomeController); + + function SomeController() { } + ``` + + ```javascript + /* recomandat */ + + // some.factory.js + angular + .module('app') + .factory('someFactory', someFactory); + + function someFactory() { } + ``` + +**[Înapoi sus](#table-of-contents)** + +### Funcții mici +###### [Style [Y002](#style-y002)] + + - Definește funcții mici, nu mai mult de 75 LDC (linii de cod). (mai puțin e mai bine). + + *De ce?*: Funcțiile mici sunt mai ușor de citit, mai ușor de testat, în special atunci când fac un singur lucru și au un singur scop. + + *De ce?*: Funcțiile mici încurajează reuzabilitatea. + + *De ce?*: Funcțiile mici sunt mai ușor de citit. + + *De ce?*: Funcțiile mici sunt mai ușor de gestionat. + + *De ce?*: Funcțiile mici ajută evitarea de buguri ascunse care apar frecvent când există funcții mari care se folosesc de aceleași variabile, care creează closure-uri nedorite, sau se leagă, în mod nedorit, de dependințe. + +**[Înapoi sus](#table-of-contents)** + +## IIFE +### Scopurile JavaScript +###### [Style [Y010](#style-y010)] + + - Învelește componentele Angular într-o Expresie de Funcție Imediat-Invocată (Immediately Invoked Function Expression (IIFE)). + + *De ce?*: Un IIFE înlătură variabilele din scopul global. Acest lucru previne ca variabilele și declarațiile de funcții să trăiască mai mult decât ar trebui în scopul global, ceea ce totodată ajută la evitarea conflictelor dintre variabile. + + *De ce?*: Când codul tău e minificat și împachetat într-un singur fișier pentru deployment pe un server de producție, ai putea avea conflicte între variabile și foarte multe variabile globale. Un IIFE te protejează împotriva ambelor probleme oferind scop variabilelor pentru fiecare fișier. + + ```javascript + /* evită */ + // logger.js + angular + .module('app') + .factory('logger', logger); + + // funcția de logare e adăugată ca o variabilă globală + function logger() { } + + // storage.js + angular + .module('app') + .factory('storage', storage); + + // funcția de stocare e adăugată ca o variabilă globală + function storage() { } + ``` + + ```javascript + /** + * recomandat + * + * nicio variabilă globală nu e lăsată-n urmă + */ + + // logger.js + (function() { + 'use strict'; + + angular + .module('app') + .factory('logger', logger); + + function logger() { } + })(); + + // storage.js + (function() { + 'use strict'; + + angular + .module('app') + .factory('storage', storage); + + function storage() { } + })(); + ``` + + - Notă: Doar pentru brevitate, restul exemplelor din ghidul acesta ar putea omite sintaxa IIFE. + + - Notă: IIFE-urile previn codul de test din a ajunge la membrii privați, cum ar fi expresiile regulate sau funcțiile ajutătoare, care sunt de multe ori testabile direct pe cont propriu. Cu toate acestea, le puteți testa prin membrii accesibili sau prin expunerea lor prin propriile lor componente. De exemplu, plasarea de funcții ajutătoare, expresii regulate sau constante în proprile lor factory-uri sau constante. + + +**[Înapoi sus](#table-of-contents)** + +## Module + +### Evită coliziunile de denumiri +###### [Style [Y020](#style-y020)] + + - Folosește convenții de denumire unice cu separatori pentru sub-module. + + *De ce?*: Numele unice ajută la evitarea coliziunilor de denumiri a submodulelor. Separatorii ajută la definirea modulelor și a ierarhiei submodulelor lor. De exemplu, `app` ar putea fi modulul tău de bază, iar `app.dashboard` și `app.users` ar putea fi module care să fie folosite ca și dependințe ale `app`. + +### Definiri (sau Setteri) +###### [Style [Y021](#style-y021)] + + - Declară module fără o variabilă folosind sintaxa setter. + + *De ce?*: Cu un component per fișier, apare rar nevoia de a introduce o variabilă pentru modul. + + ```javascript + /* evită */ + var app = angular.module('app', [ + 'ngAnimate', + 'ngRoute', + 'app.shared', + 'app.dashboard' + ]); + ``` + + În schimb, folosește sintaxa setter, mai simplă. + + ```javascript + /* recomandat */ + angular + .module('app', [ + 'ngAnimate', + 'ngRoute', + 'app.shared', + 'app.dashboard' + ]); + ``` + +### Getteri +###### [Style [Y022](#style-y022)] + + - Când folosești un modul, evită folosirea unei variabile și folosește înlănțuirea cu sintaxa getter. + + *De ce?*: Acest lucru produce cod mai lizibil, și evită coliziunile de variabile sau scurgerile. + + ```javascript + /* evită */ + var app = angular.module('app'); + app.controller('SomeController', SomeController); + + function SomeController() { } + ``` + + ```javascript + /* recomandat */ + angular + .module('app') + .controller('SomeController', SomeController); + + function SomeController() { } + ``` + +### Setare vs Obținere +###### [Style [Y023](#style-y023)] + + - Setează doar o dată și obține pentru toate celelalte instanțe. + + *De ce?*: Un modul ar trebui să fie creat doar o dată, și preluat de-acolo-ncolo. + + ```javascript + /* recomandat */ + + // pentru a seta un modul + angular.module('app', []); + + // pentru a obține un modul + angular.module('app'); + ``` + +### Funcții Denumite vs Anonime +###### [Style [Y024](#style-y024)] + + - Folosește funcții denumite în locul pasării funcțiilor anonime ca și callback. + + *De ce?*: Produce cod mai lizibil, ușurează debug-ul, și reduce cantitatea de cod cu multe callback-uri cuibărite (??? xD) + + ```javascript + /* evită */ + angular + .module('app') + .controller('DashboardController', function() { }) + .factory('logger', function() { }); + ``` + + ```javascript + /* recomandat */ + + // dashboard.js + angular + .module('app') + .controller('DashboardController', DashboardController); + + function DashboardController() { } + ``` + + ```javascript + // logger.js + angular + .module('app') + .factory('logger', logger); + + function logger() { } + ``` + +**[Înapoi sus](#table-of-contents)** + +## Controllere + +### Sintaxa pentru View 'controllerAs' +###### [Style [Y030](#style-y030)] + + - Folosește sintaxa [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) în locul `classicei sintaxe pentru controller cu $scope`. + + *De ce?*: Controllerele sunt construite, "reînnoite" și oferă o singură nouă instanță, iar sintaxa `controllerAs` e mult mai aproape de cea a unui constructor de JavaScript decât cea a `clasicei sintaxe cu $scope`. + + *De ce?*: Încurajează folosirea de binduri cu un obiect "dotted" din View (e.g. `customer.name` în loc de `name`), ceea ce e mult mai contextual, mai ușor de citit, și evită orice probleme de referințe ce-ar putea apărea cu "dotting". + + *De ce?*: Ajută la evitarea de apeluri la `$parent` în View-uri cu Controllere ierarhizate. + ```html + +

+ {{ name }} +
+ ``` + + ```html + +
+ {{ customer.name }} +
+ ``` + +### Sintaxa de controller 'controllerAs' +###### [Style [Y031](#style-y031)] + + - Folosește sintaxa `controllerAs` în locul `sintaxei clasice de controller cu $scope`. + + - Sintaxa `controllerAs` folosește `this` înăuntrul controllerelor, iar acesta se leagă de `$scope`. + + *De ce?*: `controllerAs` e syntactic sugar pentru `$scope`. Poți face binduri în continuare la View și să și folosești metodele de pe `$scope`. + + *De ce?*: Ajută la evitarea temptației de a folosi metode ale `$scope` înăuntrul unui controller atunci când, de fapt, ar fi mai bine să le evităm sau să mutăm metoda într-un factory și să facem referință la ea din controller. Încearcă să folosești `$scope` într-un controller doar când e nevoie. De exemplu, atunci când publici sau abonezi evenimente folosind [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast), sau [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on). + + ```javascript + /* evită */ + function CustomerController($scope) { + $scope.name = {}; + $scope.sendMessage = function() { }; + } + ``` + + ```javascript + /* recomandat - dar vezi secțiunea viitoare */ + function CustomerController() { + this.name = {}; + this.sendMessage = function() { }; + } + ``` + +### controllerAs cu vm +###### [Style [Y032](#style-y032)] + + - Folosește o variabilă de captură pentru `this` când folosești sintaxa `controllerAs`. Alege un nume de variabilă consecvent precum `vm`, care vine de la 'ViewModel'. + + *De ce?*: Keyword-ul `this` este contextual iar atunci când e folosit înăuntrul unei funcții dintr-un controller și-ar putea schimba contextul. Capturarea contextului lui `this` evită întâlnirea acestei probleme. + + ```javascript + /* evită */ + function CustomerController() { + this.name = {}; + this.sendMessage = function() { }; + } + ``` + + ```javascript + /* recomandat */ + function CustomerController() { + var vm = this; + vm.name = {}; + vm.sendMessage = function() { }; + } + ``` + + Notă: Poți evita avertizările [jshint](http://jshint.com/) prin lăsarea unui comentariu deasupra liniei de cod. Totuși, acest lucru nu e necesar atunci când funcția e denumită folosind UpperCasing, deoarece această convenție înseamnă că este o funcție-constructor, ceea ce și este un controller în Angular. + + ```javascript + /* jshint validthis: true */ + var vm = this; + ``` + + Notă: Când creezi un watch într-un controller folosind `controller as`, poti urmări membrul `vm.*` folosind următoarea sintaxă. (creează watch-uri cu atenție, acestea adaugă timp de execuție ciclului de digest) + + ```html + + ``` + + ```javascript + function SomeController($scope, $log) { + var vm = this; + vm.title = 'Some Title'; + + $scope.$watch('vm.title', function(current, original) { + $log.info('vm.title a fost %s', original); + $log.info('vm.title este acum %s', current); + }); + } + ``` + + Notă: Când lucrezi cu baze de coduri mai mari, folosirea unui nume mai descriptiv ajută la ușurarea căutabilității și la reducerea overheadului cognitiv. Evită nume prea verboase care sunt prea greu de tastat. + + ```html + + + ``` + + ```html + + + ``` + +### Membrii Bindabili Sus +###### [Style [Y033](#style-y033)] + + - Plasează membrii bindabili în partea de sus a controllerului, în ordine alfabetică, și nu împrăștiați prin codul controllerului. + + *De ce?*: Plasarea membriilor bindabili sus îmbunătățește lizibilitatea și te ajută să identifici instant care membri ai controllerului pot fi bindabili și folosiți în View. + + *De ce?*: Setarea funcțiilor anonime in-line poate fi ușoară, dar atunci când funcțiile respective sunt constituite din mai mult de o linie de cod ele pot reduce lizibilitatea. Definirea de funcții sub membrii bindabili (funcțiile vor fi hoistate) mută detaliile implementării jos, păstrează membrii bindabili sus, și face totul mai ușor de citit. + + ```javascript + /* evită */ + function SessionsController() { + var vm = this; + + vm.gotoSession = function() { + /* ... */ + }; + vm.refresh = function() { + /* ... */ + }; + vm.search = function() { + /* ... */ + }; + vm.sessions = []; + vm.title = 'Sessions'; + } + ``` + + ```javascript + /* recomandat */ + function SessionsController() { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = refresh; + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + + //////////// + + function gotoSession() { + /* */ + } + + function refresh() { + /* */ + } + + function search() { + /* */ + } + } + ``` + + ![Controller care folosește "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) + + Notă: Dacă funcția este constituită dintr-o linie de cod ia în considerare păstrarea ei sus, atâta vreme cât lizibilitatea nu este afectată. + + ```javascript + /* evită */ + function SessionsController(data) { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = function() { + /** + * liniile + * de + * cod + * afectează + * lizibilitatea + */ + }; + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + } + ``` + + ```javascript + /* recomandat */ + function SessionsController(sessionDataService) { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = sessionDataService.refresh; // 1 liner is OK + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + } + ``` + +### Declarări de funcții pentru ascunderea detaliilor de implementare +###### [Style [Y034](#style-y034)] + + - Folosește declarări de funcții pentru a ascunde detaliile implementării. Păstrează-ți membrii bindabili sus. Când ai nevoie de o funcție într-un controller, pointeaz-o la o declarație de funcție ce apare mai târziu în fișier. Acest lucru e direct legat de secțiunea "Membrii Bindabili Sus". Pentru mai multe detalii vezi [acest articol](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code/). + + *De ce?*: Punerea membriilor bindabili sus face mai ușoară citirea și ajută să identifici instant ce membri ai controllerului pot fi folosiți în View. (La fel ca mai sus.) + + *De ce?*: Mutarea detaliilor de implementare a unei funcții mai târziu în cod mută toată complexitatea în afara câmpului vizibil, astfel că poți vedea toate lucrurile importante sus. + + *De ce?*: Declarațiile funcțiilor sunt hoistate deci nu sunt griji cum că ai folosi o funcție înainte să o definești (precum ar fi cu expresiile de funcții). + + *De ce?*: În legătură cu declarațiile de funcții, nu trebuie să-ți faci griji niciodată cum că mutând `var a` înainte de `var b` îți va strica codul fiindcă `a` depinde de `b`. + + *De ce?*: Ordinea e critică cu expresiile de funcții + + ```javascript + /** + * evitî + * Folosirea de expresii de funcții. + */ + function AvengersController(avengersService, logger) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + var activate = function() { + return getAvengers().then(function() { + logger.info('Activated Avengers View'); + }); + } + + var getAvengers = function() { + return avengersService.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + + vm.getAvengers = getAvengers; + + activate(); + } + ``` + + Observă că lucrurile importante sunt împrăștiate în exemplul de mai sus. În exemplul de mai jos, observă că lucrurile importante sunt sus. De exemplu, membri legați de controller precum `vm.avengers` și `vm.title`. Detaliile de implementare sun jos. Asta e pur și simplu mai ușor de citit. + + ```javascript + /* + * recomandat + * Folosirea de declarații de funcții + * și membri bindabili sus. + */ + function AvengersController(avengersService, logger) { + var vm = this; + vm.avengers = []; + vm.getAvengers = getAvengers; + vm.title = 'Avengers'; + + activate(); + + function activate() { + return getAvengers().then(function() { + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + return avengersService.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Mută Logica din Controllere în Servicii +###### [Style [Y035](#style-y035)] + + - Scapă de logica dintr-un controller dând-o la servicii și factory-uri. + + *De ce?*: Logica ar putea fi refolosită de mai multe controllere dacă e pusă într-un serviciu și expusă printr-o funcție + + *De ce?*: Logica într-un serviciu poate fi izolată mult mai ușor într-un unit test, iar logica de apelare dintr-un controller poate fi generată mai ușor. + + *De ce?*: Înlătură dependințele și ascunde detaliile implementării din controller. + + *De ce?*: Păstrează controller-ul subțire, curat și focusat. + + ```javascript + + /* evită */ + function OrderController($http, $q, config, userInfo) { + var vm = this; + vm.checkCredit = checkCredit; + vm.isCreditOk; + vm.total = 0; + + function checkCredit() { + var settings = {}; + // Ia URL-ul de bază a serviciului de credit din config + // Setează antetele necesare serviciului de credit + // Pregătește query string-ul URL-ului sau obiectul de date cu datele de interogare + // Adaugă informații de identificare a utilizatorului pentru ca serviciul să ia limita corectă de credite pentru utilizatorul curent + // Folosește JSONP pentru acest browser dacă nu suportă CORS + return $http.get(settings) + .then(function(data) { + // Desfă data JSON din obiectul de răspus + // ca să găsești maxRemainingAmount + vm.isCreditOk = vm.total <= maxRemainingAmount + }) + .catch(function(error) { + // Interpretează eroarea + // Tratezi timeout-ul? reîncerci? încerci un serviciu alternat? + // Respinge cu eroarea adecvată pentru utilizator + }); + }; + } + ``` + + ```javascript + /* recomandat */ + function OrderController(creditService) { + var vm = this; + vm.checkCredit = checkCredit; + vm.isCreditOk; + vm.total = 0; + + function checkCredit() { + return creditService.isOrderTotalOk(vm.total) + .then(function(isOk) { vm.isCreditOk = isOk; }) + .catch(showError); + }; + } + ``` + +### Păstrează controllerele focusate +###### [Style [Y037](#style-y037)] + + - Definește un controller pentru un view, și încearcă să nu reutilizezi controllerul pentru alte view-uri. În schimb, mută logica reutilizabilă în factory-uri și păstrează controllerul simplu și focusat pe view-ul său. + + *De ce?*: Reutilizarea controllerelor cu mai multe view-uri este fragilă și necesită teste end-to-end (e2e) cu acoperire foarte bună pentru a asigura stabilitatea în aplicații mari. + +### Asignarea de Controllere +###### [Style [Y038](#style-y038)] + + - Când un controller trebuie legat cu un view și unul dintre cele două componente trebuie refolosite de alte Controllere sau view-uri, definește Controllere și rutele lor. + + Notă: Dacă un View este încărcat printr-un alt procedeu decât prin rutare, foloselște syntaxa `ng-controller="Avengers as vm"`. + + *De ce?*: Combinarea unui controller în rută permite mai multor rute să invoce diferite perechi de Controllere și view-uri. Când controllerele sunt asignat în view folosind [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), acel view este întotdeauna asociat cu același controller. + + ```javascript + /* evită - când este folosit cu o rută iar combinarea dinamică e dorită */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html' + }); + } + ``` + + ```html + +
+
+ ``` + + ```javascript + /* recomandat */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'Avengers', + controllerAs: 'vm' + }); + } + ``` + + ```html + +
+
+ ``` + +**[Înapoi sus](#table-of-contents)** + +## Serviciile + +### Singletonurile +###### [Style [Y040](#style-y040)] + + - Serviciile sunt instanțiate cu keywordul `new`, folosește `this` pentru metode publice și variabile. De vreme ce acestea sunt atât de similare cu factory-urile, foloseste un factory în schimb pentru consecvență. + + Notă: [Toate serviciile angular sunt singletonuri](https://docs.angularjs.org/guide/services). Aceasta înseamnă că este una și numai o instanță de serviciu per injector. + + ```javascript + // serviciu + angular + .module('app') + .service('logger', logger); + + function logger() { + this.logError = function(msg) { + /* */ + }; + } + ``` + + ```javascript + // factory + angular + .module('app') + .factory('logger', logger); + + function logger() { + return { + logError: function(msg) { + /* */ + } + }; + } + ``` + +**[Înapoi sus](#table-of-contents)** + +## Factory-urile + +### Responsibilitate unică +###### [Style [Y050](#style-y050)] + + - Factory-urile ar trebui să aibă o [responsabilitate unică](https://en.wikipedia.org/wiki/Single_responsibility_principle), ce e encapsulată de contextul său. Când un factory începe să depășească acel scop unic, un factory nou trebuie creat. + +### Siingletonuri +###### [Style [Y051](#style-y051)] + + - Factory-urile sunt singletonuri și returnează un obiect ce conține membrii unui serviciu. + + Notă: [Toate serviciile angular sunt singletonuri](https://docs.angularjs.org/guide/services). + +### Membrii accesibili sus +###### [Style [Y052](#style-y052)] + + - Expune membrii apelabili ai unui serviciu (interfața sa) sus, folosind o tehnică derivată din șablonul [Revealing Module Pattern](https://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). + + *De ce?*: Plasând membri apelabili sus face mai ușoară citirea și te ajută să identifici instant care membri ai serviciului pot fi apelați și trebuie să fie testați (și/sau generați) + + *De ce?*: Acest lucru este util în special când fișierul devine mai mare și evită nevoia de a face scroll pentru a vedea ce e expus. + + *De ce?*: Setarea funcțiilor pe parcurs poate fi ușoară, dar când acele funcții sunt mai mult de o linie de cod ele pot reduce din lizibilitate și cauza mai mult scrolling. Definirea interfeței apelabile prin serviciul returnat mută detaliile de implementare jos, păstrează interfața apelabilă sus, și face totul mai ușor de citit. + + ```javascript + /* evită */ + function dataService() { + var someValue = ''; + function save() { + /* */ + }; + function validate() { + /* */ + }; + + return { + save: save, + someValue: someValue, + validate: validate + }; + } + ``` + + ```javascript + /* recomandat */ + function dataService() { + var someValue = ''; + var service = { + save: save, + someValue: someValue, + validate: validate + }; + return service; + + //////////// + + function save() { + /* */ + }; + + function validate() { + /* */ + }; + } + ``` + + În modul acesta bindurile sunt oglindite în obiectul gazdă, iar valorile primitive nu pot fi modificate prin șablonului modulului expunător. + + ![Factory care folosește "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) + +### Declarații de funcții pentru ascunderea detaliilor de implementare +###### [Style [Y053](#style-y053)] + + - Folosește declarații de funcții pentru ascunderea detaliilor de implementare. Păstrează membrii accesibili ai factory-ului sus. Pointează-i spre declarații de funcții care apar mai târziu în fișier. Pentru mai multe detalii vezi [acest articol](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). + + *De ce?*: Plasarea membriilor accesibili sus face mai totul mai lizibil și te-ajută să identifici instant ce funcții ale factory-ului se pot accesa în exterior. + + *De ce?*: Plasarea detaliilor implementării unei funcții mai târziu în fișier mută toată complexitatea respectivă în afara câmpului vizual așa că poți vedea lucrurile importante sus. + + *De ce?*: Declarațiile funcțiilor sunt hoistate deci nu există griji în legătură cu utilizarea unei funcții înaintea definirii ei (așa cum ar fi cazul cu expresiile de funcții). + + *De ce?*: În legătură cu declarațiile de funcții, nu trebuie să-ți faci griji niciodată cum că mutând `var a` înainte de `var b` îți va strica codul fiindcă `a` depinde de `b`. + + *De ce?*: Ordinea e critică cu expresiile de funcții. + + ```javascript + /** + * evitp + * Folosirea de expresii de funcții + */ + function dataservice($http, $location, $q, exception, logger) { + var isPrimed = false; + var primePromise; + + var getAvengers = function() { + // implementation details go here + }; + + var getAvengerCount = function() { + // implementation details go here + }; + + var getAvengersCast = function() { + // implementation details go here + }; + + var prime = function() { + // implementation details go here + }; + + var ready = function(nextPromises) { + // implementation details go here + }; + + var service = { + getAvengersCast: getAvengersCast, + getAvengerCount: getAvengerCount, + getAvengers: getAvengers, + ready: ready + }; + + return service; + } + ``` + + ```javascript + /** + * recomandat + * Folosirea de declarații de funcții + * și membrii accesibili sus. + */ + function dataservice($http, $location, $q, exception, logger) { + var isPrimed = false; + var primePromise; + + var service = { + getAvengersCast: getAvengersCast, + getAvengerCount: getAvengerCount, + getAvengers: getAvengers, + ready: ready + }; + + return service; + + //////////// + + function getAvengers() { + // detaliile implementării vin aici + } + + function getAvengerCount() { + // detaliile implementării vin aici + } + + function getAvengersCast() { + // detaliile implementării vin aici + } + + function prime() { + // detaliile implementării vin aici + } + + function ready(nextPromises) { + // detaliile implementării vin aici + } + } + ``` + +**[Înapoi sus](#table-of-contents)** + +## Servicii de date + +### Apeluri de date separate +###### [Style [Y060](#style-y060)] + + - Mută codul care face operații de date și interacționează cu date într-un factory. Fă serviciile de date responsabile de apeluri XHR, stocare locală, stashing în memorie, sau orice alte operații de date. + + *De ce?*: Responsabilitatea controllerului este de a prezenta și aduna informații pentru view. Lui nu ar trebui să-i pese cum ia datele, ci doar să știe de la cine să le ceară. Separarea serviciilor de date mută logica luării datelor într-un serviciu de date, și lasă controllerul să fie mai simplu și mai focusat pe view. + + *De ce?*: Face mai ușoară testarea și generarea de date false pentru când se testează un controller ce folosește un serviciu de date. + + *De ce?*: Implementarea unui serviciu de date ar putea avea cod foarte specific depozitului de date. Asta ar putea include antete, metode de comunicare cu datele, sau alte servicii precum `$http`. Separarea logicii într-un serviciu de date encapsulează această logică într-un singur loc, ascunzând implementarea de utilizatorii externi (poate un controller), și face și mai ușoară o eventuală schimbare a implementării. + + ```javascript + /* recomandat */ + + // factory de serviciu de date + angular + .module('app.core') + .factory('dataservice', dataservice); + + dataservice.$inject = ['$http', 'logger']; + + function dataservice($http, logger) { + return { + getAvengers: getAvengers + }; + + function getAvengers() { + return $http.get('/api/maa') + .then(getAvengersComplete) + .catch(getAvengersFailed); + + function getAvengersComplete(response) { + return response.data.results; + } + + function getAvengersFailed(error) { + logger.error('XHR eșuat pentru getAvengers.' + error.data); + } + } + } + ``` + + Notă: Serviciul de date e apelat de consumatori, precum un controller, ascunzând implementarea pentru aceștia, precum e arătat mai jos. + + ```javascript + /* recomandat */ + + // controller care apelează factory-ul serviciului de date + angular + .module('app.avengers') + .controller('AvengersController', AvengersController); + + AvengersController.$inject = ['dataservice', 'logger']; + + function AvengersController(dataservice, logger) { + var vm = this; + vm.avengers = []; + + activate(); + + function activate() { + return getAvengers().then(function() { + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + return dataservice.getAvengers() + .then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Returnează un promise prin apeluri la date +###### [Style [Y061](#style-y061)] + + - Când apelezi un serviciu de date ce returnează un promise, precum `$http`, returnează de asemenea un promise în funcția ta. + + *De ce?*: Poți înlănțui promise-urile și să iei decizii după ce apelul de date e gata și se rezolvă sau respinge promise-ul. + + ```javascript + /* recomandat */ + + activate(); + + function activate() { + /** + * Pas 1 + * Cere funcției getAvengers datele + * și așteaptă promise-ul + */ + return getAvengers().then(function() { + /** + * Pas 4 + * Execută o acțiune la rezolvarea promse-ului final + */ + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + /** + * Step 2 + * Cere datele servicului de date + * și așteaptă promise-ul + */ + return dataservice.getAvengers() + .then(function(data) { + /** + * Step 3 + * setează datele și rezolvă promise-ul + */ + vm.avengers = data; + return vm.avengers; + }); + } + ``` + +**[Înapoi sus](#table-of-contents)** + +## Directive +### Limitează-te la 1 per fișier +###### [Style [Y070](#style-y070)] + + - Creează o directivă per fișier. Numește fișierul precum directiva. + + *De ce?*: E ușor să arunci toate directivele într-un singur fișier, dar greu să le spargi pe urmă astfel încât unele din ele sunt folosite de mai multe aplicații, unele doar de mai multe module, și altele doar pentru un singur modul. + + *De ce?*: O directivă per fișier face lucrurile mai ușor de gestionat. + + > Notă: "**Best Practice**: Directivele ar trebui să curețe după ele. Poți folosi `element.on('$destroy', ...)` sau `$scope.$on('$destroy', ...)` ca să rulezi o funcție de clean-up când directiva e înlăturată" ... din documentația Angular. + + ```javascript + /* evită */ + /* directives.js */ + + angular + .module('app.widgets') + + /* directivă pentru comenzi specifică modulului de comenzi */ + .directive('orderCalendarRange', orderCalendarRange) + + /* directivă pentru vânzări ce poate fi folosită oriunde în aplicația de sales */ + .directive('salesCustomerInfo', salesCustomerInfo) + + /* directivă pentru spinner ce poate fi folosită oriunde */ + .directive('sharedSpinner', sharedSpinner); + + function orderCalendarRange() { + /* detalii de implementare */ + } + + function salesCustomerInfo() { + /* detalii de implementare */ + } + + function sharedSpinner() { + /* detalii de implementare */ + } + ``` + + ```javascript + /* recomandat */ + /* calendar-range.directive.js */ + + /** + * @desc directivă pentru comenzi specifică modulului de comenzi la o companie numită Acme + * @example
+ */ + angular + .module('sales.order') + .directive('acmeOrderCalendarRange', orderCalendarRange); + + function orderCalendarRange() { + /* detalii de implementare */ + } + ``` + + ```javascript + /* recomandat */ + /* customer-info.directive.js */ + + /** + * @desc directivă pentru vânzări ce poate fi folosită oriunde în aplicația de sales la o companie numită Acme + * @example
+ */ + angular + .module('sales.widgets') + .directive('acmeSalesCustomerInfo', salesCustomerInfo); + + function salesCustomerInfo() { + /* detalii de implementare */ + } + ``` + + ```javascript + /* recomandat */ + /* spinner.directive.js */ + + /** + * @desc directivă pentru spinner ce poate fi folosită oriunde la o companie numită Acme + * @example
+ */ + angular + .module('shared.widgets') + .directive('acmeSharedSpinner', sharedSpinner); + + function sharedSpinner() { + /* detalii de implementare */ + } + ``` + + Notă: Sunt multe opțiuni de denumire pentru directive, în special datoriă faptului că pot fi folosite în scopuri mai înguste sau mai largi. Alege un nume ce face directiva și numele fișierului său distinct și clar. Niște example sunt mai jos, dar vezi secțiunea de [Denumire](#naming) pentru mai multe recomandări. + +### Manipulează DOM-ul într-o Directivă +###### [Style [Y072](#style-y072)] + + - Când manipulezi DOM-ul direct, folosește o directivă. Dacă metode alternative pot fi folosite, precum folosirea de CSS pentru a seta stilurile sau [serviciile de animație](https://docs.angularjs.org/api/ngAnimate), șablonarea Angular, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) sau [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), atunci folosește-le pe acelea în schimb. De exemplu, dacă directiva doar arată / ascunde, folosește ngHide/ngShow. + + *De ce?*: Manipulările de DOM pot fi greu de testat, reparat, și de multe ori există soluții mai bune (e.g. CSS, animații, șablonare). + +### Oferă un prefix unic Directivei +###### [Style [Y073](#style-y073)] + + - Oferă un prefix scurt, unic și descriptiv directivei precum `acmeSalesCustomerInfo` care, în HTML, ar fi declarat ca `acme-sales-customer-info`. + + *De ce?*: Prefixul scurt și unic identifică contextul și originea directivei. De exemplu, un prefix ca `cc-` ar putea indica că directiva face parte dintr-o aplicație CodeCamper, iar `acme-` ar putea indica o directivă pentru compania Acme. + + Notă: Evită `ng-`, deoarece acestea sunt rezervate pentru directivele Angular. Află mai multe despre directivele populare pentru a evita conflictele de nume, precum `ion-` pentru [Framework-ul Ionic](http://ionicframework.com/). + +### Rămâi la Elemente și Atribute +###### [Style [Y074](#style-y074)] + + - Când creezi o directivă care are sens pe cont propriu, permite `E` (element custom) și opțional interzice `A` (atribut custom). În general, dacă poate fi un control de sine stătător, `E` e adecvat. Ghidul geeneral e să permiți `EA` dar tinde spre implementarea ca un element atunci când e folosibil pe cont-propriu și ca atribut când extinde elementul său existent. + + *De ce?*: Are sens. + + *De ce?*: Chiar dacă putem lăsa directiva să fie folosită ca și clasă, dacă directiva e într-adevăr un element are mai mult sens ca un element sau măcar ca un atribut. + + Notă: EA este implicitul pentru Angular 1.3 + + + ```html + +
+ ``` + + ```javascript + /* evită */ + angular + .module('app.widgets') + .directive('myCalendarRange', myCalendarRange); + + function myCalendarRange() { + var directive = { + link: link, + templateUrl: '/template/is/located/here.html', + restrict: 'C' + }; + return directive; + + function link(scope, element, attrs) { + /* */ + } + } + ``` + + ```html + + +
+ ``` + + ```javascript + /* recomandat */ + angular + .module('app.widgets') + .directive('myCalendarRange', myCalendarRange); + + function myCalendarRange() { + var directive = { + link: link, + templateUrl: '/template/is/located/here.html', + restrict: 'EA' + }; + return directive; + + function link(scope, element, attrs) { + /* */ + } + } + ``` + +### Directive și ControllerAs +###### [Style [Y075](#style-y075)] + + - Folosește sintaxa `controller as` cu o directivă pentru a fi consecvent cu folosirea `controller as` cu perechile de view și controller. + + *De ce?*: Are sens și nu e greu. + + Notă: Directiva de mai jos demonstrează unele din modalitățile pe care poți folosi într-un controller de link / directivă, folosind controllerAs. Am pus șablonul in-line doar ca să am totul în același loc. + + Notă: Cât despre injectarea de dependințe, vezi [Identificarea Manuală a Dependințelor](#manual-annotating-for-dependency-injection). + + Notă: Observă că controllerul directivei este în afara closure-ului directivei. Acest stil elimină problemele unde injectarea se creează ca și cod la care nu se poate ajunge după un `return`. + + ```html +
+ ``` + + ```javascript + angular + .module('app') + .directive('myExample', myExample); + + function myExample() { + var directive = { + restrict: 'EA', + templateUrl: 'app/feature/example.directive.html', + scope: { + max: '=' + }, + link: linkFunc, + controller: ExampleController, + // Notă: Acesta ar fi 'ExampleController' (numele controllerului exportat, ca string) + // dacă s-ar referi la un controller definit în fișierul său separat + controllerAs: 'vm', + bindToController: true // fiindcă scope-ul e izolat + }; + + return directive; + + function linkFunc(scope, el, attr, ctrl) { + console.log('LINK: scope.min = %s *** ar trebui să fie undefined', scope.min); + console.log('LINK: scope.max = %s *** ar trebui să fie undefined', scope.max); + console.log('LINK: scope.vm.min = %s', scope.vm.min); + console.log('LINK: scope.vm.max = %s', scope.vm.max); + } + } + + ExampleController.$inject = ['$scope']; + + function ExampleController($scope) { + // Injectarea de $scope doar pentru comparație + var vm = this; + + vm.min = 3; + + console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); + console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); + console.log('CTRL: vm.min = %s', vm.min); + console.log('CTRL: vm.max = %s', vm.max); + } + ``` + + ```html + +
hello world
+
max={{vm.max}}
+
min={{vm.min}}
+ ``` + + Notă: Poți, de asemenea denumi controller-ul când îl injectezi în funcția de legare și accesezi atributele directivelor ca proprietăți alte controlerului. + + ```javascript + // Alternativă la exemplul de mai sus + function linkFunc(scope, el, attr, vm) { + console.log('LINK: scope.min = %s *** should be undefined', scope.min); + console.log('LINK: scope.max = %s *** should be undefined', scope.max); + console.log('LINK: vm.min = %s', vm.min); + console.log('LINK: vm.max = %s', vm.max); + } + ``` + +###### [Style [Y076](#style-y076)] + + - Folosește `bindToController = true` cănd ai sintaxa `controller as` cu o directivă dacă dorești să legi scopul extern de scopul controllerului directivei. + + *De ce?*: Face ușoară legarea scopului extern de scopul controllerului directivei. + + Notă: `bindToController` a fost introdus în 1.3.0. + + ```html +
+ ``` + + ```javascript + angular + .module('app') + .directive('myExample', myExample); + + function myExample() { + var directive = { + restrict: 'EA', + templateUrl: 'app/feature/example.directive.html', + scope: { + max: '=' + }, + controller: ExampleController, + controllerAs: 'vm', + bindToController: true + }; + + return directive; + } + + function ExampleController() { + var vm = this; + vm.min = 3; + console.log('CTRL: vm.min = %s', vm.min); + console.log('CTRL: vm.max = %s', vm.max); + } + ``` + + ```html + +
hello world
+
max={{vm.max}}
+
min={{vm.min}}
+ ``` + +**[Înapoi sus](#table-of-contents)** + +## Resolvarea de promise-uri +### Promise-uri Activante de Controllere +###### [Style [Y080](#style-y080)] + + - Pune logica de inițiere într-o funcție `activate`. + + *De ce?*: Punerea logicii de început într-un loc consecvent în controller o face mai ușor de localizat, e mai consecvent la testare, li ajută la evitarea răspândirii logicii inițiale de-alungul controllerului. + + *De ce?*: Funcția `activate` face mai convenabilă reutilizarea logicii a unui refresh al Controllerului/View-ului, păstrează logica împreună, îndreaptă utilizatorul la View mai repede, face animațiile mai ușoare pe `ng-view` sau `ui-view`, și se simte mai fluent pentru utilizator. + + Notă: Dacă ai nevoie să anulezi condițional ruta înainte de-a folosi controllerul, folosește un [route resolver](#style-y081) în schimb. + + ```javascript + /* evită */ + function AvengersController(dataservice) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + dataservice.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + ``` + + ```javascript + /* recomandat */ + function AvengersController(dataservice) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + activate(); + + //////////// + + function activate() { + return dataservice.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Promise-uri Rezolvatoare de Rute +###### [Style [Y081](#style-y081)] + + - Când un controller depinde de un promise să fie rezolvat înainte ca el să fie activat, rezolvă acele dependințe în `$routeProvider` înainte ca logica controllerului să fie executată. Dacă ai nevoie să anulezi o rută înainte ca un controller să fie activat, folosește un resolver de rută. + + - Folosește un resolver de rută când vrei să decizi anularea rutei înainte de-a tranziționa în View. + + *De ce?*: Un controller ar putea necesita date înainte de a se încărca. Acele date pot veni de la un promise printr-un factory custom sau [$http](https://docs.angularjs.org/api/ng/service/$http). Folosirea unui [resolver de rută](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) permite promise-ului să fie rezolvat înainte ca logica controllerului să fie executată, așa că ar putea acționa bazându-se pe data de la promise. + + *De ce?*: Codul se execută după rută și în funcția de 'activate' din controller. View-ul începe să se încarce direct. Legarea datelor apare când promise-ul de activare se rezolvă. O animație de "busy" poate fi arătată pe durata tranziției view-ului (prin `ng-view` sau `ui-view`) + + Notă: Codul se execută înaintea rutei printr-un promise. Respingerea promise-ului anulează ruta. Rezolvarea face ca noul view să aștepte ca ruta să fie rezolvată. O animație de "busy" poate fi arătată înainte de rezolvare și de-alungul tranziției view-ului. Dacă ai nevoie de View mai repede și nu necesiți un checkpoint ca să decizi dacă poți ajunge la View, consideră folosirea tehnicii [controller `activate`](#style-y080) în schimb. + + ```javascript + /* evită */ + angular + .module('app') + .controller('AvengersController', AvengersController); + + function AvengersController(movieService) { + var vm = this; + // nerezolvat + vm.movies; + // rezolvat asincron + movieService.getMovies().then(function(response) { + vm.movies = response.movies; + }); + } + ``` + + ```javascript + /* mai bine */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { + moviesPrepService: function(movieService) { + return movieService.getMovies(); + } + } + }); + } + + // avengers.js + angular + .module('app') + .controller('AvengersController', AvengersController); + + AvengersController.$inject = ['moviesPrepService']; + function AvengersController(moviesPrepService) { + var vm = this; + vm.movies = moviesPrepService.movies; + } + ``` + + Notă: Exemplul de mai jos arată punctele de rezolvare a rutei spre o funcție denumită, ceea ce face codul mai ușor de reparat și mai ușoară de gestionat injectarea de dependințe. + + ```javascript + /* și mai bine */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { + moviesPrepService: moviesPrepService + } + }); + } + + function moviesPrepService(movieService) { + return movieService.getMovies(); + } + + // avengers.js + angular + .module('app') + .controller('AvengersController', AvengersController); + + AvengersController.$inject = ['moviesPrepService']; + function AvengersController(moviesPrepService) { + var vm = this; + vm.movies = moviesPrepService.movies; + } + ``` + Notă: Dependința codului la `movieService` nu este sigură pentru minificare. Pentru detalii despre cum să faci acest cod sigur pentru minificare, vezi secțiunile despre [injectarea de dependințe](#manual-annotating-for-dependency-injection) și despre [minificare și adnotare](#minification-and-annotation). + +**[Înapoi sus](#table-of-contents)** + +### Tratarea Excepțiilor cu Promise-uri +###### [Style [Y082](#style-y082)] + + - Blocul de `catch` al unui promise trebuie să returneze un promise respins pentru a păstra excepția în lanțul promise-ului. + + - Tratează întotdeauna excepțiile în servicii/factory-uri. + + *De ce?*: Dacă blocul de `catch` nu returnează un promise respins, apelatorul promise-ului nu va știi că s-a întâmplat o excepție. Metoda de `then` se va executa. Deci, utilizatorul ar putea să nu știe niciodată ce s-a întâmplat. + + *De ce?*: Pentru a evita înghițirea erorilor și informarea greșită a utilizatorului. + + Notă: Ia în considerare punerea tratărilor de excepții într-o funcție dintr-un modul general sau serviciu. + + ```javascript + /* evită */ + + function getCustomer(id) { + return $http.get('/api/customer/' + id) + .then(getCustomerComplete) + .catch(getCustomerFailed); + + function getCustomerComplete(data, status, headers, config) { + return data.data; + } + + function getCustomerFailed(e) { + var newMessage = 'XHR eșuat pentur getCustomer' + if (e.data && e.data.description) { + newMessage = newMessage + '\n' + e.data.description; + } + e.data.description = newMessage; + logger.error(newMessage); + // *** + // Observă că aici nu este nicio returnare a promise-ului respins + // *** + } + } + + /* recomandat */ + function getCustomer(id) { + return $http.get('/api/customer/' + id) + .then(getCustomerComplete) + .catch(getCustomerFailed); + + function getCustomerComplete(data, status, headers, config) { + return data.data; + } + + function getCustomerFailed(e) { + var newMessage = 'XHR eșuat pentru getCustomer' + if (e.data && e.data.description) { + newMessage = newMessage + '\n' + e.data.description; + } + e.data.description = newMessage; + logger.error(newMessage); + return $q.reject(e); + } + } + ``` + +**[Înapoi sus](#table-of-contents)** + +## Adnotare Manuală pentru Injectarea de Dependințe + +### NeSigur Pentru Minificare +###### [Style [Y090](#style-y090)] + + -Evită folosirea the sintaxei-scurtătură de declarare de dependințe fără folosirea unei metode sigură pentru minificare. + + *De ce?*: Parametrii componentei (e.g. controller, factory, etc) vor fi convertiți în variabile deformate. De exemplu, `common` și `dataservice` ar putea deveni `a` sau `b` și să nu fie găsiți de Angular. + + ```javascript + /* evită - nesigur pentru minificare */ + angular + .module('app') + .controller('DashboardController', DashboardController); + + function DashboardController(common, dataservice) { + } + ``` + + Acest cod ar putea produce variabile deformate și cauza erori în momentul execuției. + + ```javascript + /* evită - nesigur pentru minificare*/ + angular.module('app').controller('DashboardController', d);function d(a, b) { } + ``` + +### Identificarea Manuală a Dependințelor +###### [Style [Y091](#style-y091)] + + - Folosește `$inject` pentru a identifica manual dependințele pentru componentele Angular. + + *De ce?*: Această tehnică oglindește tehnica folosită de [`ng-annotate`](https://github.com/olov/ng-annotate), pe care o recomand pentru automatizarea creerii de dependințe sigure pentru minificare. Dacă `ng-annotate` detectează că injectarea s-a făcut deja, el nu o va duplica. + + *De ce?*: Acest lucru îți protejează dependințele de la a fi vulnerabile la problemele minificării ce apar când parametrii sunt deformați. De exemply, `common` și `dataservice` pot deveni `a` sau `b` și să nu fie găsite de Angular. + + *De ce?*: Evită creerea de dependințe in-line dacă lista e greu de citit. De asemeanea, poate creea confuzie faptul că array-ul e o serie de string-uri, în timp ce ultimul element este funcția componentei. + + ```javascript + /* evită */ + angular + .module('app') + .controller('DashboardController', + ['$location', '$routeParams', 'common', 'dataservice', + function Dashboard($location, $routeParams, common, dataservice) {} + ]); + ``` + + ```javascript + /* evită */ + angular + .module('app') + .controller('DashboardController', + ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); + + function Dashboard($location, $routeParams, common, dataservice) { + } + ``` + + ```javascript + /* recomandat */ + angular + .module('app') + .controller('DashboardController', DashboardController); + + DashboardController.$inject = ['$location', '$routeParams', 'common', 'dataservice']; + + function DashboardController($location, $routeParams, common, dataservice) { + } + ``` + + Notă: Când funcția ta e sub un statement de return, `$inject` ar putea fi inaccesibil (posibil să se întâmple într-o directivă). Poți rezolva acest lucru prin mutarea Controller-ului în afara directivei. + + ```javascript + /* evită */ + // înăuntrul definiției unei directive + function outer() { + var ddo = { + controller: DashboardPanelController, + controllerAs: 'vm' + }; + return ddo; + + DashboardPanelController.$inject = ['logger']; // Inaccesibil + function DashboardPanelController(logger) { + } + } + ``` + + ```javascript + /* recomandat */ + // înafara definiției unei directive + function outer() { + var ddo = { + controller: DashboardPanelController, + controllerAs: 'vm' + }; + return ddo; + } + + DashboardPanelController.$inject = ['logger']; + function DashboardPanelController(logger) { + } + ``` + +### Identificarea Manuală a Dependințelor Resolverului de Rută +###### [Style [Y092](#style-y092)] + + - Folosește `$inject` pentru identificarea dependințelor resolverului de rută pentru componentele Angular. + + *De ce?*: Această tehnică scapă din funcția anonimă a resolverului de rută, făcând lucrurile mai lizibile. + + *De ce?*: Un statement de `$inject` poate preceda cu ușurință resolver-ul pentru a trata creerea dependințelor sigure pentru minificare. + + ```javascript + /* recomandat */ + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { + moviesPrepService: moviesPrepService + } + }); + } + + moviesPrepService.$inject = ['movieService']; + function moviesPrepService(movieService) { + return movieService.getMovies(); + } + ``` + +**[Înapoi sus](#table-of-contents)** + +## Minificare și Adnotare + +### ng-annotate +###### [Style [Y100](#style-y100)] + + - Folosește [ng-annotate](//github.com/olov/ng-annotate) pentru [Gulp](http://gulpjs.com) sau [Grunt](http://gruntjs.com) și comentează funcțiile ce au nevoie de injectare automată de dependințe folosind `/* @ngInject */` + + *De ce?*: Acest lucru îți asigură codul împotriva dependințelor ce ar putea să nu folosească metode sigure pentru minificare. + *De ce?*: [`ng-min`](https://github.com/btford/ngmin) e depreciat. + + >Prefer Gulp fiindcă mi se pare mai ușor de scris, citit, și pentru debug. + + Codul următor nu folosește dependințe sigure pentru minificare. + + ```javascript + angular + .module('app') + .controller('AvengersController', AvengersController); + + /* @ngInject */ + function AvengersController(storage, avengerService) { + var vm = this; + vm.heroSearch = ''; + vm.storeHero = storeHero; + + function storeHero() { + var hero = avengerService.find(vm.heroSearch); + storage.save(hero.name, hero); + } + } + ``` + + Când codul de mai sus e rulat prin ng-annotate el va produce următorul output cu adnotarea `$inject` și va deveni sigur pentru minificare. + + ```javascript + angular + .module('app') + .controller('AvengersController', AvengersController); + + /* @ngInject */ + function AvengersController(storage, avengerService) { + var vm = this; + vm.heroSearch = ''; + vm.storeHero = storeHero; + + function storeHero() { + var hero = avengerService.find(vm.heroSearch); + storage.save(hero.name, hero); + } + } + + AvengersController.$inject = ['storage', 'avengerService']; + ``` + + Notă: Dacă `ng-annotate` detectează că injectarea s-a făcut deja (e.g. `@ngInject` a fost detectat), el nu va duplica codul de `$inject`. + + Notă: Când folosești un resolver de rută poți prefixa funcția resolverului cu `/* @ngInject */` și el va produce cod adnotat corect, păstrând dependințele injectate sigure pentru minificare. + + ```javascript + // Folosind adnotări @ngInject + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { /* @ngInject */ + moviesPrepService: function(movieService) { + return movieService.getMovies(); + } + } + }); + } + ``` + + > Notă: Începând cu Angular 1.3 poți folosi parametrul `ngStrictDi` din [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp) pentru a detecta dependințele nesigure pentru minificare. Atunci când injectorul este prezent el va fi creat în modul "strict-di" mode, cauzând aplicația să eșueze invocarea funcțiilor care nu folosesc adnotare implicită (acestea ar putea să nu fie sigure pentru minificare). Informațile de debug vor fi înregistrate în consolă pentru a ajuta găsirea codului stricat. Prefer să folosesc `ng-strict-di` doar în scopuri de debug. + `` + +### Folosește Gulp sau Grunt pentru ng-annotate +###### [Style [Y101](#style-y101)] + + - Folosește [gulp-ng-annotate](https://www.npmjs.com/package/gulp-ng-annotate) sau [grunt-ng-annotate](https://www.npmjs.com/package/grunt-ng-annotate) într-un task de build automatizat. Injectează `/* @ngInject */` înaintea oricărei funcții ce nu are dependințe. + + *De ce?*: ng-annotate va prinde majoritatea dependințelor, dar câteodată necesită sugestii folosind sintaxa `/* @ngInject */`. + + Următorul cod este un exemplu de task gulp care folosește ngAnnotate + + ```javascript + gulp.task('js', ['jshint'], function() { + var source = pkg.paths.js; + + return gulp.src(source) + .pipe(sourcemaps.init()) + .pipe(concat('all.min.js', {newLine: ';'})) + // Adnotează înaintea uglificării pentru ca codul să fie minificat corect + .pipe(ngAnnotate({ + // true ajută la adăuărui acolo unde @ngInject nu este folosit. Folosește deduceri. + // Nu funcționează cu resolve, deci trebuie să fim expliciți aici + add: true + })) + .pipe(bytediff.start()) + .pipe(uglify({mangle: true})) + .pipe(bytediff.stop()) + .pipe(sourcemaps.write('./')) + .pipe(gulp.dest(pkg.paths.dev)); + }); + + ``` + +**[Înapoi sus](#table-of-contents)** + +## Tratarea Excepțiilor + +### Decoratori +###### [Style [Y110](#style-y110)] + + + - Folosește un [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator) când faci configurarea folosind serviciul [`$provide`](https://docs.angularjs.org/api/auto/service/$provide), pe serviciul [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) pentru a executa operații custom când apar excepții. + + *De ce?*: Furnizează o modalitate consecventă pentru tratarea excepțiilor neprinse din Angular, pentru perioada de development sau cea de execuție. + + Notă: O altă opțiune este să faci override la serviciu în locul folosirii unui decorator. Aceasta ar fi o soluție bună, dar dacă vrei să foloseși comportamentul implicit și să-l extinzi, un decorator e recomandat. + + ```javascript + /* recomandat */ + angular + .module('blocks.exception') + .config(exceptionConfig); + + exceptionConfig.$inject = ['$provide']; + + function exceptionConfig($provide) { + $provide.decorator('$exceptionHandler', extendExceptionHandler); + } + + extendExceptionHandler.$inject = ['$delegate', 'toastr']; + + function extendExceptionHandler($delegate, toastr) { + return function(exception, cause) { + $delegate(exception, cause); + var errorData = { + exception: exception, + cause: cause + }; + /** + * Ai putea adăuga eroarea la colecția unui serviciu, + * adăuga eroarea la $rootScope, înregistra eroarea pe un server remote, + * sau înregistra local. Sau să faci throw simpl. Depinde doar de tine. + * throw exception; + */ + toastr.error(exception.msg, errorData); + }; + } + ``` + +### Prinzători de Excepții +###### [Style [Y111](#style-y111)] + + - Creează un factory ce expune o interfață de prindere și tratează grațios excepțiile. + + *De ce?*: Furnizează o metodă consecventă de prindere a excepțiilor ce ar putea fi aruncate în codul tău (e.g. îmn timpul apelurilor XHR sau eșecurilor de promise-uri). + + Notă: Prinzătorul de excepții e bun pentru prinderea și reacționarea la excepții specifice din apeluri care știi că ar putea arunca una. De exemplu, când faci un apel XHR pentru luarea de date de la un serviciu web remote și vrei să prinzi excepțiile de la acel serviciu și să reacționezi unitar la ele. + + ```javascript + /* recomandat */ + angular + .module('blocks.exception') + .factory('exception', exception); + + exception.$inject = ['logger']; + + function exception(logger) { + var service = { + catcher: catcher + }; + return service; + + function catcher(message) { + return function(reason) { + logger.error(message, reason); + }; + } + } + ``` + +### Erori de Rută +###### [Style [Y112](#style-y112)] + + - Tratează și înregistrează toate erorile folosind [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). + + *De ce?*: Furnizează un mod consecvent de prindere a tuturor erorilor de rutare. + + *De ce?*: Are potențialul de a furniza o experiență mai bună pentru utilizator dacă atunci când se întâmplă erori de rutare îl redirecționezi spre un ecran prietenos cu mai multe detalii sau opțiuni de redresare. + + ```javascript + /* recomandat */ + var handlingRouteChangeError = false; + + function handleRoutingErrors() { + /** + * Anularea rutei: + * La o eroare de rutare, du-te la dashboard. + * Oferă o clauză de ieșire dacă încearcă să o facă de două ori. + */ + $rootScope.$on('$routeChangeError', + function(event, current, previous, rejection) { + if (handlingRouteChangeError) { return; } + handlingRouteChangeError = true; + var destination = (current && (current.title || + current.name || current.loadedTemplateUrl)) || + 'unknown target'; + var msg = 'Eroare la rutarea spre ' + destination + '. ' + + (rejection.msg || ''); + + /** + * Opțional, înregistrează folosind un serviciu custom sau $log. + * (Nu uita să injectezi serviciul custom) + */ + logger.warning(msg, [current]); + + /** + * La erori de rută, du-te la o altă rută/stare. + */ + $location.path('/'); + + } + ); + } + ``` + +**[Înapoi sus](#table-of-contents)** + +## Denumire + +### Instrucțiuni de Denumire +###### [Style [Y120](#style-y120)] + + - Folosește nume consecvente pentru toate componentele folosind un șablon ce descrie trăsătura componentei și apoi (opțional) tipul său. `trăsăturî.tip.js`. Există 2 nume pentru mai toate elementele: + * numele fișierului (`avengers.controller.js`) + * numele componentei înregistrate cu Angular (`AvengersController`) + + *De ce?*: Convențiile de nume ajută la furnizarea unei metode mai consecvente de găsire a conținutului dintr-un foc. Consecvența într-un proiect e vitală. Consecvența cu echipa e importantă. Consecvența într-o companie furnizează eficiență imensă. + + *De ce?*: Convențiile de nume ar trebui pur și simplu să te ajute să-ți găsești codul mai repede și să-l facă mai ușor de înțeles. + +### Nume de Fișere pe Baza Trăsăturilor +###### [Style [Y121](#style-y121)] + + - Folosește nume consecvente pentru toate componentele, urmând un șablon ce descrie trăsătura componentei și apoi (opțional) tipul său. Șablonul meu recomandat este `trăsătură.tip.js`. + + *De ce?*: Furnizează o metodă consecventă de a identifica rapid compoenentele. + + *De ce?*: Conferă potrivire de șabloane pentru orice taskuri automate. + + ```javascript + /** + * opțiuni comune + */ + + // Controllere + avengers.js + avengers.controller.js + avengersController.js + + // Service-uri/Factory-uri + logger.js + logger.service.js + loggerService.js + ``` + + ```javascript + /** + * recomandat + */ + + // controllers + avengers.controller.js + avengers.controller.spec.js + + // servicii/factory-uri + logger.service.js + logger.service.spec.js + + // constante + constants.js + + // definirea modulelor + avengers.module.js + + // rute + avengers.routes.js + avengers.routes.spec.js + + // configurare + avengers.config.js + + // directive + avenger-profile.directive.js + avenger-profile.directive.spec.js + ``` + + Notă: O altă convenție comună este numirea fișierelor de controller fără folosirea cuvântului `controller` în numele fișierului, precum `avengers.js` în loc de `avengers.controller.js`. Toate celelalte convenții rămân încă valide și ar trebui să folosească un sufix pentru tip. Preferința mea e ca `avengers.controller.js` să identifice `AvengersController`. + + ```javascript + /** + * recomandat + */ + // Controllere + avengers.js + avengers.spec.js + ``` + +### Numele Fișierelor de Test +###### [Style [Y122](#style-y122)] + + - Denumește specificațiile de test similar cu componentele pe care le testează și un sufix ca `spec`. + + *De ce?*: Conferă o metodă consecventă de identificare a componentelor. + + *De ce?*: Furnizează potrivire de șabloane pentru [karma](http://karma-runner.github.io/) sau alte utilități de teste. + + ```javascript + /** + * recomandat + */ + avengers.controller.spec.js + logger.service.spec.js + avengers.routes.spec.js + avenger-profile.directive.spec.js + ``` + +### Numele Controllerelor +###### [Style [Y123](#style-y123)] + + - Folosește nume consecvente pentru toate controllere, numindu-le după trăsăturile lor. Folosește UpperCamelCase pentru controllere, de vreme ce aceștia sunt constructori. + + *De ce?*: Oferă o metodă consecventă de a identifica și referenția Controllere. + + *De ce?*: UpperCamelCase e convenția pentru identificarea unui obiect ce poate fi instanțiat folosind un constructor. + + ```javascript + /** + * recomandat + */ + + // avengers.controller.js + angular + .module + .controller('HeroAvengersController', HeroAvengersController); + + function HeroAvengersController() { } + ``` + +### Sufixul Numelui Controllerui +###### [Style [Y124](#style-y124)] + + - Adaugă numelui controllerui sufixul `Controller`. + + *De ce?*: Sufixul `Controller` e folosit mai des și e mai descriptiv. + + ```javascript + /** + * recomandat + */ + + // avengers.controller.js + angular + .module + .controller('AvengersController', AvengersController); + + function AvengersController() { } + ``` + +### Numele de Factory-uri și Servicii +###### [Style [Y125](#style-y125)] + + - Folosește nume consecvente pentru toate factory-urile și serviciile numite după trăsăturile lor. Folosește camel-casing pentru servicii și factory-uri. Evită prefixarea factory-urilor și serviciilor cu `$`. Sufixează serviciile și factory-urile doar cu `Service` atunci când nu e clar ce sunt (i.e. când sunt substantive). + + *De ce?*: Furnizează o metodă rapidă de găsire și referențiere a factory-urilor. + + *De ce?*: Evită conflictele de nume cu factory-urile built-in și cu serviciile ce folosesc prefixul `$`. + + *De ce?*: Numele clare de servicii precum `logger` nu au nevoie de un sufix. + + *De ce?*: Numele de servicii precum `avengers` sunt substantive și au nevoie de un sufix, deci ar trebui numite `avengersService`. + + ```javascript + /** + * recomandat + */ + + // logger.service.js + angular + .module + .factory('logger', logger); + + function logger() { } + ``` + + ```javascript + /** + * recomandat + */ + + // credit.service.js + angular + .module + .factory('creditService', creditService); + + function creditService() { } + + // customer.service.js + angular + .module + .service('customerService', customerService); + + function customerService() { } + ``` + +### Numele Componentelor Directivelor +###### [Style [Y126](#style-y126)] + + - Folosește nume consecvente pentru toate directivele folosind camel-case. Folosește un prefix scurt pentru a descrie zona din care fac parte directivele respective (niște exemple ar fi prefixul companiei sau prefixul proiectului). + + *De ce?*: Furnizează un mod consecvent de a identifica și referenția rapid componentele. + + ```javascript + /** + * recomandat + */ + + // avenger-profile.directive.js + angular + .module + .directive('xxAvengerProfile', xxAvengerProfile); + + // se folosește ca + + function xxAvengerProfile() { } + ``` + +### Module +###### [Style [Y127](#style-y127)] + + - Atunci când sunt mai multe module, fișierul modululului principal se numește `app.module.js` iar celelalte module dependente sunt numite după ceea ce reprezintă. De exemplu, un modul de admin se va numi `admin.module.js`. Numele modulelor înregistrate aferente ar fi `app` și `admin`. + + *De ce?*: Conferă consecvență pentru mai aplicații cu mai multe module, și pentru scalarea înspre aplicații mari. + + *De ce?*: Furnizează o metodă ușoară de automatizare a taskurilor pentru a încărca definițiile modulelor prima dată, iar apoi toate celelalte fișiere Angular (pentru împachetare). + +### Configurare +###### [Style [Y128](#style-y128)] + + - Separă configurația unui modul în fișierul său separat numit după modul. Un fișier de configurare pentru modulul principal `app` se va numi `app.config.js` (sau simplu doar `config.js`). Un fișier de configurare pentru un modul de admin `admin.module.js` se va numi `admin.config.js`. + + *De ce?*: Separă configurarea de definirea modulelor, a componentelor și de codul activ. + + *De ce?*: Furnizează un loc identificabil unde să setezi configurarea unui modul. + +### Rutele +###### [Style [Y129](#style-y129)] + + - Separă cofigurarea rutelor în fișiere lor separate. De exemplu `app.route.js` pentru modulul principal, `admin.route.js` pentru modulul de `admin`. Chiar și în aplicații mai mici prefer separarea de restul configurării. + +**[Înapoi sus](#table-of-contents)** + +## Structura Aplicației - Principul LIFT +### LIFT +###### [Style [Y140](#style-y140)] + + - Structurează-ți aplicația în așa fel încât să-ți poți `L`ocaliza codul rapid, `I`dentifica codul rapid, păstrează cea mai plată (`F`lattest) structură posibilă, și încearcă (`T`ry) să rămâi DRY. Structura ar trebui să urmeze aceste 4 orientări de bază: + + *De ce LIFT?*: Furnizează un mod consecvent de a scala cum trebuie, e modular, și face ca eficiența unui programator să crească prin găsirea codului rapid. O altă metodă de a-ți verifica structura aplicației este: Cât de repede pot să deschid și să lucrez în toate fișierele legate de o funcționalitate? + + Când consider că structura mea nu e confortabilă, mă întorc și revizitez aceste orientări LIFT + + 1. `L`ocalizarea codului tău e ușor + 2. `I`dentificarea codului se face rapid + 3. Structură plată (`F`lat) pe cât posibil + 4. Încearcă (`T`ry) să rămâi DRY (Nu te repeta - Don’t Repeat Yourself) sau T-DRY + +### Localizează +###### [Style [Y141](#style-y141)] + + - Fă localizarea codului tău intuitivă, simplă, și rapidă. + + *De ce?*: Observ că acest lucru e super important pentru un proiect. Dacă membri echipei nu pot găsi rapid fișierele de care au nevoie, nu vor putea lucra cât de eficient se poate, iar structura va trebui să sufere modificări. Posibil să nu știi numele fișierului sau care îi sunt fișierele relatate, așa că punerea lor în cele mai intuitive locații și una lângă alta salvează mult timp. O structură de directoare descriptivă poate de asemenea să ajute în această chestiune. + + ``` + /bower_components + /client + /app + /avengers + /blocks + /exception + /logger + /core + /dashboard + /data + /layout + /widgets + /content + index.html + .bower.json + ``` + +### Identifică +###### [Style [Y142](#style-y142)] + + - Când vezi un fișier ar trebui să știi instant ce conține și ce reprezintă. + + *De ce?*: Petreci mai puțin timp vânând și căutând cod, și devii mai eficient. Dacă acest lucru înseamnă să folosești nume de fișier mai mari, fă-o. Fii descriptiv cu numele fișierelor și păstrează conținutul fișierului la exact 1 per component. Evită fișierele cu mai multe controllere, mai multe servicii, sau o amestecătură. Există devieri de la regula de 1 per fișier atunci când am un set de funcționalități foarte mici care sunt toate legate una de cealaltă, ele fiind în continuare ușor identificabile. + +### Plat +###### [Style [Y143](#style-y143)] + + - Păstrează o structură plată a directoarelor pe cât posibil. Când ajungi la mai mult de 7 fișiere, începe să iei în considerare separarea. + + *De ce?*: Nimeni nu vrea să caute în 7 nivele de foldere ca să găsească un fișier. Gândește-te la meniurile de pe web site-uri.. orice mai adânc de 2 nivele ar trebui să fie revizuit serios. Într-o structură de foldere nu este o regulă rapidă și strictă, dar când un folder are 7-10 fișiere, cam acela ar fi momentul în care să fie create subfoldere. Bazează-te pe nivelul tău de confort. Folosește o structură mai plată până când există o valoare clară (ca să ajute la restul de LIFT) în creerea de foldere noi. + +### T-DRY (Încearcă să Rămâi DRY) +###### [Style [Y144](#style-y144)] + + - Fii DRY, dar nu o du la extrem și sacrifica lizibilitatea. + + *De ce?*: Să fii DRY e important, dar nu crucial dacă sacrifici alți membri ai LIFT, motiv pentru care îl și numesc T-DRY. Nu vreau să scriu session-view.html pentru un view pentru că, ei bine, e bineînțeles un view. Dacă nu e clar sau din convenție, atunci îl numesc. + +**[Înapoi sus](#table-of-contents)** + +## Structura Aplicației + +### Ghiduri Generale +###### [Style [Y150](#style-y150)] + + - Să ai o viziune rapidă de implementare și o viziune largă, de lungă durată. Cu alte cuvinte, începe mic dar ține minte încotro se îndreaptă aplicația. Tot codul aplicației merge într-un folder-rădăcină numit `app`. Tot conținutul e o funcționalitate per fișier. Fiecare controller, serviciu, modul, view e în fișierul său separat. Toate scripturile 3rd party sunt stocate în alt folder și nu în folder-ul `app`. Nu le-am scris eu și nu vreau ca ele să-mi aglomereze aplicația (`bower_components`, `scripts`, `lib`). + + Notă: Găsește mai multe detalii și raționarea din spatele structuri în [acest articol original despre structura aplicației](http://www.johnpapa.net/angular-app-structuring-guidelines/). + +### Plan General +###### [Style [Y151](#style-y151)] + + - Pune componentele ce definesc layout-ul general al aplicației într-un folder numit `layout`. Acestea pot include de asemenea un view și un controller și pot fi de asemenea recipiente pentru aplicație, navigare, meniuri, zone de conținut, și alte regiuni. + + *De ce?*: Organizează tot layout-ul într-un singur loc refolosit în toată aplicația. + +### Structură Folders-per-Caracteristică +###### [Style [Y152](#style-y152)] + + - Creează foldere numite după funcționalitatea pe care o reprezintă. Când un folder începe să conțină mai mult de 7 fișiere, ia în considerare creearea unui folder pentru ele. Limita ta ar putea să difere, așa că ajustarea e necesară. + + *De ce?*: Un programator poate localiza codul, identifica ce reprezintă fiecare fișier dintr-un foc, structura e cât se poate de plată, și nu există nume repetitive sau redundante. + + *De ce?*: Ghidurile LIFT sunt toate acoperite. + + *De ce?*: Ajută la prevenirea aplicației din a deveni prea aglomerată prin organizarea conținutului și păstrarea lui aliniat cu principiile LIFT. + + *De ce?*: Când există multe fișiere (10+), localizarea lor e mai ușoară cu o structură de foldere consecventă și mai grea în structuri plate. + + ```javascript + /** + * recomandat + */ + + app/ + app.module.js + app.config.js + components/ + calendar.directive.js + calendar.directive.html + user-profile.directive.js + user-profile.directive.html + layout/ + shell.html + shell.controller.js + topnav.html + topnav.controller.js + people/ + attendees.html + attendees.controller.js + people.routes.js + speakers.html + speakers.controller.js + speaker-detail.html + speaker-detail.controller.js + services/ + data.service.js + localstorage.service.js + logger.service.js + spinner.service.js + sessions/ + sessions.html + sessions.controller.js + sessions.routes.js + session-detail.html + session-detail.controller.js + ``` + + ![Model de Structură a Aplicației](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-2.png) + + Notă: Nu-ți structura aplicația folosind folder-per-tip. Acest lucru necesită mutarea în foldere multiple când lucrezi la o funcționalitate și devine groaie rapid, pe măsură ce aplicație crește la 5, 10 sau 25+ de view-uri și controllere (și alte funcționalități), ceea ce face totul mau greu decât folder-per-caracteristică la localizarea fișierelor. + + ```javascript + /* + * evită + * Alternativă: foldere-per-tip. + * Recomand "folder-per-funcționalitate" în schimb. + */ + + app/ + app.module.js + app.config.js + app.routes.js + directives.js + controllers/ + attendees.js + session-detail.js + sessions.js + shell.js + speakers.js + speaker-detail.js + topnav.js + directives/ + calendar.directive.js + calendar.directive.html + user-profile.directive.js + user-profile.directive.html + services/ + dataservice.js + localstorage.js + logger.js + spinner.js + views/ + attendees.html + session-detail.html + sessions.html + shell.html + speakers.html + speaker-detail.html + topnav.html + ``` + +**[Înapoi sus](#table-of-contents)** + +## Modularitate + +### Multe Module Mici, Autonome +###### [Style [Y160](#style-y160)] + + - Creează module mici ce encapsulează o singură responsabilitate. + + *De ce?*: Aplicațiile modulare fac ușoară folosirea și permit echipelor de dezvoltare să construiască felii verticale ale aplicației și să le livreze incremental. Acest lucru înseamnă că putem adăuga funcționalități noi în timp ce le dezvoltăm. + +### Creează un Modul App +###### [Style [Y161](#style-y161)] + + - Creează un modul de bază al aplicației al cărui rol să fie agregarea tuturor modulelor și a funcționalităților aplicației tale. Denumește-l bazându-te pe aplicația ta. + + *De ce?*: Angular încurajează modularitatea și șabloanele de separare. Creerea unui modul de bază al cărui rol este să lege celelalte module conferă un mod foarte direct de a adăuga și înlătura module din aplicație. + +### Păstrează Modulul Subțire +###### [Style [Y162](#style-y162)] + + - Pune doar logica de agregare în modulul de bază. Lasă funcționalitățile în modulele proprii. + + *De ce?*: Adăugarea de roluri adiționale la rădăcina aplicației pentru luare de date remote, afișare de view-uri, sau altă logică ce nu ține de agregarea aplicației murdărește modulul aplicației și face ambele mulțimi de funcționalități mai greu de reutilizat sau dezactivat. + + *De ce?*: Modulul app devine un manifest ce descrie ce module contribuie la definirea aplicației. + +### Zonele de Funcționalități sunt Module +###### [Style [Y163](#style-y163)] + + - Creează module ce reprezintă zone de funcționalități, precum layout, servicii partajate și refolosibile, dashboard-uri, și funcționalități specifice aplicației (e.g. customers, admin, sales). + + *De ce?*: Module autonome pot fi adăugate la aplicație cu conflict redus sau inexistent. + + *De ce?*: Sprinturile sau iterațiile se pot axa pe zone de funcționalitate și să le activeze la sfărșitul sprintului sau iterației. + + *De ce?*: Separarea zonelor viitoare în module face mai ușoară testarea modulelor în izolare și refolosirea codului. + +### Blocurile Refolosibile sunt Module +###### [Style [Y164](#style-y164)] + + - Creează module ce reprezintă blocuri refolosibile ale aplicației pentru servicii comune precum tratarea excepțiilor, logare, diagnoză, securitate, și stashing de date locale. + + *De ce?*: Aceste tipuri de funcționalități sunt necesare în multe aplicații, deci prin păstrarea lor separat în modulele lor proprii ele pot fi generale și refolosite în mai multe aplicații. + +### Dependințele Modulelor +###### [Style [Y165](#style-y165)] + + - Modulul de bază al aplicației depinde de module bazate pe funcționalități, specifice aplicației, și pe alte module partajate sau reutilizabile. + + ![Modularitate și dependințe](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/modularity-1.png) + + *De ce?*: Modulul de bază al aplicației conține un manifest rapid indentificabil ce conține funcționalitățile aplicației. + + *De ce?*: Fiecare zonă de funcționalitate conține un manifest a ceea de ce depinde, așa că poate fi luat ca o dependință în alte aplicații și să funcționeze în continuare. + + *De ce?*: Funcționalități Intra-Aplicație precum serviciile de date partajate devin ușor de localizat și partajat din `app.core` (alege numele tău favorit pentru acest modul) + + Notă: Aceasta e o strategie pentru consecvență. Există multe opțiuni bune aici. Alege una ce e consecventă, urmează regulile de dependințe a Angular, și e ușor de întreținut și scalat. + + > Structurile mele variază ușor între proiecte dar toate urmează aceste reguli pentru structură și modularitate. Implementarea poate varia în funcție de funcționalități și echipă. Cu alte cuvinte, nu te axa pe o structură bătută în cuie, dar construiește structura ta având în vedere consecvență, mentenabilitate, și eficiență. + + > Într-o aplicație mică, poți lua în considerare punerea tuturor dependințelor partajate în modulul de bază unde modulele de funcționalități nu au dependințe direct. Acest lucru face mai ușoară întreținerea aplicațiilor mici, dar face mai grea refolosirea acestor module în afara aplicației acesteia. + +**[Înapoi sus](#table-of-contents)** + +## Logica de Start + +### Configurare +###### [Style [Y170](#style-y170)] + + - Injectează cod în [configurarea modulului] (https://docs.angularjs.org/guide/module#module-loading-dependencies), ce trebuie să fie configurat înaintea rulării aplicației. Candidații ideali sunt providerii și constantele. + + *De ce?*: Acest lucru reduce numărul de locuri în care ai configurație. + + ```javascript + angular + .module('app') + .config(configure); + + configure.$inject = + ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; + + function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { + exceptionHandlerProvider.configure(config.appErrorPrefix); + configureStateHelper(); + + toastr.options.timeOut = 4000; + toastr.options.positionClass = 'toast-bottom-right'; + + //////////////// + + function configureStateHelper() { + routerHelperProvider.configure({ + docTitle: 'NG-Modular: ' + }); + } + } + ``` + +### Blocuri de rulare +###### [Style [Y171](#style-y171)] + + - Orice cod ce trebuie să ruleze când o aplicație pornește ar trebui declarat într-un factory, expus printr-o funcție, și injectat în [blocul de rularee](https://docs.angularjs.org/guide/module#module-loading-dependencies). + + *De ce?*: Codul direct într-un bloc de rulare poate fi greu de testat. Punându-l într-un factory il face mai ușor de abstractizat și generat. + + ```javascript + angular + .module('app') + .run(runBlock); + + runBlock.$inject = ['authenticator', 'translator']; + + function runBlock(authenticator, translator) { + authenticator.initialize(); + translator.initialize(); + } + ``` + +**[Înapoi sus](#table-of-contents)** + +## Servicii Angular $ Wrapper + +### $document și $window +###### [Style [Y180](#style-y180)] + + - Folosește [`$document`](https://docs.angularjs.org/api/ng/service/$document) și [`$window`](https://docs.angularjs.org/api/ng/service/$window) în loc de `document` și `window`. + + *De ce?*: Aceste servicii sunt învelite de Angular și sunt mai ușor testabile decât folosind document și window in teste. Acest lucru ajută la evitarea generării manuale a 'document' și 'window'. + +### $timeout și $interval +###### [Style [Y181](#style-y181)] + + - Folosește [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) și [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) în loc de `setTimeout` și `setInterval` . + + *De ce?*: Aceste servicii sunt învelite de Angular și sunt mai ușor testabile și folosesc ciclul de digest al Angular, deci păstrează data-bind-ul sincronizat. + +**[Înapoi sus](#table-of-contents)** + +## Testarea +Unit testurile ajută la menținerea curățeniei codului, iar din acest motiv am inclus niște recomandări pentru unit testing-ul fundațiilor cu link-uri pentru mai multe informații. + +### Scrie Teste cu Story-uri +###### [Style [Y190](#style-y190)] + + - Scrie un set de teste pentru fiecare story. Începe cu un test gol și umple-le în timp ce scrii codul pentru story. + *De ce?*: Scriind descrierea testelor ajută la clarificarea definirii a ceea ce va face un story, a ceea ce nu va face, și cum se poate măsura succesul. + + ```javascript + it('ar trebui să aibă Avengers controller', function() { + // TODO + }); + + it('ar trebui să găsească 1 Avenger când se filtrează după nume', function() { + // TODO + }); + + it('ar trebui să aibă 10 Avengeri', function() { + // TODO (generează date?) + }); + + it('ar trebui să returneze Avengers prin XHR', function() { + // TODO ($httpBackend?) + }); + + // și așa mai departe + ``` + +### Biblioteca de Testare +###### [Style [Y191](#style-y191)] + + - Folosește [Jasmine](http://jasmine.github.io/) sau [Mocha](http://mochajs.org) pentru unit testing. + + *De ce?*: Atât Jasmine cât și Mocha sunt frecvent folosite de către comunitatea Angular. Ambele sunt stabile, întreținute bine, și furnizează funcționalități robuste de testare. + + Notă: Când folosești Mocha, ia în calcul de asemenea folosirea unei biblioteci de assert precum [Chai](http://chaijs.com). Eu prefer Mocha. + +### Rularea Testelor +###### [Style [Y192](#style-y192)] + + - Folosește [Karma](http://karma-runner.github.io) pentru rularea testelor. + + *De ce?*: Karma e ușor de configurat ca să ruleze o dată automat sau automat când faci schimbări în cod. + + *De ce?*: Karma se leagă de procesul tău de Continuous Integration ușor pe cont propriu sau prin intermediul Grunt sau Gulp. + + *De ce?*: unele IDE-uri încep să se integreze cu Karma, precum [WebStorm](http://www.jetbrains.com/webstorm/) și [Visual Studio](https://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). + + *De ce?*: Karma funcționează bine cu leaderii de automatizare de taskuri precum [Grunt](http://gruntjs.com/) (cu [grunt-karma](https://github.com/karma-runner/grunt-karma)) și [Gulp](http://gulpjs.com/). Când folosești Gulp, folosește [Karma](https://github.com/karma-runner/karma) direct și nu un API, de vreme ce API-ul poate fi apelat direct. + + ```javascript + /* recomandat */ + + // Exemplu de Gulp direct cu Karma + function startTests(singleRun, done) { + var child; + var excludeFiles = []; + var fork = require('child_process').fork; + var Server = require('karma').Server; + var serverSpecs = config.serverIntegrationSpecs; + + if (args.startServers) { + log('Starting servers'); + var savedEnv = process.env; + savedEnv.NODE_ENV = 'dev'; + savedEnv.PORT = 8888; + child = fork(config.nodeServer); + } else { + if (serverSpecs && serverSpecs.length) { + excludeFiles = serverSpecs; + } + } + + var karmaOptions = { + configFile: __dirname + '/karma.conf.js', + exclude: excludeFiles, + singleRun: !!singleRun + }; + + let server = new Server(karmaOptions, karmaCompleted); + server.start(); + + //////////////// + + function karmaCompleted(karmaResult) { + log('Karma completed'); + if (child) { + log('shutting down the child process'); + child.kill(); + } + if (karmaResult === 1) { + done('karma: tests failed with code ' + karmaResult); + } else { + done(); + } + } + } + ``` + +### Stubbing-ul și Spionarea +###### [Style [Y193](#style-y193)] + + - Folosește [Sinon](http://sinonjs.org/) pentru stubbing și spionare. + + *De ce?*: Sinon funcționează bine atât cu Jasmine cât și cu Mocha și extinde caracteristicile de stubbing și spionare pe care acestea le oferă. + + *De ce?*: Sinon face mai ușoară trecerea între Jasmine și Mocha, dacă vrei să le încerci pe ambele. + + *De ce?*: Sinon are mesaje descriptive pentru când testele eșuază assert-urile. + +### Browser Headless +###### [Style [Y194](#style-y194)] + + - Folosește [PhantomJS](http://phantomjs.org/) pentru a-ți rula testele pe un server. + + *De ce?*: PhantomJS e un browser headless ce ajută la rularea testelor tale fără a avea nevoie de un browser "vizual". Așa că nu ai nevoie să instalezi Chrome, Safari, IE, sau alte browsere pe serverul tău. + + Notă: Ar trebui, în continuare, să testezi pe toate browserele din mediul tău, mai ales acelea relative pentru utilizatorii tăi. + +### Analiza Codului +###### [Style [Y195](#style-y195)] + + - Rulează JSHint pe testele tale. + + *De ce?*: Testele sunt cod. JSHint poate identifica probleme de calitate a codului ce pot apărea și cauza test să nu ruleze cum trebuie. + +### Atenuează Globalele pentru Regulile JSHint despre Teste +###### [Style [Y196](#style-y196)] + + - Atenuează regulile pe codul tău de test pentru a permite globale comune precum `describe` și `expect`. Atenuează regulile pentru expresii, de vreme ce Mocha le folosește. + + *De ce?*: Testele tale sunt cod și au nevoie de aceași atenție pentru regulile de calitate a codului precum codul tău de producție. Însă variablilele globale folosite de framework-ul de testare de exemplu, pot fi relaxate prin includerea lui "this" în specificațiile de test. + + ```javascript + /* jshint -W117, -W030 */ + ``` + Sau poți adăuga următoarele în fișierul tău de opțiuni JSHint. + + ```javascript + "jasmine": true, + "mocha": true, + ``` + + ![Unelte de Testare](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) + +### Organizarea Testelor +###### [Style [Y197](#style-y197)] + + - Pune fișierele de unit test (specs) lângă codul tău de client. Pune specificațiile ce acoperă integrarea cu serverul sau testează mai multe componente într-un foloder separat `tests`. + + *De ce?*: Unit testurile au corelare directă cu o componentă și un fișier specific din codul sursă. + + *De ce?*: E mai ușor să le menții la zi de vreme ce sunt întotdeauna la vedere. Când scrii cod, indiferent dacă faci TDD sau testezi în timpul dezvoltării sau după dezvoltare, specificațiile sunt una lângă alta și niciodată în afara câmpului vizual, așa că au mai multe șanse să fie aduse la zi, și deci să menții code coverage-ul. + + *De ce?*: Când faci update la codul sursă e mai ușor să faci update și la teste în același timp. + + *De ce?*: Punându-le una lângă alta face mai ușoară găsirea lor și mai ușor să le muți împreună cu codul sursă dacă muți sursa. + + *De ce?*: Avutul specificației aproape, face mai ușoară citirea pentru cititorul codului sursă în scopul înțelegerii a cum ar trebui ca o componentă să fie folosită și să descopere limitările acesteia. + + *De ce?*: Separarea specificațiilor în așa fel încât acestea nu sunt într-un build distribuit e ușor cu grunt sau gulp. + + ``` + /src/client/app/customers/customer-detail.controller.js + /customer-detail.controller.spec.js + /customers.controller.js + /customers.controller.spec.js + /customers.module.js + /customers.route.js + /customers.route.spec.js + ``` + +**[Înapoi sus](#table-of-contents)** + +## Animații + +### Folosire +###### [Style [Y210](#style-y210)] + + - Folosește [animații subtile cu Angular](https://docs.angularjs.org/guide/animations) pentru a tranziționa între stări și view-uri și pentru elemente vizuale primare. Include [modulul ngAnimate](https://docs.angularjs.org/api/ngAnimate). Cele 3 chei sunt subtil, lin, și fluid. + + *De ce?*: Animațiile subtile pot îmbunătăți experiența utilizatoruli atunci când sunt folosite în mod corespuzător. + + *De ce?*: Animațiile subtile pot îmbunătăți peformanța percepută când view-urile tranziționează. + +### Sub o Secundă +###### [Style [Y211](#style-y211)] + + - Folosește durate scurte pentru animații. Eu prefer în general să încep cu 300ms și să ajustez până când e corespunzător. + + *De ce?*: Animațiile lungi pot avea efectul invers asupra experienței utilizatorului și a performanței percepute prin darea unei senzații de aplicație înceată. + +### animate.css +###### [Style [Y212](#style-y212)] + + - Folosește [animate.css](http://daneden.github.io/animate.css/) pentru animații convenționale. + + *De ce?*: Animațiile pe care animate.css le oferă sunt rapide, fluide și foarte ușoare de adăugat în aplicație. + + *De ce?*: Conferă consecvență în animații. + + *De ce?*: animate.css e folosit și testat în mod larg. + + Notă: Vezi [acest articol foarte bun al lui Matias Niemelä despre animațiile Angular](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) + +**[Înapoi sus](#table-of-contents)** + +## Comentarii + +### jsDoc +###### [Style [Y220](#style-y220)] + + - Dacă planifici să produci documentație, folosește sintaxa [`jsDoc`](http://usejsdoc.org/) pentru a documenta numele de funcții, descrierile, parametrii și returnurile. Folosește `@namespace` și `@memberOf` pentru a potrivi structura aplicației. + + *De ce?*: Poți genera (și regenera) documentație din codul tău, în locul scrierii sale de la 0. + + *De ce?*: Folosirea unui tool comun în industrie conferă consecvență. + + ```javascript + /** + * Factory-uri de log + * @namespace Factories + */ + (function() { + angular + .module('app') + .factory('logger', logger); + + /** + * @namespace Logger + * @desc Logger în toată aplicația + * @memberOf Factories + */ + function logger($log) { + var service = { + logError: logError + }; + return service; + + //////////// + + /** + * @name logError + * @desc Logs errors + * @param {String} msg Mesaj pentru logare + * @returns {String} + * @memberOf Factories.Logger + */ + function logError(msg) { + var loggedMsg = 'Error: ' + msg; + $log.error(loggedMsg); + return loggedMsg; + }; + } + })(); + ``` + +**[Înapoi sus](#table-of-contents)** + +## JS Hint + +### Folosește un fișier de opțiuni +###### [Style [Y230](#style-y230)] + + - Folosește JS Hint pentru linting-ul codului tău JavaScript și asigură-te că personalizezi fișierul JS hint de opțiuni și îl incluzi în source control. Vezi [documentația JS Hint](http://jshint.com/docs/) pentru detalii despre opțiuni. + + *De ce?*: Furnizează o primă alertă înaintea comiterii codului în source control. + + *De ce?*: Furnizează consecvență în echipă. + + ```javascript + { + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "esversion": 6, + "forin": true, + "freeze": true, + "immed": true, + "indent": 4, + "latedef": "nofunc", + "newcap": true, + "noarg": true, + "noempty": true, + "nonbsp": true, + "nonew": true, + "plusplus": false, + "quotmark": "single", + "undef": true, + "unused": false, + "strict": false, + "maxparams": 10, + "maxdepth": 5, + "maxstatements": 40, + "maxcomplexity": 8, + "maxlen": 120, + "asi": false, + "boss": false, + "debug": false, + "eqnull": true, + "esnext": false, + "evil": false, + "expr": false, + "funcscope": false, + "globalstrict": false, + "iterator": false, + "lastsemic": false, + "laxbreak": false, + "laxcomma": false, + "loopfunc": true, + "maxerr": 50, + "moz": false, + "multistr": false, + "notypeof": false, + "proto": false, + "scripturl": false, + "shadow": false, + "sub": true, + "supernew": false, + "validthis": false, + "noyield": false, + + "browser": true, + "node": true, + + "globals": { + "angular": false, + "$": false + } + } + ``` + +**[Înapoi sus](#table-of-contents)** + +## JSCS + +### Folosește un Fișier de Opțiuni +###### [Style [Y235](#style-y235)] + + - Folosește JSCS pentru verificarea stilului codului tău JavaScript și asigură-te că personalizezi fișierul de opțiuni JSCS și-l incluzi în source control. Vezi [documentația JSCS](http://jscs.info/). + + *De ce?*: Furnizează o primă alertă înaintea comiterii codului în source control. + + *De ce?*: Furnizează consecvență în echipă. + + ```javascript + { + "excludeFiles": ["node_modules/**", "bower_components/**"], + + "requireCurlyBraces": [ + "if", + "else", + "for", + "while", + "do", + "try", + "catch" + ], + "requireOperatorBeforeLineBreak": true, + "requireCamelCaseOrUpperCaseIdentifiers": true, + "maximumLineLength": { + "value": 100, + "allowComments": true, + "allowRegex": true + }, + "validateIndentation": 4, + "validateQuoteMarks": "'", + + "disallowMultipleLineStrings": true, + "disallowMixedSpacesAndTabs": true, + "disallowTrailingWhitespace": true, + "disallowSpaceAfterPrefixUnaryOperators": true, + "disallowMultipleVarDecl": null, + + "requireSpaceAfterKeywords": [ + "if", + "else", + "for", + "while", + "do", + "switch", + "return", + "try", + "catch" + ], + "requireSpaceBeforeBinaryOperators": [ + "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", + "&=", "|=", "^=", "+=", + + "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", + "|", "^", "&&", "||", "===", "==", ">=", + "<=", "<", ">", "!=", "!==" + ], + "requireSpaceAfterBinaryOperators": true, + "requireSpacesInConditionalExpression": true, + "requireSpaceBeforeBlockStatements": true, + "requireLineFeedAtFileEnd": true, + "disallowSpacesInsideObjectBrackets": "all", + "disallowSpacesInsideArrayBrackets": "all", + "disallowSpacesInsideParentheses": true, + + "jsDoc": { + "checkAnnotations": true, + "checkParamNames": true, + "requireParamTypes": true, + "checkReturnTypes": true, + "checkTypes": true + }, + + "disallowMultipleLineBreaks": true, + + "disallowCommaBeforeLineBreak": null, + "disallowDanglingUnderscores": null, + "disallowEmptyBlocks": null, + "disallowTrailingComma": null, + "requireCommaBeforeLineBreak": null, + "requireDotNotation": null, + "requireMultipleVarDecl": null, + "requireParenthesesAroundIIFE": true + } + ``` + +**[Înapoi sus](#table-of-contents)** + +## Constante + +### Globale Specifice Furnizorului +###### [Style [Y240](#style-y240)] + + - Creează o Constantă Angular pentru variabilele globale care aparțin biblioteciilor furnizorilor. + + *De ce?*: Oferă o metodă de a injecta biblioteci ale furnizorilor care altfel ar fi globale. Acest lucru îmbunătățește testabilitatea codului prin faptul că îți permite să știi mai ușor care sunt dependințele componentelor tale (evită abstracțiile indiscrete). + + ```javascript + // constants.js + + /* global toastr:false, moment:false */ + (function() { + 'use strict'; + + angular + .module('app.core') + .constant('toastr', toastr) + .constant('moment', moment); + })(); + ``` + +###### [Style [Y241](#style-y241)] + + - Folosește constante pentru valori ce nu se schimbă și care nu vin dintr-un alt serviciu. Când constantele sunt folosite doar pentru un modul ce ar putea fi folosit în mai multe aplicații, pleasează constantele într-un fișier per modul numit după modul. Până când acest lucru e necesar, păstrează constantele din modulul principal într-un fișier `constants.js`. + + *De ce?*: O valoare ce s-ar putea schimba, până și rar, ar trebui luată dintr-un serviciu, astfel încât tu să nu trebuiască să schimbi codul sursă. De exemplu, un url pentru un serviciu de date ar putea fi pus într-o constantă dar un loc mai bun ar fi încărcarea lui dintr-un serviciu web. + + *De ce?*: Constantele pot fi injectate în orice component angular, încluzând providerii. + + *De ce?*:Când o aplicație e separată în module ce ar putea fi refolosite în alte aplicații, fiecare modul stand-alone ar trebui să poată să opereze pe cont propriu incluzând orice constante dependente. + + ```javascript + // Constante folosite de întreaga aplicație + angular + .module('app.core') + .constant('moment', moment); + + // Constants used only by the sales module + angular + .module('app.sales') + .constant('events', { + ORDER_CREATED: 'event_order_created', + INVENTORY_DEPLETED: 'event_inventory_depleted' + }); + ``` + +**[Înapoi sus](#table-of-contents)** + +## Șabloane de Fișiere și Snippeturi +Folosește șabloane de fișier sau snippeturi pentru a ajuta urmarea de stiluri și șabloane consecvente. Aici sunt niște șabloane și/sau snippeturi pentru unele dintre editoarele de web development și IDE-uri. + +### Sublime Text +###### [Style [Y250](#style-y250)] + + - Snippeturi Angular ce urmează aceste stiluri și ghiduri. + + - Descarcă [Sublime Angular snippets](assets/sublime-angular-snippets?raw=true) + - Pune-l în folderul Packages + - Restartează Sublime + - Într-un fișier JavaScript scrie aceste comenzi urmate de un `TAB`: + + ```javascript + ngcontroller // creează un controller Angular + ngdirective // creează o directivă Angular + ngfactory // creează un factory Angular + ngmodule // creează un modul Angular + ngservice // creează un serviciu Angular + ngfilter // creează un filtru Angular + ``` + +### Visual Studio +###### [Style [Y251](#style-y251)] + + - Snippeturi Angular ce urmează aceste stiluri și ghiduri pot fi găsite pe site-ul [SideWaffle](http://www.sidewaffle.com) + + - Download the [SideWaffle](http://www.sidewaffle.com) Visual Studio extension (vsix file) + - Descarcă extensia [SideWaffle](http://www.sidewaffle.com) pentru Visual Studio (fișierul vsix) + - Rulează fișierul vsix + - Restartează Visual Studio + +### WebStorm +###### [Style [Y252](#style-y252)] + + - Live Template-uri Angular ce urmează aceste stiluri și ghiduri. + + - Descarcă [webstorm-angular-live-templates.xml](assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml?raw=true) + - Pune-l în fișierul tău de [template-uri](https://www.jetbrains.com/webstorm/help/project-and-ide-settings.html) + - Restartează WebStorm + - Într-un fișier JavaScript scrie aceste comenzi urmate de un `TAB`: + + ```javascript + // Acestea sunt snippet-uri pe fișiere întregi ce conțin IIFE-uri + ngapp // creează un modul Angular setter + ngcontroller // creează un controller Angular + ngdirective // creează o directivă Angular + ngfactory // creează un factory Angular + ngfilter // creează un filtru Angular + ngservice // creează un serviciu Angular + + // Acestea sunt snippet-uri parțiale menite a fi înlănțuite + ngmodule // creează un getter de modul Angular + ngstate // creează o definiție de stare pentru Angular UI Router + ngconfig // definește o funcție pentru faza de configurare + ngrun // definește o funcție pentru faza de rulare + ngwhen // creează o definiție ngRoute cu 'when' + ``` + + *Template-uri individuale sunt de asemenea disponibile pentru descărcare în folder-ul [webstorm-angular-live-templates](assets/webstorm-angular-live-templates?raw=true)* + +### Atom +###### [Style [Y253](#style-y253)] + + - Snippet-uri Angular ce urmează aceste stiluri și ghiduri. + + ``` + apm install angularjs-styleguide-snippets + ``` + or + - Deschide Atom, apoi deschide Package Manager (Packages -> Settings View -> Install Packages/Themes) + - Caută pachetul 'angularjs-styleguide-snippets' + - Apasă 'Install' pentru a instala pachetul + + - Într-un fișier JavaScript scrie aceste comenzi, urmate de un `TAB` + + ```javascript + ngcontroller // creează un controller Angular + ngdirective // creează o directivă Angular + ngfactory // creează un factory Angular + ngmodule // creează un modul Angular + ngservice // creează un serviciu Angular + ngfilter // creează un filtru Angular + ``` + +### Brackets +###### [Style [Y254](#style-y254)] + + - Snippet-uri Angular ce urmează aceste stiluri și ghiduri. + - Descarcă [Brackets Angular snippets](assets/brackets-angular-snippets.yaml?raw=true) + - Brackets Extension manager ( File > Extension manager ) + - Instalează ['Brackets Snippets (by edc)'](https://github.com/chuyik/brackets-snippets) + - Apasă becul din partea din dreapta sus a Brackets + - Apasă `Settings` și apoi `Import` + - Selectează fișierul și apoi alege dacă faci skip sau override + - Apasă `Start Import` + + - Într-un fișier JavaScript scrie aceste comenzi, urmate de un `TAB` + + ```javascript + // Acestea sunt snippet-uri pe fișiere întregi ce conțin IIFE-uri + ngcontroller // creează un controller Angular + ngdirective // creează o directivă Angular + ngfactory // creează un factory Angular + ngmodule // creează un modul Angular + ngservice // creează un serviciu Angular + ngfilter // creează un filtru Angular + + // Acestea sunt snippet-uri parțiale menite a fi înlănțuite + ngmodule // creează un getter de modul Angular + ngstate // creează o definiție de stare pentru Angular UI Router + ngconfig // definește o funcție pentru faza de configurare + ngrun // definește o funcție pentru faza de rulare + ngwhen // creează o definiție ngRoute cu 'when' + ngtranslate // folosește serviciul $translate cu promise-ul său + ``` + +### vim +###### [Style [Y255](#style-y255)] + + - Snippet-uri vim ce urmează aceste stiluri și ghiduri. + + - Descarcă [vim Angular snippets](assets/vim-angular-snippets?raw=true) + - Setează [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) + - Copiază snippet-urile în folder-ul de snippeturi + + - Snippet-uri vim UltiSnips ce urmează aceste stiluri și ghiduri. + + - Descarcă[vim Angular UltiSnips snippets](assets/vim-angular-ultisnips?raw=true) + - Setează [UltiSnips](https://github.com/SirVer/ultisnips) + - Copiază snippet-urile în folderul UltiSnips + + ```javascript + ngcontroller // creează un controller Angular + ngdirective // creează o directivă Angular + ngfactory // creează un factory Angular + ngmodule // creează un modul Angular + ngservice // creează un serviciu Angular + ngfilter // creează un filtru Angular + ``` + +### Visual Studio Code + +###### [Style [Y256](#style-y256)] + + - Snippeturi [Visual Studio Code](https://code.visualstudio.com/) ce urmează aceste stiluri și ghiduri. + + - Descarcă [VS Code Angular snippets](assets/vscode-snippets/javascript.json?raw=true) + - Copiază snippeturile în folder-ul de snippet-uri, sau copiază snippet-urile în cele deja existente + + ```javascript + ngcontroller // creează un controller Angular + ngdirective // creează o directivă Angular + ngfactory // creează un factory Angular + ngmodule // creează un modul Angular + ngservice // creează un serviciu Angular + ngfilter // creează un filtru Angular + ``` + +### Emacs +###### [Style [Y257](#style-y257)] + + - Snippet-uri [Emacs](https://www.gnu.org/software/emacs/) ce urmează aceste stiluri și ghiduri. + + - Descarcă [Emacs Angular snippets](assets/emacs-angular-snippets?raw=true) + + Yyasnippet categorizează snippet-urile prin mod-ul major, și sunt mai multe moduri major pentru Emacs pentru editarea codului JavaScript. Snippet-urile sunt în `js2-mode`, și celelalte directoare conțin doar un dotfile ce le referențiază de acolo. + + - Instalează [yasnippet](https://github.com/capitaomorte/yasnippet) (`M-x package-install RET yasnippet RET`) + - Copiază snippet-urile în folder-ul de snippeturi, sau modifică init-ul Emacs init ca să adaugi directorul de snippeturi la `yas-snippet-dirs` + + ```javascript + ngcontroller // creează un controller Angular + ngdirective // creează o directivă Angular + ngfactory // creează un factory Angular + ngmodule // creează un modul Angular + ngservice // creează un serviciu Angular + ngfilter // creează un filtru Angular + ``` + +**[Înapoi sus](#table-of-contents)** + +## Generatorul Yeoman +###### [Style [Y260](#style-y260)] + +Poți folosi [generatorul Yeoman HotTowel](http://jpapa.me/yohottowel) pentru a crea o aplicație ce servește ca un punct de plecare pentru Angular ce urmează acest ghid stilistic. + +1. Instalează generator-hottowel + + ``` + npm install -g generator-hottowel + ``` + +2. Creează un nou folder și intră în el + + ``` + mkdir myapp + cd myapp + ``` + +3. Rulează generatorul + + ``` + yo hottowel helloWorld + ``` + +**[Înapoi sus](#table-of-contents)** + +## Rutarea +Rutarea pe partea de client e importantă pentru creerea unui flow de navigație între view-uri și view-uri compuse ce sunt făcute din mai multe șabloane și directive. + +###### [Style [Y270](#style-y270)] + + - Folosește [AngularUI Router](http://angular-ui.github.io/ui-router/) pentru rutare pe client. + + *De ce?*: UI Router oferă toate funcționalitățile unui Angular router plus niște trăsături în plus incluzând rute fiu și stări. + + *De ce?*: Sintaxa e destul de similară cu cea a Angular router, ceea ce face ușoară migrarea spre UI Router. + + - Notă: Poți folosi un provider precum cel arătat mai jos - `routerHelperProvider` - pentru a ajuta la configurarea stărilor între fișiere, în timpul fazei de rulare. + + ```javascript + // customers.routes.js + angular + .module('app.customers') + .run(appRun); + + /* @ngInject */ + function appRun(routerHelper) { + routerHelper.configureStates(getStates()); + } + + function getStates() { + return [ + { + state: 'customer', + config: { + abstract: true, + template: '', + url: '/customer' + } + } + ]; + } + ``` + + ```javascript + // routerHelperProvider.js + angular + .module('blocks.router') + .provider('routerHelper', routerHelperProvider); + + routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; + /* @ngInject */ + function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { + /* jshint validthis:true */ + this.$get = RouterHelper; + + $locationProvider.html5Mode(true); + + RouterHelper.$inject = ['$state']; + /* @ngInject */ + function RouterHelper($state) { + var hasOtherwise = false; + + var service = { + configureStates: configureStates, + getStates: getStates + }; + + return service; + + /////////////// + + function configureStates(states, otherwisePath) { + states.forEach(function(state) { + $stateProvider.state(state.state, state.config); + }); + if (otherwisePath && !hasOtherwise) { + hasOtherwise = true; + $urlRouterProvider.otherwise(otherwisePath); + } + } + + function getStates() { return $state.get(); } + } + } + ``` + +###### [Style [Y271](#style-y271)] + + - Definește rute pentru view-uri în modulul în care sunt. Fiecare modul ar trebui să definească rutele pentru view-urile din modulul respectiv. + + *De ce?*: Fiecare modul ar trebui să fie capabil să funcționeze pe cont propriu. + + *De ce?*: Când ștergi un modul sau adaugi un modul, aplicația va conține doar rute ce pointează spre view-uri existente. + + *De ce?*: Acest lucru face activarea sau dezactivare anuor porțiuni ale aplicației fără griji despre rute orfane. + +**[Înapoi sus](#table-of-contents)** + +## Automatizarea Task-urilor +Folosește [Gulp](http://gulpjs.com) sau [Grunt](http://gruntjs.com) pentru creerea de task-uri automate. Gulp tinde spre cod în locul configurării, în timp ce Grunt tinde spre configurare în locul codului. Eu personal prefer Gulp, fiindcă consider că e mai ușor de scris și citit, dar amândouă sunt excelente. + +> Află mai multe despre gulp și șabloane pentru automatizarea task-urilor în [cursul meu Gulp de pe Pluralsight](http://jpapa.me/gulpps) + +###### [Style [Y400](#style-y400)] + + - Folosește automatizarea taskurilor pentru listarea definițiilor de module `*.module.js` înaintea oricăror altor fișiere JavaScript din aplicație. + + *De ce?*: Angular are nevoie ca definițiile de module să fie înregistrate înainte ca acestea să fie folosite. + + *De ce?*: Numirea modulelor cu un pattern specific, precum `*.module.js` face mai ușoară luarea lor și listarea lor prima dată. + + ```javascript + var clientApp = './src/client/app/'; + + // Întotdeaună procesează fișierele de module prima dată + var files = [ + clientApp + '**/*.module.js', + clientApp + '**/*.js' + ]; + ``` + +**[Înapoi sus](#table-of-contents)** + +## Filtre + +###### [Style [Y420](#style-y420)] + + - Evită folositrea filtrelor pentru scanarea tuturor proprietăților unui obiect complex sau graf. Folosește filtre pentru proprietăți selecte. + + *De ce?*: Filtrele pot fi foarte ușor abuzate și pot foarte ușor afecta performanța dacă nu sunt folosite corect, de exemplu când un filtru procesează un graf de obiect mare și adânc. + +**[Înapoi sus](#table-of-contents)** + +## Angular docs +## Documentația Angular +Pentru orice altceva, referință API, vezi [documentația Angular](//docs.angularjs.org/api). + +**[Înapoi sus](#table-of-contents)** \ No newline at end of file From 2be733fc3ec84554a5c5f4bfd6bf2871797ebe89 Mon Sep 17 00:00:00 2001 From: Anil Kumar krishanshetty Date: Thu, 2 Mar 2017 17:22:43 +0100 Subject: [PATCH 101/114] support ngcomponent (#800) --- .../angular.component.sublime-snippet | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 a1/assets/sublime-angular-snippets/angular.component.sublime-snippet diff --git a/a1/assets/sublime-angular-snippets/angular.component.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.component.sublime-snippet new file mode 100644 index 00000000..f3659efe --- /dev/null +++ b/a1/assets/sublime-angular-snippets/angular.component.sublime-snippet @@ -0,0 +1,25 @@ + + + ngcomponent + text.plain, source.js + From 15a7cf9cab271768f7e62e01546ee567cc6678ec Mon Sep 17 00:00:00 2001 From: Irvin Sandoval Date: Thu, 2 Mar 2017 10:23:39 -0600 Subject: [PATCH 102/114] Improve orthography (#786) --- a1/i18n/es-ES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/a1/i18n/es-ES.md b/a1/i18n/es-ES.md index f8edf6d6..f98a76de 100644 --- a/a1/i18n/es-ES.md +++ b/a1/i18n/es-ES.md @@ -2,7 +2,7 @@ *Guía de estilos colaborativa de Angular para equipos por [@john_papa](//twitter.com/john_papa)* -Si estás buscando una guía colaborativa sobre sintaxis, convenciones y estructura de aplicaciones con AngularJS, este es el sitio. Estos estilos están basados en mi experiencia desarrollando con [AngularJS](//angularjs.org), persentaciones, [Cursos en Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) y trabajando en equipos. +Si estás buscando una guía colaborativa sobre sintaxis, convenciones y estructura de aplicaciones con AngularJS, este es el sitio. Estos estilos están basados en mi experiencia desarrollando con [AngularJS](//angularjs.org), presentaciones, [Cursos en Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) y trabajando en equipos. El propósito de esta guía de estilos es proporcionar una guía de cómo construir aplicaciones con Angular enseñando convenciones que uso y, lo más importante, el porqué. @@ -737,7 +737,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti ### Miembros accesibles Arriba ###### [Style [Y052](#style-y052)] - - Expón las variables que se llaman del servicio (su interfaz) arriba, usando la técnica deribada de [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). + - Expón las variables que se llaman del servicio (su interfaz) arriba, usando la técnica derivada de [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). *¿Por qué?*: Colocar los elementos que se llaman arriba hace más fácil la lectura y te ayuda a identificar los elementos del servicio que se pueden llamar y se deben testear (y/o mockear). From 50b8e76b8fb03a93e6d2acf55cf85ed255da21d3 Mon Sep 17 00:00:00 2001 From: laranhee Date: Fri, 3 Mar 2017 01:25:49 +0900 Subject: [PATCH 103/114] translate 'Small Functions' (#796) --- a1/i18n/ko-KR.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/a1/i18n/ko-KR.md b/a1/i18n/ko-KR.md index 62f6f6b3..7d2a1d65 100644 --- a/a1/i18n/ko-KR.md +++ b/a1/i18n/ko-KR.md @@ -115,6 +115,23 @@ Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타 **[Back to top](#table-of-contents)** +### Small Functions +###### [style [Y002](#style-y002)] + + - 작은 함수를 정의하세요, 75줄을 넘지 마세요 (적을수록 좋습니다). + + *이유*: 하나의 일을하고 하나의 목적을 수행하는 작은 함수는 테스트하기 쉽습니다. + + *이유*: 작은 함수는 재사용을 촉진합니다. + + *이유*: 작은 함수는 읽기 쉽습니다. + + *이유*: 작은 함수는 유지하기 쉽습니다. + + *이유*: 작은 함수는 외부 scpoe와 변수를 공유하거나, 원하지 않는 closure를 만들거나, 원하지 않는 의존성과의 결합을 통해 만들어지는 숨겨진 버그를 피하는데 도움이 됩니다. + +**[Back to top](#table-of-contents)** + ## IIFE ### JavaScript Closures ###### [Style [Y010](#style-y010)] From 47f021ad4f982fb7bb322a5c6d9116e781020112 Mon Sep 17 00:00:00 2001 From: Dan Farino Date: Mon, 15 May 2017 21:14:18 -0700 Subject: [PATCH 104/114] Fix directive controllerAs name (#832) --- a1/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/README.md b/a1/README.md index 3242f6ae..905703b1 100644 --- a/a1/README.md +++ b/a1/README.md @@ -1235,7 +1235,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see ### Directives and ControllerAs ###### [Style [Y075](#style-y075)] - - Use `controller as` syntax with a directive to be consistent with using `controller as` with view and controller pairings. + - Use `controllerAs` syntax with a directive to be consistent with using `controller as` with view and controller pairings. *Why?*: It makes sense and it's not difficult. From 05dd1fc45c066abe7b2ce90ff460672705f788e8 Mon Sep 17 00:00:00 2001 From: JSabio Date: Tue, 16 May 2017 06:14:27 +0200 Subject: [PATCH 105/114] es-ES.md (#831) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * es-ES.md Change one link that doesn´t work (above Y070) and little changes on traduction. * Update es-ES.md --- a1/i18n/es-ES.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/a1/i18n/es-ES.md b/a1/i18n/es-ES.md index f98a76de..46ee923e 100644 --- a/a1/i18n/es-ES.md +++ b/a1/i18n/es-ES.md @@ -59,7 +59,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti ### La regla del 1 ###### [Style [Y001](#style-y001)] - - Define 1 componente por archivo. +  - Define 1 componente por archivo, es recomendable que sea menor a 400 líneas de código. *¿Por qué?*: Un componente por archivo promueve pruebas unitarias más fáciles. @@ -627,7 +627,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti ### Asignando Controladores ###### [Style [Y038](#style-y038)] - - Cuando un controlador debe ser asociado a una vista y cada componente puede ser reutilizado por otros controladores o vistas, define controladores con sus rutas. + - Cuando un controlador debe ser asociado a una vista y cada componente puede ser reutilizado por otros controladores o vistas, define los controladores con sus rutas. Nota: Si una Vista es cargada por otra además de por la ruta, entonces usa la sintaxis `ng-controller="Avengers as vm"`. @@ -1014,7 +1014,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti } ``` - **[Volver arriba](#tabla-de-contenidos)** +**[Volver arriba](#tabla-de-contenidos)** ## Directivas ### Limitadas a 1 Por Archivo @@ -1220,7 +1220,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti link: linkFunc, controller: ExampleController, controllerAs: 'vm', - bindToController: true // porque el scope is aislado + bindToController: true // porque el scope es aislado }; return directive; From 61b61b1b7c557c0cb5317acda355413ae6f3d874 Mon Sep 17 00:00:00 2001 From: "Kevin R. Dufendach, MD" Date: Tue, 16 May 2017 00:15:18 -0400 Subject: [PATCH 106/114] Update README.md to include $onInit (#830) * Update README.md to include $onInit Updates bindToController examples to reference Angular 1.5+ $onInit life-cycle callbacks. per https://code.angularjs.org/1.6.1/docs/guide/migration#commit-bcd0d4 and https://docs.angularjs.org/api/ng/service/$compile * Fixed controller divider character --- a1/README.md | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/a1/README.md b/a1/README.md index 905703b1..ca614f2b 100644 --- a/a1/README.md +++ b/a1/README.md @@ -1244,6 +1244,8 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see Note: Regarding dependency injection, see [Manually Identify Dependencies](#manual-annotating-for-dependency-injection). Note: Note that the directive's controller is outside the directive's closure. This style eliminates issues where the injection gets created as unreachable code after a `return`. + + Note: Life-style hooks were introduced in Angular 1.5. Initialization logic that relies on bindings being present should be put in the controller's $onInit() method, which is guarranteed to always be called after the bindings have been assigned. ```html
@@ -1284,13 +1286,23 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see function ExampleController($scope) { // Injecting $scope just for comparison var vm = this; - vm.min = 3; - + vm.$onInit = onInit; + + ////////// + console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); - console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); + console.log('CTRL: $scope.vm.max = %s', $scope.vm.max); // undefined in Angular 1.5+ console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); + console.log('CTRL: vm.max = %s', vm.max); // undefined in Angular 1.5+ + + // Angular 1.5+ does not bind attributes until calling $onInit(); + function onInit() { + console.log('CTRL-onInit: $scope.vm.min = %s', $scope.vm.min); + console.log('CTRL-onInit: $scope.vm.max = %s', $scope.vm.max); + console.log('CTRL-onInit: vm.min = %s', vm.min); + console.log('CTRL-onInit: vm.max = %s', vm.max); + } } ``` @@ -1348,8 +1360,12 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see function ExampleController() { var vm = this; vm.min = 3; - console.log('CTRL: vm.min = %s', vm.min); - console.log('CTRL: vm.max = %s', vm.max); + vm.$onInit = onInit; + + function onInit() = { + console.log('CTRL: vm.min = %s', vm.min); + console.log('CTRL: vm.max = %s', vm.max); + } } ``` From 2698fc41411af990076267ded9b80ec88a76ae67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0smail=20Ar=C4=B1l=C4=B1k?= Date: Tue, 16 May 2017 07:15:35 +0300 Subject: [PATCH 107/114] Fix a typo in README (#827) camel-case => camelCase --- a1/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/a1/README.md b/a1/README.md index ca614f2b..ddff62db 100644 --- a/a1/README.md +++ b/a1/README.md @@ -2151,7 +2151,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see ### Directive Component Names ###### [Style [Y126](#style-y126)] - - Use consistent names for all directives using camel-case. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). + - Use consistent names for all directives using camelCase. Use a short prefix to describe the area that the directives belong (some example are company prefix or project prefix). *Why?*: Provides a consistent way to quickly identify and reference components. From 59078dbbb9537d98b364475675ca630832db65c7 Mon Sep 17 00:00:00 2001 From: Tal Safran Date: Mon, 15 May 2017 21:15:49 -0700 Subject: [PATCH 108/114] Fix translations header (#824) This header needs an extra space so it displays properly --- a1/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/a1/README.md b/a1/README.md index ddff62db..f59aacc5 100644 --- a/a1/README.md +++ b/a1/README.md @@ -22,7 +22,8 @@ Many of my styles have been from the many pair programming sessions [Ward Bell]( ## See the Styles in a Sample App While this guide explains the *what*, *why* and *how*, I find it helpful to see them in practice. This guide is accompanied by a sample application that follows these styles and patterns. You can find the [sample application (named modular) here](https://github.com/johnpapa/ng-demos) in the `modular` folder. Feel free to grab it, clone it, or fork it. [Instructions on running it are in its readme](https://github.com/johnpapa/ng-demos/tree/master/modular). -##Translations +## Translations + [Translations of this Angular style guide](https://github.com/johnpapa/angular-styleguide/tree/master/a1/i18n) are maintained by the community and can be found here. ## Table of Contents From 54b8a8a028452605fc89a392257f11004b079a15 Mon Sep 17 00:00:00 2001 From: Santiago Petrone Date: Tue, 16 May 2017 06:16:17 +0200 Subject: [PATCH 109/114] Update es-ES.md (#820) * Update es-ES.md Fix typo, as commented in https://github.com/johnpapa/angular-styleguide/pull/816#issuecomment-283070773 The word is "archivos". "arhivos" doesn't exist. :) * Update es-ES.md Fix `previente` with `previene`, as pointed out in https://github.com/johnpapa/angular-styleguide/pull/787 --- a1/i18n/es-ES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/a1/i18n/es-ES.md b/a1/i18n/es-ES.md index 46ee923e..ea18ed3e 100644 --- a/a1/i18n/es-ES.md +++ b/a1/i18n/es-ES.md @@ -176,7 +176,7 @@ Mientras que esta guía explica el *qué*, *por qué* y *cómo*, me resulta úti - Nota: Para acortar únicamente, el resto de los ejemplos de esta guía podrían omitir la sintaxis IIFE. - - Nota: IIFE previente que el código de los tests llegue a sus variables privadas, como expresiones regulares o funciones de ayuda que normalmente vienen bien para hacer pruebas por sí solas. Sin embargo, puedes acceder a ellas creando accesorios o accediendo a través de sus componentes. Por ejemplo, poniendo las funciones de ayuda, expresiones regulares o constantes en su propia factoría. + - Nota: IIFE previene que el código de los tests llegue a sus variables privadas, como expresiones regulares o funciones de ayuda que normalmente vienen bien para hacer pruebas por sí solas. Sin embargo, puedes acceder a ellas creando accesorios o accediendo a través de sus componentes. Por ejemplo, poniendo las funciones de ayuda, expresiones regulares o constantes en su propia factoría. **[Volver arriba](#tabla-de-contenidos)** @@ -2711,7 +2711,7 @@ Usa Plantillas o snippets para ayudarte a seguir estilos consistentes o patrones ### WebStorm ###### [Style [Y252](#style-y252)] - - Snippets y arhivos de Angular que siguen estos estilos y directrices. Puedes importarlos en tus configuraciones de WebStorm: + - Snippets y archivos de Angular que siguen estos estilos y directrices. Puedes importarlos en tus configuraciones de WebStorm: - Descarga los [snippets y plantillas de Angular para WebStorm](assets/webstorm-angular-file-template.settings.jar?raw=true) - Abre WebStorm y ve al menú `File` From 153f3de75dc0b3475aed883938833246f7b984bf Mon Sep 17 00:00:00 2001 From: Isaac Durand Date: Tue, 15 Aug 2017 06:16:24 -0700 Subject: [PATCH 110/114] Render image of controller code inline (#837) * Render image of controller code inline * Render image of factory code inline * Prevent notes from rendering as code blocks --- a1/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/a1/README.md b/a1/README.md index f59aacc5..624c2f1c 100644 --- a/a1/README.md +++ b/a1/README.md @@ -489,7 +489,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see } ``` - ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) +![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-1.png) Note: If the function is a 1 liner consider keeping it right up top, as long as readability is not affected. @@ -834,7 +834,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see This way bindings are mirrored across the host object, primitive values cannot update alone using the revealing module pattern. - ![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) +![Factories Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/above-the-fold-2.png) ### Function Declarations to Hide Implementation Details ###### [Style [Y053](#style-y053)] @@ -980,7 +980,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see } ``` - Note: The data service is called from consumers, such as a controller, hiding the implementation from the consumers, as shown below. +Note: The data service is called from consumers, such as a controller, hiding the implementation from the consumers, as shown below. ```javascript /* recommended */ @@ -1153,7 +1153,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see } ``` - Note: There are many naming options for directives, especially since they can be used in narrow or wide scopes. Choose one that makes the directive and its file name distinct and clear. Some examples are below, but see the [Naming](#naming) section for more recommendations. +Note: There are many naming options for directives, especially since they can be used in narrow or wide scopes. Choose one that makes the directive and its file name distinct and clear. Some examples are below, but see the [Naming](#naming) section for more recommendations. ### Manipulate DOM in a Directive ###### [Style [Y072](#style-y072)] @@ -1314,7 +1314,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see
min={{vm.min}}
``` - Note: You can also name the controller when you inject it into the link function and access directive attributes as properties of the controller. +Note: You can also name the controller when you inject it into the link function and access directive attributes as properties of the controller. ```javascript // Alternative to above example @@ -1489,7 +1489,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see } ``` - Note: The example below shows the route resolve points to a named function, which is easier to debug and easier to handle dependency injection. +Note: The example below shows the route resolve points to a named function, which is easier to debug and easier to handle dependency injection. ```javascript /* even better */ @@ -1526,7 +1526,7 @@ While this guide explains the *what*, *why* and *how*, I find it helpful to see vm.movies = moviesPrepService.movies; } ``` - Note: The code example's dependency on `movieService` is not minification safe on its own. For details on how to make this code minification safe, see the sections on [dependency injection](#manual-annotating-for-dependency-injection) and on [minification and annotation](#minification-and-annotation). +Note: The code example's dependency on `movieService` is not minification safe on its own. For details on how to make this code minification safe, see the sections on [dependency injection](#manual-annotating-for-dependency-injection) and on [minification and annotation](#minification-and-annotation). **[Back to top](#table-of-contents)** From 5958711f26413c55a731e9597020d721d5a1f7c4 Mon Sep 17 00:00:00 2001 From: Jacob Ward Date: Mon, 25 Sep 2017 16:51:20 +0100 Subject: [PATCH 111/114] Webstorm live template to create a component (#843) * Webstorm live template to create a component * Add component to Webstorm live templates collection * Add missing comma after templateUrl * Add missing comma after templateUrl --- ...gular.component.webstorm-live-template.xml | 36 +++++++++++++++++++ .../webstorm-angular-live-templates.xml | 36 ++++++++++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 a1/assets/webstorm-angular-live-templates/angular.component.webstorm-live-template.xml diff --git a/a1/assets/webstorm-angular-live-templates/angular.component.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.component.webstorm-live-template.xml new file mode 100644 index 00000000..aa33e186 --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/angular.component.webstorm-live-template.xml @@ -0,0 +1,36 @@ + + + diff --git a/a1/assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml b/a1/assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml index 71188130..b1dc9d56 100644 --- a/a1/assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml +++ b/a1/assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml @@ -1,5 +1,39 @@ - +