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 4b788714..2c92c15b 100644 --- a/README.md +++ b/README.md @@ -1,3068 +1,32 @@ # Angular Style Guide -*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. - -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. - - [![AngularJs 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. - -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. - -## 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 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)] - - - Define 1 component per file. - - 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 */ - - // someController.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)] - - - 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. - - - Use `angular.module('app', []);` to set a module. - - Use `angular.module('app');` to get a module. - -### 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('Dashboard', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* recommended */ - - // dashboard.js - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard() { } - ``` - - ```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 them to a factory. Consider using `$scope` in a factory, or if in a controller just 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) consider moving these uses to a factory and invoke from the controller. - - ```javascript - /* avoid */ - function Customer($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommended - but see next section */ - function Customer() { - 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 Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommended */ - function Customer() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - 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. - - ```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); - }); - } - ``` - -### 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 Sessions() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* recommended */ - 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) - - Note: If the function is a 1 liner consider keeping it right up top, as long as readability is not affected. - - ```javascript - /* avoid */ - 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 - /* recommended */ - function Sessions(dataservice) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = dataservice.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 Avengers(dataservice, 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 dataservice.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 Avengers(dataservice, 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 dataservice.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 Order($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 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(showServiceError); - }; - } - ``` - -### 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](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. - -### 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](http://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('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; - }); - } - } - ``` - -### 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 */ - /* calendarRange.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 */ - /* customerInfo.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, - 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 for a Controller -### 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 use the controller, use a [route resolve](#style-y081) instead. - - ```javascript - /* avoid */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* recommended */ - 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; - }); - } - } - ``` - -### 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('Avengers', Avengers); - - function Avengers(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: '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; - } - ``` - - 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: '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: 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)** - -## 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('Dashboard', Dashboard); - - function Dashboard(common, dataservice) { - } - ``` - - This code may produce mangled variables when minified and thus cause runtime errors. - - ```javascript - /* avoid - not minification-safe*/ - angular.module('app').controller('Dashboard', 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('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* avoid */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* recommended */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function Dashboard($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('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storageService.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('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero() { - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - - Avengers.$inject = ['storageService', '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: 'Avengers', - 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.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. - - *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`. - - ```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 Names -###### [Style [Y125](#style-y125)] - - - Use consistent names for all factories named after their feature. Use camel-casing for services and factories. Avoid prefixing factories and services with `$`. - - *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. - - ```javascript - /** - * recommended - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger() { } - ``` - -### 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. +## Versions +There are multiple versions of Angular, and thus there are multiple versions of the guide. Choose your guide appropriately. - *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. +### Angular 1 Style Guide +[The Angular 1 Style Guide](https://github.com/johnpapa/angular-styleguide/tree/master/a1/README.md). -**[Back to top](#table-of-contents)** +### Angular 2 Style Guide +[The Angular 2 Style Guide](https://github.com/johnpapa/angular-styleguide/tree/master/a2/README.md). -## Application Structure +## Angular Team Endorsed +Special thanks to Igor Minar, lead on the Angular team, for reviewing, contributing feedback, and entrusting me to shepherd this guide. -### 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 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 a 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](http://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) (with [gulp-karma](https://github.com/lazd/gulp-karma)). - -### 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 affect 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://www.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://www.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, - - "validateJSDoc": { - "checkParamNames": true, - "requireParamTypes": 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 snippets and file templates that follow these styles and guidelines. You can import them into your WebStorm settings: - - - Download the [WebStorm Angular file templates and snippets](assets/webstorm-angular-file-template.settings.jar?raw=true) - - 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`: - - ```javascript - ng-c // creates an Angular controller - ng-f // creates an Angular factory - ng-m // creates an Angular module - ``` - -### 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 defintion - ngconfig // defines a configuration phase function - ngrun // defines a run phase function - ngroute // 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 - - ```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 -###### [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 +## Purpose +*Opinionated Angular style guide for teams by [@john_papa](//twitter.com/john_papa)* -###### [Style [Y420](#style-y420)] +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. - - Avoid using filters for scanning all properties of a complex object graph. Use filters for select properties. +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. - *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. +>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. -**[Back to top](#table-of-contents)** + [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) -## Angular docs -For anything else, API reference, check the [Angular documentation](//docs.angularjs.org/api). +## 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. ## 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.* @@ -3078,7 +42,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 @@ -3100,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..4a94f919 --- /dev/null +++ b/a1/README.md @@ -0,0 +1,3331 @@ +# 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/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. + +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 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 `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 */ + + // some.factory.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 Scopes +###### [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/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. + + ```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 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`. + + *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/a1/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 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`. + + *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 `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. + + 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`. + + Note: Lifecycle 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 +
+ ``` + + ```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; + vm.$onInit = onInit; + + ////////// + + console.log('CTRL: $scope.vm.min = %s', $scope.vm.min); + 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); // 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); + } + } + ``` + + ```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; + vm.$onInit = onInit; + + function onInit() = { + 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 + // *** + } + } + ``` + + ```javascript + /* 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 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. + + ```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/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. + + ```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/a1/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). + + - 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') + .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 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 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/a1/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, + "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 + } + } + ``` + +**[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 + ``` + +### 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 +###### [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/a1/assets/above-the-fold-1.png b/a1/assets/above-the-fold-1.png new file mode 100644 index 00000000..e6052bd1 Binary files /dev/null and b/a1/assets/above-the-fold-1.png differ 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 97% rename from assets/brackets-angular-snippets.yaml rename to a1/assets/brackets-angular-snippets.yaml index ec207d87..39c8a5bf 100644 --- a/assets/brackets-angular-snippets.yaml +++ b/a1/assets/brackets-angular-snippets.yaml @@ -102,10 +102,6 @@ /* @ngInject */ function ${2: directive}(${3:dependencies}) { - // Usage: - // - // Creates: - // var directive = { bindToController: true, controller: ${4:Controller}, @@ -171,9 +167,9 @@ scope: javascript text: | .state('${1:state}', { - url: '${2:/url}' + url: '${2:/url}', templateUrl: '${3:template}.html', - controller: '${4:Controller}' + controller: '${4:Controller}', controllerAs: '${5:vm}' })${6:} 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 diff --git a/a1/assets/gde.png b/a1/assets/gde.png new file mode 100644 index 00000000..caa39f01 Binary files /dev/null and b/a1/assets/gde.png differ 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/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 + diff --git a/assets/sublime-angular-snippets/angular.controller.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.controller.sublime-snippet similarity index 92% rename from assets/sublime-angular-snippets/angular.controller.sublime-snippet rename to a1/assets/sublime-angular-snippets/angular.controller.sublime-snippet index 5e3c7c5f..5a46e787 100644 --- a/assets/sublime-angular-snippets/angular.controller.sublime-snippet +++ b/a1/assets/sublime-angular-snippets/angular.controller.sublime-snippet @@ -23,5 +23,5 @@ })(); ]]> ngcontroller - source.js + text.plain, source.js diff --git a/assets/sublime-angular-snippets/angular.directive.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.directive.sublime-snippet similarity index 85% rename from assets/sublime-angular-snippets/angular.directive.sublime-snippet rename to a1/assets/sublime-angular-snippets/angular.directive.sublime-snippet index f2bcf719..a4bbd6a2 100644 --- a/assets/sublime-angular-snippets/angular.directive.sublime-snippet +++ b/a1/assets/sublime-angular-snippets/angular.directive.sublime-snippet @@ -9,7 +9,7 @@ ${2:directive}.\$inject = [${3/(\$(\w+)|\w+)/'$1'/g}]; /* @ngInject */ - function ${2:directive} (${3:dependencies}) { + function ${2:directive}(${3:dependencies}) { // Usage: // // Creates: @@ -30,11 +30,11 @@ } /* @ngInject */ - function ${4:Controller} () { + function ${4:Controller}() { } })(); ]]> ngdirective - source.js + text.plain, source.js diff --git a/assets/sublime-angular-snippets/angular.factory.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.factory.sublime-snippet similarity index 92% rename from assets/sublime-angular-snippets/angular.factory.sublime-snippet rename to a1/assets/sublime-angular-snippets/angular.factory.sublime-snippet index 94dab353..64ed5656 100644 --- a/assets/sublime-angular-snippets/angular.factory.sublime-snippet +++ b/a1/assets/sublime-angular-snippets/angular.factory.sublime-snippet @@ -23,5 +23,5 @@ })(); ]]> ngfactory - source.js + text.plain, source.js diff --git a/assets/sublime-angular-snippets/angular.filter.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.filter.sublime-snippet similarity index 90% rename from assets/sublime-angular-snippets/angular.filter.sublime-snippet rename to a1/assets/sublime-angular-snippets/angular.filter.sublime-snippet index 7e1bc54e..784c3673 100644 --- a/assets/sublime-angular-snippets/angular.filter.sublime-snippet +++ b/a1/assets/sublime-angular-snippets/angular.filter.sublime-snippet @@ -19,5 +19,5 @@ })(); ]]> ngfilter - source.js + text.plain, source.js diff --git a/assets/sublime-angular-snippets/angular.module.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.module.sublime-snippet similarity index 84% rename from assets/sublime-angular-snippets/angular.module.sublime-snippet rename to a1/assets/sublime-angular-snippets/angular.module.sublime-snippet index 663b8666..afb742f9 100644 --- a/assets/sublime-angular-snippets/angular.module.sublime-snippet +++ b/a1/assets/sublime-angular-snippets/angular.module.sublime-snippet @@ -9,5 +9,5 @@ })(); ]]> ngmodule - source.js + text.plain, source.js diff --git a/assets/sublime-angular-snippets/angular.service.sublime-snippet b/a1/assets/sublime-angular-snippets/angular.service.sublime-snippet similarity index 91% rename from assets/sublime-angular-snippets/angular.service.sublime-snippet rename to a1/assets/sublime-angular-snippets/angular.service.sublime-snippet index e980c4b1..c57ed25f 100644 --- a/assets/sublime-angular-snippets/angular.service.sublime-snippet +++ b/a1/assets/sublime-angular-snippets/angular.service.sublime-snippet @@ -20,5 +20,5 @@ })(); ]]> ngservice - source.js + text.plain, source.js 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/a1/assets/vim-angular-ultisnips/javascript_angular.controller.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.controller.snippets new file mode 100644 index 00000000..51cfdc16 --- /dev/null +++ b/a1/assets/vim-angular-ultisnips/javascript_angular.controller.snippets @@ -0,0 +1,22 @@ +snippet ngcontroller +(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() { + } + } +})(); +endsnippet diff --git a/a1/assets/vim-angular-ultisnips/javascript_angular.directive.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.directive.snippets new file mode 100644 index 00000000..14c882f5 --- /dev/null +++ b/a1/assets/vim-angular-ultisnips/javascript_angular.directive.snippets @@ -0,0 +1,35 @@ +snippet ngdirective +(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() { + + } +})(); +endsnippet diff --git a/a1/assets/vim-angular-ultisnips/javascript_angular.factory.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.factory.snippets new file mode 100644 index 00000000..41966ff7 --- /dev/null +++ b/a1/assets/vim-angular-ultisnips/javascript_angular.factory.snippets @@ -0,0 +1,22 @@ +snippet ngfactory +(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() { + } + } +})(); +endsnippet diff --git a/a1/assets/vim-angular-ultisnips/javascript_angular.filter.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.filter.snippets new file mode 100644 index 00000000..e2b10ef6 --- /dev/null +++ b/a1/assets/vim-angular-ultisnips/javascript_angular.filter.snippets @@ -0,0 +1,19 @@ +snippet ngfilter +(function() { + 'use strict'; + + angular + .module('${1:module}') + .filter('${2:filter}', $2); + + function $2() { + return $2Filter; + + //////////////// + function $2Filter(${3:params}) { + return $3; + }; + } + +})(); +endsnippet diff --git a/a1/assets/vim-angular-ultisnips/javascript_angular.module.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.module.snippets new file mode 100644 index 00000000..972018df --- /dev/null +++ b/a1/assets/vim-angular-ultisnips/javascript_angular.module.snippets @@ -0,0 +1,10 @@ +snippet ngmodule +(function() { + 'use strict'; + + angular + .module('${1:module}', [ + '${2:dependencies}' + ]); +})(); +endsnippet diff --git a/a1/assets/vim-angular-ultisnips/javascript_angular.service.snippets b/a1/assets/vim-angular-ultisnips/javascript_angular.service.snippets new file mode 100644 index 00000000..53a8692d --- /dev/null +++ b/a1/assets/vim-angular-ultisnips/javascript_angular.service.snippets @@ -0,0 +1,19 @@ +snippet ngservice +(function() { + 'use strict'; + + angular + .module('${1:module}') + .service('${2:Service}', $2); + + /* @ngInject */ + function $2(${3:dependencies}) { + this.${4:func} = $4; + + //////////////// + + function $4() { + } + } +})(); +endsnippet diff --git a/a1/assets/vscode-snippets/javascript.json b/a1/assets/vscode-snippets/javascript.json new file mode 100644 index 00000000..0b1920ad --- /dev/null +++ b/a1/assets/vscode-snippets/javascript.json @@ -0,0 +1,124 @@ +{ + "Angular Controller": { + "prefix": "ngcontroller", + "body": [ + "(function() {", + "'use strict';", + "", + "\tangular", + "\t\t.module('${Module}')", + "\t\t.controller('${Controller}Controller', ${Controller}Controller);", + "", + "\t${Controller}Controller.$inject = ['${dependency1}'];", + "\tfunction ${Controller}Controller(${dependency1}) {", + "\t\tvar vm = this;", + "\t\t$0", + "", + "\t\tactivate();", + "", + "\t\t////////////////", + "", + "\t\tfunction activate() { }", + "\t}", + "})();" + ], + "description": "Angular 1 controller" + }, + "Angular Service": { + "prefix": "ngservice", + "body": [ + "(function() {", + "'use strict';", + "", + "\tangular", + "\t\t.module('${Module}')", + "\t\t.service('${Service}', ${Service});", + "", + "\t${Service}.$inject = ['${dependency1}'];", + "\tfunction ${Service}(${dependency1}) {", + "\t\tthis.${exposedFn} = ${exposedFn};", + "\t\t$0", + "\t\t////////////////", + "\t\tfunction ${exposedFn}() { }", + "\t}", + "})();" + ], + "description": "Angular 1 service" + }, + "Angular Factory": { + "prefix": "ngfactory", + "body": [ + "(function() {", + "'use strict';", + "", + "\tangular", + "\t\t.module('${Module}')", + "\t\t.factory('${Service}', ${Service});", + "", + "\t${Service}.$inject = ['${dependency1}'];", + "\tfunction ${Service}(${dependency1}) {", + "\t\tvar service = {", + "\t\t\t${exposedFn}:${exposedFn}", + "\t\t};", + "\t\t$0", + "\t\treturn service;", + "", + "\t\t////////////////", + "\t\tfunction ${exposedFn}() { }", + "\t}", + "})();" + ], + "description": "Angular 1 factory" + }, + "Angular Directive": { + "prefix": "ngdirective", + "body": [ + "(function() {", + "\t'use strict';", + "", + "\tangular", + "\t\t.module('${Module}')", + "\t\t.directive('${Directive}', ${Directive});", + "", + "\t${Directive}.$inject = ['${dependency1}'];", + "\tfunction ${Directive}(${dependency1}) {", + "\t\t// Usage:", + "\t\t//", + "\t\t// Creates:", + "\t\t//", + "\t\tvar directive = {", + "\t\t bindToController: true,", + "\t\t controller: ${Controller}Controller,", + "\t\t controllerAs: '${vm}',", + "\t\t link: link,", + "\t\t restrict: 'A',", + "\t\t scope: {", + "\t\t }", + "\t\t};", + "\t\treturn directive;", + "\t\t", + "\t\tfunction link(scope, element, attrs) {", + "\t\t}", + "\t}", + "\t/* @ngInject */", + "\tfunction ${Controller}Controller () {", + "\t\t$0", + "\t}", + "})();" + ], + "description": "Angular 1 directive" + }, + "Angular Module": { + "prefix": "ngmodule", + "body": [ + "(function() {", + "\t'use strict';", + "", + "\tangular.module('${module}', [", + "\t\t$0", + "\t]);", + "})();" + ], + "description": "Angular 1 module" + } +} \ No newline at end of file diff --git a/a1/assets/vscode-snippets/typescript.json b/a1/assets/vscode-snippets/typescript.json new file mode 100644 index 00000000..716dc70e --- /dev/null +++ b/a1/assets/vscode-snippets/typescript.json @@ -0,0 +1,64 @@ +{ + "Angular Controller": { + "prefix": "ngcontroller", + "body": [ + "namespace ${module} {", + "'use strict';", + "", + "export class ${Controller}Controller {", + "\tstatic $inject: Array = ['${dependency1}'];", + "\tconstructor(private ${dependency1}: ${dependency1Type}) {", + "$0", + "}", + "", + "\t${property}: ${propertyType} = ${propertyValue};", + "", + "\t${fn}() { }", + "}", + "", + "angular", + "\t.module('${Module}')", + "\t.controller('${Controller}Controller', ${Controller}Controller);", + "}" + ] + }, + "Angular Service": { + "prefix": "ngservice", + "body": [ + "namespace ${module} {", + "'use strict';", + "", + "export interface I${Service} {", + "\t${serviceFn}: (${dependency1}:${dependency1Type}) => ${returnType};", + "}", + "export class ${Service} implements I${Service} {", + "\tstatic $inject: Array = ['${dependency1}'];", + "\tconstructor(private ${dependency1}: ${dependency1Type}) {", + "", + "}", + "", + "\t${serviceFn}: (${dependency1}:${dependency1Type}) => ${returnType} = (${dependency1}:${dependency1Type}) => {", + "\t\t$0", + "\t}", + "", + "}", + "", + "angular", + "\t.module('${Module}')", + "\t.service('${Service}', ${Service});", + "}" + ] + }, + "Angular Module": { + "prefix": "ngmodule", + "body": [ + "namespace ${module} {", + "\t'use strict';", + "", + "\tangular.module('${module}', [", + "\t$0", + "\t]);", + "}" + ] + } +} \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.app.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.app.webstorm-live-template.xml new file mode 100644 index 00000000..c0e9ea43 --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/angular.app.webstorm-live-template.xml @@ -0,0 +1,33 @@ + + + \ No newline at end of file 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/angular.config.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.config.webstorm-live-template.xml new file mode 100644 index 00000000..61e73945 --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/angular.config.webstorm-live-template.xml @@ -0,0 +1,34 @@ + + + \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.controller.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.controller.webstorm-live-template.xml new file mode 100644 index 00000000..566b434c --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/angular.controller.webstorm-live-template.xml @@ -0,0 +1,35 @@ + + + \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.directive.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.directive.webstorm-live-template.xml new file mode 100644 index 00000000..a55e2e0c --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/angular.directive.webstorm-live-template.xml @@ -0,0 +1,37 @@ + + + diff --git a/a1/assets/webstorm-angular-live-templates/angular.factory.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.factory.webstorm-live-template.xml new file mode 100644 index 00000000..761b56f6 --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/angular.factory.webstorm-live-template.xml @@ -0,0 +1,36 @@ + + + \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.filter.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.filter.webstorm-live-template.xml new file mode 100644 index 00000000..fa4c92f8 --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/angular.filter.webstorm-live-template.xml @@ -0,0 +1,34 @@ + + + \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.module.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.module.webstorm-live-template.xml new file mode 100644 index 00000000..ba57b16f --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/angular.module.webstorm-live-template.xml @@ -0,0 +1,31 @@ + + + \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.route.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.route.webstorm-live-template.xml new file mode 100644 index 00000000..3955ec12 --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/angular.route.webstorm-live-template.xml @@ -0,0 +1,35 @@ + + + \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.run.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.run.webstorm-live-template.xml new file mode 100644 index 00000000..8941f99b --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/angular.run.webstorm-live-template.xml @@ -0,0 +1,34 @@ + + + \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.service.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.service.webstorm-live-template.xml new file mode 100644 index 00000000..445893a4 --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/angular.service.webstorm-live-template.xml @@ -0,0 +1,36 @@ + + + \ No newline at end of file diff --git a/a1/assets/webstorm-angular-live-templates/angular.state.webstorm-live-template.xml b/a1/assets/webstorm-angular-live-templates/angular.state.webstorm-live-template.xml new file mode 100644 index 00000000..9dcf5bbf --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/angular.state.webstorm-live-template.xml @@ -0,0 +1,36 @@ + + + \ No newline at end of file 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 new file mode 100644 index 00000000..b1dc9d56 --- /dev/null +++ b/a1/assets/webstorm-angular-live-templates/webstorm-angular-live-templates.xml @@ -0,0 +1,395 @@ + + + + + + + + + + + + + + diff --git a/a1/i18n/README.md b/a1/i18n/README.md new file mode 100644 index 00000000..40e24863 --- /dev/null +++ b/a1/i18n/README.md @@ -0,0 +1,41 @@ +#Translations + +The [original English version](http://jpapa.me/ngstyles) is the source of truth, as it is maintained and updated first. + +*All translations are created by and maintained by the community.* + + 1. [French](fr-FR.md) by [Eric Le Merdy](https://github.com/ericlemerdy) and [Xavier Haniquaut] (@xavhan) + 1. [German](de-DE.md) by [Michael Seeger](https://github.com/miseeger), [Sascha Hagedorn](https://github.com/saesh) and [Johannes Weber](https://github.com/johannes-weber) + 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. [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 [Youngjae 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. + +All translations must preserve the intention of the original document. + +> All contributions fall under the [MIT License of this repository](https://github.com/johnpapa/angularjs-styleguide#license). In other words, you would be providing these free to the community. + +### New Translations +1. Fork the repository +2. Create a translation file and name it using the 118n standard format. +3. Put this file in the i18n folder +4. Translate the original English version to be current with the latest changes +3. Make a Pull Request + +Once you do these I will merge, point the translation links to it, and enter the translation credit to you. + +### Updated Translations +1. Fork the repository +2. Make the translation changes +3. Make a Pull Request + +Once you do these I will merge, point the translation links to it, and enter the translation credit to you. + diff --git a/i18n/de-DE.md b/a1/i18n/de-DE.md similarity index 87% rename from i18n/de-DE.md rename to a1/i18n/de-DE.md index e43b8717..99be9755 100644 --- a/i18n/de-DE.md +++ b/a1/i18n/de-DE.md @@ -2,25 +2,21 @@ *Dogmatischer Angular Styleguide für Teams von John Papa [@john_papa](//twitter.com/john_papa)* -*Translated by [miseeger](https://github.com/miseeger)* - ->The [original English version](http://jpapa.me/ngstyles) is the source of truth, as it is maintained and updated first. - -Sind Sie auf der Suche nach einem dogmatischen Styleguide zur Syntax, zu Konventionen und zur Struktur von Angular-Anwendungen, dann treten sie näher. Diese Vorlagen basieren auf meinen Erfahrungen mit [Angular](//angularjs.org), Präsentationen, [Pluralsight Trainingskursen](http://pluralsight.com/training/Authors/Details/john-papa) und der Arbeit in Teams. +Sind Sie auf der Suche nach einem dogmatischen Styleguide zur Syntax, zu Konventionen und zur Struktur von Angular-Anwendungen, dann treten Sie näher. Diese Vorlagen basieren auf meinen Erfahrungen mit [Angular](//angularjs.org), Präsentationen, [Pluralsight Trainingskursen](http://pluralsight.com/training/Authors/Details/john-papa) und der Arbeit in Teams. Der Zweck dieses Styleguides ist es, eine Anleitung für die Erstellung von Angular-Anwendungen bereitzustellen, indem ich die Konventionen, die ich nutze, zeige und - wichtiger als das - beschreibe, warum ich sie wähle. ->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 dartellt. +>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 and 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ühr für seinen Ansatz zu entwickeln und ihn vergleichen zu können. +## 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. Viele meiner Vorlagen entstanden aus Pair-Programming-Sessions, die [Ward Bell](http://twitter.com/wardbell) und ich hatten. Mein Freund Ward hat sicherlich die endgültige Entwicklung dieser Anleitung beeinflusst. ## Schauen Sie sich die Vorlagen in einer Beispielanwendung an -Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebenso hilfreich, sie auch in der Praxis zu sehen. Diese Anleitung wird von einer Beispielanwendung begleitet, die diesen Vorlagen und Mustern folgt. Sie finden die [Beispielanwendung (namens "modular") hier] (https://github.com/johnpapa/ng-demos) im `modular`-Ordner. Fühlen Sie sich frei, sich diese zu holen, indem Sie sie clonen oder einen Fork erstellen.[Anweisungen, sie zum Laufen zu bringen, finden Sie im Readme](https://github.com/johnpapa/ng-demos/tree/master/modular). +Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebenso hilfreich, sie auch in der Praxis zu sehen. Diese Anleitung wird von einer Beispielanwendung begleitet, die diesen Vorlagen und Mustern folgt. Sie finden die [Beispielanwendung (namens "modular") hier] (https://github.com/johnpapa/ng-demos) im `modular`-Ordner. Fühlen Sie sich frei, diese zu holen, indem Sie sie clonen oder einen Fork erstellen.[Anweisungen, sie zum Laufen zu bringen, finden Sie im Readme](https://github.com/johnpapa/ng-demos/tree/master/modular). ##Übersetzungen [Übersetzungen dieses Styleguides](https://github.com/johnpapa/angular-styleguide/tree/master/i18n) werden von der Community hier verwaltet. @@ -29,36 +25,36 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe 1. [Single Responsibility](#single-responsibility) 1. [IIFE](#iife) - 1. [Module](#modules) - 1. [Controller](#controllers) + 1. [Module](#module) + 1. [Controller](#controller) 1. [Services](#services) 1. [Factories](#factories) - 1. [Dataservices](#data-services) - 1. [Direktiven](#directives) - 1. [Promises für einen Controller auflösen](#resolving-promises-for-a-controller) - 1. [Manuelle Code-Anmerkungen für das Einfügen von Abhängigkeiten (Dependency Injection)](#manual-annotating-for-dependency-injection) - 1. [Minifizierung und Code-Anmerkungen](#minification-and-annotation) - 1. [Fehlerbehandlung](#exception-handling) - 1. [Namensgebung](#naming) - 1. [Anwendungsstruktur: LIFT Prinzip](#application-structure-lift-principle) - 1. [Anwendungsstruktur](#application-structure) - 1. [Modularität](#modularity) - 1. [Startlogik](#startup-logic) + 1. [Datenservices](#datenservices) + 1. [Direktiven](#direktiven) + 1. [Promises für einen Controller auflösen](#promises-für-einen-controller-auflösen) + 1. [Manuelle Code-Anmerkungen für das Einfügen von Abhängigkeiten (Dependency Injection)](#manuelle-code-anmerkungen-für-das-einfügen-von-abhängigkeiten-dependency-injection) + 1. [Minifizierung und Code-Anmerkungen](#minifizierung-und-code-anmerkungen) + 1. [Fehlerbehandlung](#fehlerbehandlung) + 1. [Namensgebung](#namensgebung) + 1. [Anwendungsstruktur: LIFT Prinzip](#anwendungsstruktur-das-lift-prinzip) + 1. [Anwendungsstruktur](#anwendungsstruktur) + 1. [Modularität](#modularität) + 1. [Startlogik](#startlogik) 1. [Angular $ Wrapper Services](#angular--wrapper-services) - 1. [Testen](#testing) - 1. [Animationen](#animations) - 1. [Kommentare](#comments) + 1. [Testen](#testen) + 1. [Animationen](#animationen) + 1. [Kommentare](#kommentare) 1. [JS Hint](#js-hint) 1. [JSCS](#jscs) - 1. [Konstanten](#constants) - 1. [Dateitemplates und Snippets](#file-templates-and-snippets) + 1. [Konstanten](#konstanten) + 1. [Dateitemplates und Snippets](#dateitemplates-und-snippets) 1. [Yeoman Generator](#yeoman-generator) 1. [Routing](#routing) - 1. [Automatisierung von Aufgaben](#task-automation) - 1. [Filter](#filters) - 1. [Angular Dokumentation](#angular-docs) - 1. [Beiträge](#contributing) - 1. [Lizenz](#license) + 1. [Automatisierung von Aufgaben](#automatisierung-von-aufgaben) + 1. [Filter](#filter) + 1. [Angular Dokumentation](#angular-dokumentation) + 1. [Beiträge](#beiträge) + 1. [Lizenz](#lizenz) ## Single Responsibility @@ -67,7 +63,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Definiere eine Komponente pro Datei. - Das folgende Beispoiel definiert das `app`-Modul und seine Abhängigkeiten, einen Controller und eine Factory in ein und derselben Datei. + Das folgende Beispiel definiert das `app`-Modul und seine Abhängigkeiten, einen Controller und eine Factory in ein und derselben Datei. ```javascript /* zu vermeiden */ @@ -119,12 +115,12 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### JavaScript Closures ###### [Style [Y010](#style-y010)] - - Packen sie Angular-Komponenten in eine Funktion, die sich sofort selbst ausführt (Immediately Invoked Function Expression, kurz: IIFE). + - Packen Sie Angular-Komponenten in eine Funktion, die sich sofort selbst ausführt (Immediately Invoked Function Expression, kurz: IIFE). + + *Warum?*: Eine IIFE entfernt Variablen aus dem global scope. Dies verhindert, dass Variablen- und Funktionsdeklarationen länger als erwartet im global scope bleiben. Und es verhindert zusätzlich, Kollisionen bei Variablen zu verhindern. + + *Warum?*: Wird Ihr Code für das Deployment auf einem Produktionsserver minifiziert und in einer einzigen Datei zusammengepackt, kann es zur Kollision von Variablen (auch globalen) kommen. Eine IIFE schützt Sie hiervor, indem sie den Gültigkeitsbereich der Variablen auf die jeweilige Datei beschränkt. - *Warum?*: Eine IIFE entfernt Variablen aus dem Global Scope. Dies verhindert, dass Variablen- und Funktionsdeklarationen länger als erwartet im global scope bleiben. Und es verhindert zusätzlich, Kollisionen bei Variablen zu verhindern. - - *Warum?*: Wird Ihr Code für das Deployment auf einem Produktionsserver minifiziert und in einer einzigen Datei zusammengepackt, kann es zur Kollision von Variablen (auch Globalen) kommen. Eine IIFE schützt Sie hiervor, indem sie den Gültigkeitsbereich der Variablen auf die jeweilige Datei beschränkt. - ```javascript /* zu vermeiden */ // logger.js @@ -185,16 +181,16 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Namenskollisionen vermeiden ###### [Style [Y020](#style-y020)] - - Benutzen sie eindeutige Namenskonventionen mit Trennzeichen für Untermodule. + - Benutzen Sie eindeutige Namenskonventionen mit Trennzeichen für Untermodule. - *Warum?*: Eindeutige Namen helfen, Kollisionen bei Modulnamen zu verhindern. Trennzeichen helfen, bei der Definition von Modulen und deren Untermodul-Hierarchie. Zum Beispiel kann `app` Ihr Root-Modul sein, während `app.dashboard` und `app.users` Module sein können, die von `app` als Abhängigkeiten genutzt werden. + *Warum?*: Eindeutige Namen helfen Kollisionen bei Modulnamen zu verhindern. Trennzeichen helfen bei der Definition von Modulen und deren Untermodul-Hierarchie. Zum Beispiel kann `app` Ihr Root-Modul sein, während `app.dashboard` und `app.users` Module sein können, die von `app` als Abhängigkeiten genutzt werden. ### Definitionen (auch: Setter) ###### [Style [Y021](#style-y021)] - Deklarieren Sie Module ohne eine Variable, indem Sie die Setter-Syntax verwenden. - *Warum?*: Bei einer Komponente pro Datei besteht kaum die Notwendigkeit, eine Variable für das Modul einzuführen. + *Warum?*: Bei einer Komponente pro Datei besteht kaum die Notwendigkeit eine Variable für das Modul einzuführen. ```javascript /* zu vermeiden */ @@ -224,7 +220,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Wenn Sie ein Modul nutzen, vermeiden Sie die Nutzung einer Variablen. Nutzen Sie stattdessen eine Verkettung mit der Getter-Syntax. - *Warum?*: Dies führt zu mehr lesbarem Code und verhindert Variablenkollistionen oder Leaks. + *Warum?*: Dies führt zu mehr lesbarem Code und verhindert Variablenkollisionen oder Leaks. ```javascript /* zu vermeiden */ @@ -248,7 +244,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Nur einmal setzen und für alle anderen Instanzen lesen (get). - *Warum?*: Ein Modul sollte nur einmal erstellt werden, ab diesem Punkt an nur noch gelesen werden. + *Warum?*: Ein Modul sollte nur einmal erstellt werden und ab diesem Punkt an nur noch gelesen werden. - Benutzen Sie `angular.module('app', []);` um das Modul zu erzeugen (set). - Benutzen Sie `angular.module('app');` um das Modul zu erhalten (get). @@ -258,7 +254,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Benutzen Sie für Callbacks benannte Funktionen, anstatt eine anonyme Funktion zu übergeben. - *Warum?*: Dies führt zu lesbarerem Code, ist einfach zu debuggen und veringert die Schachtelung des Callback-Codes. + *Warum?*: Dies führt zu lesbarerem Code, ist einfach zu debuggen und verringert die Schachtelung des Callback-Codes. ```javascript /* zu vermeiden */ @@ -297,11 +293,11 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Ziehen Sie die [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/)-Syntax der `klassischen Controller-Mit-$scope`-Syntax vor. - *Warum?*: Controller werden immer wieder neu erstellt. Man erhält jedes Mal eine neue Instanz und die `controllerAs`-Syntax ist näher an der eines JavaScript-Konstruktors, als die`klassische Controller-Mit-$scope-Syntax`. + *Warum?*: Controller werden immer wieder neu erstellt. Man erhält jedes Mal eine neue Instanz und die `controllerAs`-Syntax ist näher an der eines JavaScript-Konstruktors, als die `klassische Controller-Mit-$scope-Syntax`. - *Warum?*: Es begünstigt die Bindung von "Punkt-Notierten" Objekten in der View (z. B. `customer.name` statt `name`), was kontextbezogener und einfacher zu lesen ist und Referenzproblemen, die ohne diese "Punkt-Notation" auftreten können, vorbeugt. + *Warum?*: Es begünstigt die Bindung von "Punkt-Notierten" Objekten im View (z. B. `customer.name` statt `name`), was kontextbezogener und einfacher zu lesen ist und Referenzproblemen, die ohne diese "Punkt-Notation" auftreten können, vorbeugt. - *Warum?*: Hilft, die Nutzung von `$parent`-Aufrufen in Views und geschachtelten Controllern zu vermeiden. + *Warum?*: Hilft die Nutzung von `$parent`-Aufrufen in Views und geschachtelten Controllern zu vermeiden. ```html @@ -324,7 +320,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Die `controllerAs`-Syntax nutzt `this` innerhalb des Controllers, welches an `$scope` gebunden wird. - *Warum?*: `controllerAs` stellt eine syntaktische "Versüßung" `$scope` dar. Sie können immer noch Bindungen an die View vornehmen und auf die `$scope`-Methoden zugreifen. + *Warum?*: `controllerAs` stellt eine syntaktische "Versüßung" `$scope` dar. Sie können immer noch Bindungen an dem View vornehmen und auf die `$scope`-Methoden zugreifen. *Warum?*: Hilft, die verführerische Nutzung von `$scope`-Methoden innerhalb eines Controllers zu unterbinden, wenn es besser wäre, sie zu vermeiden oder in eine Factory auszulagern. Man sollte die Nutzung von `$scope` in einer Factory oder einem Controller nur dann in Erwägung ziehen, wenn es notwendig ist. Wenn zum Beispiel Events mit [`$emit`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast) oder [`$on`](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on) abonniert oder publiziert werden, sollte man überlegen, diese nicht in eine Factory auszulagern und vom Controller aus auszulösen. @@ -347,7 +343,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### controllerAs mit vm ###### [Style [Y032](#style-y032)] - - Benutzen Sie eine Variable, um `this` zu übernehmen, wenn sie die `controllerAs`-Syntax verwenden. Wählen Sie einen konsistenten Variablennanen, wie `vm`, welcher für ViewModel steht. + - Benutzen Sie eine Variable, um `this` zu übernehmen, wenn Sie die `controllerAs`-Syntax verwenden. Wählen Sie einen konsistenten Variablennamen, wie `vm`, welcher für ViewModel steht. *Warum?*: Das `this`-Schlüsselwort ist kontextbezogen und kann diesen Kontext ändern, wenn es innerhalb einer Funktion eines Controllers verwendet wird. Wird der Kontext von `this` übernommen, wird dieses Problem verhindert. @@ -368,14 +364,14 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe } ``` - Anmerkung: Sie können jegliche [jshint](http://www.jshint.com/)-Warnungen unterbinden, indem sie den Kommentar vor der Codezeile einfügen. Allerdingst ist dies nicht notwendig, wenn die Funktion großgeschrieben ist (UpperCasing). Diese Konvention besagt, dass es sich um eine Kontruktor-Funktion handelt, was einem Controller in Angular entspricht. + Anmerkung: Sie können jegliche [jshint](http://www.jshint.com/)-Warnungen unterbinden, indem Sie den Kommentar vor der Codezeile einfügen. Allerdingst ist dies nicht notwendig, wenn die Funktion großgeschrieben ist (UpperCasing). Diese Konvention besagt, dass es sich um eine Konstruktor-Funktion handelt, was einem Controller in Angular entspricht. ```javascript /* jshint validthis: true */ var vm = this; ``` - Anmerkung: Wenn Sie Watches in einem Controller einsetzen, der über `controller as` genutzt wrid, können Sie die `vm.*`-Member über die folgende Syntax überwachen. (Erstellen Sie Watches mit Vorsicht, denn sie belasten den "digest cycle".) + Anmerkung: Wenn Sie Watches in einem Controller einsetzen, der über `controller as` genutzt wird, können Sie die `vm.*`-Member über die folgende Syntax überwachen. (Erstellen Sie Watches mit Vorsicht, denn sie belasten den "digest cycle".) ```html @@ -396,11 +392,11 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Zu bindende Bestandteile nach oben ###### [Style [Y033](#style-y033)] - - Platzieren sie zu bindende Elemente alphabeisch sortiert am Anfang des Controllers und nicht verteilt im Code des Controllers. + - Platzieren Sie zu bindende Elemente alphabetisch sortiert am Anfang des Controllers und nicht verteilt im Code des Controllers. - *Warum?*: Die Platzierung von zu bindenden Elementen am Anfang verbessert die Lesbarkeit und hilft Ihnen, die zur Bindung und Nutzung in einer View vorgesehenen Elemente des Controllers schnell zu identifizieren. + *Warum?*: Die Platzierung von zu bindenden Elementen am Anfang verbessert die Lesbarkeit und hilft Ihnen, die zur Bindung und Nutzung in einem View vorgesehenen Elemente des Controllers schnell zu identifizieren. - *Warum?*: Anonyme Funktionen einzusetzen kann einfach sein, aber wenn diese Funktionen die Länge von einer Zeile überschreiten, wird die Lesbarkeit des Codes verschlechtert. Die Definition der Funktionen unterhalb der Deklaration der zur Bindung vorgesehenen Elemente verschiebt die Details der Implementierung nach unten, hält die zu Bindenden Elemente ganz oben und macht es lesbarer (die Funktionen werden quasi "hochgezogen"). + *Warum?*: Anonyme Funktionen einzusetzen kann einfach sein, aber wenn diese Funktionen die Länge von einer Zeile überschreiten, wird die Lesbarkeit des Codes verschlechtert. Die Definition der Funktionen unterhalb der Deklaration der zur Bindung vorgesehenen Elemente verschiebt die Details der Implementierung nach unten, hält die zu bindenden Elemente ganz oben und macht es lesbarer (die Funktionen werden quasi "hochgezogen"). ```javascript /* zu vermeiden */ @@ -446,9 +442,9 @@ 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 verlagen, so lange die Lesbarkeit nicht betroffen ist. + 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. ```javascript /* zu vermeiden */ @@ -485,13 +481,13 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Funktionsdeklarationen, um Details der Implementierung zu verbergen ###### [Style [Y034](#style-y034)] - - Nutzen Sie Funktionsdeklarationen, um Implementierungsdetails zu verbergen. Halten Sie Ihre zur Bindung vorgesehenen Elemente oben. Wenn sie eine Controller-Funktion zur Bindung vorsehen müssen, dann lassen Sie diese auf die Funktionsdeklaration zeigen, die weiter unten erscheint. Diese wird direkt an den Abschnitt mit den zur Bindung vorgesehenen Element geknüpft. Mehr erfahren sie hier in [diesem Beitrag](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). + - Nutzen Sie Funktionsdeklarationen, um Implementierungsdetails zu verbergen. Halten Sie Ihre zur Bindung vorgesehenen Elemente oben. Wenn Sie eine Controller-Funktion zur Bindung vorsehen müssen, dann lassen Sie diese auf die Funktionsdeklaration zeigen, die weiter unten erscheint. Diese wird direkt an den Abschnitt mit den zur Bindung vorgesehenen Element geknüpft. Mehr erfahren Sie hier in [diesem Beitrag](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - *Warum?*: Die zur Bindung vorgesehenen Elemente am Anfang zu platzieren, erhöht die Lesbarkeit und hilft Ihnen die Elemente zu identifizieren, die gebunden und in einer View genutzt werden können. (Das Gleiche, wie zuvor.) + *Warum?*: Die zur Bindung vorgesehenen Elemente am Anfang zu platzieren, erhöht die Lesbarkeit und hilft Ihnen die Elemente zu identifizieren, die gebunden und in einem View genutzt werden können. (Das Gleiche, wie zuvor.) - *Warum?*: Das Platzieren der Implementierungsdetails einer Funktion weiter unten in der Datei, hält diese Codemenge außer Sicht und Sie sehen die wichtigen Dinge am Anfang. + *Warum?*: Das Platzieren der Implementierungsdetails einer Funktion weiter unten in der Datei hält diese Codemenge außer Sicht und Sie sehen die wichtigen Dinge am Anfang. - *Warum?*: Funktionsdeklarationen werden "nach oben gezogen" (sog. Hoisting), so dass es keine Probleme damit gibt, ob eine Funktion vor ihrer Benutzung deklariert werden sollte (wie es bei Funktiosausdrücken der Fall wäre). + *Warum?*: Funktionsdeklarationen werden "nach oben gezogen" (sog. Hoisting), so dass es keine Probleme damit gibt, ob eine Funktion vor ihrer Benutzung deklariert werden sollte (wie es bei Funktionsausdrücken der Fall wäre). *Warum?*: Sie müssen sich niemals Sorgen darum machen, wenn Sie in Funktionsdeklarationen `var a` vor `var b` platzieren, weil `a` von `b` abhängig ist. @@ -499,7 +495,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ```javascript /** - * avoid + * zu vermeiden * Nutzung von Funktionsausdrücken. */ function Avengers(dataservice, logger) { @@ -557,7 +553,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe } ``` -### Verlagern Sie Controller-Logik in Services +### Verlagern Sie Controller-Logik in Services ###### [Style [Y035](#style-y035)] - Verlagern Sie die Logik eines Controllers, indem Sie diese in Services oder Factories übertragen. @@ -620,22 +616,22 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Halten Sie die Controller auf ihre Aufgabe ausgerichtet ###### [Style [Y037](#style-y037)] - - Definieren Sie einen Controller für eine View und versuchen Sie nicht, diesen Controller für weitere Views zu verwenden. Verlagern Sie stattdessen wiederzuverwendende Logik in Factories und halten Sie den Controller einfach und ausgerichtet auf seine View. + - Definieren Sie einen Controller für einen View und versuchen Sie nicht, diesen Controller für weitere Views zu verwenden. Verlagern Sie stattdessen wiederzuverwendende Logik in Factories und halten Sie den Controller einfach und ausgerichtet auf seinen View. - *Warum?*: Controller in mehreren Views wiederzuverwenden ist kritisch und bedingt eine gute End-Zu-End (e2e) Testabdeckung, um die Stabilität in großen Anwendungen zu garantieren. + *Warum?*: Controller in mehreren Views wiederzuverwenden ist kritisch und bedingt eine gute End-Zu-End (e2e) Testabdeckung, um die Stabilität in großen Anwendungen zu garantieren. ### Controller zuweisen ###### [Style [Y038](#style-y038)] - - Wenn ein Controller mit einer View verbunden werden muss und eine der beiden Komponenten aber von anderen Controllern oder Views wiederverwendet werden muss, dann sollten die Controller bei ihren Routen definiert werden. + - Wenn ein Controller mit einem View verbunden werden muss und eine der beiden Komponenten aber von anderen Controllern oder Views wiederverwendet werden muss, dann sollten die Controller bei ihren Routen definiert werden. Anmerkung: Sollte eine View in einem anderen Kontext als einer Route geladen werden, dann benutzen Sie die `ng-controller="Avengers as vm"`-Syntax. - *Warum?*: Wird der Controller inerhalb einer Route verbunden, dann ist es möglich, dass unterschiedliche Routen auch unterschiedliche Controller-View-Bindungen verwenden können. Sind Controller in einer View mit [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController) angebunden, dann ist diese View immer mit dem gleichen Controller verbunden. + *Warum?*: Wird der Controller innerhalb einer Route verbunden, dann ist es möglich, dass unterschiedliche Routen auch unterschiedliche Controller-View-Bindungen verwenden können. Sind Controller in einer View mit [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController) angebunden, dann ist diese View immer mit dem gleichen Controller verbunden. ```javascript - /* zu vermeiden - bei Nutzung mit eiuner Route, wenn eine dynamische Verbindung gewünscht ist */ + /* zu vermeiden - bei Nutzung mit einer Route, wenn eine dynamische Verbindung gewünscht ist */ // route-config.js angular @@ -687,7 +683,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Singletons ###### [Style [Y040](#style-y040)] - - Services werden mit dem `new`-Schlüsselwort Instantiiert, und benutzen `this` für öffentliche Methoden und Variablen. Auch wenn sie den Factories so ähnlich sind, setzen Sie stattdessen aus Konsistenzgründen eine Factory ein. + - Services werden mit dem `new`-Schlüsselwort instanziiert, und benutzen `this` für öffentliche Methoden und Variablen. Auch wenn sie den Factories so ähnlich sind, setzen Sie stattdessen aus Konsistenzgründen eine Factory ein. Anmerkung: [Alle Angular-Services sind Singletons](https://docs.angularjs.org/guide/services). Das bedeutet, dass es nur eine Instanz eines Services pro Injector gibt. @@ -738,11 +734,11 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Zugreifbare Bestandteile an den Anfang ###### [Style [Y052](#style-y052)] - - Halten sie die zugreifbaren Bestandteile eines Service (sein Interface) oben, indem Sie eine Technik anwenden, die aus [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript) entlehnt ist. + - Halten Sie die zugreifbaren Bestandteile eines Service (sein Interface) oben, indem Sie eine Technik anwenden, die aus [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript) entlehnt ist. *Warum?*: Die zugreifbaren Bestandteile oben zu platzieren, erhöht die Lesbarkeit und hilft Ihnen, schnell zu identifizieren, welche Elemente des Service aufgerufen werden können und getestet (oder simuliert) werden müssen. - *Warum?*: Dies ist besondert hilfreich, wenn die Datei länger wird, weil ein Scrollen unnötig wird, um zu sehen, was verfügbar ist. + *Warum?*: Dies ist besonders hilfreich, wenn die Datei länger wird, weil ein Scrollen unnötig wird, um zu sehen, was verfügbar ist. *Warum?*: Einfach nur Funktionen einzusetzen kann leicht sein. Wenn diese aber den Umfang einer Zeile überschreiben, kann dies die Lesbarkeit verringern und es muss mehr gescrollt werden. Ein aufrufbares Interface im zurückgelieferten Service zu definieren, verlagert die Implementierungsdetails nach unten, hält das aufrufbare Interface ganz oben und macht es lesbarer. @@ -790,19 +786,19 @@ 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)] - - Benutzen Sie Funktionsdeklarationen, um die Details der Implementierung zu verbergen. Halten Sie Ihre zugreifbaren Bestandteile der Factory ganz oben. Lassen Sie diese auf Funktionsdeklarationen verweisen, die weiter unten in der Datei aufeführt werden. Mehr erfahren sie hier in [diesem Beitrag](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). + - Benutzen Sie Funktionsdeklarationen, um die Details der Implementierung zu verbergen. Halten Sie Ihre zugreifbaren Bestandteile der Factory ganz oben. Lassen Sie diese auf Funktionsdeklarationen verweisen, die weiter unten in der Datei aufgeführt werden. Mehr erfahren Sie hier in [diesem Beitrag](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). *Warum?*: Zugreifbare Elemente am Anfang zu platzieren, erhöht die Lesbarkeit und hilft Ihnen, zu identifizieren, auf welche Funktionen der Factory von außen zugegriffen werden kann. *Warum?*: Das Platzieren der Implementierungsdetails einer Funktion weiter unten in der Datei, hält diese Codemenge außer Sicht und Sie sehen die wichtigen Dinge am Anfang. - *Warum?*: Funktionsdeklarationen werden "nach oben gezogen" (sog. Hoisting), so dass es keine Probleme damit gibt, ob eine Funktion vor ihrer Benutzung deklariert werden sollte (wie es bei Funktiosausdrücken der Fall wäre). - + *Warum?*: Funktionsdeklarationen werden "nach oben gezogen" (sog. Hoisting), so dass es keine Probleme damit gibt, ob eine Funktion vor ihrer Benutzung deklariert werden sollte (wie es bei Funktionsausdrücken der Fall wäre). + *Warum?*: Sie müssen sich niemals Sorgen darum machen, wenn Sie in Funktionsdeklarationen `var a` vor `var b` platzieren, weil `a` von `b` abhängig ist. *Warum?*: Die Reihenfolge ist kritisch in Funktionsausdrücken. @@ -896,13 +892,13 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Separate Datenzugriffe ###### [Style [Y060](#style-y060)] - - Verlagern sie die Datenzugriffslogik und die Operationen mit Daten in eine Factory Machen Sie die Datenservices verantwortlich für die XHR-Aufrufe, die lokale Speicherung, die Ablage im Speicher oder jede andere Datenoperation. + - Verlagern Sie die Datenzugriffslogik und die Operationen mit Daten in eine Factory Machen Sie die Datenservices verantwortlich für die XHR-Aufrufe, die lokale Speicherung, die Ablage im Speicher oder jede andere Datenoperation. - *Warum?*: Die Verantwortung des Controllers liegt in der Zusammenstellung und Präsentation der Informationen für die und in der View. Er sollte sich nicht darum kümmern müssen, wie er die Daten bekommt, sondern wen er dazu ansprechen muss. Die Datenservices zu trennen verlagert die Logik der Datenermittlung in den Datenservice und belässt den Controller in seiner Einfachheit und seinem Fokus auf die View. + *Warum?*: Die Verantwortung des Controllers liegt in der Zusammenstellung und Präsentation der Informationen für die und in der View. Er sollte sich nicht darum kümmern müssen, wie er die Daten bekommt, sondern wen er dazu ansprechen muss. Die Datenservices zu trennen verlagert die Logik der Datenermittlung in den Datenservice und belässt den Controller in seiner Einfachheit und seinem Fokus auf den View. *Warum?*: Das macht das Testen der Datenabrufe (simuliert oder real) einfacher, wenn man einen Controller testet, der einen Datenservice nutzt. - *Warum?*: Datenserivce-Implementierungen enthalten spezifischen Code, um die Daten zu handhaben. Dies können bestimmte Header sein, die beschreiben, wie mit den Datenquellen oder anderen Services wie `$http` kommuniziert werden muss. Die Separierung dieser Logik in einem Datenservice kapselt sie an einem einzigen Platz und verbirgt die Implementierung vor den Konsumenten dieses Service (z. B. einem Controller). Das macht es auch einfacher, die Implementierung auszutauschen. + *Warum?*: Datenservice-Implementierungen enthalten spezifischen Code, um die Daten zu handhaben. Dies können bestimmte Header sein, die beschreiben, wie mit den Datenquellen oder anderen Services wie `$http` kommuniziert werden muss. Die Separierung dieser Logik in einem Datenservice kapselt sie an einem einzigen Platz und verbirgt die Implementierung vor den Konsumenten dieses Service (z. B. einem Controller). Das macht es auch einfacher, die Implementierung auszutauschen. ```javascript /* empfohlen */ @@ -974,7 +970,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Wenn Sie einen Datenservice ansprechen, der einen Promise wie `$http` zurückliefert, so liefern Sie in Ihrer aufrufenden Funktion ebenso einen Promise zurück. - *Warum?*: Sie können die Promises aneinanderhängen und weitere Aktionen ausführen, wenn der Datenabruf beendet ist und den Promise im Erfolgsfall entweder auflöst oder bei Fehlschlagen zurückweist. + *Warum?*: Sie können die Promises aneinanderhängen und weitere Aktionen ausführen, wenn der Datenabruf beendet ist und den Promise im Erfolgsfall entweder auflöst oder bei Fehlschlagen zurückweist. ```javascript /* empfohlen */ @@ -984,7 +980,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe function activate() { /** * Schritt 1 - * Bei der getAvengers Funktion nach den Avenger-Daten + * Bei der getAvengers Funktion nach den Avenger-Daten * fragen und auf den Promise warten */ return getAvengers().then(function() { @@ -999,7 +995,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe function getAvengers() { /** * Schritt 2 - * Beim Datenservice nach den Daten fragen und + * Beim Datenservice nach den Daten fragen und * auf den Promise warten */ return dataservice.getAvengers() @@ -1022,7 +1018,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Erstellen Sie eine Direktive pro Datei. Benennen Sie die Datei nach der Direktive. - *Warum?*: Es ist einfach, alle Direktiven in einer Datei zu halten, aber schwer, sie dann wieder herauszulösen sodass sie zwischen Anwendungen oder Modulen ausgetauscht werden können oder einfach nur in einem Modulzu genutzt werden. + *Warum?*: Es ist einfach, alle Direktiven in einer Datei zu halten, aber schwer, sie dann wieder herauszulösen sodass sie zwischen Anwendungen oder Modulen ausgetauscht werden können oder einfach nur in einem Modulzu genutzt werden. *Warum?*: Eine Direktive pro Datei ist einfach zu warten. @@ -1062,7 +1058,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe /* calendarRange.directive.js */ /** - * @desc Bestell-Direktive, die speziell für das Bestell-Modul der Firma Acme bestimmt ist. + * @desc Bestell-Direktive, die speziell für das Bestell-Modul der Firma Acme bestimmt ist. * @example
*/ angular @@ -1108,21 +1104,21 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe } ``` - Anmerkung: Es gibt viele Bennennungsmöglichkeiten für Direktiven, weil sie in einem schmalen oder weiten Gültigkeitsbereich genutzt werden können. Wählen sie eine, die den Namen der Direktive und ihren Dateinamen eindeutig und klar verständlich macht. Einige Beispiele befinden sich weiter unten, aber schauen Sie sich den Abschnitt zur [Namensgebung](#naming) an, um weitere Empfehlungen zu sehen. + Anmerkung: Es gibt viele Benennungsmöglichkeiten für Direktiven, weil sie in einem schmalen oder weiten Gültigkeitsbereich genutzt werden können. Wählen Sie eine, die den Namen der Direktive und ihren Dateinamen eindeutig und klar verständlich macht. Einige Beispiele befinden sich weiter unten, aber schauen Sie sich den Abschnitt zur [Namensgebung](#naming) an, um weitere Empfehlungen zu sehen. ### DOM-Maniuplation in einer Directive ###### [Style [Y072](#style-y072)] - - Benutzen Sie zur direkten Manipulation des DOM eine Direktive. Wenn es alternative Wege gibt, wie zum Beispiel CSS, um Stile zu setzen oder [Animation Services](https://docs.angularjs.org/api/ngAnimate), Angular Templates, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) oder [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), dann nutzen sie diese anstatt. Wenn die Direktive zum Beispiel einfach nur etwas versteckt oder zeigt, dann benutzen Sie ngHide/ngShow. + - Benutzen Sie zur direkten Manipulation des DOM eine Direktive. Wenn es alternative Wege gibt, wie zum Beispiel CSS, um Stile zu setzen oder [Animation Services](https://docs.angularjs.org/api/ngAnimate), Angular Templates, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) oder [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), dann nutzen Sie diese anstatt. Wenn die Direktive zum Beispiel einfach nur etwas versteckt oder zeigt, dann benutzen Sie ngHide/ngShow. *Warum?*: DOM-Manipulationen können schwer zu testen oder zu debuggen sein und es gibt oftmals bessere Wege (z. B. CSS, Animationen oder Templates) -### Vergeben sie einen eindeutigen Prefix für eine Direktive +### Vergeben Sie einen eindeutigen Präfix für eine Direktive ###### [Style [Y073](#style-y073)] - - Vergeben sie einen kurzen, eindeutigen und beschreibenden Prefix für die Direktive, wie `acmeSalesCustomerInfo`. Dieser würde in HTML als `acme-sales-customer-info` genutzt. + - Vergeben Sie einen kurzen, eindeutigen und beschreibenden Präfix für die Direktive, wie `acmeSalesCustomerInfo`. Dieser würde in HTML als `acme-sales-customer-info` genutzt. - *Warum?*: Der eindeutige kurze Prefix gibt den Kontext und den Ursprung der Direktive wieder. Ein Prefix wie `cc-` könnte ausweisen, dass die Direktive Teil einer "CodeCamper"-Anwendung ist, wohingegegen `acme-` auf eine Direktive der Firma Acme hinweisen könnte. + *Warum?*: Der eindeutige kurze Präfix gibt den Kontext und den Ursprung der Direktive wieder. Ein Prefix wie `cc-` könnte ausweisen, dass die Direktive Teil einer "CodeCamper"-Anwendung ist, wohingegegen `acme-` auf eine Direktive der Firma Acme hinweisen könnte. Anmerkung: Vermeiden Sie `ng-`, weil dieser Präfix für Angular-Direktiven reserviert ist. Recherchieren Sie viel genutzte Direktiven, um einem Namenskonflikt wie zum Beispiel mit `ion-` für das [Ionic Framework](http://ionicframework.com/) vorzubeugen. @@ -1320,13 +1316,13 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Promises beim Aktivieren eines Controllers ###### [Style [Y080](#style-y080)] - - Verlagern sie die Start-Logik eines Controllers in eine `activate`-Funktion. + - Verlagern Sie die Start-Logik eines Controllers in eine `activate`-Funktion. *Warum?*: Ist die Start-Logik an einem einheitlichen Platz innerhalb des Controllers, wird ihr Auffinden vereinfacht, sie ist besser zu testen und diese Methode hilft dabei, zu verhindern, dass die Startlogik überall im Controller verteilt ist. - *Warum?*: Das `activate` ist eine komfortable Art und Weise, diese Logik für einen Refresh des Controllers / der View zu nutzen. Es hält die Logik zusammen, liefert die View schneller an den Benutzer, erleichtert Animationen mit `ng-view` oder `ui-view`, und macht auch einen flotteren Eindruck beim Benutzer. + *Warum?*: Das `activate` ist eine komfortable Art und Weise, diese Logik für einen Refresh des Controllers / der View zu nutzen. Es hält die Logik zusammen, liefert den View schneller an den Benutzer, erleichtert Animationen mit `ng-view` oder `ui-view`, und macht auch einen flotteren Eindruck beim Benutzer. - Anmerkung: Wenn Sie die Routennavigation bedingt abbrechen müssen, bevor der Controller gestartet wird, dann sollten sie stattdessen ein [route resolve](#style-y081) nutzen. + Anmerkung: Wenn Sie die Routennavigation bedingt abbrechen müssen, bevor der Controller gestartet wird, dann sollten Sie stattdessen ein [route resolve](#style-y081) nutzen. ```javascript /* zu vermeiden */ @@ -1367,13 +1363,13 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Ist ein Controller abhängig von der Auflösung eines Promise, der vor der Aktivierung des Controllers aufgelöst sein muss, dann muss diese Abhängigkeit im `$routeProvider` aufgelöst werden, und zwar bevor die Controller-Logik ausgeführt wird. Wenn Sie eine Routen-Navigation bedingt abbrechen müssen, bevor der Controller aktiviert ist, nutzen Sie einen Route-Resolver. - - Nutzen Sie ein "route resolve" wenn Sie bestimmen wollen, ob eine Routennavigation abgebrochen werden soll, bevor die View eingeblendet wird. + - Nutzen Sie ein "route resolve" wenn Sie bestimmen wollen, ob eine Routennavigation abgebrochen werden soll, bevor der View eingeblendet wird. - *Warum?*: Es kann sein, dass ein Controller Daten benötigt, noch bevor er geladen wird. Diese Daten können von einem Promise aus einer Factory oder über [$http](https://docs.angularjs.org/api/ng/service/$http) kommen. Ein ["route resolve"](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) zu benutzen, ermöglicht, dass der Promise aufgelöst wird, bevor die Controller-Logik ausgeüfhrt wird. Also kann es notwendig werden, eine Aktion aufgrund der Daten aus dem Promis auszuführen. + *Warum?*: Es kann sein, dass ein Controller Daten benötigt, noch bevor er geladen wird. Diese Daten können von einem Promise aus einer Factory oder über [$http](https://docs.angularjs.org/api/ng/service/$http) kommen. Ein ["route resolve"](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) zu benutzen, ermöglicht, dass der Promise aufgelöst wird, bevor die Controller-Logik ausgeführt wird. Also kann es notwendig werden, eine Aktion aufgrund der Daten aus dem Promis auszuführen. *Warum?*: Der Code wird nach den Routennavigation innerhalb der activate-Funktion des Controllers ausgeführt. Der View wird ebenso geladen. Die Datenbindung steht, wenn der aktive Promise aufgelöst ist. Eine "Busy-Animation" kann während der Einblendung des views (via `ng-view` oder `ui-view`) angezeigt werden. - Anmerkung: Der Code wird vor der Routennavigation über einen Promise ausgeführt. Wird der Promise zurückgewiesen, wird die Navigation abgebrochen. Resolve bewirkt, dass die neue View auf die Auflösung der Route wartet. Ein "Busy-Indikator" kann vor dem Auflösen und während der Einblendung des Views angezeigt werden. Wenn Sie die View schneller einblenden wollen und keinen Kontrollpunkt benötigen, an dem geprüft wird, ob die View überhaupt zur Verfügung steht, sollten Sie die [Controller `activate` Technik](#style-y080) in Betracht ziehen. + Anmerkung: Der Code wird vor der Routennavigation über einen Promise ausgeführt. Wird der Promise zurückgewiesen, wird die Navigation abgebrochen. Resolve bewirkt, dass die neue View auf die Auflösung der Route wartet. Ein "Busy-Indikator" kann vor dem Auflösen und während der Einblendung des Views angezeigt werden. Wenn Sie den View schneller einblenden wollen und keinen Kontrollpunkt benötigen, an dem geprüft wird, ob der View überhaupt zur Verfügung steht, sollten Sie die [Controller `activate` Technik](#style-y080) in Betracht ziehen. ```javascript /* zu vermeiden */ @@ -1426,7 +1422,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe } ``` - Anmerkung: Das untenstehende Beispiel zeigt die Stellen, an denen die Route mit einer benannten Funktion aufgelöst wird. Das ist einfacher zu debuggen und vereinfacht auch die Handhabung von Dependency Injection. + Anmerkung: Das untenstehende Beispiel zeigt die Stellen, an denen die Route mit einer benannten Funktion aufgelöst wird. Das ist einfacher zu debuggen und vereinfacht auch die Handhabung von Dependency Injection. ```javascript /* noch besser */ @@ -1472,7 +1468,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Unsichere Minifizierung ###### [Style [Y090](#style-y090)] - - Vermeiden Sie es, die kurze Deklarationssyntax für Ahängigkeiten ohne einen für die Minifizierung sicheren Ansatz zu verwenden. + - Vermeiden Sie es, die kurze Deklarationssyntax für Abhängigkeiten ohne einen für die Minifizierung sicheren Ansatz zu verwenden. *Warum?*: Die Parameter der Komponente (z. B. Controller, Factory, etc.) werden in abgekürzte Variablen gewandelt. So kann zum Beispiel aus `common` und `dataservice` ein `a` oder `b` werden, was von Angular nicht gefunden wird. @@ -1498,7 +1494,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Benutzen Sie `$inject` um Ihre Abhängigkeiten für Angular-Komponenten manuell zu identifizieren. - *Warum?*: Dieses Verfahren spiegelt die Technik wieder, die von [`ng-annotate`](https://github.com/olov/ng-annotate) genutzt wird, welche ich für die Automatisierung der Erstellung von minifikationssicheren Abhängigkeiten empfehleh. Wenn `ng-annotate` erkennt, dass eine solche Deklaration vorgenommen wurde, wird diese nicht dupliziert. + *Warum?*: Dieses Verfahren spiegelt die Technik wieder, die von [`ng-annotate`](https://github.com/olov/ng-annotate) genutzt wird, welche ich für die Automatisierung der Erstellung von minifikationssicheren Abhängigkeiten empfehlen. Wenn `ng-annotate` erkennt, dass eine solche Deklaration vorgenommen wurde, wird diese nicht dupliziert. *Warum?*: Dies bewahrt Ihre Abhängigkeiten vor Problemen bei einer Minifizierung, bei der die Parameter abgekürzt werden. Zum Beispiel wird aus `common` und `dataservice` ein `a` oder `b`, was von Angular nicht gefunden wird. @@ -1537,7 +1533,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe } ``` - Anmerkung: Wenn sich Ihre Funktion unterhalb eines returns befindet, kann `$inject` unerreichbar werden (das kann in einer Direktive passieren). Sie können dies vermeiden, indem sie den Controller aus der Direktive herauslösen. + Anmerkung: Wenn sich Ihre Funktion unterhalb eines returns befindet, kann `$inject` unerreichbar werden (das kann in einer Direktive passieren). Sie können dies vermeiden, indem Sie den Controller aus der Direktive herauslösen. ```javascript /* zu vermeiden */ @@ -1607,7 +1603,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### ng-annotate ###### [Style [Y100](#style-y100)] - - Benutzen Sie [ng-annotate](//github.com/olov/ng-annotate) für [Gulp](http://gulpjs.com) oder [Grunt](http://gruntjs.com) und versehen Sie die Funktionen mit den notwendigen `/** @ngInject */`-Kommentaren, die für die "automatische" Dependency Injection genutzt werden sollen. + - Benutzen Sie [ng-annotate](//github.com/olov/ng-annotate) für [Gulp](http://gulpjs.com) oder [Grunt](http://gruntjs.com) und versehen Sie die Funktionen mit den notwendigen `/** @ngInject */`-Kommentaren, die für die "automatische" Dependency Injection genutzt werden sollen. *Warum?*: Dies schützt Ihren Code vor Abhängigkeiten, die keiner minifizierungssicheren Technik entsprechen. @@ -1659,7 +1655,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe Anmerkung: Entdeckt `ng-annotate` bereits vorhandene Kommentare (z. B. bei erkanntem `@ngInject`), werden die `$inject`-Befehle nicht dupliziert. - Anmerkung: Wenn Sie einen Route Resolver nutzen, können sie die Funktion des Resolvers mit `/* @ngInject */` markieren, und es wird eine korrekte Code-Anmerkung erstellt, die alle eingefügten Abhängigkeiten minifizierungssicher hält. + Anmerkung: Wenn Sie einen Route Resolver nutzen, können Sie die Funktion des Resolvers mit `/* @ngInject */` markieren, und es wird eine korrekte Code-Anmerkung erstellt, die alle eingefügten Abhängigkeiten minifizierungssicher hält. ```javascript // Using @ngInject annotations @@ -1678,13 +1674,13 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe } ``` - > Anmerkung: Ab der 1.3er Version von Angular können Sie den `ngStrictDi` Parameter der [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp)-Direktive nutzen, um potentiell minfizierungsunsichere Abhängigkeiten aufzuspühren. Wurde eine solche Abhängigkeit entdeckt, dann wird der Injector im "strict-di"-Modus erstellt und verursacht Fehler beim Ausfüren von Funktionen, die keine explizite Code-Anmerkung besitzen (was nicht minifizerungssicher ist). Debug-Informationen werden in der Konsole ausgegeben, um den betreffenden Code nachvollziehen zu können. Ich bevorzuge die Nutzung von `ng-strict-di` für das Debugging. + > Anmerkung: Ab der 1.3er Version von Angular können Sie den `ngStrictDi` Parameter der [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp)-Direktive nutzen, um potentiell minfizierungsunsichere Abhängigkeiten aufzuspüren. Wurde eine solche Abhängigkeit entdeckt, dann wird der Injector im "strict-di"-Modus erstellt und verursacht Fehler beim Ausführen von Funktionen, die keine explizite Code-Anmerkung besitzen (was nicht minifizerungssicher ist). Debug-Informationen werden in der Konsole ausgegeben, um den betreffenden Code nachvollziehen zu können. Ich bevorzuge die Nutzung von `ng-strict-di` für das Debugging. `` ### Gulp oder Grunt für ng-annotate nutzen ###### [Style [Y101](#style-y101)] - - Benutzen Sie [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) oder [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) in einer autmatisierten Build-Task. Fügen sie `/* @ngInject */` vor jeder Funktion ein, die Abhängigkeiten hat. + - Benutzen Sie [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) oder [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) in einer automatisierten Build-Task. Fügen Sie `/* @ngInject */` vor jeder Funktion ein, die Abhängigkeiten hat. *Warum?*: ng-annotate erkennt die meisten Abhängigkeiten automatisch, benötigt manchmal aber Hinweise durch die `/* @ngInject */`-Syntax. @@ -1721,7 +1717,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Benutzen Sie einen [Decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator) während der Konfiguration, indem Sie den [`$provide`](https://docs.angularjs.org/api/auto/service/$provide)-Service im [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler)-Service ansprechen, um eigene Aktionen bei einem auftauchenden Fehler (einer Ausnahme) auszuführen. - *Warum?*: Dies bietet einen einen stimmigen Weg, unbehandelte Angular-Fehler während der Entwicklung oder zur Laufzeit abzufangen. + *Warum?*: Dies bietet einen stimmigen Weg, unbehandelte Angular-Fehler während der Entwicklung oder zur Laufzeit abzufangen. Anmerkung: Eine weitere Option neben der Benutzung eines Decorators, stellt das Überschreiben des Service dar. Diese Möglichkeit ist gut, wenn Sie aber das Standardverhalten beibehalten wollen, dann ist die Erweiterung mit einem Decorator angebracht. @@ -1748,9 +1744,9 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe }; /** * Der Fehler könnte zu einer Liste im Service oder - * zum $rootScope hinzugefügt werden oder bei einem - * Remote-Webserver oder lokal protokolliert oder - * einfach wieder hart "geworfen" werden. Es obliegt + * zum $rootScope hinzugefügt werden oder bei einem + * Remote-Webserver oder lokal protokolliert oder + * einfach wieder hart "geworfen" werden. Es obliegt * ganz Ihnen. * * throw exception; @@ -1767,7 +1763,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe *Warum?*: Dies bietet eine konsistente Methode, Fehler abzufangen, die in Ihrem Code geworfen werden (z. B. während eines XHR-Aufrufs oder bei Fehlern in einem Promise). - Anmerkung: Eine Komponente, die die Fehler abfängt stellt eine gute Möglichkeit dar, Fehler an den Stellen abzufangen, von denen Sie wissen, dass sie auftreten können. Zum Beispiel, wenn Sie Daten über einen XHR-Aufruf von einem Webservice anfragen und Sie jegliche Art von Fehler, die von diesem Service zurück kommen, speziell behandeln wollen. + Anmerkung: Eine Komponente, die die Fehler abfängt stellt eine gute Möglichkeit dar, Fehler an den Stellen abzufangen, von denen Sie wissen, dass sie auftreten können. Zum Beispiel, wenn Sie Daten über einen XHR-Aufruf von einem Webservice anfragen und Sie jegliche Art von Fehler, die von diesem Service zurückkommen, speziell behandeln wollen. ```javascript /* empfohlen */ @@ -1798,7 +1794,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe *Warum?*: Bietet einen stimmigen Weg, um alle Routingfehler zu behandeln. - *Warum?*: Bietet potentiell die Möglichkeit die Akzeptanz beim Benutzer zu steigern, wenn ein Routingfehler autftritt und dieser auf informative Weise mit Möglichkeiten zur Behebung am Bildschirm angezeigt wird. + *Warum?*: Bietet potentiell die Möglichkeit die Akzeptanz beim Benutzer zu steigern, wenn ein Routingfehler auftritt und dieser auf informative Weise mit Möglichkeiten zur Behebung am Bildschirm angezeigt wird. ```javascript /* empfohlen */ @@ -1844,7 +1840,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Richtlinien der Namensgebung ###### [Style [Y120](#style-y120)] - - Benutzen sie stimmige Namen für alle Komponenten, einem Muster folgend, welches die Hauptfunktionen (Features) einer Komponente und dann (optional) ihren Typ beschreibt. + - Benutzen Sie stimmige Namen für alle Komponenten, einem Muster folgend, welches die Hauptfunktionen (Features) einer Komponente und dann (optional) ihren Typ beschreibt. Meine empfohlenes Muster ist `feature.typ.js`. Es gibt zwei zu vergebene Namen für die meisten Komponenten: * der Dateiname (`avengers.controller.js`) * der Name der bei Angular zu registrierenden Komponente (`AvengersController`) @@ -1858,7 +1854,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Benutzen Sie stimmige Namen für alle Komponenten, die einem Muster folgen: Hauptfunktion einer Komponente, und dann (optional) gefolgt vom Typ. Mein empfohlenes Muster ist `feature.typ.js`. - *Warum?*: Bietet einen stimmigen Weg, Komponenten schnell zu identifizeren. + *Warum?*: Bietet einen stimmigen Weg, Komponenten schnell zu identifizieren. *Warum?*: Bietet ein Suchmuster für alle automatisierten Aufgaben. @@ -1925,7 +1921,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Benennen Sie Testspezifikationen gemäß der Komponente, die getestet werden soll, gefolgt vom Suffix `spec`. - *Warum?*: Bietet einen stimmigen Weg, Komponenten schnell zu indentifizieren. + *Warum?*: Bietet einen stimmigen Weg, Komponenten schnell zu identifizieren. *Warum?*: Bietet ein Suchmuster für [karma](http://karma-runner.github.io/) oder andere Testrunner. @@ -1942,11 +1938,11 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Namen für Controller ###### [Style [Y123](#style-y123)] - - Nutzen Sie stimmige Namen für alle Controller und bennenen Sie diese nach ihrem Hauptmerkmal. Benutzen Sie UpperCamelCase für Controller, weil sie Konstruktoren sind. + - Nutzen Sie stimmige Namen für alle Controller und benennen Sie diese nach ihrem Hauptmerkmal. Benutzen Sie UpperCamelCase für Controller, weil sie Konstruktoren sind. - *Warum?*: Bietet einen stimmigen Weg, Controller schnell zu indentifizieren und zu referenzieren. + *Warum?*: Bietet einen stimmigen Weg, Controller schnell zu identifizieren und zu referenzieren. - *Warum?*: UpperCamelCase ist eine Konvention, ein Objekt zu identifizieren, welches über einen Konstruktor instantiiert werden kann. + *Warum?*: UpperCamelCase ist eine Konvention, ein Objekt zu identifizieren, welches über einen Konstruktor instanziiert werden kann. ```javascript /** @@ -1984,9 +1980,9 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Namen für Factories ###### [Style [Y125](#style-y125)] - - Nutzen Sie stimmige Namen für alle Factories und vergeben sie diese nach deren Hauptfunktion. Benutzen Sie Camel-Casing für Services und Factroies. Vermeiden Sie es, einer Factory oder einem Service ein `$` voranzutellen. + - Nutzen Sie stimmige Namen für alle Factories und vergeben Sie diese nach deren Hauptfunktion. Benutzen Sie Camel-Casing für Services und Factories. Vermeiden Sie es, einer Factory oder einem Service ein `$` voranzustellen. - *Warum?*: Bietet einen stimmigen Weg, Factories schnell zu identifizeren und zu referenzieren. + *Warum?*: Bietet einen stimmigen Weg, Factories schnell zu identifizieren und zu referenzieren. *Warum?*: Verhindert Namenskollisionen mit eingebauten Factories und Serivces, die `$` als Präfix nutzen. @@ -2008,7 +2004,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe - Benutzen Sie stimmige Namen für alle Direktiven gemäß der Camel-Case-Regel. Nutzen Sie einen kurzen Präfix, um den Bereich zu beschreiben, zu dem die Direktive gehört (Firmen- oder Projekt-Präfix). - *Warum?*: Bietet einen stimmigen Weg, Direktiven schnell zu identifizeren und zu referenzieren. + *Warum?*: Bietet einen stimmigen Weg, Direktiven schnell zu identifizieren und zu referenzieren. ```javascript /** @@ -2037,11 +2033,11 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Konfiguration ###### [Style [Y128](#style-y128)] - - Trennen Sie die Konfiguration vom Modul und lagern sie diese in eine eigene Datei aus, die nach dem Modul benannt wird. Eine Konfigurationsdatei für das Hauptmodul `app` wird `app.config.js` genannt (oder einfach `config.js`). Eine Konfigurationsdatei für ein Modul namens `admin.module.js` wird `admin.config.js` genannt. + - Trennen Sie die Konfiguration vom Modul und lagern Sie diese in eine eigene Datei aus, die nach dem Modul benannt wird. Eine Konfigurationsdatei für das Hauptmodul `app` wird `app.config.js` genannt (oder einfach `config.js`). Eine Konfigurationsdatei für ein Modul namens `admin.module.js` wird `admin.config.js` genannt. *Warum?*: Trennt Konfiguration von der Moduldefinition, den Komponenten und dem "aktiven" Code. - *Warum?*: Bietet einen leicht zu identifizierenden Platz, um die Kofiguration eines Moduls vorzunehmen. + *Warum?*: Bietet einen leicht zu identifizierenden Platz, um die Konfiguration eines Moduls vorzunehmen. ### Routen ###### [Style [Y129](#style-y129)] @@ -2054,9 +2050,9 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### LIFT ###### [Style [Y140](#style-y140)] - - LIFT steht für `L`ocate (auffinden), `I`dentify (identifizieren), `F`lat (flach), T`ry to stay DRY` (versuchen Sie, Ihren Code nicht zu wiederholen). Das bedeutet also, sie sollten Ihre Anwendung so strukturieren, dass sie Ihren Code schnell auffinden und auf einen Blick identifizieren können, für was der Code gut ist. Dabei sollten Sie die Struktur so flach wie möglich halten. Vermeiden Sie es unbedingt, Ihren Code zu wiederholen. + - LIFT steht für `L`ocate (auffinden), `I`dentify (identifizieren), `F`lat (flach), T`ry to stay DRY` (versuchen Sie, Ihren Code nicht zu wiederholen). Das bedeutet also, Sie sollten Ihre Anwendung so strukturieren, dass Sie Ihren Code schnell auffinden und auf einen Blick identifizieren können, für was der Code gut ist. Dabei sollten Sie die Struktur so flach wie möglich halten. Vermeiden Sie es unbedingt, Ihren Code zu wiederholen. - *Warum LIFT?*: Bietet eine konsistente und gut skalierbare Stuktur, ist modular und macht es einfacher die Effizienz eines Entwicklers zu steigern, weil er seinen Code schneller finden kann. Prüfen Sie Ihre Andwendungsstruktur, indem Sie sich fragen: Wie schnell kann ich all die Dateien, die zu einem Feature gehören öffnen und mit ihnen arbeiten?" + *Warum LIFT?*: Bietet eine konsistente und gut skalierbare Struktur, ist modular und macht es einfacher die Effizienz eines Entwicklers zu steigern, weil er seinen Code schneller finden kann. Prüfen Sie Ihre Anwendungsstruktur, indem Sie sich fragen: Wie schnell kann ich all die Dateien, die zu einem Feature gehören öffnen und mit ihnen arbeiten?" Falls ich mich mit meiner nicht mehr wohl fühle, dann schaue ich mir die LIFT-Anweisungen an: @@ -2093,23 +2089,23 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Identify (Code identifizieren) ###### [Style [Y142](#style-y142)] - - Wenn Sie einen Dateinamen sehen, sollten sie sofort wissen, was die Datei beinhaltet und für was sie steht. + - Wenn Sie einen Dateinamen sehen, sollten Sie sofort wissen, was die Datei beinhaltet und für was sie steht. - *Warum?*: Sie brauchen weniger Zeit, um nach Ihrem Code zu suchen und werden so effizienter. Wenn das bedeutet, dass Sie längere Dateinamen brauchen, dann sei es so. Seien Sie beschreibend bei der Namensvergabe und sorgen Sie dafür, dass eine Datei nur eine Komponente enthält. Vermeiden Sie Dateien mir mehreren Controllern, Services oder gar mit beidem. Ich weiche von dieser Regel ab, wenn ich sehr kleine Features habe, die alle miteinander verbunden und leicht identifizierbar sind. + *Warum?*: Sie brauchen weniger Zeit, um nach Ihrem Code zu suchen und werden so effizienter. Wenn das bedeutet, dass Sie längere Dateinamen brauchen, dann sei es so. Seien Sie beschreibend bei der Namensvergabe und sorgen Sie dafür, dass eine Datei nur eine Komponente enthält. Vermeiden Sie Dateien mir mehreren Controllern, Services oder gar mit beidem. Ich weiche von dieser Regel ab, wenn ich sehr kleine Features habe, die alle miteinander verbunden und leicht identifizierbar sind. ### Flat (flache Struktur) ###### [Style [Y143](#style-y143)] - Halten Sie die Verzeichnisstruktur so lange es geht so flach wie möglich. Sollten mehr als sieben Dateien in einem Verzeichnis stehen, denken Sie über eine Neuaufteilung nach. - *Warum?*: Niemand will Dateien in einer Verzeichnisstruktur über sieben Ebenen suchen. Denken Sie an Menüs von Webseiten ... Alles, was über mehr als zwei Ebenen geht, sollte ernsthaft überdacht werden. Für eine Verzeichnisstruktur gibt es keine feste Regelung, aber sollte ein Verzeichnis sieben bis zehn Dateien enthalten, dann ist es vielleicht an der Zeit, Unterverzeichnisse einzurichten. Machen Sie es für sich selbst an Ihrem Wohlbefinden mit der Struktur fest. Benutzen sie eine flachere Struktur, bis Sie den Punkt erreichen, an dem es Sinn macht, ein neues Verzeichnis zu erstellen. + *Warum?*: Niemand will Dateien in einer Verzeichnisstruktur über sieben Ebenen suchen. Denken Sie an Menüs von Webseiten ... Alles, was über mehr als zwei Ebenen geht, sollte ernsthaft überdacht werden. Für eine Verzeichnisstruktur gibt es keine feste Regelung, aber sollte ein Verzeichnis sieben bis zehn Dateien enthalten, dann ist es vielleicht an der Zeit, Unterverzeichnisse einzurichten. Machen Sie es für sich selbst an Ihrem Wohlbefinden mit der Struktur fest. Benutzen Sie eine flachere Struktur, bis Sie den Punkt erreichen, an dem es Sinn macht, ein neues Verzeichnis zu erstellen. ### T-DRY (Versuchen Sie Ihren Code nicht zu wiederholen) ###### [Style [Y144](#style-y144)] - Seien Sie "DRY": Versuchen Sie Ihren Code nicht zu wiederholen. Aber übertreiben Sie es nicht, indem Sie die Lesbarkeit Ihres Codes dafür opfern. - *Warum?*: Sich nicht ständig zu wiederholen ist wichtig, aber nicht entscheident, wenn Sie dafür andere Punkte von LIFT opfern. Ich möchte für eine View nicht session-view.html tippen, da es ja offentlichtlich eine View ist. Wenn etwas nicht offensichtlich oder einer Konvention unterliegt, dann benenne ich es. + *Warum?*: Sich nicht ständig zu wiederholen ist wichtig, aber nicht entscheidend, wenn Sie dafür andere Punkte von LIFT opfern. Ich möchte für eine View nicht session-view.html tippen, da es ja öffentlich eine View ist. Wenn etwas nicht offensichtlich oder einer Konvention unterliegt, dann benenne ich es. **[Zurück zum Anfang](#table-of-contents)** @@ -2118,21 +2114,21 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Allgemeine Richtlinien ###### [Style [Y150](#style-y150)] - - Sie sollten eine kurzfristige und langfristive Sicht auf Ihre Implemtierung haben. Das bedeutet: Fangen Sie klein an, behalten Sie dabei aber im Auge, wohin Sie mir Ihrer Anwendung wollen. Jeder Code der Anwendung wird in einem Stammverzeichnis namens `app` abgelegt. Für den Inhalt gilt: Ein Feature pro Datei. Jeder Controller, Service, jedes Modul, jede View befindet sich in ihrer/seiner eigenen Datei. Alle externen Scripts (3rd Party Bibliotheken) werden in einem anderen Stammverzeichnis, nicht aber im `app`-Verzeichnis abgelegt. Ich habe sie nicht geschrieben und ich möchte nicht, dass sie meine Anwendung durcheinander bringen.(`bower_components`, `scripts`, `lib`). + - Sie sollten eine kurzfristige und langfristige Sicht auf Ihre Implementierung haben. Das bedeutet: Fangen Sie klein an, behalten Sie dabei aber im Auge, wohin Sie mir Ihrer Anwendung wollen. Jeder Code der Anwendung wird in einem Stammverzeichnis namens `app` abgelegt. Für den Inhalt gilt: Ein Feature pro Datei. Jeder Controller, Service, jedes Modul, jede View befindet sich in ihrer/seiner eigenen Datei. Alle externen Scripts (3rd Party Bibliotheken) werden in einem anderen Stammverzeichnis, nicht aber im `app`-Verzeichnis abgelegt. Ich habe sie nicht geschrieben und ich möchte nicht, dass sie meine Anwendung durcheinander bringen.(`bower_components`, `scripts`, `lib`). Anmerkung: Sie finden mehr Details und Gründe für diese Struktur in [diesem Originalbeitrag zur Anwendungsstruktur](http://www.johnpapa.net/angular-app-structuring-guidelines/). ### Layout ###### [Style [Y151](#style-y151)] - - Platzieren Sie Komponenten, die das allgemeingültige Layout der Anwendung ausmachen, in einem Verzeichnis namens `layout`. Dieses sollte eine Shell-View mit Controller enthalten. Die View agiert als Container für die Anwendung und enthält die Anwendung an sich: Navigation, Menüs, Bereiche für die Inhalte und andere Bereiche. + - Platzieren Sie Komponenten, die das allgemeingültige Layout der Anwendung ausmachen, in einem Verzeichnis namens `layout`. Dieses sollte eine Shell-View mit Controller enthalten. Der View agiert als Container für die Anwendung und enthält die Anwendung an sich: Navigation, Menüs, Bereiche für die Inhalte und andere Bereiche. - *Warum?*: Organzisieren Sie das Layout an einem einzigen Ort, damit es innerhalb der Anwendung von überall her genutzt werden kann. + *Warum?*: Organisieren Sie das Layout an einem einzigen Ort, damit es innerhalb der Anwendung von überall her genutzt werden kann. -### Vezeichnisse nach Hauptmerkmalen +### Verzeichnisse nach Hauptmerkmalen ###### [Style [Y152](#style-y152)] - - Erstellen Sie Verzeichnisse gemäß der Hauptmerkmale, die sie darstellen. Wenn der Inhalt eines Verzeichnisse wächst und mehr als sieben Dateien fasst, sollten Sie darüber nachdenken, ein neues Verzeichnis zu erstellen. Dabei ist der Grenzwert aber individuell. + - Erstellen Sie Verzeichnisse gemäß der Hauptmerkmale, die sie darstellen. Wenn der Inhalt eines Verzeichnisses wächst und mehr als sieben Dateien fasst, sollten Sie darüber nachdenken, ein neues Verzeichnis zu erstellen. Dabei ist der Grenzwert aber individuell. *Warum?*: En Entwickler kann den gesuchten Code schnell auffinden, auf einen Blick identifizieren für was jede Datei steht, die Struktur ist so flach wie möglich und es gibt keine redundanten Namen. @@ -2181,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. @@ -2229,26 +2225,26 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ## Modularität -### Viele kleine, eigentsändige Module +### Viele kleine, eigenständige Module ###### [Style [Y160](#style-y160)] - Erstellen Sie kleine Module, die für eine einzige Aufgabe zuständig sind und diese in sich kapseln. - *Warum?*: Modulare Anwendungen machen es möglich, dass Funktionsmerkmale (Features) einfach eingeklinkt werden können. Somit kann ein Entwicklungsteam vertikale Stücke einer Applikation sukzessive ausrollen. Das bedeutet, dass neue Funtionsmerkmale nach ihrer Entwicklung einfach eingeklink werden können. + *Warum?*: Modulare Anwendungen machen es möglich, dass Funktionsmerkmale (Features) einfach eingeklinkt werden können. Somit kann ein Entwicklungsteam vertikale Stücke einer Applikation sukzessive ausrollen. Das bedeutet, dass neue Funktionsmerkmale nach ihrer Entwicklung einfach eingeklinkt werden können. ### Erstellen Sie ein Modul für die Hauptanwendung (App-Modul) ###### [Style [Y161](#style-y161)] - Erstellen Sie ein Hauptmodul für die Anwendung, dessen Rolle es ist, alle Module und Funktionen der Anwendung zusammenzutragen. Nennen Sie das Modul nach Ihrer Anwendung. - *Warum?*: Angular begünstigt Modularität und Muster für die Aufteilung von Code. Ein Hauptmodul für eine Anwendung zu erstellen, die andere Module zusammenzieht, ist ein einfacher Weg, um Module in eine Anwendung einzuklinken oder aus ihr auskzuklinken. + *Warum?*: Angular begünstigt Modularität und Muster für die Aufteilung von Code. Ein Hauptmodul für eine Anwendung zu erstellen, die andere Module zusammenzieht, ist ein einfacher Weg, um Module in eine Anwendung einzuklinken oder aus ihr auszuklinken. ### Halten Sie das App-Modul klein ###### [Style [Y162](#style-y162)] - Stellen Sie nur Logik ins App-Modul, die dazu dient, die Anwendungsbestandteile zusammenzuziehen. Features bleiben in ihren eigenen Feature-Modulen. - *Warum?*: Weitere Logik außerhalb der ursprünglichen Aufgabe ins Hauptmodul einzubinden, wie zum Beispiel Datenabfragen, darstellen von Views, oder eine Logik, die nicht zum zusammenziehen der Module dient, bringt Verwirrung. Es wird schwierig, diese Features zu verwalten und auch zu testen. + *Warum?*: Weitere Logik außerhalb der ursprünglichen Aufgabe ins Hauptmodul einzubinden, wie zum Beispiel Datenabfragen, darstellen von Views, oder eine Logik, die nicht zum Zusammenziehen der Module dient, bringt Verwirrung. Es wird schwierig, diese Features zu verwalten und auch zu testen. *Warum?*: Das App-Modul wird zum Manifest, welches die Module aufführt, die die Applikation ausmachen. @@ -2261,7 +2257,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe *Warum?*: Sprints oder Iterationen können sich auf Funktionsbereiche beziehen. Diese können am Ende eines Sprints oder einer Iteration eingebunden werden. - *Warum?*: Die Trennung von Funkstionsbereichen in Module erleichtert das isolierte Testen der Module und deren Wiederverwengung. + *Warum?*: Die Trennung von Funktionsbereichen in Module erleichtert das isolierte Testen der Module und deren Wiederverwendung. ### Wiederverwendbare Bausteine sind Module ###### [Style [Y164](#style-y164)] @@ -2273,9 +2269,9 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe ### Modulabhängigkeiten ###### [Style [Y165](#style-y165)] - - Das Hauptmodul einer Applikation ist abhängig von den applikationsspezifischen Funktionsmodulen und den allgemeingültigen oder wiedervendbaren Modulen. + - 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. @@ -2283,7 +2279,7 @@ Während diese Anleitung das *Was*, *Warum* und *Wie* erklärt, finde ich es ebe *Warum?*: Interne Anwendungsfunktionen wie allgemeingültige Datenservices werden einfach aufgefunden und innerhalb von `app.core` (wählen Sie ihren Lieblingsnamen für diese Modul) genutzt. - Anmerkung: Dies ist eine Strategie, die die Konstistenz innerhalb einer Anwendung begünstigt. Es gibt hierzu viele gute Möglichkeiten. Wählen Sie eine für sich aus, die stimmig ist, den Regeln von Angular in Puncto Abhängigkeiten folgt und einfach zu verwalten und zu skalieren ist. + Anmerkung: Dies ist eine Strategie, die die Konsistenz innerhalb einer Anwendung begünstigt. Es gibt hierzu viele gute Möglichkeiten. Wählen Sie eine für sich aus, die stimmig ist, den Regeln von Angular in Puncto Abhängigkeiten folgt und einfach zu verwalten und zu skalieren ist. > Meine Strukturen unterscheiden sich von Projekt zu Projekt, aber sie folgen allesamt diesen Richtlinien zur Struktur und Modularität. Auch die Implementierung kann sich in Abhängigkeit der benötigten Funktionen und dem Team unterscheiden. Mit anderen Worten: Bestehen Sie nicht auf die exakte Nachbildung der hier vorgestellten Struktur, sondern passen Sie sie Ihren Gegebenheiten an. Behalten Sie dabei Konsistenz, Wartbarkeit und Effizienz im Hinterkopf. @@ -2373,7 +2369,7 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei - Schreiben Sie Ihre Tests für jede Anforderung. Beginnen Sie mit einem leeren Test und füllen Sie diesen, während Sie den Code für die Anforderung schreiben. - *Warum?*: Testbeschreibungen zu zu verfassen, hilft dabei festzulegen, was in der Anforderung passiert, was nicht passiert und wie ein Testerfolg gemessen werden kann. + *Warum?*: Testbeschreibungen zu verfassen, hilft dabei festzulegen, was in der Anforderung passiert, was nicht passiert und wie ein Testerfolg gemessen werden kann. ```javascript it('Soll einen Avengers-Controller enthalten', function() { @@ -2402,7 +2398,7 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei *Warum?*: Die Nutzung von Jasmin und Mocha ist sehr verbreitet in der Angular-Community. Beide sind stabil, gut gepflegt und liefern robuste Testfunktionen. - Anmerkung: Wenn Sie Moche nutzen, sollten Sie in Erwägung ziehen, eine sogenannte Assert-Library, wie [Chai](http://chaijs.com) zu nutzen. Ich ziehe dem Mocha vor. + Anmerkung: Wenn Sie Mocha nutzen, sollten Sie in Erwägung ziehen, eine sogenannte Assert-Library, wie [Chai](http://chaijs.com) zu nutzen. Ich ziehe dem Mocha vor. ### Testrunner ###### [Style [Y192](#style-y192)] @@ -2413,7 +2409,7 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei *Warum?*: Karma hängt sich leicht von allein in einen CI-Prozess (in Grunt oder Gulb) ein. - *Warum?*: Verschiedene IDE'n wie [WebStorm](http://www.jetbrains.com/webstorm/) und [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225) haben damit begonnen, Karma einzubinden. + *Warum?*: Verschiedene IDEs wie [WebStorm](http://www.jetbrains.com/webstorm/) und [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225) haben damit begonnen, Karma einzubinden. *Warum?*: Karma arbeitet wunderbar mit Task-Managern für Automatisierte Aufgaben wie [Grunt](http://www.gruntjs.com) (with [grunt-karma](https://github.com/karma-runner/grunt-karma)) und [Gulp](http://www.gulpjs.com) (with [gulp-karma](https://github.com/lazd/gulp-karma)) zusammen. @@ -2424,7 +2420,7 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei *Warum?*: Sinon arbeitet wunderbar mit Jasmine und Mocha zusammen und erweitert deren Fähigkeiten der Simulation von Komponenten. - *Warum?*: Sinon erleichtert den Wechsel zwischen Jasmine and Mocha, wenn Sie beide ausprobieren möchten. + *Warum?*: Sinon erleichtert den Wechsel zwischen Jasmine und Mocha, wenn Sie beide ausprobieren möchten. *Warum?*: Sinon liefert gut verständliche, beschreibende Meldung, für den Fall, dass ein Test fehlschlägt. @@ -2442,9 +2438,9 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei - Lassen Sie JSHint über ihre Tests laufen. - *Warum?*: Tests sind Code. JSHint prüft die Codequalität und kann Qualitätsprobleme aufdecken, die dazu führen können, dass Tests nicht saber laufen. + *Warum?*: Tests sind Code. JSHint prüft die Codequalität und kann Qualitätsprobleme aufdecken, die dazu führen können, dass Tests nicht sauber laufen. -### Erleichternde Rahmenbedingungen for JSHint und Regeln für Tests +### Erleichternde Rahmenbedingungen fur JSHint und Regeln für Tests ###### [Style [Y196](#style-y196)] - Lockern sie die JSHint-Regeln für Ihren Testcode, damit `describe` und `expect` erlaubt werden. Lockern sie die Regeln auch für Ausdrücke, da Mocha diese benutzt. @@ -2461,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)] @@ -2470,7 +2466,7 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei *Warum?*: Unit-Tests stehen in direktem Bezug zu einer spezifischen Komponente und Datei im Quellcode. - *Warum?*: Es ist einfacher, sie auf dem neuesten Stand zu halten, weil Sie sie immer im Blick haben. Während Sie programmieren (ob Sie jetzt TDD betreiben oder während oder nach der Entwicklung testen), sind die Testspezifikationen weder aus der Sicht noch aus Ihren Gedanken. Deshalt ist es wahrscheinlicher, dass sie auch gepflegt werden, das dazu beiträgt, die Abdeckung des Codes durch Tests zu verbessern. + *Warum?*: Es ist einfacher, sie auf dem neuesten Stand zu halten, weil Sie sie immer im Blick haben. Während Sie programmieren (ob Sie jetzt TDD betreiben oder während oder nach der Entwicklung testen), sind die Testspezifikationen weder aus der Sicht noch aus Ihren Gedanken. Deshalb ist es wahrscheinlicher, dass sie auch gepflegt werden, das dazu beiträgt, die Abdeckung des Codes durch Tests zu verbessern. *Warum?*: Wenn Sie Code aktualisieren ist es einfacher, auch die Tests im gleichen Zuge zu aktualisieren. @@ -2497,7 +2493,7 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei ### Anwendung ###### [Style [Y210](#style-y210)] - - Benutzen Sie die ["subtle" Animationen von Angular](https://docs.angularjs.org/guide/animations) um zwischen Stati, Views oder primären sichtbaren Elementen hin und her zu wechseln. Binden Sie das [ngAnimate-Modul](https://docs.angularjs.org/api/ngAnimate) ein. Die drei Schlüssel hierzu sind "subtle", "smooth", "seamless". + - Benutzen Sie die ["subtle" Animationen von Angular](https://docs.angularjs.org/guide/animations) um zwischen Status, Views oder primären sichtbaren Elementen hin und her zu wechseln. Binden Sie das [ngAnimate-Modul](https://docs.angularjs.org/api/ngAnimate) ein. Die drei Schlüssel hierzu sind "subtle", "smooth", "seamless". *Warum?*: Angulars "subtle" Animationen können die User-Experience erhöhen, wenn sie entsprechend eingesetzt werden. @@ -2506,9 +2502,9 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei ### Sub Second ###### [Style [Y211](#style-y211)] - - Nutzen Sie eine Kurze Dauer für Animationen. Ich starte immer bei 300ms und passe diese dann entsprechend an. + - Nutzen Sie eine kurze Dauer für Animationen. Ich starte immer bei 300ms und passe diese dann entsprechend an. - *Warum?*: Lange Animationen können sich negativ auf die wahrgenomme Performance auswirken und einen gegentiligen Einfluss auf die User Experience haben und die Anwendung langsam aussehen lassen. + *Warum?*: Lange Animationen können sich negativ auf die wahrgenommene Performance auswirken und einen gegenteiligen Einfluss auf die User Experience haben und die Anwendung langsam aussehen lassen. ### animate.css ###### [Style [Y212](#style-y212)] @@ -2519,7 +2515,7 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei *Warum?*: Bietet Konsistenz Ihrer Animationen. - *Warum?*: animate.css ist weit verbeitet und gut getestet. + *Warum?*: animate.css ist weit verbreitet und gut getestet. Anmerkung: Schauen Sie sich diesen [tollen Beitrag von Matias Niemelä über Angular Animationen](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) an. @@ -2749,7 +2745,7 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei - Erstellen Sie eine Angular-Konstante für die globalen Variablen aus Bibliotheken von Drittanbietern. - *Warum?*: Bietet einen Weg, Bibliotheken von Drittanbietern in einem sicheren Umfeld anzubieten, die andererseits "Globals" wären. Dies verbessert die Testbarkeit, weil Sie so einfacher die Abhängigkeiten Ihres Codes erfahren (verindert lückenhafte Abstraktionen). Auch die Simulation dieser Abhängigkeiten wird zugelassen, wo sie Sinn macht. + *Warum?*: Bietet einen Weg, Bibliotheken von Drittanbietern in einem sicheren Umfeld anzubieten, die andererseits "Globals" wären. Dies verbessert die Testbarkeit, weil Sie so einfacher die Abhängigkeiten Ihres Codes erfahren (verhindert lückenhafte Abstraktionen). Auch die Simulation dieser Abhängigkeiten wird zugelassen, wo sie Sinn macht. ```javascript // constants.js @@ -2767,13 +2763,13 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei ###### [Style [Y241](#style-y241)] - - Benutzen Sie Konstanten für Wete, die sich nicht ändern und nicht aus einem anderen Service kommen. Wenn Konstanten nur für ein bestimmtes Modul gebraucht werden, welches zudem wiederverwendbar sein soll, dann platzieren Sie die Konstanten in einer Datei (pro Modul) und benennen Sie die Datei nach dem Modul. Bis dahin halten Sie die Konstanten im Hauptmodul in einer `constants.js`-Datei. + - Benutzen Sie Konstanten für Werte, die sich nicht ändern und nicht aus einem anderen Service kommen. Wenn Konstanten nur für ein bestimmtes Modul gebraucht werden, welches zudem wiederverwendbar sein soll, dann platzieren Sie die Konstanten in einer Datei (pro Modul) und benennen Sie die Datei nach dem Modul. Bis dahin halten Sie die Konstanten im Hauptmodul in einer `constants.js`-Datei. + + *Warum?*: Ein Wert, der sich ändert - wenn auch nur unregelmäßig - sollte von einem Service ermittelt werden, so dass er nicht im Quellcode geändert werden muss. Zum Beispiel könnte eine URL für einen Datenservice in einer Konstanten abgelegt werden. Besser wäre es aber, diesen Wert über einen WebService zu ermitteln. - *Warum?*: Ein Wert, der sich ändert - wenn auch nur unregelmäßig - sollte von einem Service ermittelt werden, so dass er nicht im Quellcode geändert werden muss. Zum Beispiel könnte eine URL für einen Datenservice in einer Konstanten abgelegt werden. Besser wäre es aber, diesen Wert über einen WebService zu ermitteln. - *Warum?*: Konstanten können in jede Angular-Komponente (auch in einen Provider) eingefügt werden. - *Warum?*: Ist eine Anwendung in Module unterteilt, die in anderen Anwendungen genutzt werden können, so solte jedes alleinstehende Modul für sich selbst funktionieren, eingeschlossen seiner Konstanten. + *Warum?*: Ist eine Anwendung in Module unterteilt, die in anderen Anwendungen genutzt werden können, so sollte jedes alleinstehende Modul für sich selbst funktionieren, eingeschlossen seiner Konstanten. ```javascript // Konstanten für die gesamte Anwendung @@ -2793,7 +2789,7 @@ Unit-Tests tragen dazu bei, sauberen Code zu erhalten. Daher habe ich einige mei **[Zurück zum Anfang](#table-of-contents)** ## Dateitemplates und Snippets -Nutzen Sie Templates oder Snippets, um stimmigen Richtlinien und Mustern zu folgen. Hier sind Templates oder Snippets für einige Editoren und IDE'n zur Webentwicklung. +Nutzen Sie Templates oder Snippets, um stimmigen Richtlinien und Mustern zu folgen. Hier sind Templates oder Snippets für einige Editoren und IDEs zur Webentwicklung. ### Sublime Text ###### [Style [Y250](#style-y250)] @@ -2830,7 +2826,7 @@ Nutzen Sie Templates oder Snippets, um stimmigen Richtlinien und Mustern zu folg - Angular Dateitemplates, die diesen Mustern und Richtlinien folgen. Sie können sie in Ihre WebStorm-Einstellungen importieren: - Laden Sie die [WebStorm Angular Dateitemplates und Snippets](assets/webstorm-angular-file-template.settings.jar?raw=true) herunter - - Öffnen Sie WebStorm and gehen Sie ins `File`-Menü + - Öffnen Sie WebStorm und gehen Sie ins `File`-Menü - Wählen Sie die `Import Settings` Menüoption - Wählen Sie die Datei aus und klicken Sie `OK` @@ -2890,7 +2886,7 @@ Nutzen Sie Templates oder Snippets, um stimmigen Richtlinien und Mustern zu folg // Dies sind Teilsnippets, die zum Verketten gedacht sind: ngmodule // erstellt einen Angular module getter - ngstate // erstellt eine Angular UI Router State-Defintion + ngstate // erstellt eine Angular UI Router State-Definition ngconfig // definiert eine Funktion für die Konfigurationsphase ngrun // definiert eine Funktion für die Run-Phase ngroute // definiert eine Angular ngRoute 'when'-Definition @@ -2949,7 +2945,7 @@ Das Routing auf der Client-Seite ist für die Erstellung eines Navigationsflusse - Benutzen Sie den [AngularUI Router](http://angular-ui.github.io/ui-router/) für das clientseitige Routing. - *Warum?*: Der UI Router bietet alle Funktionen des Angular Routers und zusätzlich Weitere, wie geschachtelte Routen und Stati. + *Warum?*: Der UI Router bietet alle Funktionen des Angular Routers und zusätzlich Weitere, wie geschachtelte Routen und Status. *Warum?*: Die Syntax ähnelt der des Angular Routers und ist einfach auf den UI Router umzustellen. @@ -3025,24 +3021,24 @@ Das Routing auf der Client-Seite ist für die Erstellung eines Navigationsflusse ###### [Style [Y271](#style-y271)] - - Definieren sie die Routen für die Views in den Modulen, in denen sie enthalten sind. Jedes Modul sollte die Routen seiner Views enthalten. + - Definieren Sie die Routen für die Views in den Modulen, in denen sie enthalten sind. Jedes Modul sollte die Routen seiner Views enthalten. *Warum?*: Jedes Modul sollte für sich allein lauffähig sein. *Warum?*: Wird ein Modul zur Anwendung hinzugefügt oder aus ihr ausgeklinkt, enthält die Anwendung nur Routen, die zu vorhanden Views führen. - *Warum?*: Dies erleichtert es, Teile eine Anwendung zu aktivieren oder zu deaktiveren, ohne dass man sich um verwaiste Routen Sorgen machen muss. + *Warum?*: Dies erleichtert es, Teile eine Anwendung zu aktivieren oder zu deaktivieren, ohne dass man sich um verwaiste Routen Sorgen machen muss. **[Zurück zum Anfang](#table-of-contents)** ## Automatisierung von Aufgaben -Nutzen Sie [Gulp](http://gulpjs.com) oder [Grunt](http://gruntjs.com), um Aufgaben zu automatisieren. Bei Gulp geht der Code vor Konfiguration, bei Grund Konfiguration vor Code. Ich persönlich bevorzuge Gulp, weil ich denke, es ist einfacher zu lesen und zu schreiben, aber beide sind erstklassig. +Nutzen Sie [Gulp](http://gulpjs.com) oder [Grunt](http://gruntjs.com), um Aufgaben zu automatisieren. Bei Gulp geht der Code vor Konfiguration, bei Grunt Konfiguration vor Code. Ich persönlich bevorzuge Gulp, weil ich denke, es ist einfacher zu lesen und zu schreiben, aber beide sind erstklassig. -> Erfahren Sie mehr über Gulp und Muster für die Automatisierung von Aufgabenin meinem [Gulp Pluralsight Kurs](http://jpapa.me/gulpps) +> Erfahren Sie mehr über Gulp und Muster für die Automatisierung von Aufgaben in meinem [Gulp Pluralsight Kurs](http://jpapa.me/gulpps) ###### [Style [Y400](#style-y400)] - - Nutzen sie die Automatisierung von Aufgaben, um die Moduldefinitionsdateien `*.module.js` vor allen anderen JavaScript-Dateien in der Anwendung aufzulisten. + - Nutzen Sie die Automatisierung von Aufgaben, um die Moduldefinitionsdateien `*.module.js` vor allen anderen JavaScript-Dateien in der Anwendung aufzulisten. *Warum?*: Angular muss die Moduldefinitionen registrieren, bevor die Module benutzt werden können. @@ -3051,7 +3047,7 @@ Nutzen Sie [Gulp](http://gulpjs.com) oder [Grunt](http://gruntjs.com), um Aufgab ```javascript var clientApp = './src/client/app/'; - // Immer zuerst die Moduldateien + // Immer zuerst die Moduldateien var files = [ clientApp + '**/*.module.js', clientApp + '**/*.js' @@ -3066,7 +3062,7 @@ Nutzen Sie [Gulp](http://gulpjs.com) oder [Grunt](http://gruntjs.com), um Aufgab - Vermeiden Sie es, Filter dazu zu nutzen, alle Eigenschaften in einem komplexen Objektgraphen zu prüfen. Nutzen Sie Filter um Eigenschaften auszuwählen. - *Warum?*: Filter können sehr leicht missbraucht werden und dann die Performance einer Anwendung negativ beeinflussen, wenn sie nicht überlegt eingesetzt werden. Zum Beispiel: Wenn ein Filter auf einen großen und tief geschachtelten Objektgraphen angewendet wird. + *Warum?*: Filter können sehr leicht missbraucht werden und dann die Performance einer Anwendung negativ beeinflussen, wenn Sie nicht überlegt eingesetzt werden. Zum Beispiel: Wenn ein Filter auf einen großen und tief geschachtelten Objektgraphen angewendet wird. **[Zurück zum Anfang](#table-of-contents)** diff --git a/i18n/es-ES.md b/a1/i18n/es-ES.md similarity index 95% rename from i18n/es-ES.md rename to a1/i18n/es-ES.md index 2edc626b..8f86bc62 100644 --- a/i18n/es-ES.md +++ b/a1/i18n/es-ES.md @@ -2,17 +2,13 @@ *Guía de estilos colaborativa de Angular para equipos por [@john_papa](//twitter.com/john_papa)* -*Translation by [Alberto Calleja](https://github.com/AlbertoImpl) and [Gilberto](https://github.com/ingilniero)* - ->The [original English version](http://jpapa.me/ngstyles) is the source of truth, as it is maintained and updated first. - -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), 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é. >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. @@ -27,13 +23,12 @@ 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) 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) @@ -64,9 +59,15 @@ 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. + + *¿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. - El siguiente ejemplo define el módulo `app` y sus dependencias, define un controlador, y defines una fábrica todo en el mismo archivo. + *¿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 factoría todo en el mismo archivo. ```javascript /* evitar */ @@ -175,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 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)** @@ -325,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 */ @@ -445,7 +446,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. @@ -561,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. @@ -619,14 +620,14 @@ 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. ### 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"`. @@ -719,24 +720,24 @@ 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). ### 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). @@ -788,18 +789,18 @@ 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) + ![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 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`. @@ -1013,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 @@ -1219,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; @@ -1357,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) @@ -1745,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). @@ -1967,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 /** @@ -2164,7 +2165,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. @@ -2257,7 +2258,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. @@ -2310,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 @@ -2413,7 +2414,7 @@ Las pruebas unitarias ayudan a mantener el código limpio, así que incluyo algu - Usa [PhantomJS](http://phantomjs.org/) para correr tus pruebas en un servidor. - *¿Por qué?*: PhantomJS es un navegador headless que ayuda a correr las pruebas necesitar una navegador "visual". Así que no necesitas instalar Chrom, Safari u otros navegadores en tu servidor. + *¿Por qué?*: PhantomJS es un navegador headless que ayuda a correr las pruebas necesarias en un navegador "visual". Así que no necesitas instalar Chrome, Safari u otro navegador en tu servidor. Nota: Aún debes testear en todos los navegadores de tu entorno, así como sea apropiado para tu audiencia meta. @@ -2435,7 +2436,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)] @@ -2686,7 +2687,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` @@ -2710,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 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` diff --git a/a1/i18n/fr-FR.md b/a1/i18n/fr-FR.md new file mode 100644 index 00000000..637f7673 --- /dev/null +++ b/a1/i18n/fr-FR.md @@ -0,0 +1,3185 @@ +# Charte stylistique Angular + +*Guide de style subjectif pour Angular par [@john_papa](//twitter.com/john_papa)* + +Si vous cherchez un guide de style pour la syntaxe, les conventions, et la structuration d'applications Angular, alors vous êtes au bon endroit. Ces styles sont basés sur mon expérience de développement avec [Angular](//angularjs.org), mes présentations, [mes cours sur Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) et mon travail au sein de diverses équipes. + +Le but de ce guide de style est de proposer des conseils sur le développement d'applications Angular en exposant les conventions que j'utilise et plus important encore, pourquoi je les ai choisies. + +>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/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. + +Beaucoup de mes styles proviennent des nombreuses séances de pair programming avec [Ward Bell](http://twitter.com/wardbell). Mon ami Ward a assurément contribué à influencer l'évolution ultime de ce guide. + +## Visualiser les styles dans une application d'exemple +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/a1/i18n) sont maintenues par la communauté et peuvent être trouvées ici. + +## Table des matières + + 1. [Responsabilité Unique](#responsabilité-unique) + 1. [IIFE](#iife) + 1. [Modules](#modules) + 1. [Contrôleurs](#contrôleurs) + 1. [Services](#services) + 1. [Factories](#factories) + 1. [Services de données](#services-de-données) + 1. [Directives](#directives) + 1. [Résolution de promesses pour un contrôleur](#résolution-des-promises-pour-un-contrôleur) + 1. [Annoter manuellement les dépendances à injecter](#annotation-manuelle-pour-linjection-de-dépendances) + 1. [Minification et annotation](#minification-et-annotation) + 1. [Gestion des exceptions](#gestion-des-exceptions) + 1. [Nommage](#nommage) + 1. [Architecture L.I.F.T.](#architecture-lift) + 1. [Architecture de l'application](#architecture-de-lapplication) + 1. [Modularité](#modularité) + 1. [Logique d'initialisation](#logique-dinitialisation) + 1. [Services $ d'Angular](#les-services--dangular) + 1. [Tests](#tests) + 1. [Animations](#animations) + 1. [Commentaires](#commentaires) + 1. [JSHint](#jshint) + 1. [JSCS](#jscs) + 1. [Constantes](#constantes) + 1. [Templates et snippets](#modèles-de-fichiers-et-snippets) + 1. [Générateur Yeoman](#générateur-yeoman) + 1. [Routage](#routage) + 1. [Automatisation des tâches](#automatisation-des-tâches) + 1. [Filtres](#filtres) + 1. [Documentation Angular](#documentation) + 1. [Contribuer](#contribuer) + 1. [Licence](#license) + +## Responsabilité unique + +### Règle d'unicité +###### [Style [Y001](#style-y001)] + + - Définissez un composant par fichier. + + L'exemple suivant définit le module `app` et ses dépendances, définit un contrôleur, et définit une factory le tout dans le même fichier. + + ```javascript + /* à éviter */ + angular + .module('app', ['ngRoute']) + .controller('SomeController', SomeController) + .factory('someFactory', someFactory); + + function SomeController() { } + + function someFactory() { } + ``` + + Les même composants sont maintenant séparés dans leurs propres fichiers. + + ```javascript + /* recommandé */ + + // app.module.js + angular + .module('app', ['ngRoute']); + ``` + + ```javascript + /* recommandé */ + + // someController.js + angular + .module('app') + .controller('SomeController', SomeController); + + function SomeController() { } + ``` + + ```javascript + /* recommandé */ + + // someFactory.js + angular + .module('app') + .factory('someFactory', someFactory); + + function someFactory() { } + ``` + +**[Retour en haut de page](#table-des-matières)** + +## IIFE +### Les fermetures (*closures*) JavaScript +###### [Style [Y010](#style-y010)] + + - Encapsulez les composants Angular dans une *Immediately Invoked Function Expression* (IIFE) ou Expression de Fonction Immédiatement Invoquée. + + *Pourquoi ?* : Une IIFE supprime les variables du scope global. Cela aide à éviter que les déclarations de variables et de fonctions ne vivent plus longtemps que prévu dans le scope global, ce qui aide aussi à éviter les collisions de variables. + + *Pourquoi ?* : Lorsque votre code est minifié et embarqué dans un unique fichier pour le déploiement dans un serveur de production, vous pouvez avoir des collisions de variables et de nombreuses variables globales. Une IIFE vous protège contre ces dernières en fournissant un scope différent pour chaque fichier. + + ```javascript + /* à éviter */ + // logger.js + angular + .module('app') + .factory('logger', logger); + + // la fonction logger est ajoutée en tant que variable globale + function logger() { } + + // storage.js + angular + .module('app') + .factory('storage', storage); + + // la fonction storage est ajoutée en tant que variable globale + function storage() { } + ``` + + ```javascript + /** + * recommandé + * + * plus aucune variable globale ne reste après. + */ + + // 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 : Pour des raisons de concision seulement, le reste des exemples de ce guide ne seront pas écrits avec la syntaxe IIFE. + + - Note : Les IIFE empêchent le code de test d'atteindre des membres privés, comme les expressions régulières ou les fonctions utilitaires (*helpers*), qu'il est souvent meilleur de tester indépendamment. Cependant, vous pouvez les tester à travers les membres accessibles ou en les exposant à travers leur propre composant. Par exemple en les plaçant dans leur propre factory ou constante. + +**[Retour en haut de page](#table-des-matières)** + +## Modules + +### Éviter les collisions de nommage +###### [Style [Y020](#style-y020)] + + - Utilisez des conventions de nommages uniques avec des séparateurs pour les sous-modules. + + *Pourquoi ?* : Les noms uniques aident à éviter les collisions de nom de module. Les séparateurs aident à définir les modules et la hiérarchie de leurs sous-modules. Par exemple, `app` pourrait être le module principal (*root*) tandis que `app.dashboard` et `app.users` seraient des sous-modules utilisés en tant que dépendances de `app`. + +### Mutateurs (*Setters*) +###### [Style [Y021](#style-y021)] + + - Déclarez les modules sans variables en utilisant la syntaxe *setter*. + + *Pourquoi ?* : Avec un composant par fichier, on ne devrait pas avoir besoin d'introduire une variable pour le module. + + ```javascript + /* à éviter */ + var app = angular.module('app', [ + 'ngAnimate', + 'ngRoute', + 'app.shared', + 'app.dashboard' + ]); + ``` + + Utilisez à la place la syntaxe *setter*. + + ```javascript + /* recommandé */ + angular + .module('app', [ + 'ngAnimate', + 'ngRoute', + 'app.shared', + 'app.dashboard' + ]); + ``` + +### Accesseurs (*Getters*) +###### [Style [Y022](#style-y022)] + + - Lorsque vous utilisez un module, évitez d'utiliser une variable en utilisant plutôt le chaînage avec la syntaxe *getter*. + + *Pourquoi ?* : Le code est plus lisible et évite les collisions de variables ou les fuites. + + ```javascript + /* à éviter */ + var app = angular.module('app'); + app.controller('SomeController', SomeController); + + function SomeController() { } + ``` + + ```javascript + /* recommandé */ + angular + .module('app') + .controller('SomeController', SomeController); + + function SomeController() { } + ``` + +### Setting ou Getting +###### [Style [Y023](#style-y023)] + + - N'utilisez le *setter* qu'une fois et le *getter* pour toutes les autres instances. + + *Pourquoi ?* : Un module ne devrait être créé qu'une seule fois, et ensuite récupéré à partir de ce point. + + ```javascript + /* recommended */ + + // pour setter un module + angular.module('app', []); + + // pour getter un module + angular.module('app'); + ``` + +### Fonctions nommées ou anonymes +###### [Style [Y024](#style-y024)] + + - Utilisez des fonctions nommées au lieu de passer des fonction anonymes dans les *callbacks*. + + *Pourquoi ?* : Le code plus lisible, est plus facile à déboguer, et réduit l'imbrication des *callbacks*. + + ```javascript + /* à éviter */ + angular + .module('app') + .controller('Dashboard', function() { }) + .factory('logger', function() { }); + ``` + + ```javascript + /* recommandé */ + + // dashboard.js + angular + .module('app') + .controller('Dashboard', Dashboard); + + function Dashboard() { } + ``` + + ```javascript + // logger.js + angular + .module('app') + .factory('logger', logger); + + function logger() { } + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Contrôleurs + +### Syntaxe de la vue avec `controllerAs` +###### [Style [Y030](#style-y030)] + + - Utilisez la syntaxe avec [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) au lieu de la syntaxe classique avec `$scope`. + + *Pourquoi ?* : Les contrôleurs sont construits, recréés, et fournissent une unique nouvelle instance. La syntaxe utilisant `controllerAs` est plus proche de celle d'un constructeur Javascript que la syntaxe classique avec `$scope`. + + *Pourquoi ?* : Elle encourage l'usage du *binding* entre un objet (avec la notation pointée) et la vue (ex. `customer.name` au lieu de `name`). Elle est plus contextuelle, plus facile à lire, et évite tout problème de référence qui peut arriver sans la notation « point ». + + *Pourquoi ?* : Elle permet d'éviter l'usage des appels à `$parent` dans les vues avec des contrôleurs imbriqués. + + ```html + +
+ {{ name }} +
+ ``` + + ```html + +
+ {{ customer.name }} +
+ ``` + +### Syntaxe du contrôleur avec `controllerAs` +###### [Style [Y031](#style-y031)] + + - Utilisez la syntaxe avec `controllerAs` au lieu de la syntaxe de classique avec `$scope`. + + - La syntaxe avec `controllerAs` utilise `this` à l'intérieur des contrôleurs qui se fait *binder* à `$scope` implicitement. + + *Pourquoi ?* : `controllerAs` est une simplification (sucre) syntaxique de `$scope`. Vous pouvez toujours vous *binder* dans la vue et accéder aux méthodes de `$scope`. + + *Pourquoi ?* : Permet d'éviter la tentation d'utiliser les méthodes de `$scope` à l'intérieur d'un contrôleur. Il est par ailleurs, meilleure pratique de les éviter dans les contrôleurs mais plutôt de les déplacer dans une factory. Considérez l'utilisation de `$scope` dans un contrôleur seulement si nécessaire. Par exemple lorsqu'il faut publier ou souscrire à des événements en utilisant [`$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) considérez déplacer ces usages dans une factory et les invoquer depuis le contrôleur. + + ```javascript + /* à éviter */ + function Customer($scope) { + $scope.name = {}; + $scope.sendMessage = function() { }; + } + ``` + + ```javascript + /* recommandé - mais voir la section suivante */ + function Customer() { + this.name = {}; + this.sendMessage = function() { }; + } + ``` + +### `controllerAs` avec `vm` +###### [Style [Y032](#style-y032)] + + - Utilisez une variable de capture pour `this` quand vous utilisez la syntaxe avec `controllerAs`. Choisissez un nom de variable consistent tel que `vm` (pour « ViewModel »). + + *Pourquoi ?* : `this` est contextuel et son utilisation au sein d'une fonction à l'intérieur d'un contrôleur pourrait faire changer son contexte. Capturer le contexte de `this` évite de rencontrer ce problème. + + ```javascript + /* à éviter */ + function Customer() { + this.name = {}; + this.sendMessage = function() { }; + } + ``` + + ```javascript + /* recommandé */ + function Customer() { + var vm = this; + vm.name = {}; + vm.sendMessage = function() { }; + } + ``` + + Note : Vous pouvez évitez n'importe quel avertissement [jshint](http://www.jshint.com/) en plaçant le commentaire suivant au dessus de la ligne de code. Cependant, il n'est pas nécessaire lorsque la fonction est nommée en utilisant la CasseEnMajuscule, puisque cette convention signifie que c'est la fonction est un constructeur. C'est précisément la nature d'un contrôleur dans Angular. + + ```javascript + /* jshint validthis: true */ + var vm = this; + ``` + + Note : Lors de la création de *watchers* dans un contrôleur en utilisant `controlleAs`, vous pouvez *watcher* les différents `vm.*` en utilisant la syntaxe suivante. (Créez des *watchers* avec prudence puisqu'ils ajoutent plus de charge au cycle 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 was %s', original); + $log.info('vm.title is now %s', current); + }); + } + ``` + +### Placement des membres *bindables* au début +###### [Style [Y033](#style-y033)] + + - Placez les membres *bindables* au début du contrôleur, par ordre alphabétique, et non pas dispersés à travers le code du contrôleur. + + *Pourquoi ?* : Placer les membres *bindables* au début permet de faciliter la lecture et vous aide à identifier instantanément quels membres du contrôleur peuvent-être *bindés* et utilisés dans la vue. + + *Pourquoi ?* : Définir des fonctions anonymes *in-line* peut être facile, mais lorsque ces fonctions font plus d'une ligne de code elles peuvent réduire la lisibilité. Définir les fonctions sous les membres *bindables* (les fonctions seront *hoistées*) déplace les détails d'implémentation en bas, gardant les membres *bindables* en haut, + + ```javascript + /* avoid */ + function Sessions() { + var vm = this; + + vm.gotoSession = function() { + /* ... */ + }; + vm.refresh = function() { + /* ... */ + }; + vm.search = function() { + /* ... */ + }; + vm.sessions = []; + vm.title = 'Sessions'; + ``` + + ```javascript + /* recommended */ + 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() { + /* */ + } + ``` + + ![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. + + ```javascript + /* à éviter */ + function Sessions(data) { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = function() { + /** + * Nombreuses lignes + * de + * code + * affectant + * la lisibilité + */ + }; + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + ``` + + ```javascript + /* recommandé */ + function Sessions(sessionDataService) { + var vm = this; + + vm.gotoSession = gotoSession; + vm.refresh = sessionDataService.refresh; // *oneliner* acceptable + vm.search = search; + vm.sessions = []; + vm.title = 'Sessions'; + ``` + +### Déclaration des fonctions pour cacher les détails d'implémentation +###### [Style [Y034](#style-y034)] + + - Utilisez les déclarations de fonctions pour cacher les détails d'implémentation. Gardez vos membres *bindables* en haut. Quand vous avez besoin de *binder* une fonction dans un contrôleur, faites-la pointer vers la déclaration de la fonction plus bas dans le fichier. Ceci est directement lié à la section du placement des membres *bindables* au début. Pour plus de détails, vous pouvez lire [cet article](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). + + *Pourquoi ?* : Placer les membres *bindables* en haut facilite la lecture et vous aide instantanément à identifier quels membres du contrôleur peuvent être *bindés* et utilisés dans la vue. + + *Pourquoi ?* : Placer les détails d'implémentation d'une fonction plus bas dans le fichier permet de masquer la complexité. Ainsi vous ne pouvez voir que les choses importantes en haut. + + *Pourquoi ?* : Les déclarations de fonctions sont *hoistées* donc il n'y a pas problème à utiliser une fonction avant qu'elle ne soit définie (alors que ça serait le cas avec les expressions de fonction). + + *Pourquoi ?* : Vous ne vous préoccuperez plus des déclarations de fonctions déplaçant `var a` avant `var b` cassant ainsi votre code car `a` dépendait de `b`. + + *Pourquoi ?* : L'ordre est critique avec les expressions de fonctions. + + ```javascript + /** + * Evitez + * l'utilisation des expressions de fonctions. + */ + function Avengers(dataservice, 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 dataservice.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + + vm.getAvengers = getAvengers; + + activate(); + } + ``` + + Remarquez que dans l'exemple précédent les choses importantes sont dispersées. Dans l'exemple ci-dessous, vous noterez que le contenu important est en haut. Par exemple, les membres *bindables* au contrôleur tels que `vm.avengers` ou `vm.title`. Les détails d'implémentation sont plus bas. C'est simplement plus facile à lire. + + ```javascript + /* + * recommandé + * Utilisation des déclarations de fonction + * et les membres bindables au début. + */ + function Avengers(dataservice, 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 dataservice.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + } + ``` + +### Déplacez la logique métier dans les services +###### [Style [Y035](#style-y035)] + + - Déplacer la logique d'un contrôleur en la déléguant à des services ou *factories*. + + *Pourquoi ?* : La logique peut être réutilisée par plusieurs contrôleurs lorsqu'elle est placée au sein d'un service et exposée via une fonction. + + *Pourquoi ?* : La logique dans un service peut être facilement isolée pour les tests unitaires, tandis que la logique d'appel dans un contrôleur peut facilement être *mockée*. + + *Pourquoi ?* : Cela supprime des dépendances et cache les détails d'implémentation au contrôleur. + + *Pourquoi ?* : Permet de garder le contrôleur le plus minimal et focalisé possible. + + ```javascript + + /* à éviter */ + function Order($http, $q, config, userInfo) { + var vm = this; + vm.checkCredit = checkCredit; + vm.isCreditOk; + vm.total = 0; + + function checkCredit() { + var settings = {}; + // Obtenir l'URL de base du service crédit à partir de la config + // Positionne les headers requis pour le service crédit + // Prépare l'URL query string ou l'objet de données avec les données de requête. + // Ajoute les infos d'identification de l'utilisateur afin que le service obtienne les bons droits de limite credit pour cet utilisateur. + // Utilise JSONP pour ce navigateur s'il ne supporte pas les CORS + return $http.get(settings) + .then(function(data) { + // Décompresse les données JSON dans l'objet de réponse + // afin de rechercher maxRemainingAmount + vm.isCreditOk = vm.total <= maxRemainingAmount + }) + .catch(function(error) { + // Interpréter l'erreur + // Gérer le timeout ? Réessayer ? Essayer un service alternatif ? + // Re-rejeter avec une erreur appropriée de l'utilisateur final + }); + }; + } + ``` + + ```javascript + /* recommandé */ + 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(showServiceError); + }; + } + ``` + +### Gardez les contrôleurs focalisés +###### [Style [Y037](#style-y037)] + + - Définissez un contrôleur pour une vue, et n'essayez pas de réutiliser le contrôleur pour d'autres vues. Au lieu de cela, déplacez la logique réutilisable vers les *factories* et gardez le contrôleur simple et focalisé sur sa vue. + + *Pourquoi ?*: La réutilisation des contrôleurs sur plusieurs vues est fragilisante pour l'application et une bonne couverture de tests *end-to-end* (*e2e*) est requise afin d'assurer la stabilité sur l'ensemble d'une grosse application. + +### Assignation des contrôleurs +###### [Style [Y038](#style-y038)] + + - Lorsqu'un contrôleur doit être associé à une vue et qu'un composant pourraient être réutilisés par d'autres contrôleurs ou vues, définissez les contrôleurs avec leurs routes. + + Note : Si une vue est chargée via d'autres moyens qu'une route, alors utilisez la syntaxe avec `ng-controller="Avengers as vm"`. + + *Pourquoi ?* : Associer le contrôleur dans la route permet à différentes routes d'invoquer d'autres paires contrôleur-vue. Lorsque les contrôleurs sont assignés dans la vue avec [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), cette vue est toujours associée avec le même contrôleur. + + ```javascript + /* à éviter - lorsque l'utilisation avec une route et une association dynamique est voulue */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html' + }); + } + ``` + + ```html + +
+
+ ``` + + ```javascript + /* recommandé */ + + // route-config.js + angular + .module('app') + .config(config); + + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'Avengers', + controllerAs: 'vm' + }); + } + ``` + + ```html + +
+
+ ``` + +**[Retour en haut de page](#table-des-matières)** + +## Services + +### Singletons +###### [Style [Y040](#style-y040)] + + - Les services sont instanciés avec le mot clé `new`, utilisez `this` pour les méthodes publiques et les variables. Puisque ces derniers sont tellement similaires aux *factories*, utilisez à la place une *factory* pour la cohérence. + + Note : [Tous les services Angular sont des singletons](https://docs.angularjs.org/guide/services). Cela signifie qu'il n'y a qu'une seule instance d'un service donné par injecteur. + + ```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) { + /* */ + } + }; + } + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Factories + +### Responsabilité unique +###### [Style [Y050](#style-y050)] + + - Les *factories* ne devraient avoir qu'une [seule et unique responsabilité](http://en.wikipedia.org/wiki/Single_responsibility_principle), qui serait encapsulée par son contexte. Une fois qu'une *factory* commence à dépasser cet unique cadre, une nouvelle *factory* devrait être créée. + +### Singletons +###### [Style [Y051](#style-y051)] + + - Les *factories* sont des singletons et retournent un objet qui contient les membres du service. + + Note : [Tous les services Angular sont des singletons](https://docs.angularjs.org/guide/services). + +### Membres accessibles au début +###### [Style [Y052](#style-y052)] + + - Placez les membres appelables du services (son interface) en haut, utilisant une technique dérivée du [*Revealing Module Pattern*](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). + + *Pourquoi ?* : Placer les membres appelables au début facilite la lecture et vous aide à identifier instantanément quels membres du service peuvent être appelés et doivent être testés unitairement (et/ou *mockés*). + + *Pourquoi ?* : C'est particulièrement efficace lorsque le fichier devient long et permet d'éviter de faire défiler tout le code pour voir ce qui est exposé. + + *Pourquoi ?* : Placer les fonctions au fil de l'écriture semble facile, mais quand elles font plus d'une ligne, elles peuvent vite réduire la lisibilité et causer plus de défilement. Définir l'interface à appeler via le service retourné déplace les détails d'implémentation plus bas, garde l'interface d'appel en haut, et rend le tout plus facile à lire. + + ```javascript + /* à éviter */ + function dataService() { + var someValue = ''; + function save() { + /* */ + }; + function validate() { + /* */ + }; + + return { + save: save, + someValue: someValue, + validate: validate + }; + } + ``` + + ```javascript + /* recommandé */ + function dataService() { + var someValue = ''; + var service = { + save: save, + someValue: someValue, + validate: validate + }; + return service; + + //////////// + + function save() { + /* */ + }; + + function validate() { + /* */ + }; + } + ``` + + 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/a1/assets/above-the-fold-2.png) + +### Déclaration des fonctions pour cacher les détails d'implémentation +###### [Style [Y053](#style-y053)] + + - Utilisez les déclarations de fonctions pour cacher les détails d'implémentation. Gardez les membres accessibles de la *factory* en haut. Faites-les pointer vers les déclarations de fonction qui apparaissent plus loin dans le fichier. Pour plus de détails, vous pouvez lire [cet article](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). + + *Pourquoi ?* : Placer les membres accessibles en haut facilite la lecture et vous aide instantanément à identifier quels membres de la *factory* peuvent être appelés depuis l'extérieur. + + *Pourquoi ?* : Placer les détails d'implémentation d'une fonction plus bas dans le fichier permet de masquer la complexité. Ainsi vous ne pouvez voir que les choses importantes en haut. + + *Pourquoi ?* : Les déclarations de fonctions sont *hoistées* donc il n'y a pas problème à utiliser une fonction avant qu'elle ne soit définie (alors que ça serait le cas avec les expressions de fonction). + + *Pourquoi ?* : Vous ne vous préoccuperez plus des déclarations de fonctions déplaçant `var a` avant `var b` cassant ainsi votre code car `a` dépendait de `b`. + + *Pourquoi ?* : L'ordre est critique avec les expressions de fonctions. + + ```javascript + /** + * À éviter + * Utilisation des expressions de fonctions + */ + function dataservice($http, $location, $q, exception, logger) { + var isPrimed = false; + var primePromise; + + var getAvengers = function() { + // détails d'implémentation + }; + + var getAvengerCount = function() { + // détails d'implémentation + }; + + var getAvengersCast = function() { + // détails d'implémentation + }; + + var prime = function() { + // détails d'implémentation + }; + + var ready = function(nextPromises) { + // détails d'implémentation + }; + + var service = { + getAvengersCast: getAvengersCast, + getAvengerCount: getAvengerCount, + getAvengers: getAvengers, + ready: ready + }; + + return service; + } + ``` + + ```javascript + /** + * recommandé + * Utilisation des déclarations de fonctions + * et des membres accessibles au début. + */ + 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() { + // détails d'implémentation + } + + function getAvengerCount() { + // détails d'implémentation + } + + function getAvengersCast() { + // détails d'implémentation + } + + function prime() { + // détails d'implémentation + } + + function ready(nextPromises) { + // avec les détails d'implémentation ici + } + } + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Services de données + +### Séparer les appels aux données +###### [Style [Y060](#style-y060)] + + - *Refactorez* la logique pour faire les opérations et interactions avec les données dans une *factory*. Rendez les services de données responsables des appels *XHR*, du *local storage*, du stockage en mémoire, ou de toute autre opération sur les données. + + *Pourquoi ?* : Les responsabilités du contrôleur sont la présentation et l'assemblage des informations pour la vue. Il ne devrait pas avoir à se soucier de la façon dont les données sont récupérées mais seulement de la façon de les demander. Séparer les services de données transforme la logique du contrôleur en logique de « À quel service vais-je demander ces données ? ». Le contrôleur est alors plus simple est plus focalisé sur sa vue. + + *Pourquoi ?* : Cela rend plus facile à tester (*mocké* ou en utilisant le vrai) les appels aux données lorsque l'on teste un contrôleur qui utilise un service de données. + + *Pourquoi ?* : L'implémentation d'un service de données peut contenir du code très spécifique pour gérer le système de données. Cela peut inclure des entêtes, la façon de dialoguer avec les données, ou d'autres services tels que `$http`. Séparer la logique vers un service de données permet d'encapsuler cette logique dans un unique endroit en cachant les détails d'implémentation des consommateurs externes (tel qu'un contrôleur), en rendant également plus simple les changements d'implémentation. + + ```javascript + /* Recommandé */ + + // Factory jouant le rôle de service de données + 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 : Le service de données est appelé par des consommateurs extérieur, tels que des contrôleurs, en leur cachant l'implémentation, comme ci-dessous. + + ```javascript + /* Recommandé */ + + // Contrôleur appelant la factory faisant le service de données + 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; + }); + } + } + ``` + +### Retourner une promesse suite à un appel de données +###### [Style [Y061](#style-y061)] + + - Quand vous appelez un service de données tel que `$http` qui retourne une *promise*, retournez également une *promise* dans votre fonction appelante. + + *Pourquoi ?* : Vous pouvez chaîner les promesses entre elles et entreprendre d'autres actions après que l'appel soit terminé, puis résoudre ou rejeter la promesse. + + ```javascript + /* recommandé */ + + activate(); + + function activate() { + /** + * Etape 1 + * Appelle la fonction getAvengers pour récupérer + * les données «avenger» et attend la promise. + */ + return getAvengers().then(function() { + /** + * Etape 4 + * Exécute une action à la résolution de la promise finale. + */ + logger.info('Activated Avengers View'); + }); + } + + function getAvengers() { + /** + * Etape 2 + * Appel du service de données pour récupérer les données + * et attend la promesse. + */ + return dataservice.getAvengers() + .then(function(data) { + /** + * Etape 3 + * Définit les données et résout la promesse. + */ + vm.avengers = data; + return vm.avengers; + }); + } + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Directives +### Une directive par fichier +###### [Style [Y070](#style-y070)] + + - Créez seulement une directive par fichier. Nommer le fichier en fonction de la directive. + + *Pourquoi ?* : Il est facile de placer toutes les directives dans un fichier, mais il l'est moins de les séparer après coup. Certaines sont partagées dans toute l'application, certaines par modules, et certaines juste par un seul module. + + *Pourquoi ?* : Une directive par fichier leur permet d'être plus facilement maintenables. + + > Note : "**Bonne pratique** : Les directives devraient pouvoir s'auto-nettoyer. Vous pouvez utiliser `element.on('$destroy', ...)` ou bien `scope.$on('$destroy', ...)` pour lancer une fonction de nettoyage quand une directive est enlevée" ... - Documentation d'Angular + + ```javascript + /* à éviter */ + /* directives.js */ + + angular + .module('app.widgets') + + /* directive spécifique pour le module order */ + .directive('orderCalendarRange', orderCalendarRange) + + /* directive pouvant être utilisée n'importe où dans l'application sales */ + .directive('salesCustomerInfo', salesCustomerInfo) + + /* directive pouvant être utilisée n'importe où dans l'application */ + .directive('sharedSpinner', sharedSpinner); + + function orderCalendarRange() { + /* détails d'implémentation */ + } + + function salesCustomerInfo() { + /* détails d'implémentation */ + } + + function sharedSpinner() { + /* détails d'implémentation */ + } + ``` + + ```javascript + /* recommandé */ + /* calendarRange.directive.js */ + + /** + * @desc directive spécifique pour le module order de l'entreprise Acme + * @example
+ */ + angular + .module('sales.order') + .directive('acmeOrderCalendarRange', orderCalendarRange); + + function orderCalendarRange() { + /* détails d'implémentation */ + } + ``` + + ```javascript + /* recommandé */ + /* customerInfo.directive.js */ + + /** + * @desc directive pouvant être utilisée n'importe où dans l'application sales de l'entreprise Acme + * @example
+ */ + angular + .module('sales.widgets') + .directive('acmeSalesCustomerInfo', salesCustomerInfo); + + function salesCustomerInfo() { + /* détails d'implémentation */ + } + ``` + + ```javascript + /* recommandé */ + /* spinner.directive.js */ + + /** + * @desc directive pouvant être utilisée n'importe où dans l'application de l'entreprise Acme + * @example
+ */ + angular + .module('shared.widgets') + .directive('acmeSharedSpinner', sharedSpinner); + + function sharedSpinner() { + /* détails d'implémentation */ + } + ``` + + Note : Il y a plusieurs options de nommage pour les directives puisqu'elles peuvent être utilisées à des échelles (*scopes*) plus ou moins larges. Choisissez un nom qui rend la directive et son fichier clairs et distincts. Des exemples sont définis plus bas mais regardez la section [nommage](#nommage) pour plus de recommandations. + +### Manipuler le DOM dans une directive +###### [Style [Y072](#style-y072)] + + - Quand vous manipulez le DOM directement, utilisez une directive. S'il existe des alternatives, comme par exemple le CSS pour définir les styles ou les [services d'animation](https://docs.angularjs.org/api/ngAnimate), les *templates* Angular , [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) ou [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), utilisez les à la place. Par exemple si la directive affiche ou cache un élément, utilisez ngHide ou ngShow. + + *Pourquoi ?* : La manipulation du DOM peut être difficile à tester, déboguer, et il y a souvent une meilleure façon de faire (ex : CSS, animations, *templates*). + +### Utiliser un unique préfixe de directive +###### [Style [Y073](#style-y073)] + + - Définissez un unique préfixe de directive court et descriptif comme dans `acmeSalesCustomerInfo` et utilisé dans le HTML de la façon suivante : `acme-sales-customer-info`. + + *Pourquoi ?* : Un préfixe court et unique identifie le contexte et l'origine de la directive. Par exemple, un préfixe comme `cc-` peut indiquer que la directive fait partie de l'application CodeCamper tandis que `acme-` peut indiquer une directive de l'entreprise Acme. + + Note : Évitez d'utiliser le préfixe `ng-` car il est réservé pour les directives Angular. Cherchez les directives populaire pour éviter les conflits de nommage, tel que `ion-` pour les directives du framework [Ionic](http://ionicframework.com/). + +### Restreindre aux éléments et aux attributs +###### [Style [Y074](#style-y074)] + + - Lors de la création d'une directive qui fait du sens comme élément indépendant, autorisez la restriction `E` (élément personnalisé) et éventuellement la restriction `A` (attribut personnalisé). En général, s'il devrait avoir son propre contrôle, `E` est le plus indiqué. La convention la plus utilisée est de permettre `EA` mais se dirige vers une implémentation en tant qu'élément lorsque la directive est indépendante et en tant qu'attribut lorsqu'elle augmente son propre élément de DOM. + + *Pourquoi ?* : Cela a du sens. + + *Pourquoi ?* : Même s'il est autorisé d'utiliser une directive en tant que classe, si la directive agit vraiment comme un élément, elle fait plus de sens en tant qu'élément ou au moins en tant qu'attribut. + + Note : EA est la valeur par défaut dans Angular 1.3 + + + ```html + +
+ ``` + + ```javascript + /* à éviter */ + 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 + /* recommandé */ + 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 et `controllerAs` +###### [Style [Y075](#style-y075)] + + - Utilisez la syntaxe avec `controllerAs` avec une directive pour être cohérent avec l'utilisation de `controllerAs` pour l'association de la vue et de son contrôleur. + + *Pourquoi ?* : Ça fait sens et ce n'est pas difficile. + + 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](#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`. + + ```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 // parce que le scope est isolé + }; + + 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) { + // Injection de $scope seulement pour la comparaison + 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 : Vous pouvez aussi nommer le contrôleur au moment où vous l'injectez dans la fonction de `link` et accéder aux attributs de la directive en temps que propriétés du contrôleur. + + ```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)] + + - Utilisez `bindToController = true` lorsque vous utilisez la syntaxe avec `controllerAs` avec une directive quand vous voulez *binder* le *scope* externe au *scope* du contrôleur de la directive. + + *Pourquoi ?* : Cela rend plus facile de *binder* le *scope* externe au *scope* du contrôleur de la directive. + + Note : `bindToController` a été introduit à partir de 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}}
+ ``` + +**[Retour en haut de page](#table-des-matières)** + +## Résolution des *promises* pour un contrôleur +### *Promises* d'activation du contrôleur +###### [Style [Y080](#style-y080)] + + - Résolvez la logique d'initialisation d'un contrôleur dans une fonction `activate`. + + *Pourquoi ?* : Placer la logique d'initialisation toujours au même endroit permet de la rendre plus facile à localiser, plus cohérente à tester, et permet d'éviter sa dispersion à travers le contrôleur. + + *Pourquoi ?* : La fonction `activate` du contrôleur rend pratique la réutilisation de la logique pour un rafraîchissement du contrôleur ou de la vue, garde cette logique en un seul endroit, envoie la vue à l'utilisateur plus rapidement, rend les animations faciles sur `ng-view` ou `ui-view` et rend l'interface plus réactive pour l'utilisateur. + + Note : Si vous avez besoin d'annuler de façon conditionnelle la route avant d'utiliser le contrôleur, utilisez la [résolution de route](#style-y081) à la place. + + ```javascript + /* à éviter */ + function Avengers(dataservice) { + var vm = this; + vm.avengers = []; + vm.title = 'Avengers'; + + dataservice.getAvengers().then(function(data) { + vm.avengers = data; + return vm.avengers; + }); + } + ``` + + ```javascript + /* recommandé */ + 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; + }); + } + } + ``` + +### *Promises* de résolution de route +###### [Style [Y081](#style-y081)] + + - Lorsqu'un contrôleur dépend d'une *promise* qui doit être résolue avant que le contrôleur soit activé, résolvez ces dépendances dans le `$routeProvider` avant que la logique du contrôleur soit exécutée. Si vous avez besoin d'annuler une route de façon conditionnelle avant que le contrôleur soit activé, utilisez un *resolver* de route. + + - Utilisez un *resolver* de route quand vous voulez pouvoir décider d'annuler la route avant même de commencer à naviguer vers la vue. + + *Pourquoi ?* : Un contrôleur pourrait avoir besoin de données avant de se charger. Ces données pourraient provenir d'une promesse via une *factory* ou de [`$http`](https://docs.angularjs.org/api/ng/service/$http). Utiliser une [résolution de route](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) permet à la promesse de se résoudre avant que la logique du contrôleur s’exécute, alors seulement, on peut exécuter les actions basées sur les données fournies via la *promises*. + + *Pourquoi ?* : Le code s’exécute après la route et dans la fonction `activate` du contrôleur. La vue commence à se charger tout de suite. Le *data-binding* démarre quand la *promise* d'activation se résout. Une animation de « chargement » peut être affichée pendant la transition (via `ng-view` ou `ui-view`). + + Note : Le code s’exécute avant la route via une *promise*. Le rejet de la promesse annule le routage. Sa résolution met la future vue en attente de la résolution du routage. Une animation de « chargement » peut être affichée avant la résolution et lorsque la vue change d'état. Si vous voulez aller à la vue plus vite et que vous n'avez pas besoin d'une étape pour décider si vous pouvez l'atteindre, il est conseillé d'utiliser à la place la [technique de la fonction `activate` dans le contrôleur](#style-y080). + + ```javascript + /* à éviter */ + angular + .module('app') + .controller('Avengers', Avengers); + + function Avengers(movieService) { + var vm = this; + // non-résolue + vm.movies; + // résolu de façon asynchrone + movieService.getMovies().then(function(response) { + vm.movies = response.movies; + }); + } + ``` + + ```javascript + /* mieux */ + + // 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; + } + ``` + + Note : L'exemple ci-dessous montre que la résolution du routage pointe vers une fonction nommée, laquelle est plus facile à déboguer et dont l'injection de dépendance est plus facile à gérer. + + ```javascript + /* encore mieux */ + + // 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 : 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)** + +## Annotation manuelle pour l'injection de dépendances + +### Risques pour la minification +###### [Style [Y090](#style-y090)] + + - Évitez l'utilisation de la syntaxe raccourcie pour déclaration de dépendances. Ulilisez plutôt une approche compatible avec la minification. + + *Pourquoi ?* : Les paramètres des composants (ex: contrôleur, *factory*, etc.) vont êtres convertis en variables raccourcies. Par exemple, ˋcommonˋ et ˋdataserviceˋ deviendraient ˋaˋ et ˋbˋ et ne seraient pas trouvées par Angular. + + ```javascript + /* à éviter - non compatible avec la minification */ + angular + .module('app') + .controller('Dashboard', Dashboard); + + function Dashboard(common, dataservice) { + } + ``` + + Ce code pourrait produire des variables raccourcies après minification et provoquer des erreurs à l’exécution. + + ```javascript + /* à éviter - non compatible avec la minification */ + angular.module('app').controller('Dashboard', d);function d(a, b) { } + ``` + +### Identification manuelle des dépendances +###### [Style [Y091](#style-y091)] + + - Utilisez ˋ$injectˋ pour identifier manuellement les dépendances de vos composants Angular. + + *Pourquoi ? * : Cette technique réplique celle utilisée par [`ng-annotate`](https://github.com/olov/ng-annotate), que je recommande afin d'automatiser la création de dépendances compatible avec la minification. Si ˋng-annotateˋ détecte que l'injection a déjà été faite, cela ne la dupliquera pas. + + *Pourquoi ?* : Ça assure la compatibilité de vos dépendances à la minification. Par exemple, ˋcommonˋ et ˋdataserviceˋ pourraient devenir ˋaˋ et ˋbˋ et ne pas être trouvés par Angular. + + *Pourquoi ?* : Pour éviter de créer *inline* une longue liste de dépendances car les longs tableaux sont difficiles à lire. De même, cela peut être gênant de voir un tableau composé d'une série de *strings* alors que son dernier élément est un appel à la fonction du composant. + + ```javascript + /* à éviter */ + angular + .module('app') + .controller('Dashboard', + ['$location', '$routeParams', 'common', 'dataservice', + function Dashboard($location, $routeParams, common, dataservice) {} + ]); + ``` + + ```javascript + /* à éviter */ + angular + .module('app') + .controller('Dashboard', + ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); + + function Dashboard($location, $routeParams, common, dataservice) { + } + ``` + + ```javascript + /* recommandé */ + angular + .module('app') + .controller('Dashboard', Dashboard); + + Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; + + function Dashboard($location, $routeParams, common, dataservice) { + } + ``` + + Note : Lorsque votre fonction se situe sous un `return`, le `$inject` peut ne pas être accessible (cela pourrait arriver dans une directive). Vous pouvez vous en sortir en sortant le contrôleur de la directive. + + ```javascript + /* À éviter */ + // à l'intérieur d'une définition de directive + function outer() { + var ddo = { + controller: DashboardPanelController, + controllerAs: 'vm' + }; + return ddo; + + DashboardPanelController.$inject = ['logger']; // Inatteignable + function DashboardPanelController(logger) { + } + } + ``` + + ```javascript + /* recommandé */ + // à l'exterieur d'une définition de directive + function outer() { + var ddo = { + controller: DashboardPanelController, + controllerAs: 'vm' + }; + return ddo; + } + + DashboardPanelController.$inject = ['logger']; + function DashboardPanelController(logger) { + } + ``` + +### Identification manuelle des dépendances du *route resolver* +###### [Style [Y092](#style-y092)] + + - Utilisez `$inject` pour identifier manuellement vos dépendances du *route resolver* pour les composants Angular. + + *Pourquoi ?* : Cette technique divise la fonction anonyme pour le *route resolver*, le rendant plus facile à lire. + + *Pourquoi ?* : Une instruction `$inject` peut facilement précéder le *resolver* à manipuler rendant n'importe quelle dépendance compatible avec la minification. + + ```javascript + /* recommandé */ + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'AvengersController', + controllerAs: 'vm', + resolve: { + moviesPrepService: moviePrepService + } + }); + } + + moviePrepService.$inject = ['movieService']; + function moviePrepService(movieService) { + return movieService.getMovies(); + } + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Minification et Annotation + +### ng-annotate +###### [Style [Y100](#style-y100)] + + - Utilisez [ng-annotate](//github.com/olov/ng-annotate) pour [Gulp](http://gulpjs.com) ou [Grunt](http://gruntjs.com) et commentez les fonctions qui nécessitent l'injection de dépendances automatique en utilisant `/** @ngInject */`. + + *Pourquoi ?* : Cela prévient votre code d'erreur provenant de dépendances n'utilisant pas les bonnes pratiques au regard de la minification. + + *Pourquoi ?*: [`ng-min`](https://github.com/btford/ngmin) est obsolète. + + >Je préfère Gulp car ça me paraît plus facile à écrire, lire et déboguer. + + Le code suivant n'utilise pas de dépendances compatibles avec la minification. + + ```javascript + angular + .module('app') + .controller('Avengers', Avengers); + + /* @ngInject */ + function Avengers(storageService, avengerService) { + var vm = this; + vm.heroSearch = ''; + vm.storeHero = storeHero; + + function storeHero(){ + var hero = avengerService.find(vm.heroSearch); + storageService.save(hero.name, hero); + } + } + ``` + + Lorsque le code ci-dessus sera exécuté par `ng-annotate`, il produira le résultat suivant avec l'annotation ˋ$injectˋ et deviendra alors compatible avec la minification. + + ```javascript + angular + .module('app') + .controller('Avengers', Avengers); + + /* @ngInject */ + function Avengers(storageService, avengerService) { + var vm = this; + vm.heroSearch = ''; + vm.storeHero = storeHero; + + function storeHero(){ + var hero = avengerService.find(vm.heroSearch); + storageService.save(hero.name, hero); + } + } + + Avengers.$inject = ['storageService', 'avengerService']; + ``` + + Note : Si ˋng-annotateˋ détecte que l'injection a déjà été faite (ex : ˋ@ngInjectˋ a été détecté), il ne dupliquera pas le code ˋ$injectˋ. + + Note : Lors de l'utilisation d'un *route resolver*, vous pouvez préfixer la fonction de résolution avec `/* @ngInject */` et cela produira le code proprement annoté, en gardant toutes les dépendances injectées compatibles avec la minification. + + ```javascript + // En utilisant les annotations @ngInject + function config($routeProvider) { + $routeProvider + .when('/avengers', { + templateUrl: 'avengers.html', + controller: 'Avengers', + controllerAs: 'vm', + resolve: { /* @ngInject */ + moviesPrepService: function(movieService) { + return movieService.getMovies(); + } + } + }); + } + ``` + + > Note : A partir d'Angular 1.3, utilisez le paramètre ˋngStrictDiˋ de la directive [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp) pour détecter un potentiel oubli. Avec ce paramètre, l'injecteur sera créé en mode "strict-di" qui fera échouer les invocations de fonctions de l'application qui n'utiliseraient pas explicitement les annotations de fonctions (et qui rendraient l'application non-minifiable). Des infos de débogage seront alors logguées dans la console pour aider à retrouver code à l'origine de l'alerte. Je préfère utiliser `ng-strict-di` uniquement pour le débogage. + `` + +### Utilisation de Gulp ou Grunt pour `ng-annotate` +###### [Style [Y101](#style-y101)] + + - Utilisez [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) ou [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) dans une tâche de *build* automatisée. Injectez `/* @ngInject */` avant toute fonction qui a des dépendances. + + *Pourquoi ?* : `ng-annotate` va intercepter la plupart des dépendances, mais parfois va nécessiter des indices grâce à l'utilisation de l'ajout de `/* @ngInject */ˋ. + + Le code ci-dessous est un exemple d'une tâche gulp qui utilise `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 est avant uglify pour que le code soit minifié correctement. + .pipe(ngAnnotate({ + // true permet de l'ajouter là où @ngInject n'est pas utilisé. C'est inféré. + // Ne fonctionne pas avec resolve, donc nous devons être explicite ici. + add: true + })) + .pipe(bytediff.start()) + .pipe(uglify({mangle: true})) + .pipe(bytediff.stop()) + .pipe(sourcemaps.write('./')) + .pipe(gulp.dest(pkg.paths.dev)); + }); + + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Gestion des Exceptions + +### Décorateurs +###### [Style [Y110](#style-y110)] + + - Utilisez un [décorateur](https://docs.angularjs.org/api/auto/service/$provide#decorator), au moment de la configuration en utilisant le service [`$provide`](https://docs.angularjs.org/api/auto/service/$provide), sur le service [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) pour effectuer des actions personnalisées lorsque des exceptions se produisent. + + *Pourquoi ?* : Cela fournit un moyen cohérent pour gérer les exceptions non interceptées d'Angular pendant le développement ou à l’exécution. + + Note : Une autre possibilité serait de surcharger le service au lieu d'utiliser un décorateur. C'est une bonne option, mais si vous voulez vous conformer au comportement standard et l'étendre, un décorateur est plus approprié. + + ```javascript + /* recommandé */ + 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 + }; + /** + * On pourrait ajouter l'erreur à une collection d'un service, + * ajouter les erreurs au $rootScope, loguer les erreurs sur un serveur distant, + * ou les loguer localement. Ou alors les rejeter directement. C'est entièrement votre choix. + * throw exception; + */ + toastr.error(exception.msg, errorData); + }; + } + ``` + +### Intercepteurs d'exceptions +###### [Style [Y111](#style-y111)] + + - Créez une *factory* qui expose une interface pour intercepter et gérer correctement les exceptions. + + *Pourquoi ?* : Cela fournit un moyen cohérent pour intercepter les exceptions qui peuvent être déclenchées dans votre code (par exemple, pendant une requête XHR ou lors d'un échec de *promise*). + + Note : L'intercepteur d'exceptions est bon pour intercepter et réagir à des exceptions spécifiques potentielles provenant d'appels qui pourraient en produire. Par exemple, lorsque on fait une requête XHR pour récupérer des données d'un service web distant et que vous voulez intercepter n'importe quelles exceptions provenant de ce service uniquement et réagir seulement à celles-ci. + + ```javascript + /* recommandé */ + 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); + }; + } + } + ``` + +### Erreurs de routage +###### [Style [Y112](#style-y112)] + + - Gérez et loguez toutes les erreurs de routage en utilisant [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). + + *Pourquoi ?* : Pour fournir un moyen cohérent de gestion des erreurs de routage. + + *Pourquoi ?* : Pour fournir potentiellement une meilleure expérience utilisateur si une erreur de routage se produit et rediriger vers un écran approprié avec plus de détails ou les possibilités pour s'en sortir. + + ```javascript + /* recommandé */ + var handlingRouteChangeError = false; + + function handleRoutingErrors() { + /** + * Annulation du routage : + * sur une erreur de routage, aller au dashboard. + * Fournit une clause de sortie s'il essaye de le faire deux fois. + */ + $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 || ''); + + /** + * Loguer éventuellement en utilisant un service personnalisé ou $log. + * (N'oubliez pas d'injecter votre service personnalisé) + */ + logger.warning(msg, [current]); + + /** + * Lors d'une erreur de routage aller sur une autre route / état. + */ + $location.path('/'); + + } + ); + } + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Nommage + +### Conventions de nommage +###### [Style [Y120](#style-y120)] + + - Utilisez des noms cohérents pour tous les composants en utilisant un motif qui décrit la fonctionnalité du composant puis (éventuellement) son type. Le motif que je recommande est `fonctionnalité.type.js`. Il y a deux noms pour la plupart des ressources : + * le nom du fichier (`avengers.controller.js`) + * le nom du composant déclaré à Angular (`AvengersController`) + + *Pourquoi ?* : Les conventions de nommage permettent de de nommer de façon cohérente et de retrouver du contenu en un clin d'œil. La cohérence à toute l'échelle du projet est vitale. La cohérence au sein d'une équipe est importante. La cohérence dans une entreprise apporte une efficacité redoutable. + + *Pourquoi ?* : Les conventions de nommage doivent simplement vous aider à naviguer dans votre code plus vite et le rendre plus facile à comprendre. + +### Nom des fichiers de fonctionnalités +###### [Style [Y121](#style-y121)] + + - Utilisez des noms cohérents pour tous les composants qui suivent un motif qui décrit leur fonctionnalité et (éventuellement) leur type. Le motif que je recommande est `fonctionnalité.type.js`. + + *Pourquoi ?* : Offre une façon cohérente d'identifier rapidement les composants. + + *Pourquoi ?* : Permet de faire *matcher* par nom de fichier au sein d'un processus d'automatisation. + + ```javascript + /** + * Options courantes + */ + + // Contrôleurs + avengers.js + avengers.controller.js + avengersController.js + + // Services/Factories + logger.js + logger.service.js + loggerService.js + ``` + + ```javascript + /** + * recommandé + */ + + // contrôleurs + avengers.controller.js + avengers.controller.spec.js + + // services/factories + logger.service.js + logger.service.spec.js + + // constantes + constants.js + + // definition de module + 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 : Une autre convention courante consiste à nommer les fichiers des contrôleurs sans le mot `controller` dans le nom de fichier comme `avengers.js`au lieu de `avengers.controller.js`. Toutes les autres conventions tiennent à garder le suffixe du type. Le contrôleur étant le type de composant le plus courant, cela permet alors d'économiser à la frappe tout en restant facilement identifiable. Je vous conseille de choisir une convention et de vous y tenir dans toute l'équipe. Ma préference est `avengers.controller.js`. + + ```javascript + /** + * recommandé + */ + // Contrôleurs + avengers.js + avengers.spec.js + ``` + +### Nommage des fichiers de test +###### [Style [Y122](#style-y122)] + + - Le nommage des spécifications de test est similaire à celui des celui du composant qu'il teste avec le suffixe `spec`. + + *Pourquoi ?* : Fournit une façon cohérente d'identifier rapidement les composants. + + *Pourquoi ?* : Permet de faire *matcher* par nom de fichier dans [karma](http://karma-runner.github.io/) ou d'autres moteurs de tests. + + ```javascript + /** + * recommandé + */ + avengers.controller.spec.js + logger.service.spec.js + avengers.routes.spec.js + avenger-profile.directive.spec.js + ``` + +### Nommage des Contrôleurs +###### [Style [Y123](#style-y123)] + + - Utilisez des noms cohérents pour tous les contrôleurs nommés d'après leur fonctionnalité. Utilisez le UpperCamelCase pour les contrôleurs, car ce sont des constructeurs. + + *Pourquoi ?* : Fournit une façon cohérente d'identifier rapidement et de référencer les contrôleurs. + + *Pourquoi ?* : Le UpperCamelCase est la convention pour identifier les objets qui peuvent être instanciés avec un constructeur. + + ```javascript + /** + * recommandé + */ + + // avengers.controller.js + angular + .module + .controller('HeroAvengersController', HeroAvengersController); + + function HeroAvengersController() { } + ``` + +### Suffixe du nom des contrôleurs +###### [Style [Y124](#style-y124)] + + - Ajoutez au nom du contrôleur le suffixe ˋControllerˋ. + + *Pourquoi ?* : Le suffixe ˋControllerˋ est souvent utilisé et il est explicite. + + ```javascript + /** + * recommandé + */ + + // avengers.controller.js + angular + .module + .controller('AvengersController', AvengersController); + + function AvengersController(){ } + ``` + +### Nommage des *factory* et des services +###### [Style [Y125](#style-y125)] + + - Utilisez des noms cohérents pour toutes les *factories* et les services nommés d'après leur fonctionnalités. Utilisez le lowerCamelCase pour les services et les *factories*. Évitez de préfixer les *factories* et les services par `$`. N'ajoutez le suffixe `Service` à leurs noms uniquement si sa nature n'est pas claire (par exemple dans le cas des noms). + + *Pourquoi ?* : Fournit une façon cohérente d'identifier rapidement et de référencer les *factories* et les services. + + *Pourquoi ?* : Permet d'éviter les collisions avec les services et *factories* d'Angular qui ont déjà le préfixe `$`. + + *Pourquoi ?* : Les services aux noms explicites tels que `logger` n'ont pas besoin d'être préfixés. + + *Pourquoi ?* : Des alias tels que 'avengers' sont des noms et ont besoin d'un suffixe. Ce dernier doit être nommé `avengersService`. + + ```javascript + /** + * recommandé + */ + + // logger.service.js + angular + .module + .factory('logger', logger); + + function logger(){ } + ``` + + ```javascript + /** + * recommended + */ + + // credit.service.js + angular + .module + .factory('creditService', creditService); + + function creditService() { } + + // customers.service.js + angular + .module + .service('customersService', customersService); + + function customersService() { } + ``` + +### Nommage des directives +###### [Style [Y126](#style-y126)] + + - Utilisez des noms cohérents pour toutes les directives en utilisant le lowerCamelCase. Utilisez un préfixe court pour décrire le domaine auquel les directives appartiennent (par exemple le préfixe de la société ou du projet). + + *Pourquoi ?* : Fournit une façon cohérente d'identifier rapidement et de référencer les composants. + + ```javascript + /** + * recommandés + */ + + // avenger-profile.directive.js + angular + .module + .directive('xxAvengerProfile', xxAvengerProfile); + + // l'usage est + + function xxAvengerProfile(){ } + ``` + +### Modules +###### [Style [Y127](#style-y127)] + + - 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. + + *Pourquoi ?* : Fournit une façon aisée d'utiliser des processus d'automatisation des tâches afin de charger toutes les définitions de modules en premier, puis ensuite tous les autres fichiers Angular (pour l'assemblage). + +### Configuration +###### [Style [Y128](#style-y128)] + + - Séparez la configuration d'un module dans son propre fichier nommé d'après le module. Un fichier de configuration du module principal ˋappˋ est nommé ˋapp.config.jsˋ (ou simplement ˋconfig.jsˋ). Une configuration pour un module nommé ˋadmin.module.jsˋ est nommé ˋadmin.config.jsˋ. + + *Pourquoi ?* : Sépare la configuration de la définition du module, des composants et du code actif. + + *Pourquoi ?* : Fournit un endroit bien identifié pour régler la configuration d'un module. + +### Routes +###### [Style [Y129](#style-y129)] + + - Séparez la configuration du routage dans son propre fichier. Un exemple pourrait être ˋapp.route.jsˋ pour le module principal et ˋadmin.route.jsˋ pour le module d'administration. Même pour de petites applications, il est préférable de privilégier cette séparation du reste de la configuration. + +**[Retour en haut de page](#table-des-matières)** + +## Architecture L.I.F.T. +### LIFT +###### [Style [Y140](#style-y140)] + + - Structurez votre application afin de pouvoir `L`ocaliser le code plus rapidement, `I`dentifier le code d'un seul coup, garder la structure la plus plate possible (`F`lattest), et essayez (`T`ry) de rester DRY (Don't Repeat Yourself). La structure doit suivre ces quatre règles de base. + + *Pourquoi ?* : L.I.F.T. fournit une structure cohérente qui peut grossir facilement, qui est modulaire, et augmente facilement l'efficacité de développement. Une autre façon de valider la structure de votre application est de vous demander à quelle vitesse vous pouvez ouvrir et travailler dans tous les fichiers liés à une fonctionnalité. + + Lorsque je trouve que ma structure n'est pas confortable, je reviens en arrière et consulte à nouveau les règles L.I.F.T. + + 1. `L`ocaliser notre code est facile. + 2. `I`dentifier le code d'un coup. + 3. `F`lat structure (structure plate) autant que possible. + 4. `T`ry (Essayer) de ne pas se répéter (T-DRY). + +### Localisation +###### [Style [Y141](#style-y141)] + + - Rendez la localisation du code intuitive, simple et rapide. + + *Pourquoi ?* : Je trouve que c'est super important pour un projet. Si l'équipe ne peut pas trouver rapidement les fichiers sur lesquels elle doit travailler, elle ne va pas être en mesure de travailler aussi efficacement que possible, et la structure doit changer. Vous ne connaissez peut-être pas le nom du fichier où la position des fichiers liés, alors les placer dans les endroits les plus intuitifs et proches les uns les autres permet de gagner beaucoup de temps. Une structure de répertoire descriptive peut aider. + + ``` + /bower_components + /client + /app + /avengers + /blocks + /exception + /logger + /core + /dashboard + /data + /layout + /widgets + /content + index.html + .bower.json + ``` + +### Identification +###### [Style [Y142](#style-y142)] + + - Lorsque vous regardez un fichier vous devriez instantanément savoir ce qu'il contient et représente. + + *Pourquoi ?* : Vous passez moins de temps à fouiller et vous perdre pour trouver du code, et devenez de plus en plus efficace. Si ça implique des noms de fichier plus long, alors d'accord. Soyez descriptif avec les noms des fichiers et leur contenu ne doit correspondre qu'à un seul composant. Évitez les fichiers avec plusieurs contrôleurs, plusieurs services, ou un mélange. On pourrait admettre une exception à cette règle si j'ai un ensemble de très petites fonctionnalités qui sont toutes reliées entre elles, elles sont toujours facilement identifiables. + +### Plat +###### [Style [Y143](#style-y143)] + + - Gardez une structure de répertoire à plat le plus longtemps possible. Lorsque vous avez sept fichiers ou plus, commencez à penser à séparer. + + *Pourquoi ?* : Personne ne souhaite rechercher dans sept niveaux de répertoires pour trouver un fichier. Pensez aux menus des sites web… Rien de plus profond que deux niveaux ne devrait être sérieusement pris en considération. Dans une structure de répertoire, il n'y a pas de nombre d'or, mais lorsqu'un répertoire à entre sept et dix fichiers, il serait temps de créer des sous-répertoires. Ajustez cela à votre de confort. Utilisez une structure plus plate jusqu'à ce qu'il y ait un intérêt évident (pour respecter les autres principes L.I.F.T.) à créer un sous-répertoire. + +### T-DRY (Essayer de ne pas se répéter) +###### [Style [Y144](#style-y144)] + + - Ne vous répétez pas (DRY), mais ne sacrifiez pas la lisibilité. + + *Pourquoi ?* : Ne pas se répéter (DRY) est important, mais pas critique si vous en êtes réduit à sacrifier les autres principes L.I.F.T, c'est ce qu'on sous-entend par T-DRY. Je ne veux pas écrire `session-view.html` pour une vue parce que c'est trivial. Si ce n'est pas évident ou si la convention le précise alors nommez-le. + +**[Retour en haut de page](#table-des-matières)** + +## Architecture de l'application + +### Règles globales +###### [Style [Y150](#style-y150)] + + - Vous devez avoir une vue court terme et une vision à long terme. En d'autres mots, commencez petit et gardez en tête là où veut aller votre application. Tout le code de l'application va dans un répertoire de départ nommé `app` à la racine du projet. Tout élément fonctionnel doit être rangé dans son propre et unique fichier. Chaque contrôleur, service, module, vue doit avoir son propre fichier. Tous les scripts externes doivent être rangés dans un autre répertoire à la racine du projet et non dans le répertoire `app`. Le code que l'on a pas écrit ne doit pas se mélanger avec le code de l'application (`bower_components`, `script`, `lib`). + + Note : Vous trouverez plus de détails et de justifications concernant l'architecture sur [ce post original sur la structure des applications](http://www.johnpapa.net/angular-app-structuring-guidelines/). + +### Layout +###### [Style [Y151](#style-y151)] + + - Placez les composants qui définissent l'agencement visuel principal de l'application dans un répertoire nommé `layout`. Il devrait inclure une « vue-enveloppe » et le contrôleur devrait agir comme conteneur pour l'application, la navigation, les menus, les zones de contenu, et les autres régions. + + *Pourquoi ?* : Cela organise tout l'agencement visuel à un seul endroit réutilisé dans toute l'application. + +### Structure « répertoire-fonctionnalité » +###### [Style [Y152](#style-y152)] + + - Créez des répertoires nommés d'après les fonctionnalités qu'elles représentent. Lorsqu'un répertoire grossit jusqu'à atteindre plus de sept fichiers, commencez à penser la création d'un sous-répertoire pour ceux-ci. Si votre seuil est différent, ajustez au besoin. + + *Pourquoi ?* : Un développeur peut localiser, identifier ce que représente chaque fichier en une seule fois, la structure est aussi plate que possible, et il n'y a ni répétitions ni redondance de nommage. + + *Pourquoi ?* : Les règles L.I.F.T. sont toutes respectées. + + *Pourquoi ?* : Aide à diminuer l'entropie de l'application en organisant le contenu et en le maintenant en accord avec les principes L.I.F.T. + + *Pourquoi ?* : Lorsqu'il y a de nombreux fichiers (plus de dix), les repérer est plus facile avec une structure de répertoires cohérente que dans une structure à plat. + + ```javascript + /** + * recommandé + */ + + 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 + ``` + + ![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é ». + + ```javascript + /* + * à éviter + * Alternative aux « répertoires-type » : + * Je vous conseille les « répertoires-fonctionnalité » + */ + + 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 + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Modularité + +### Nombreux petits modules auto-suffisants +###### [Style [Y160](#style-y160)] + + - Créez de petits modules qui encapsulent une seule responsabilité. + + *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)] + + - Créez un module racine pour l'application dont le rôle est d'assembler tous les modules et fonctionnalités de votre application. Nommez-le comme votre application. + + *Pourquoi ?* : Angular encourage la modularité et la séparation des responsabilités. La création d'un module racine pour l'application dont le rôle est de lier ensemble des autres modules fournit un moyen très direct d'ajouter et de retirer des modules à votre application. + +### Garder le module applicatif léger +###### [Style [Y162](#style-y162)] + + - Ne placez dans le module applicatif que la logique d'assemblage de l'application. Laissez les fonctionnalités dans leurs propres modules. + + *Pourquoi ?* : Ajouter des responsabilités supplémentaires à la racine de l'application pour récupérer des données, afficher des vues, ou toute autre logique non reliée à l'assemblage de l'application trouble le module applicatif et rend plus difficile à réutiliser ou éteindre les ensembles de fonctionnalités. + + *Pourquoi ?* : Le module applicatif devient une déclaration qui montre quels modules aident à constituer l'application. + +### Les macro-fonctionnalités sont des modules +###### [Style [Y163](#style-y163)] + + - Créez des modules qui représentent des macro-fonctionnalités, comme l'agencement graphique (*layout*), les services réutilisables et partagés, les dashboards, et les fonctionnalités applicatives spécifiques (par exemple : clients, admin, ventes). + + *Pourquoi ?* : Les modules auto-suffisants peuvent être ajoutés à l'application avec peu ou pas de friction. + + *Pourquoi ?* : Les sprints ou itérations peuvent être focalisés sur les macro-fonctionnalités. Elles peuvent être activées à la fin de ceux-ci. + + *Pourquoi ?* : Séparer les macros-fonctionnalités en modules les rend plus facile à tester de façon isolée et à la réutilisation. + +### Les Blocks Réutilisables en tant que Modules +###### [Style [Y164](#style-y164)] + + - Créez des modules qui représentent des blocs réutilisables dans l'application pour les services en commun tels que la gestion des exceptions, les logs, les diagnostics, la sécurité et la gestion des données locale. + + *Pourquoi ?* : Ces types de fonctionnalités sont requises dans de nombreuses application. Donc en les gardant séparées dans leur propres modules, elles peuvent être génériques et peuvent être réutilisées pour d'autres applications. + +### Dépendances entre modules +###### [Style [Y165](#style-y165)] + + - 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/a1/assets/modularity-1.png) + + *Pourquoi?* : Le module principal de l'application continent manifeste des fonctionnalités de l'application. + + *Pourquoi ?* : Chaque groupe de fonctionnalités contient une déclaration de ce dont il dépend, de ce fait il peut être importé comme dépendance dans d'autres applications et continuer à fonctionner. + + *Pourquoi ?* : Les fonctionnalités propres à l'application telles que les services de données partagées deviennent faciles à repérer et partager au sein d'un `app.core` (choisissez un nom de votre choix pour ce module). + + Note : C'est une stratégie pour la cohérence. Il y a ici beaucoup de bons choix. Choisissez-en une qui soit cohérente, qui suit les règles des dépendances d'Angular, et qui facilite la maintenance et la montée en charge. + + > Mes structures peuvent varier légèrement entre les projets mais elles suivent toutes ces règles pour la structure et la modularité. L'implémentation peut varier en fonction des fonctionnalités et de l'équipe. En d'autres termes, ne vous paralysez pas sur une structure exactement semblable mais pensez votre structure en termes de cohérence, maintenabilité, et efficacité. + + > Dans de petites applications, vous pouvez aussi mettre toutes vos dépendances partagées dans le module applicatif où les modules fonctionnels n'ont pas de dépendances directes. Cela pourra rendre la maintenance de petites applications plus facile, mais rend difficile la réutilisation de ces modules en dehors de celle-ci. + +**[Retour en haut de page](#table-des-matières)** + +## Logique d'initialisation + +### Configuration +###### [Style [Y170](#style-y170)] + + - Injectez le code à l'intérieur d'une [configuration de module](https://docs.angularjs.org/guide/module#module-loading-dependencies) qui doit être configurée avant l’exécution de l'application Angular. Parmi les candidats idéaux, on trouve les *providers* et les constantes. + + *Pourquoi ?* : Cela rend les choses plus faciles d'avoir le moins d'endroits possibles pour la 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: ' + }); + } + } + ``` + +### Blocs `run` +###### [Style [Y171](#style-y171)] + + - Tout code qui nécessite de s’exécuter lorsque l'application s'initialise devrait être déclaré dans une *factory*, exposé via une fonction, et injecté dans le [bloc run](https://docs.angularjs.org/guide/module#module-loading-dependencies). + + *Pourquoi ?* : Le code directement écrit dans un bloc `run` peut être difficile à tester. Le placer dans une *factory* le rend plus facile à abstraire et à *mocker*. + + ```javascript + angular + .module('app') + .run(runBlock); + + runBlock.$inject = ['authenticator', 'translator']; + + function runBlock(authenticator, translator) { + authenticator.initialize(); + translator.initialize(); + } + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Les services `$` d'Angular + +### `$document` et `$window` +###### [Style [Y180](#style-y180)] + + - Utilisez [`$document`](https://docs.angularjs.org/api/ng/service/$document) et [`$window`](https://docs.angularjs.org/api/ng/service/$window) au lieu de `document` et `window`. + + *Pourquoi ?* : Ces services sont *wrappés* par Angular et plus facilement testables qu'en utilisant document et window dans les tests. Ils vous aident à éviter d'avoir à *mocker* `document` et `window` vous-même. + +### $timeout et $interval +###### [Style [Y181](#style-y181)] + + - Utilisez [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) et [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) au lieu de `setTimeout` et `setInterval`. + + *Pourquoi ?* : Ces services sont *wrappés* par Angular et plus facilement testables et gèrent le cycle de *digest* d'Angular conservant un *data-binding* synchronisé. + +**[Retour en haut de page](#table-des-matières)** + +## Tests +Les tests unitaires aident à maintenir un code source propre, ainsi j'ai inclus quelques unes de mes recommandations sur les bases des tests unitaires avec des liens pour plus d'informations. + +### Écrire les tests avec des scenario +###### [Style [Y190](#style-y190)] + + - Écrivez un ensemble de tests pour chaque scenario. Commencer avec un test vide et complétez-les à mesure que vous écrivez le code pour le scenario. + + *Pourquoi ?* : Écrire les descriptions de tests aident à définir clairement ce que votre scenario devra faire, ne devra pas faire et comment mesurer la réussite. + + ```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?) + }); + + // et ainsi de suite + ``` + +### Librairie de test +###### [Style [Y191](#style-y191)] + + - Utilisez [Jasmine](http://jasmine.github.io/) or [Mocha](http://mochajs.org) pour les tests unitaires. + + *Pourquoi ?* : Jasmine et Mocha sont toutes deux largement utilisées dans la communauté Angular. Toutes les deux sont stables, bien maintenues, et fournissent des fonctionnalités de test robustes. + + Note : Lorsque vous utilisez Mocha, utilisez aussi une librairie d'assertion telle que [Chai](http://chaijs.com). Je prefère Mocha. + +### Lanceur de Test +###### [Style [Y192](#style-y192)] + + - Utilisez [Karma](http://karma-runner.github.io) comme lanceur de test. + + *Pourquoi ?* : Karma est facile à configurer pour lancer les tests ponctuellement ou automatiquement lorsqu'un changement est fait dans le code. + + *Pourquoi ?* : Karma s'intègre facilement dans votre processus d'intégration continue soit seul ou via Grunt ou Gulp. + + *Pourquoi ?* : Quelques EDI commencent à s'intégrer avec Karma, c'est le cas de [WebStorm](http://www.jetbrains.com/webstorm/) et [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). + + *Pourquoi ?* : Karma fonctionne bien avec les principaux outils d'automatisation de tâches tel que [Grunt](http://www.gruntjs.com) (avec [grunt-karma](https://github.com/karma-runner/grunt-karma)) ou [Gulp](http://www.gulpjs.com). Si vous utilisez Gulp, utilisez [Karma](https://github.com/karma-runner/karma) directement plutôt que via un plugin, son API peut s'utiliser directement. + + ```javascript + /* recommended */ + + // Exemple de Gulp fonctionnant directement avec Karma + 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(); + } + } + } + ``` + +### `stub` et les `spy` +###### [Style [Y193](#style-y193)] + + - Utilisez [Sinon](http://sinonjs.org/) pour les `stub` et les `spy`. + + *Pourquoi ?* : Sinon fonctionne bien avec Jasmine et Mocha et étend les fonctionnalités de `stub` et de `spy` qu'ils offrent. + + *Pourquoi ?* : Sinon rend plus facile l'alternance entre Jasmine et Mocha, si vous voulez essayer les deux. + + *Pourquoi ?* : Sinon fournit des messages descriptifs quand les tests ne valident pas les assertions. + +### Navigateur sans interface graphique +###### [Style [Y194](#style-y194)] + + - Utilisez [PhantomJS](http://phantomjs.org/) pour exécuter les tests sur un serveur. + + *Pourquoi?* : PhantomJS est un navigateur sans interface graphique qui peut vous aider à exécuter les tests sans avoir besoin d'un navigateur "visuel". Ainsi vous n'avez pas besoin d'installer Chrome, Safari, IE, ou d'autres navigateurs sur votre serveur. + + Note : Que cela ne vous dispense pas de tester sur tous les navigateurs dans votre environnement,en fonction des clients que vous ciblez. + +### Analyse du code +###### [Style [Y195](#style-y195)] + + - Exécutez JSHint sur vos tests. + + *Pourquoi ?* : Les tests c'est aussi du code. JSHint peut vous aider à identifier les problèmes de qualité de code qui pourrait amener les tests à fonctionner de façon incorrecte. + +### Assouplissement des règles de JSHint avec les variables globales dans les tests +###### [Style [Y196](#style-y196)] + + - Assouplissez les règles sur votre code de test afin de permettre l'usage des variables globales courantes telles que `describe` et `expect`. Assouplissez les règles pour les expressions puisque Mocha les utilise. + + *Pourquoi ?* : Vos tests sont du code et requièrent à ce titre la même attention avec les mêmes règles de qualité de code que votre code de production. Cependant, les variables globales utilisées par les *frameworks* de test, par exemple, peuvent être négligées en les incluant dans les spécifications de test. + + ```javascript + /* jshint -W117, -W030 */ + ``` + Ou alors vous pouvez rajouter le snippet suivant à votre fichier d'options JSHint. + + ```javascript + "jasmine": true, + "mocha": true, + ``` + + ![Outils de test](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/a1/assets/testing-tools.png) + +### Organisation des tests +###### [Style [Y197](#style-y197)] + + - Placez les fichiers des tests unitaires (specs) parallèle au code client. Placez les specs qui sont en charge de l'intégration avec le serveur ou celles qui testent plusieurs composants dans un répertoire `tests` séparé. + + *Pourquoi ?* : Les tests unitaires sont en corrélation directe avec les composants et fichiers qu'ils testent dans le code source. + + *Pourquoi ?* : Il est plus facile de les mettre à jour puisqu'ils sont toujours visibles. Quand vous développez, que vous fassiez du TDD, des tests en même temps que l'implémentation ou des tests après l'implémentation, les spécifications ne sont jamais loin ni des yeux ni de l'esprit, et ainsi ils ont plus de chance d'être maintenus, ce qui permet aussi de tenir une bon *coverage*. + + *Pourquoi ?* : Quand vous mettez à jour le code source, il est plus facile de mettre à jour les tests en même temps. + + *Pourquoi ?* : Les placer en parallèle les rend plus facile à trouver et facile à déplacer si vous déplacez les sources. + + *Pourquoi ?* : Avoir les spécifications proches permet au lecteur d'apprendre comment le composant est supposé être utilisé et découvrir ses limitations connues. + + *Pourquoi ?* : Séparer les spécifications de test afin qu'ils ne soient pas inclus dans le *build* est facile avec grunt ou gulp. + + ``` + /src/client/app/customers/customer-detail.controller.js + /customer-detail.controller.spec.js + /customers.controller.spec.js + /customers.controller-detail.spec.js + /customers.module.js + /customers.route.js + /customers.route.spec.js + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Animations + +### Utilisation +###### [Style [Y210](#style-y210)] + + - Utilisez de subtiles [animations avec Angular](https://docs.angularjs.org/guide/animations) pour les transitions entre les états pour les vues et les éléments visuels de base. Incluez le [module ngAnimate](https://docs.angularjs.org/api/ngAnimate). Les trois clés sont la subtilité, la fluidité, l’homogénéité. + + *Pourquoi ?* : Des animations subtiles peuvent améliorer l'expérience utilisateur lorsqu'elles sont utilisées de façon appropriées. + + *Pourquoi ?* : Des animations subtiles peuvent améliorer les performances perçues lorsque les vues changent. + +### Moins d'une Seconde +###### [Style [Y211](#style-y211)] + + - Utilisez de courtes durées pour les animations. Je commence en général par 300ms et j'ajuste jusqu'à ce que le résultat soit celui attendu. + + *Pourquoi ?* : Les animations longues peuvent avoir des effets inverses sur l'expérience utilisateur et les performances perçues en donnant l'impression d'une application lente. + +### animate.css +###### [Style [Y212](#style-y212)] + + - Utilisez [animate.css](http://daneden.github.io/animate.css/) pour les animations classiques. + + *Pourquoi ?* : Les animations que fournit animate.css sont rapides, fluides et faciles à ajouter à votre application. + + *Pourquoi ?* : Fournit de la cohérence à vos animations. + + *Pourquoi ?* : animate.css est largement utilisée et testé. + + Note : Lire ce [super post par Matias Niemelä sur les animations Angular](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) + +**[Retour en haut de page](#table-des-matières)** + +## Commentaires + +### jsDoc +###### [Style [Y220](#style-y220)] + + - Si vous prévoyez de documenter votre code source, utilisez la syntaxe [`jsDoc`](http://usejsdoc.org/) pour documenter les noms des fonctions, leur descriptions, paramètres et valeurs de retour. Utilisez `@namespace` et `memberOf` pour s'adapter à l'architecture de votre application. + + *Pourquoi ?* : Vous pouvez générer (et regénérer) la documentation à partir de votre code, au lieu de l'écrire intégralement. + + *Pourquoi ?* : Cela permet d'avoir de la cohérence grâce un outil industriel standard. + + ```javascript + /** + * Factory de logger + * @namespace Factories + */ + (function() { + angular + .module('app') + .factory('logger', logger); + + /** + * @namespace Logger + * @desc Logger de niveau applicatif + * @memberOf Factories + */ + function logger($log) { + var service = { + logError: logError + }; + return service; + + //////////// + + /** + * @name logError + * @desc Logue les erreurs + * @param {String} msg Le message à loguer + * @returns {String} + * @memberOf Factories.Logger + */ + function logError(msg) { + var loggedMsg = 'Error: ' + msg; + $log.error(loggedMsg); + return loggedMsg; + }; + } + })(); + ``` + +**[Retour en haut de page](#table-des-matières)** + +## JSHint + +### Utilisation d'un fichier d'options +###### [Style [Y230](#style-y230)] + + - Utilisez JSHint pour analyser votre JavaScript et assurez-vous d'avoir personnalisé son fichier d'options et incluez le dans le système de versioning. Lire la [documentation de JSHint](http://www.jshint.com/docs/) pour avoir les détails de chaque option. + + *Pourquoi ?* : Cela fournit une première alerte avant de *committer* son code dans le système de versioning. + + *Pourquoi ?* : Fournit de la cohérence dans votre équipe. + + ```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 + } + } + ``` + +**[Retour en haut de page](#table-des-matières)** + +## JSCS + +### Utiliser un fichier de configuration +###### [Style [Y235](#style-y235)] + + - Utilisez JSCS pour valider vos styles de code pour votre JavaScript et pensez à personnaliser vos options pour JSCS et de l'inclure dans votre gestionnaire de versioning. Vous pouvez consulter la [documentation de JSCS](http://www.jscs.info) pour voir les détails et les options. + + *Pourquoi ?* : Fournit une première alerte avant de *commiter* sur votre gestionnaire de versioning. + + *Pourquoi ?* : Permet d'assurer une cohérence au sein de votre équipe. + + ```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 + } + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Constantes + +### Globales des Librairies Externes +###### [Style [Y240](#style-y240)] + + - Créez une constante Angular pour les variables globales des librairies externes. + + *Pourquoi ?* : Fournit un moyen d'injecter des librairies tierces qui seraient sinon des globales. Cela améliore la testabilité du code en vous permettant de savoir plus facilement quelles sont les dépendances de vos composants (et évite de faire des abstractions qui fuient). Ça vous permet aussi de *mocker* ces dépendances, là où cela fait sens. + + ```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)] + + - Utilisez des constantes pour les valeurs qui ne changent pas et ne viennent pas d'un autre service. Quand des constantes ne sont utilisées que par un module qui peut être réutilisé dans d'autres applications, placez les constantes dans un seul fichier par module nommé comme le module. Tant que c'est possible, gardez les constantes dans le module principal dans un fichier `constants.js`. + + *Pourquoi ?* : Une valeur qui peut changer, même rarement, devrait être récupérée d'un service afin de ne pas avoir à changer le code source. Par exemple, une URL pour un service de données pourrait être définie comme constante mais il serait mieux de lire cette valeur par appel à un web service. + + *Pourquoi ?* : Les constantes peuvent être injectées dans un composant Angular, y compris les *providers*. + + *Pourquoi ?* : Quand une application est divisée en modules qui peuvent être réutilisés dans d'autres applications, chacun de ces modules individuels devrait pouvoir fonctionner tout seul, y compris avec les constantes dépendantes. + + ```javascript + // Constantes utilisées par toute l'appli + angular + .module('app.core') + .constant('moment', moment); + + // Constantes utilisées seulement par le module de vente + angular + .module('app.sales') + .constant('events', { + ORDER_CREATED: 'event_order_created', + INVENTORY_DEPLETED: 'event_inventory_depleted' + }); + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Modèles de fichiers et *snippets* +Utilisez des *templates* de fichiers ou des *snippets* pour vous aider à suivre des styles et des *patterns* cohérents. Voici des *templates* et/ou *snippets* pour quelques uns des éditeurs de texte pour le développement web et EDIs. + +### Sublime Text +###### [Style [Y250](#style-y250)] + + - *Snippets* Angular conformes avec ces styles et règles. + + - Téléchargez les [Snippets Angular pour Sublime](assets/sublime-angular-snippets.zip?raw=true) + - Placez-les dans votre répertoire `Package` + - Redémarrez Sublime + - Dans un fichier de type JavaScript, tapez ces commandes suivies par la touche `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)] + + - Les *templates* de fichiers qui suivent ces styles et règles peuvent être trouvées sur [SideWaffle](http://www.sidewaffle.com) + + - Téléchargez l'extension [SideWaffle](http://www.sidewaffle.com) pour Visual Studio (fichier vsix) + - Exécutez le fichier vsix + - Redémarrez Visual Studio + +### WebStorm +###### [Style [Y252](#style-y252)] + + - Vous pouvez importer dans les paramètres de WebStormLes les *templates* de fichiers et les *snippets* Angular qui suivent ces styles et ces règles : + + - Téléchargez les [*templates* de fichiers et *snippets* WebStorm pour Angular](assets/webstorm-angular-file-template.settings.jar?raw=true) + - Ouvrez WebStorm et allez dans le menu `File` + - Choisissez le menu `Import Settings` + - Sélectionnez le fichier et cliquez sur `OK` + - Dans un fichier de type JavaScript, tapez ces commandes suivies de la touche `TAB` : + + ```javascript + ng-c // crée un contrôleur Angular + ng-f // crée une factory Angular + ng-m // crée un module Angular + ``` + +### Atom +###### [Style [Y253](#style-y253)] + + - *Snippets* Angular qui suivent ces styles et ces règles. + ``` + apm install angularjs-styleguide-snippets + ``` + or + - Ouvrez Atom puis son *package manager* (Packages -> Settings View -> Install Packages/Themes) + - Cherchez le *package* 'angularjs-styleguide-snippets' + - Cliquez sur 'Install' pour installer le *package* + + - Dans un fichier JavaScript tapez ces commandes suivies de `TAB` : + + ```javascript + ngcontroller // Crée un contrôleur Angular + ngdirective // Crée une directive Angular + ngfactory // Crée une factory Angular + ngmodule // Crée un module Angular + ngservice // Crée un service Angular + ngfilter // Crée un filter Angular + ``` + +### Brackets +###### [Style [Y254](#style-y254)] + + - *Snippets* Angular qui suivent ces styles et ces règles. + - Téléchargez les [*snippets* Angular pour Brackets](assets/brackets-angular-snippets.yaml?raw=true). + - Brackets Extension manager ( File > Extension manager ) + - Installez ['Brackets Snippets (by edc)'](https://github.com/chuyik/brackets-snippets). + - Cliquez sur l'ampoule dans la marge droite de Brackets. + - Cliquez sur `Settings` puis sur `Import` + - Choisissez le fichier et sélectionnez `skip`ou `override`. + - Cliquez sur `Start Import` + + - Dans un fichier JavaScript tapez ces commandes suivies de `TAB` : + + ```javascript + // Snippets de fichiers complets avec IIFE + ngcontroller // Crée un controller Angular + ngdirective // Crée une directive Angular + ngfactory // Crée une factory Angular + ngapp // Crée un module Angular + ngservice // Crée un service Angular + ngfilter // Crée un filter Angular + + // Snippets à chaîner + ngmodule // Crée an Angular module getter + ngstate // Crée an Angular UI Router state définition + ngconfig // Définit une fonction de configuration + ngrun // Définit une fonction run + ngroute // Définit une clause `when` pour ngRoute + ngtranslate // Utilise le service `$translate` avec sa promise + ``` + +### vim +###### [Style [Y255](#style-y255)] + + - *Snippets* pour vim qui suivent ces styles et ces règles. + + - Téléchargez les [*snippets* vim pour Angular](assets/vim-angular-snippets?raw=true) + - Réglez [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) + - Copiez les *snippets* dans le répertoire approprié. + + ```javascript + ngcontroller // Crée un contrôleur Angular + ngdirective // Crée une directive Angular + ngfactory // Crée une factory Angular + ngmodule // Crée un module Angular + ngservice // Crée un service Angular + ngfilter // Crée un filter Angular + ``` + + +**[Retour en haut de page](#table-des-matières)** + +## Générateur Yeoman +###### [Style [Y260](#style-y260)] + +Vous pouvez utiliser le [générateur Yeoman HotTowel](http://jpapa.me/yohottowel) pour créer une application Angular qui servira de point de départ et qui suis cette charte stylistique. + +1. Installer `generator-hottowel` + + ``` + npm install -g generator-hottowel + ``` + +2. Créer un nouveau répertoire et aller dans ce répertoire + + ``` + mkdir myapp + cd myapp + ``` + +3. Exécuter le générateur + + ``` + yo hottowel helloWorld + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Routage +Le routage côté client est important pour créer un flux de navigation entre les vues et composer des vues constituées de nombreux *templates* et directives de plus petites tailles. + +###### [Style [Y270](#style-y270)] + + - Utilisez le [routeur d'AngularUI](http://angular-ui.github.io/ui-router/) pour faire le routage côté client. + + *Pourquoi ?* : `ui-router` offre toutes les fonctionnalités du routeur d'Angular et en ajoute quelques unes parmi lesquelles les routes imbriquées et les états. + + *Pourquoi ?* : La syntaxe est similaire à celle du routeur Angular par défaut et il est facile de migrer vers `ui-router`. + + - Note: Vous pouvez utiliser un *provider* tel que `routerHelperProvider` montré ci-dessous pour vous aidez à configurer les états à travers les fichiers pendant la phase de `run`. + + ```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)] + + - Définissez les routes pour les vues d'un module à l'endroit où elles existent. Chaque module devrait contenir le routage de ses vues. + + *Pourquoi ?* : Chaque module devrait être indépendant. + + *Pourquoi ?* : Si on ajoute ou enlève un module, on souhaite que l'application ne contienne que des routes qui aboutissent sur des vues existantes. + + *Pourquoi ?* : Cela rend facile l'activation ou la désactivation de portions de l'application sans se préoccuper d'avoir des routes orphelines. + +**[Retour en haut de page](#table-des-matières)** + +## Automatisation des Tâches +Utilisez [Gulp](http://gulpjs.com) ou [Grunt](http://gruntjs.com) pour créer des tâches automatisées. Gulp favorise le code plutôt que la configuration tandis que Grunt tend vers la configuration plutôt que le code. À titre personnel je préfère Gulp car il me semble plus facile à lire et écrire, mais les deux sont excellents. + +> Apprenez-en plus sur Gulp et les *patterns* pour l'automatisation en allant voir [mon cours sur Pluralsight](http://jpapa.me/gulpps) + +###### [Style [Y400](#style-y400)] + + - Utilisez l'automatisation des tâches pour lister les fichiers de définition de module `*.module.js` avant tout autre fichier JavaScript de l'application. + + *Pourquoi ?* : Angular a besoin que la définition des modules soit faite avant qu'ils puissent être utilisés. + + *Pourquoi ?* : Nommer les modules avec un pattern spécifique tel que `*.module.js` les rends faciles à aller chercher avec une expression régulière et à les lister. + + ```javascript + var clientApp = './src/client/app/'; + + // Toujours aller chercher les fichiers de module en premier + var files = [ + clientApp + '**/*.module.js', + clientApp + '**/*.js' + ]; + ``` + +**[Retour en haut de page](#table-des-matières)** + +## Filtres + +###### [Style [Y420](#style-y420)] + + - Évitez d'utiliser les filtres pour scanner toutes les propriétés de l'arborescence d'un objet complexe. Utilisez les filtres pour sélectionner des propriétés. + + *Pourquoi ?*: les filtres peuvent être sur-utilisés et peuvent avoir des effets négatifs sur les performances s'ils ne sont pas utilisés de façon appropriée. Par exemple, quand un filtre touche un gros objet dont l'arborescence est profonde. + +**[Retour en haut de page](#table-des-matières)** + +## Documentation +Pour tout le reste, allez voir la [documentation de l'API d'Angular](//docs.angularjs.org/api). + +## Contribuer + +Créez d'abord une *issue* pour discuter de potentiels changements ou ajouts à faire. Si vous avez des questions sur le guide, je vous encourage à les soumettre en temps qu'*issues*. Si vous trouvez des erreurs de frappe, créez une *pull request*. L'idée est de garder le contenu à jour et d'utiliser les fonctionnalités natives de Github pour aider à retracer l'évolution de ce dernier via les *issues* et les *pull requests*, lesquels sont indexées par Google. Pourquoi ? Parce que si vous vous posez une question, il y a des chances que quelqu'un d'autre se la soit posé ! Vous en apprendrez plus ci-dessous pour savoir comment contribuer. + +*En contribuant à ce dépôt, vous acceptez de rendre votre contenu accessible en accord avec la licence du dépôt.* + +### Processus + 1. Discuter des changements dans une *issue* GitHub. + 2. Ouvrir une *pull request* sur la branche `develop`, référencer l'*issue*, et expliquer le changement et la raison pour laquelle ce changement est pertinent. + 3. La *pull request* sera évaluée et *mergée* ou refusée. + +## License + +en bref; Utilisez ce guide. Les attributions sont appréciées._ + +### 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. + +**[Retour en haut de page](#table-des-matières)** diff --git a/i18n/it-IT.md b/a1/i18n/it-IT.md similarity index 89% rename from i18n/it-IT.md rename to a1/i18n/it-IT.md index aea42a6b..fa87b1b5 100644 --- a/i18n/it-IT.md +++ b/a1/i18n/it-IT.md @@ -1,18 +1,18 @@ # Guida stilistica ad Angular -*Guida stilistica dogmatica ad Angular per i team di [@john_papa](//twitter.com/john_papa)* - -*Traduzione di [Angelo Chiello](https://github.com/angelochiello)* +## 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. ->The [original English version](http://jpapa.me/ngstyles) is the source of truth, as it is maintained and updated first. +## 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. +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. L'obbiettivo di questa guida stilistica è di fare da vademecum alla costruzione di applicazioni con Angular mostrando le convenzioni che uso e, più importante, perché le uso. >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. @@ -22,7 +22,7 @@ Molti dei mie stili sono frutto di parecchie sessioni di pair programming che [W ## Guarda gli stili in una App di Esempio Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di aiuto vederle in pratica. Questa guida è accompagnata da una applicazione di esempio che segue questi stili e schemi. Puoi trovare l'[applicazione di esempio (chiamata modular) qui](https://github.com/johnpapa/ng-demos) nella cartella `modular`. Prendila, clonala o fanne un fork liberamente. [Le istruzioni su come eseguirla sono nel proprio readme](https://github.com/johnpapa/ng-demos/tree/master/modular). -##Traduzioni +##Traduzioni [Traduzioni di questa guida stilistica ad Angular](https://github.com/johnpapa/angularjs-styleguide/tree/master/i18n) sono gestite dalla comunità e possono essere trovate qui. ## Tavola dei contenuti @@ -46,7 +46,7 @@ Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di a 1. [Logica di Startup](#logica-di-startup) 1. [Wrapper dei Servizi $ di Angular](#wrapper-dei-servizi--di-angular) 1. [Test](#test) - 1. [Animazioni](#animazioni) + 1. [Animazioni](#animazioni) 1. [Commenti](#commenti) 1. [JSHint](#jshint) 1. [JSCS](#jscs) @@ -65,7 +65,7 @@ Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di a ### Regola dell'1 ###### [Stile [Y001](#stile-y001)] - - Definire 1 componente per file. + - Definire 1 componente per file. Il seguente esempio definisce il modulo `app` e le proprie dipendenze, definisce un controller e definisce una factory tutto nel medesimo file. @@ -75,7 +75,7 @@ Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di a .module('app', ['ngRoute']) .controller('SomeController', SomeController) .factory('someFactory', someFactory); - + function SomeController() { } function someFactory() { } @@ -85,7 +85,7 @@ Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di a ```javascript /* consigliato */ - + // app.module.js angular .module('app', ['ngRoute']); @@ -93,8 +93,8 @@ 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); @@ -104,12 +104,12 @@ Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di a ```javascript /* consigliato */ - + // someFactory.js angular .module('app') .factory('someFactory', someFactory); - + function someFactory() { } ``` @@ -119,8 +119,8 @@ Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di a ### Closures di JavaScript ###### [Stile [Y010](#stile-y010)] - - Racchiudi i componenti di Angular in una Immediately Invoked Function Expression (IIFE) (Espressione di funzione immediatamente chiamata). - + - Racchiudi i componenti di Angular in una Immediately Invoked Function Expression (IIFE) (Espressione di funzione immediatamente chiamata). + *Perché?*: Una IIFE rimuove le variabili dallo scope globale. Questo aiuta a prevenire che variabili e dichiarazioni di funzione vivano più del previsto nello scope globale, inoltre aiuta ad evitare la collisione di variabili. *Perché?*: Quando il tuo codice è minificato e raggruppato in un file singolo per il rilascio ad un server di produzione, potresti avere collisioni di variabili e parecchie variabili globali. Una IIFE ti protegge in entrambi i casi fornendo uno scope variabile per ogni file. @@ -131,7 +131,7 @@ Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di a .module('app') .factory('logger', logger); - // La funzione logger è aggiunta come variabile globale + // La funzione logger è aggiunta come variabile globale function logger() { } // storage.js @@ -139,22 +139,22 @@ Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di a .module('app') .factory('storage', storage); - // La funzione storage è aggiunta come variabile globale + // La funzione storage è aggiunta come variabile globale function storage() { } ``` - + ```javascript /** - * consigliato + * consigliato * - * non ci sono più variabili globali + * non ci sono più variabili globali */ // logger.js (function() { 'use strict'; - + angular .module('app') .factory('logger', logger); @@ -174,8 +174,8 @@ Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di a })(); ``` - - Nota: Per essere più coincisi, il resto degli esempi in questa guida potrebbe omettere l'uso della sintassi IIFE. - + - Nota: Per essere più coincisi, il resto degli esempi in questa guida potrebbe omettere l'uso della sintassi IIFE. + - Nota: Le IIFE evitano che il codice di test possa raggiungere membri privati come regular expression o funzioni di supporto le quali sono spesso oggetto di propri unit test. In ogni caso, queste possono essere testate per mezzo di membri accessibili o attraverso l'esposizione di propri componenti. Per esempio ponendo funzioni di supporto, regular expression o costanti nelle proprie factory o costanti. **[Torna all'inizio](#tavola-dei-contenuti)** @@ -185,9 +185,9 @@ Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di a ### Evitare la collisione di nomi ###### [Stile [Y020](#stile-y020)] - - Usa una convenzione unica per i nomi con separatori per sotto moduli. + - Usa una convenzione unica per i nomi con separatori per sotto moduli. - *Perché?*: Nomi unici aiutano ad evitare la collisione di nomi dei moduli. I separatori aiutano a definire gerarchie di moduli e dei propri sotto moduli. Per esempio `app` potrebbe essere il modulo principale mentre `app.dashboard` e `app.users` potrebbero essere moduli che sono usati come dipendenze di `app`. + *Perché?*: Nomi unici aiutano ad evitare la collisione di nomi dei moduli. I separatori aiutano a definire gerarchie di moduli e dei propri sotto moduli. Per esempio `app` potrebbe essere il modulo principale mentre `app.dashboard` e `app.users` potrebbero essere moduli che sono usati come dipendenze di `app`. ### Definizioni (altrimenti noti come Setter) ###### [Stile [Y021](#stile-y021)] @@ -195,7 +195,7 @@ Nonostante questa guida spieghi i *cosa*, *come* e *perché*, trovo che sia di a - Dichiara moduli senza una variabile usando la sintassi setter. *Perché?*: con 1 componente per file, raramente c'è la necessità di introdurre una variabile per il modulo. - + ```javascript /* evitare */ var app = angular.module('app', [ @@ -230,7 +230,7 @@ Invece usa la più semplice sintassi setter. /* evitare */ var app = angular.module('app'); app.controller('SomeController', SomeController); - + function SomeController() { } ``` @@ -239,7 +239,7 @@ Invece usa la più semplice sintassi setter. angular .module('app') .controller('SomeController', SomeController); - + function SomeController() { } ``` @@ -247,11 +247,18 @@ Invece usa la più semplice sintassi setter. ###### [Stile [Y023](#stile-y023)] - Setta solo una volta e prendi (get) per tutte le altre istanze. - + *Perché?*: Un modulo dovrebbe essere creato solamente una volta, quindi recuperato da lì in avanti. - - - Usa `angular.module('app', []);` per settare un modulo. - - Usa `angular.module('app');` per prendere (get) un modulo. + + ```javascript + /* consigliato */ + + // per creare un modulo + angular.module('app', []); + + // per recuperare un modulo + angular.module('app'); + ``` ### Funzioni con un nome vs funzioni anonime ###### [Stile [Y024](#stile-y024)] @@ -264,7 +271,7 @@ Invece usa la più semplice sintassi setter. /* evitare */ angular .module('app') - .controller('Dashboard', function() { }); + .controller('DashboardController', function() { }); .factory('logger', function() { }); ``` @@ -274,9 +281,9 @@ Invece usa la più semplice sintassi setter. // dashboard.js angular .module('app') - .controller('Dashboard', Dashboard); + .controller('DashboardController', DashboardController); - function Dashboard() { } + function DashboardController() { } ``` ```javascript @@ -295,9 +302,9 @@ Invece usa la più semplice sintassi setter. ### Sintassi controllerAs nella View ###### [Stile [Y030](#stile-y030)] - - Usa la sintassi [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) al posto della sintassi `classico controller con $scope`. + - Usa la sintassi [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) al posto della sintassi `classico controller con $scope`. - *Perché?*: I controller sono costruiti, creati con "new" e forniti con un nuova istanza singola, inoltre la sintassi `controllerAs` è più somigliante ad un costruttore JavaScript rispetto alla `sintassi classica con $scope`. + *Perché?*: I controller sono costruiti, creati con "new" e forniti con un nuova istanza singola, inoltre la sintassi `controllerAs` è più somigliante ad un costruttore JavaScript rispetto alla `sintassi classica con $scope`. *Perché?*: Promuove l'uso del binding ad un oggetto che "usa il punto" nella View (p.e. `customer.name` invece di `name`), il quale è più contestuale, più facile da leggere ed evita qualunque questione di riferimenti che potrebbe accadere senza "uso del punto". @@ -305,14 +312,14 @@ Invece usa la più semplice sintassi setter. ```html -
+
{{ name }}
``` ```html -
+
{{ customer.name }}
``` @@ -320,17 +327,17 @@ Invece usa la più semplice sintassi setter. ### Sintassi controllerAs nel Controller ###### [Stile [Y031](#stile-y031)] - - Usa la sintassi `controllerAs` al posto della sintassi `classico controller con $scope`. + - Usa la sintassi `controllerAs` al posto della sintassi `classico controller con $scope`. - La sintassi `controllerAs` usa `this` all'interno dei controller che fanno uso di `$scope` - *Perché?*: `controllerAs` è una semplificazione sintattica per `$scope`. Puoi ancora fare il binding con la View ed accedere ai metodi di `$scope`. + *Perché?*: `controllerAs` è una semplificazione sintattica per `$scope`. Puoi ancora fare il binding con la View ed accedere ai metodi di `$scope`. - *Perché?*: Aiuta ad evitare la tentazione ad usare i metodi di `$scope` dentro un controller quando sarebbe meglio evitare o spostarli in una factory. Considera l'uso di `$scope` in una factory o, se in un controller, soltanto quando necessario. Per esempio, quando si pubblicano o sottoscrivono eventi 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 di spostare questi tipi di utilizzi in una facotry e di invocarli da un controller. + *Perché?*: Aiuta ad evitare la tentazione ad usare i metodi di `$scope` dentro un controller quando sarebbe meglio evitare o spostarli in una factory quindi referenziarli dal controller. Considera l'uso di `$scope` in un controller soltanto quando necessario. Per esempio, quando si pubblicano o sottoscrivono eventi 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 di spostare questi tipi di utilizzi in una facotry e di invocarli da un controller. ```javascript /* evitare */ - function Customer($scope) { + function CustomerController($scope) { $scope.name = {}; $scope.sendMessage = function() { }; } @@ -338,7 +345,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato - tuttavia vedi la prossima sezione */ - function Customer() { + function CustomerController() { this.name = {}; this.sendMessage = function() { }; } @@ -348,12 +355,12 @@ Invece usa la più semplice sintassi setter. ###### [Stile [Y032](#stile-y032)] - Usa una variabile che "catturi" `this` quando si utilizza la sintassi `controllerAs`. Scegli un nome della variabile consistente come `vm`, che sta per ViewModel. - + *Perché?*: La keyword `this` è contestuale e quando usata all'interno di una funzione dentro un controller può cambiare il proprio contesto. Catturare il contesto di `this` evita di incorrere in questo problema. ```javascript /* evitare */ - function Customer() { + function CustomerController() { this.name = {}; this.sendMessage = function() { }; } @@ -361,7 +368,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato */ - function Customer() { + function CustomerController() { var vm = this; vm.name = {}; vm.sendMessage = function() { }; @@ -374,37 +381,48 @@ Invece usa la più semplice sintassi setter. /* jshint validthis: true */ var vm = this; ``` - + Nota: Quando di creano watch in un controller usando `controller as`, puoi fare il watch del membro `vm.*` usando la seguente sintassi. (Crea watch con cautela poiché aggiungono più carico al ciclo di 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 was %s', original); $log.info('vm.title is now %s', current); }); } ``` + 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)] - Poni i membri che possono fare il bind in alto nel controller, in ordine alfabetico, piuttosto che dispersi in tutto il codice del controller. - + *Perché?*: Porre i membri che posso fare il bind in alto rende semplice la lettura e aiuta l'istantanea identificazione di quali membri del controller possono essere collegati ed usati in una View. - *Perché?*: Settare funzioni anonime nella medesima linea è semplice, tuttavia quando queste funzioni sono più lunghe di 1 linea di codice possono ridurre la leggibilità. Definire le funzione al di sotto i membri che possono fare il bind (funzioni che saranno chiamate) spostano i dettagli di implementazione in basso, tengono i membri che possono fare il bind in alto e rendono il codice più facile da leggere. + *Perché?*: Settare funzioni anonime nella medesima linea è semplice, tuttavia quando queste funzioni sono più lunghe di 1 linea di codice possono ridurre la leggibilità. Definire le funzione al di sotto i membri che possono fare il bind (funzioni che saranno chiamate) spostano i dettagli di implementazione in basso, tengono i membri che possono fare il bind in alto e rendono il codice più facile da leggere. ```javascript /* evitare */ - function Sessions() { + function SessionsController() { var vm = this; vm.gotoSession = function() { @@ -418,11 +436,12 @@ Invece usa la più semplice sintassi setter. }; vm.sessions = []; vm.title = 'Sessions'; + } ``` ```javascript /* consigliato */ - function Sessions() { + function SessionsController() { var vm = this; vm.gotoSession = gotoSession; @@ -444,21 +463,22 @@ 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) + ![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. ```javascript /* evitare */ - function Sessions(data) { + function SessionsController(data) { var vm = this; vm.gotoSession = gotoSession; vm.refresh = function() { - /** - * linee + /** + * linee * di * codice * che peggiorano @@ -468,41 +488,43 @@ Invece usa la più semplice sintassi setter. vm.search = search; vm.sessions = []; vm.title = 'Sessions'; + } ``` ```javascript /* consigliato */ - function Sessions(dataservice) { + function SessionsController(sessionDataService) { var vm = this; vm.gotoSession = gotoSession; - vm.refresh = dataservice.refresh; // codice di 1 liena è OK + vm.refresh = sessionDataService.refresh; // codice di 1 liena è OK vm.search = search; vm.sessions = []; vm.title = 'Sessions'; + } ``` ### Dichiarazioni di funzione per nascondere i dettagli di implementazione ###### [Stile [Y034](#stile-y034)] - Usa le dichiarazioni di funzione per nascondere i dettagli di implementazione. Tieni i membri che possono fare il binding in alto. Quando necessiti di fare binding a una funzione nel controller, puntalo ad una dichiarazione di funzione che compaia dopo nel file. Questo è direttamente collegabile con la sezione Membri che possono fare il bind posti in alto. Per ulteriori dettagli guarda [questo post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code) (in inglese). - + *Perché?*: Porre i membri che possono fare il binding in alto rende semplice la lettura ed aiuta l'immediata identificazione dei membri del controller che possono fare il binding ed usati nella View. (Come sopra.) *Perché?*: Porre i dettagli di implementazione di una funzione in seguito nel file sposta la complessità fuori dalla vista così che puoi vedere le cose importanti in alto. *Perché?*: Dichiarazioni di funzioni che sono chiamate così che non c'è rischio dell'uso di una funzione prima che sia definita (come sarebbe in caso di espressioni di funzione). - *Perché?*: Non ti devi preoccupare di dichiarazioni di funzione che sposta `var a` prima di `var b` romperà il codice perché `a` dipende da `b`. + *Perché?*: Non ti devi preoccupare di dichiarazioni di funzione che sposta `var a` prima di `var b` romperà il codice perché `a` dipende da `b`. - *Perché?*: Con le espressioni di funzione l'ordine è critico. + *Perché?*: Con le espressioni di funzione l'ordine è critico. ```javascript - /** - * evitare + /** + * evitare * Uso di espressioni di funzione. */ - function Avengers(dataservice, logger) { + function AvengersController(avengersService, logger) { var vm = this; vm.avengers = []; vm.title = 'Avengers'; @@ -514,7 +536,7 @@ Invece usa la più semplice sintassi setter. } var getAvengers = function() { - return dataservice.getAvengers().then(function(data) { + return avengersService.getAvengers().then(function(data) { vm.avengers = data; return vm.avengers; }); @@ -534,7 +556,7 @@ Invece usa la più semplice sintassi setter. * Usare dichiarazione di funzione * e membri che fanno in binding in alto. */ - function Avengers(dataservice, logger) { + function AvengersController(avengersService, logger) { var vm = this; vm.avengers = []; vm.getAvengers = getAvengers; @@ -549,7 +571,7 @@ Invece usa la più semplice sintassi setter. } function getAvengers() { - return dataservice.getAvengers().then(function(data) { + return avengersService.getAvengers().then(function(data) { vm.avengers = data; return vm.avengers; }); @@ -571,7 +593,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; @@ -602,16 +624,16 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato */ - function Order(creditService) { + function OrderController(creditService) { var vm = this; vm.checkCredit = checkCredit; vm.isCreditOk; vm.total = 0; - function checkCredit() { + function checkCredit() { return creditService.isOrderTotalOk(vm.total) .then(function(isOk) { vm.isCreditOk = isOk; }) - .catch(showServiceError); + .catch(showError); }; } ``` @@ -619,16 +641,16 @@ Invece usa la più semplice sintassi setter. ### Tenere i controller "a fuoco" ###### [Stile [Y037](#stile-y037)] - - Definisci un controller per vista e prova a non riutilizzare il controller per altre view. Piuttosto, sposta la logica riutilizzabile alle factory e mantieni il controller semplice ed a fuoco sulla propria view. - + - Definisci un controller per vista e prova a non riutilizzare il controller per altre view. Piuttosto, sposta la logica riutilizzabile alle factory e mantieni il controller semplice ed a fuoco sulla propria view. + *Perché?*: Riutilizzare i controller con diverse view è precario e sono necessari dei buoni test end-to-end (e2e) per assicurarne la stabilità in applicazioni su larga scala. ### Assegnazione dei Controller ###### [Stile [Y038](#stile-y038)] - - Quando un controller deve essere accoppiato ad una view ed un componente può essere riutilizzato da altri controller o view, definisci i controller insieme alle loro route. + - Quando un controller deve essere accoppiato ad una view ed un componente può essere riutilizzato da altri controller o view, definisci i controller insieme alle loro route. - Nota: Se una View è caricata attraverso altri mezzi che una route, allora usa la sintassi `ng-controller="AvengersController as avengers"`. + Nota: Se una View è caricata attraverso altri mezzi che una route, allora usa la sintassi `ng-controller="AvengersController as avengers"`. *Perché?*: Accoppiare il controller in una route consente a route diverse di invocare diversi accoppiamenti di controller e view. Quando i controller sono assegnati in una view usando [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController) quella view sarà sempre associata al medesimo controller. @@ -685,8 +707,8 @@ Invece usa la più semplice sintassi setter. ### Singleton ###### [Stile [Y040](#stile-y040)] - - I Service sono istanziati con la keyword `new`, usa `this` per metodi e variabili pubbliche. Dal momento che sono molto simili alle factory, usa queste ultime per consistenza. - + - I Service sono istanziati con la keyword `new`, usa `this` per metodi e variabili pubbliche. Dal momento che sono molto simili alle factory, usa queste ultime per consistenza. + Nota: [Tutti i servizi di Angular sono singleton](https://docs.angularjs.org/guide/services). Questo significa che c'è soltanto una istanza di un dato servizio per iniettore. ```javascript @@ -721,7 +743,7 @@ Invece usa la più semplice sintassi setter. ## Factory -### Singola responsabilità +### Singola responsabilità ###### [Stile [Y050](#stile-y050)] - Le factory dovrebbero avere la [singola responsabilità](http://en.wikipedia.org/wiki/Single_responsibility_principle) che è incapsulata nel proprio contesto. Una volta che una factory eccede quello che è un singolo scopo, dovrebbe essere creata una nuova factory. @@ -730,15 +752,15 @@ Invece usa la più semplice sintassi setter. ###### [Stile [Y051](#stile-y051)] - Le factory sono singleton e ritornano un oggetto che contiene i membri del servizio. - + Nota: [Tutti i servizi di Angular sono singleton](https://docs.angularjs.org/guide/services). ### Membri accessibili in alto ###### [Stile [Y052](#stile-y052)] - - Esponi tutti i membri richiamabili del servizio (l'interfaccia) in alto, usando una tecnica derivata dal [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). + - Esponi tutti i membri richiamabili del servizio (l'interfaccia) in alto, usando una tecnica derivata dal [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - *Perché?*: Porre i membri richiamabili in alto lo rende semplice da leggere e aiuta ad identificare istantaneamente quali membri del servizio possono essere chiamati ed essere oggetto di unit test (e/o simulati). + *Perché?*: Porre i membri richiamabili in alto lo rende semplice da leggere e aiuta ad identificare istantaneamente quali membri del servizio possono essere chiamati ed essere oggetto di unit test (e/o simulati). *Perché?*: Questo è particolarmente utile quando i file iniziano ad allungarsi così da evitare la necessità di scorrere per leggere cosa è esposto. @@ -748,10 +770,10 @@ Invece usa la più semplice sintassi setter. /* evitare */ function dataService() { var someValue = ''; - function save() { + function save() { /* */ }; - function validate() { + function validate() { /* */ }; @@ -776,11 +798,11 @@ Invece usa la più semplice sintassi setter. //////////// - function save() { + function save() { /* */ }; - function validate() { + function validate() { /* */ }; } @@ -789,22 +811,22 @@ 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)] - Usa le dichiarazioni di funzioni per nascondere i dettagli di implementazione. Tieni i membri accessibili della factory in alto. Puntali alle dichiarazioni di funzioni che compaiono dopo nel file. Per ulteriori dettagli guarda [questo post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code) (in inglese). - *Perché?*: Porre i membri richiamabili in alto la rende semplice da leggere e aiuta ad identificare istantaneamente quali funzioni della factory possono accessibili esternamente. + *Perché?*: Porre i membri richiamabili in alto la rende semplice da leggere e aiuta ad identificare istantaneamente quali funzioni della factory possono accessibili esternamente. *Perché?*: Porre i dettagli di implementazione di una funzione dopo nel file sposta la complessità fuori dalla vista così che puoi vedere le cose importanti prima. *Perché?*: Le dichiarazioni di funzione sono richiamate così da non avere preoccupazioni circa l'uso di una funzione prima della sua definizione (come sarebbe nel caso di espressioni di funzione). - *Perché?*: Non dovrai mai preoccuparti di dichiarazioni di funzione che spostano `var a` prima di `var b` rompendo il codice perché `a` dipende da `b`. + *Perché?*: Non dovrai mai preoccuparti di dichiarazioni di funzione che spostano `var a` prima di `var b` rompendo il codice perché `a` dipende da `b`. - *Perché?*: Con le espressioni di funzione l'ordine è critico. + *Perché?*: Con le espressioni di funzione l'ordine è critico. ```javascript /** @@ -908,7 +930,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato */ - // factory del servizio ai dati + // factory del servizio ai dati angular .module('app.core') .factory('dataservice', dataservice); @@ -935,7 +957,7 @@ Invece usa la più semplice sintassi setter. } } ``` - + Nota: Il servizio ai dati è chiamato dai consumatori, come un controller, nascondendo l'implementazione ai consumatori, come mostrato sotto. ```javascript @@ -944,11 +966,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 = []; @@ -967,7 +989,7 @@ Invece usa la più semplice sintassi setter. return vm.avengers; }); } - } + } ``` ### Ritornare una promessa dalle chiamate ai dati @@ -1021,12 +1043,12 @@ Invece usa la più semplice sintassi setter. ### Limita 1 per file ###### [Stile [Y070](#stile-y070)] - - Crea una directive per file. Nomina il file per la directive. + - Crea una directive per file. Nomina il file per la directive. - *Perché?*: È facile mescolare tutte le directive in un unico file ma difficoltoso da separare così che alcune siano condivise tra le applicazioni, alcune tra moduli, altre solo per un module. + *Perché?*: È facile mescolare tutte le directive in un unico file ma difficoltoso da separare così che alcune siano condivise tra le applicazioni, alcune tra moduli, altre solo per un module. *Perché?*: Una directive per file è semplice da manutenere. - + > Nota: "**Best Practice**: Le directive dovrebbero fare pulizia alla fine. Puoi usare `element.on('$destroy', ...)` oppure `scope.$on('$destroy', ...)` per lanciare una funzione di pulizia quando la directive è rimossa" ... dalla documentazione di Angular. ```javascript @@ -1061,7 +1083,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 @@ -1078,12 +1100,12 @@ 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 * @example
- */ + */ angular .module('sales.widgets') .directive('acmeSalesCustomerInfo', salesCustomerInfo); @@ -1115,7 +1137,7 @@ Invece usa la più semplice sintassi setter. ### Manipolare il DOM in una Directive ###### [Stile [Y072](#stile-y072)] - - Quando devi manipolare direttamente il DOM, usa una directive. Se possono essere usate delle alternative come usare CSS per settare stili o i [servizi di animazione](https://docs.angularjs.org/api/ngAnimate), templating di Angular, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) oppure [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), allora usa questi. Per esempio, se la directive deve semplicemente nascondere e mostrare, usa ngHide/ngShow. + - Quando devi manipolare direttamente il DOM, usa una directive. Se possono essere usate delle alternative come usare CSS per settare stili o i [servizi di animazione](https://docs.angularjs.org/api/ngAnimate), templating di Angular, [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) oppure [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), allora usa questi. Per esempio, se la directive deve semplicemente nascondere e mostrare, usa ngHide/ngShow. *Perché?*: Manipolare il DOM può essere difficoltoso da testare, debuggare e spesso ci sono modi migliori (p.e. CSS, animazioni, template) @@ -1124,9 +1146,9 @@ Invece usa la più semplice sintassi setter. - Utilizza un corto, unico e descrittivo prefisso alla directive come `acmeSalesCustomerInfo` che potrebbe essere dichiarato in HTML come `acme-sales-customer-info`. - *Perché?*: L'unico breve prefisso identifica il contesto delle directive e l'origine. Per esempio un prefisso `cc-` potrebbe indicare che la directive è parte di una app CodeCamper mentre `acme-` potrebbe indicare una direttiva per l'azienda Acme. - - Nota: Evita `ng-` poiché queste sono riservate per le directive di Angular. Cerca directive che sono largamente utilizzate per evitare il conflitto di nomi, come `ion-` per il [Framework Ionic ](http://ionicframework.com/). + *Perché?*: L'unico breve prefisso identifica il contesto delle directive e l'origine. Per esempio un prefisso `cc-` potrebbe indicare che la directive è parte di una app CodeCamper mentre `acme-` potrebbe indicare una direttiva per l'azienda Acme. + + Nota: Evita `ng-` poiché queste sono riservate per le directive di Angular. Cerca directive che sono largamente utilizzate per evitare il conflitto di nomi, come `ion-` per il [Framework Ionic ](http://ionicframework.com/). ### Restringi a Elementi and Attributi ###### [Stile [Y074](#stile-y074)] @@ -1169,7 +1191,7 @@ Invece usa la più semplice sintassi setter.
``` - + ```javascript /* consigliato */ angular @@ -1197,9 +1219,9 @@ Invece usa la più semplice sintassi setter. *Perché?*: È sensato e non è difficile. - Nota: La directive sotto dimostra alcuni dei modi in cui puoi usare lo scope all'interno di link e controller di directive usando controllerAs. Ho usato sulla stessa linea il template solo per mettere tutto in un unico posto. + Nota: La directive sotto dimostra alcuni dei modi in cui puoi usare lo scope all'interno di link e controller di directive usando controllerAs. Ho usato sulla stessa linea il template solo per mettere tutto in un unico posto. - Nota: In relazione alla dependency injection, guarda [Annotazioni manuali per la Dependency Injection](#annotazioni-manuali-per-la-dependency-injection). + Nota: In relazione alla dependency injection, guarda [Annotazioni manuali per la Dependency Injection](#annotazioni-manuali-per-la-dependency-injection). Nota: Notare che il controller della directive è al di fuori della closure della directive. Questo stile elimina problematiche dove l'iniezione viene creata come codice non raggiungibile dopo un `return`. @@ -1221,6 +1243,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 }; @@ -1233,15 +1257,15 @@ Invece usa la più semplice sintassi setter. console.log('LINK: scope.vm.max = %s', scope.vm.max); } } - + ExampleController.$inject = ['$scope']; - + function ExampleController($scope) { // Iniettare $scope solo per confronto 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); @@ -1257,7 +1281,7 @@ Invece usa la più semplice sintassi setter. ``` Nota: Puoi inoltre nominare il controller quando lo inietti nella link function e accedi agli attributi della directive come proprietà del controller. - + ```javascript // Alternativa all'esempio sopra riportato function linkFunc(scope, el, attr, vm) { @@ -1273,7 +1297,7 @@ Invece usa la più semplice sintassi setter. *Perché?*: Rende semplice il bind tra lo scope esterno e lo scope del controller delle directive. - Nota: `bindToController` è stato introdotto con Angular 1.3.0. + Nota: `bindToController` è stato introdotto con Angular 1.3.0. ```html
@@ -1321,16 +1345,16 @@ Invece usa la più semplice sintassi setter. ###### [Stile [Y080](#stile-y080)] - Risolvi la logica di start-up per un controller in una funzione `activate`. - + *Perché?*: Porre la logica di start-up in una posizione consistente nel controller la rende semplice da localizzare, più consistente da testare e aiuta a prevenire la diffusione della logica di attivazione su tutto il controller. *Perché?*: La funzione `activate` del controller rende il riuso della logica adatto in caso di un refresh del controller/view, tiene la logica assieme, porta l'utente alla view più rapidamente, rende le animazini più facili su `ng-view` o `ui-view`e da la sensazione all'utente di istantaneità. - + Nota: Se hai necessità di annullare condizionalmente il route prima di iniziare ad usare il controller, usa piuttosto una [risoluzione nella route](#stile-y081). - + ```javascript /* evitare */ - function Avengers(dataservice) { + function AvengersController(dataservice) { var vm = this; vm.avengers = []; vm.title = 'Avengers'; @@ -1344,7 +1368,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato */ - function Avengers(dataservice) { + function AvengersController(dataservice) { var vm = this; vm.avengers = []; vm.title = 'Avengers'; @@ -1379,9 +1403,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; @@ -1404,7 +1428,7 @@ Invece usa la più semplice sintassi setter. $routeProvider .when('/avengers', { templateUrl: 'avengers.html', - controller: 'Avengers', + controller: 'AvengersController', controllerAs: 'vm', resolve: { moviesPrepService: function(movieService) { @@ -1417,15 +1441,15 @@ 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; } ``` - + Nota: L'esempio sotto mostra il punto di risoluzione della route in una funzione con il nome per cui è più semplice da fare il debug e più semplice da gestire nella iniezione delle dependenze. ```javascript @@ -1440,7 +1464,7 @@ Invece usa la più semplice sintassi setter. $routeProvider .when('/avengers', { templateUrl: 'avengers.html', - controller: 'Avengers', + controller: 'AvengersController', controllerAs: 'vm', resolve: { moviesPrepService: moviesPrepService @@ -1455,10 +1479,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; } @@ -1473,16 +1497,16 @@ Invece usa la più semplice sintassi setter. ###### [Stile [Y090](#stile-y090)] - Evita di usare abbreviazioni sintattiche per la dichiarazione di dipendenze senza usare un approccio a prova di minificazione. - + *Perché?*: I parametri dei componenti (p.e. controller, factory, etc.) saranno convertiti in variabili dal nome ridotto. Per esempio, `common` e `dataservice` potrebbero diventare `a` o `b` e non essere piò ritrovate da Angular. ```javascript /* evita - non a prova di minificazione*/ angular .module('app') - .controller('Dashboard', Dashboard); + .controller('DashboardController', DashboardController); - function Dashboard(common, dataservice) { + function DashboardController(common, dataservice) { } ``` @@ -1490,37 +1514,37 @@ 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 ###### [Stile [Y091](#stile-y091)] - Usa `$inject` per identificare manualmente le tue dipendenze per i componenti di Angular. - + *Perché?*: Questa tecnica rispecchia la tecnica usata da [`ng-annotate`](https://github.com/olov/ng-annotate), che raccomando per l'automazione della creazione della minificazione sicura delle dipendenze. Se `ng-annotate` rileva che una iniezione è stata fatta, non la duplicherà. *Perché?*: Questo salvaguarda le tue dipendenze dall'essere vulnerabili alla questione della minificazione quando i parametri possono essere passati con nomi ridotti. Per esempio, `common` e `dataservice` possono diventare `a` o `b` e non essere più trovati da Angular. - *Perché?*: Evita la creazione di dipendenze sulla stessa linea dal momento che lunghe liste possono essere difficili da leggere nell'array. Inoltre può essere fuorviante che l'array è una serie di stringhe mentre l'ultimo elemento è la funzione del componente. + *Perché?*: Evita la creazione di dipendenze sulla stessa linea dal momento che lunghe liste possono essere difficili da leggere nell'array. Inoltre può essere fuorviante che l'array è una serie di stringhe mentre l'ultimo elemento è la funzione del componente. ```javascript /* evitare */ angular .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); + .controller('DashboardController', + ['$location', '$routeParams', 'common', 'dataservice', + function DashboardController($location, $routeParams, common, dataservice) {} + ]); ``` ```javascript /* evitare */ angular .module('app') - .controller('Dashboard', + .controller('DashboardController', ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - + function Dashboard($location, $routeParams, common, dataservice) { } ``` @@ -1529,11 +1553,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) { + DashboardController.$inject = ['$location', '$routeParams', 'common', 'dataservice']; + + function DashboardController($location, $routeParams, common, dataservice) { } ``` @@ -1575,7 +1599,7 @@ Invece usa la più semplice sintassi setter. ###### [Stile [Y092](#stile-y092)] - Usa `$inject` per identificare manualmente le tue dipendenze di resolver della route per i componenti di Angular. - + *Perché?*: Questa tecnica evade le funzioni anonime per il resolver della route, rendendolo più semplice da leggere. *Perché?*: Una dichiarazione `$inject` può facilmente precedere il resolver per gestire la produzione di dipendenze che siano a prova di minificazione. @@ -1607,11 +1631,11 @@ Invece usa la più semplice sintassi setter. ### ng-annotate ###### [Stile [Y100](#stile-y100)] - - Usa [ng-annotate](//github.com/olov/ng-annotate) per [Gulp](http://gulpjs.com) o [Grunt](http://gruntjs.com) e commenta le funzioni che necessitano di automatizzare la dependency injection usando `/** @ngInject */` - + - Usa [ng-annotate](//github.com/olov/ng-annotate) per [Gulp](http://gulpjs.com) o [Grunt](http://gruntjs.com) e commenta le funzioni che necessitano di automatizzare la dependency injection usando `/* @ngInject */` + *Perché?*: Questo salvaguarda il tuo codice da ogni dipendenza che non segua le pratiche a prova di minificazione - *Perché?*: [`ng-min`](https://github.com/btford/ngmin) è deprecato. + *Perché?*: [`ng-min`](https://github.com/btford/ngmin) è deprecato. >Preferisco Gulp poiché lo ritengo più semplice da scrivere, leggere e fare il debug. @@ -1620,17 +1644,17 @@ Invece usa la più semplice sintassi setter. ```javascript angular .module('app') - .controller('Avengers', Avengers); + .controller('AvengersController', AvengersController); /* @ngInject */ - function Avengers(storageService, avengerService) { + function AvengersController(storage, avengerService) { var vm = this; vm.heroSearch = ''; vm.storeHero = storeHero; function storeHero(){ var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); + storage.save(hero.name, hero); } } ``` @@ -1640,21 +1664,21 @@ Invece usa la più semplice sintassi setter. ```javascript angular .module('app') - .controller('Avengers', Avengers); + .controller('AvengersController', AvengersController); /* @ngInject */ - function Avengers(storageService, avengerService) { + function AvengersController(storage, avengerService) { var vm = this; vm.heroSearch = ''; vm.storeHero = storeHero; function storeHero(){ var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); + storage.save(hero.name, hero); } } - Avengers.$inject = ['storageService', '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`. @@ -1667,7 +1691,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) { @@ -1685,7 +1709,7 @@ Invece usa la più semplice sintassi setter. ###### [Stile [Y101](#stile-y101)] - Usa [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) o [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) in un task di automatizzazione delle build. Inietta `/* @ngInject */` prima di qualunque funzione che abbia delle dipendenze. - + *Perché?*: ng-annotate carpirà la maggior parte delle dipendenze ma talvolta necessita dell'uso del suggerimento sintattico `/* @ngInject */`. Il seguente codice è un esempio di un task di gulp che utilizza ngAnnotate. @@ -1693,7 +1717,7 @@ Invece usa la più semplice sintassi setter. ```javascript gulp.task('js', ['jshint'], function() { var source = pkg.paths.js; - + return gulp.src(source) .pipe(sourcemaps.init()) .pipe(concat('all.min.js', {newLine: ';'})) @@ -1720,7 +1744,7 @@ Invece usa la più semplice sintassi setter. ###### [Stile [Y110](#stile-y110)] - Usa un [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator), al momento del config usando il servizio [`$provide`](https://docs.angularjs.org/api/auto/service/$provide), sul servizio [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) per eseguire azioni ad hoc quando l'eccezione occorre. - + *Perché?*: Fornisce un modo consistente per la gestione delle eccezioni non trattate da Angular sia durante lo sviluppo che a runtime. Nota: Un'altra opzione è di fare l'override del servizio invece che usare un decorator. Questa è una buona opzione ma se vuoi tenere il comportamento di default ed estenderlo un decorator è consigliato. @@ -1742,9 +1766,9 @@ Invece usa la più semplice sintassi setter. function extendExceptionHandler($delegate, toastr) { return function(exception, cause) { $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause + var errorData = { + exception: exception, + cause: cause }; /** * Potresti aggiungere l'errore ad una collezione del servizio, @@ -1800,7 +1824,7 @@ Invece usa la più semplice sintassi setter. ```javascript /* consigliato */ var handlingRouteChangeError = false; - + function handleRoutingErrors() { /** * Annullamento del route: @@ -1822,7 +1846,7 @@ Invece usa la più semplice sintassi setter. * (Non dimenticare di iniettare il servizio ad hoc) */ logger.warning(msg, [current]); - + /** * Su un errore di routing, vai ad un'altra route/stato. */ @@ -1842,10 +1866,10 @@ Invece usa la più semplice sintassi setter. - Usa nomi consistenti per tutti i componenti seguendo uno schema che descriva le funzionalità dei componenti e poi (a scelta) il suo tipo. Lo schema che consiglio è `feature.type.js`. Ci sono 2 nomi per la maggior parte dei componenti: * il nome del file (`avengers.controller.js`) * il nome del componente registrato con Angular (`AvengersController`) - + *Perché?*: Convezioni sui nomi aiutano a fornire un modo consistente per trovate i contenuti a colpo d'occhio. Essere consistenti in un progetto è vitale. Essere consistenti in un team è importante. Essere consistenti nell'insieme di un'azienda è tremendamente efficiente. - *Perché?*: Le convezioni sulla nomenclatura dovrebbe semplicemente aiutare a trovare il tuo codice più rapidamente e renderlo più semplice da comprendere. + *Perché?*: Le convezioni sulla nomenclatura dovrebbe semplicemente aiutare a trovare il tuo codice più rapidamente e renderlo più semplice da comprendere. ### Nomi dei file per funzionalità ###### [Stile [Y121](#stile-y121)] @@ -1858,7 +1882,7 @@ Invece usa la più semplice sintassi setter. ```javascript /** - * opzioni comuni + * opzioni comuni */ // Controllers @@ -1887,7 +1911,7 @@ Invece usa la più semplice sintassi setter. // constants constants.js - + // module definition avengers.module.js @@ -1897,13 +1921,13 @@ Invece usa la più semplice sintassi setter. // configuration avengers.config.js - + // directives avenger-profile.directive.js 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 /** @@ -1917,7 +1941,7 @@ Invece usa la più semplice sintassi setter. ### Nomi dei file di test ###### [Stile [Y122](#stile-y122)] - - Nomina le specifiche dei test in modo similare al componente che testano aggiundendo il suffisso `spec`. + - Nomina le specifiche dei test in modo similare al componente che testano aggiundendo il suffisso `spec`. *Perché?*: Fornisce un modo consistente per identificare facilmente i componenti. @@ -1954,7 +1978,7 @@ Invece usa la più semplice sintassi setter. function HeroAvengers(){ } ``` - + ### Suffisso nel nome di un controller ###### [Stile [Y124](#stile-y124)] @@ -1975,15 +1999,19 @@ Invece usa la più semplice sintassi setter. function AvengersController(){ } ``` -### Nomi delle factory +### Nomi delle factory e dei service ###### [Stile [Y125](#stile-y125)] - - Usa una nomenclatura consistente per tutte le factory dando i nomi a seguito delle loro funzionalità. Usa il camel-case per service e factory. Evita di pre-nominare factory e service con `$` + - Usa una nomenclatura consistente per tutte le factory e i service dando i nomi a seguito delle loro funzionalità. Usa il camel-case per service e factory. Evita di pre-nominare factory e service con `$`. Aggiungi il suffisso `Service` a service e factory soltanto quando non è chiaro cosa siano (p. es. quando si tratta di nomi). *Perché?*: Fornisce un modo consistente per identificare facilmente e referenziare le factory. - + *Perché?*: Evita collisione di nomi con factory e servizi di Angular esistenti che usano il prefisso `$`. + *Perché?*: Service con nomi evidenti quali `logger` on richiedono il suffisso. + + *Perché?*: Nomi di service quali `avengers` sono nomi, richiedono in suffisso e dovrebbero essere nominati `avengersService`. + ```javascript /** * consigliato @@ -1997,6 +2025,26 @@ Invece usa la più semplice sintassi setter. function logger(){ } ``` + ```javascript + /** + * consigliato + */ + + // credit.service.js + angular + .module + .factory('creditService', creditService); + + function creditService() { } + + // customer.service.js + angular + .module + .service('customerService', customerService); + + function customerService() { } + ``` + ### Nomi dei componenti directive ###### [Stile [Y126](#stile-y126)] @@ -2009,7 +2057,7 @@ Invece usa la più semplice sintassi setter. * consigliato */ - // avenger-profile.directive.js + // avenger-profile.directive.js angular .module .directive('xxAvengerProfile', xxAvengerProfile); @@ -2048,12 +2096,12 @@ Invece usa la più semplice sintassi setter. ### LIFT ###### [Stile [Y140](#stile-y140)] - - Struttura la tua app tale da poter `L`ocate (localizzare) il codice facilmente, `I`dentify (identificare) il codice a colpo d'occhio, tenere la struttura più `F`lattest (piatta) che puoi, e `T`ry (provare) a rimanere DRY (Don't Repeat Yourself - Non ripetersi). La struttura dovrebbe seguire queste 4 linee guida basilari. + - Struttura la tua app tale da poter `L`ocate (localizzare) il codice facilmente, `I`dentify (identificare) il codice a colpo d'occhio, tenere la struttura più `F`lattest (piatta) che puoi, e `T`ry (provare) a rimanere DRY (Don't Repeat Yourself - Non ripetersi). La struttura dovrebbe seguire queste 4 linee guida basilari. *Perché LIFT?*: Fornisce una struttura consistente che scala bene, è modulare e rende più semplice aumentare l'efficienza dello sviluppatore nel trovare facilmente il codice. Un altro modo per verificare la struttura della tua app è chiederti: Quanto rapidamente puoi aprire e lavorare ad una funzionalità in tutti i file che sono collegati? Quando ritengo che la mia struttura non sia confortevole, torno indietro a rivedere le linee guida LIFT - + 1. `L`ocalizzare il nostro codice con facilità 2. `I`dentificare il codice a colpo d'occhio 3. `F`lat (pitta) struttura quanto più possibile @@ -2104,7 +2152,7 @@ Invece usa la più semplice sintassi setter. - Si DRY, ma non diventare pazzo e sacrificare la leggibilità. - *Perché?*: Non ripetersi è importante ma non è cruciale se sacrifica altri principi LIFT, per questo il principio è Try (provare) DRY. Non voglio digitare session-view.html perché è ovvio essere una view. Se non è ovvio o se per convenzione allora nominala così. + *Perché?*: Non ripetersi è importante ma non è cruciale se sacrifica altri principi LIFT, per questo il principio è Try (provare) DRY. Non voglio digitare session-view.html perché è ovvio essere una view. Se non è ovvio o se per convenzione allora nominala così. **[Torna all'inizio](#tavola-dei-contenuti)** @@ -2113,14 +2161,14 @@ Invece usa la più semplice sintassi setter. ### Linee guida generali ###### [Stile [Y150](#stile-y150)] - - Abbi una visione a breve termine dell'implementazione e una a lunga scadenza. In altre parole, parti in piccolo ma tieni in mente su dove l'app è diretta lungo il percorso. Tutto il codice dell'app va nella cartella principale chiamata `app`. Tutto il contenuto rispetta 1 funzione per file. Ogni controller, service, module, view nel proprio file. Tutti gli script di terze party sono poste in una altra cartella principale e non nella cartella `app`. Non le ho scritte e non voglio facciano disordine nella mia app (`bower_components`, `scripts`, `lib`). + - Abbi una visione a breve termine dell'implementazione e una a lunga scadenza. In altre parole, parti in piccolo ma tieni in mente su dove l'app è diretta lungo il percorso. Tutto il codice dell'app va nella cartella principale chiamata `app`. Tutto il contenuto rispetta 1 funzione per file. Ogni controller, service, module, view nel proprio file. Tutti gli script di terze parti sono poste in una altra cartella principale e non nella cartella `app`. Non le ho scritte e non voglio facciano disordine nella mia app (`bower_components`, `scripts`, `lib`). Nota: Trovi più dettagli e le motivazioni di questa struttura nel [post originale sulla struttura delle applicazioni](http://www.johnpapa.net/angular-app-structuring-guidelines/) (in inglese). ### Layout ###### [Stile [Y151](#stile-y151)] - - Metti i componenti che definiscono il layout globale dell'applicazione in una cartella con il nome `layout`. Questi possono includere un shell view e controller che agiscono come contenitori per l'app, navigazione, menù, aree per i contenuti ed altre regioni. + - Metti i componenti che definiscono il layout globale dell'applicazione in una cartella con il nome `layout`. Questi possono includere un shell view e controller che agiscono come contenitori per l'app, navigazione, menù, aree per i contenuti ed altre regioni. *Perché?*: Organizza tutto il layout in una sola posizione riutilizzabile lungo tutta l'applicazione. @@ -2146,78 +2194,78 @@ Invece usa la più semplice sintassi setter. app.module.js app.config.js app.routes.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html + components/ + calendar.directive.js + calendar.directive.html + user-profile.directive.js + user-profile.directive.html layout/ - shell.html + shell.html shell.controller.js - topnav.html - topnav.controller.js + topnav.html + topnav.controller.js people/ attendees.html - attendees.controller.js + attendees.controller.js speakers.html speakers.controller.js speaker-detail.html speaker-detail.controller.js - services/ - data.service.js + services/ + data.service.js localstorage.service.js - logger.service.js + logger.service.js spinner.service.js sessions/ - sessions.html + sessions.html sessions.controller.js session-detail.html - session-detail.controller.js + 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à. ```javascript - /* + /* * evitare * Alternativa cartella-per-tipo * Consiglio invece "cartella-per-funzionalità". */ - + 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 + 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 + logger.js spinner.js views/ - attendees.html + attendees.html session-detail.html - sessions.html - shell.html - speakers.html + sessions.html + shell.html + speakers.html speaker-detail.html - topnav.html - ``` + topnav.html + ``` **[Torna all'inizio](#tavola-dei-contenuti)** @@ -2243,7 +2291,7 @@ Invece usa la più semplice sintassi setter. - Nel modulo principale metti solo la logica che serva da collante per l'app. Lascia le funzionalità ognuna al proprio modulo. *Perché?*: L'aggiunta di ruoli addizionali al modulo principale per il recupero dei dati, il mostrare viste o altra logica non correlata al tenere insieme l'applicazione sporca il modulo principale e rende entrambi gli insiemi di funzionalità più complessi da riusare o rimuovere. - + *Perché?*: Il modulo app diventa un manifesto che descrive quali moduli aiutano a definire l'applicazione. ### Aree di funzionalità sono Moduli @@ -2255,7 +2303,7 @@ Invece usa la più semplice sintassi setter. *Perché?*: Sprint o iterazioni possono focalizzarsi sulle aree di funzionalità e renderle disponibili alla fine dello sprint o dell'iterazione. - *Perché?*: Separare aree di funzioni in moduli rende più semplice testare i moduli in isolamento e il riutilizzo del codice. + *Perché?*: Separare aree di funzioni in moduli rende più semplice testare i moduli in isolamento e il riutilizzo del codice. ### Blocchi riutilizzabili sono Moduli ###### [Stile [Y164](#stile-y164)] @@ -2269,18 +2317,18 @@ 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. + *Perché?*: Il modulo principale dell'app contiene un manifesto che sia facilmente identificabile con le funzionalità dell'applicazione. *Perché?*: Ogni area di funzionalità contiene un manifesto di ciò da cui dipende, in modo tale da poter essere usato come dipendenza in altre applicazioni e continuare a funzionare. - *Perché?*: Funzionalità intra-app come servizio ai dati condiviso diventano facilmente localizzabili da dentro `app.core` (scegli il nome che più di piaccia per questo modulo). + *Perché?*: Funzionalità intra-app come servizio ai dati condiviso diventano facilmente localizzabili da dentro `app.core` (scegli il nome che più ti piace per questo modulo). Nota: Questa è una strategia per la consistenza. Ci sono diverse buone opzioni in questo caso. Scegline una che sia consistente, segua le regole delle dipendenze di Angular e sia facile da manutenere e scalare. - > La mia struttura varia leggermente tra progetti ma tutti seguono queste linee guida per la strutturazione e modularità. L'implementazione può variare in relazione alle funzionalità ed al team. In altre parole, non ti bloccare su una struttura che sia esattamente uguale ma giustifica la tua struttura tenendo a mente l'uso di consistenza, manutenibilità ed efficienza. - + > La mia struttura varia leggermente tra progetti ma tutti seguono queste linee guida per la strutturazione e modularità. L'implementazione può variare in relazione alle funzionalità ed al team. In altre parole, non ti bloccare su una struttura che sia esattamente uguale ma giustifica la tua struttura tenendo a mente l'uso di consistenza, manutenibilità ed efficienza. + > In una applicazione piccola, si può considerare di mettere tutte le dipendenze condivise nel modulo dell'app dove i moduli delle funzionalità non hanno dipendenze dirette. Ciò rende semplice mantenere l'applicazione più piccola ma rende più difficile riutilizzare i moduli fuori dell'applicazione stessa. **[Torna all'inizio](#tavola-dei-contenuti)** @@ -2299,7 +2347,7 @@ Invece usa la più semplice sintassi setter. .module('app') .config(configure); - configure.$inject = + configure.$inject = ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { @@ -2409,7 +2457,53 @@ Gli unit test aiutano a mantenere il codice più chiaro, perciò ho incluso alcu *Perché?*: Alcuni IDE cominciano ad integrarsi con Karma, come [WebStorm](http://www.jetbrains.com/webstorm/) e [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). - *Perché?*: Karma lavora bene con leader di automazione di processo quali [Grunt](http://www.gruntjs.com) (con [grunt-karma](https://github.com/karma-runner/grunt-karma)) e [Gulp](http://www.gulpjs.com) (con [gulp-karma](https://github.com/lazd/gulp-karma)). + *Perché?*: Karma lavora bene con leader di automazione di processo quali [Grunt](http://www.gruntjs.com) (con [grunt-karma](https://github.com/karma-runner/grunt-karma)) e [Gulp](http://www.gulpjs.com). Quando usi Gulp, usa [Karma](https://github.com/karma-runner/karma) direttamente e non con un plugin dal momento che le API possono essere richiamate direttamente. + + ```javascript + /* consigliato */ + + // Esempio di Gulp che usa direttamente Karma + 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 e Spying ###### [Stile [Y193](#stile-y193)] @@ -2425,18 +2519,18 @@ Gli unit test aiutano a mantenere il codice più chiaro, perciò ho incluso alcu - Usa [PhantomJS](http://phantomjs.org/) per eseguire i test su un server. - *Perché?*: PhantomJS è un headless browser (browser senza interfaccia grafica) che aiuta l'esecuzione di test senza la necessità di un browser "visuale". Quindi non devi installare Chrome, Safari, IE o altri browser sul server. + *Perché?*: PhantomJS è un headless browser (browser senza interfaccia grafica) che aiuta l'esecuzione di test senza la necessità di un browser "visuale". Quindi non devi installare Chrome, Safari, IE o altri browser sul server. Nota: Dovresti in ogni caso testare tutti i browser nel tuo ambiente, come appropriato per il pubblico che ne è il target. ### Analisi del codice ###### [Stile [Y195](#stile-y195)] - - Esegui JSHint sui tuoi test. + - Esegui JSHint sui tuoi test. *Perché?*: I test sono codice. JSHint può aiutare ad identificare problemi di qualità del codice che causano l’improprio funzionamento del test. -### Alleviare le regole sulle variabili globali di JSHint per i test +### Alleviare le regole sulle variabili globali di JSHint per i test ###### [Stile [Y196](#stile-y196)] - Rilassa le regole sul codice dei test per consentire variabili globali comuni quali `describe` ed `expect`. @@ -2447,27 +2541,27 @@ Gli unit test aiutano a mantenere il codice più chiaro, perciò ho incluso alcu /* jshint -W117, -W030 */ ``` Oppure puoi aggiungere le righe che seguono al tuo file JSHint Options. - + ```javascript "jasmine": true, "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)] - Posiziona i file degli unit test (spec) vicino al codice del client. Posiziona le specifiche che coprono l'integrazione con il server o che testano più componenti in una cartella separata `tests`. - *Perché?*: Gli unit test hanno una correlazione diretta con un componente specifico e file nei sogenti. + *Perché?*: Gli unit test hanno una correlazione diretta con un componente specifico e file nei sogenti. *Perché?*: È più semplice da tenere aggiornati dal momento che sono sempre a vista. Quando scrivi codice, sia che tu faccia TDD o fai i test durante o dopo lo sviluppo, le scpecifiche sono sempre di fianco e mai fuori dalla vista o dai pensieri, quindi è più probabile che siano aggiornati e ciò consente inoltre a mantenere una migliore copertura del codice. *Perché?*: Quando aggiorni i sorgenti, è più semplice andare ad aggiornare anche i test. *Perché?*: Posizionarli vicino rende semplice trovarli e spostarli con i sorgenti qualora ciò accada. - + *Perché?*: Avere le specifiche vicino rende più facile al lettore del codice sorgente imparare come il componente dovrebbe essere usato e scoprire le sue limitazioni. *Perché?*: Separare le specifiche così da non essere nella build di distribuzione è semplice con grunt o gulp. @@ -2498,7 +2592,7 @@ Gli unit test aiutano a mantenere il codice più chiaro, perciò ho incluso alcu ### Sotto il secondo ###### [Stile [Y211](#stile-y211)] - - Usa animazioni che abbiano una durata breve. Generalmente parto con 300 ms e aggiusto finché non è appropriato. + - Usa animazioni che abbiano una durata breve. Generalmente parto con 300 ms e aggiusto finché non è appropriato. *Perché?*: Animazioni lunghe possono avere l'effetto contrario sull'esperienza dell'utente e di percezzione delle prestazioni che danno il senso di una applicazione lenta. @@ -2530,7 +2624,7 @@ Gli unit test aiutano a mantenere il codice più chiaro, perciò ho incluso alcu ```javascript /** - * Factory di Log + * Factory di Log * @namespace Factories */ (function() { @@ -2755,7 +2849,7 @@ Gli unit test aiutano a mantenere il codice più chiaro, perciò ho incluso alcu .constant('moment', moment); })(); ``` - + ###### [Stile [Y241](#stile-y241)] - Usa constanti per i valori che non cambiano e che non provengono da un altro servizio. Quando le costanti sono utilizzate solo per un modulo che potrebbe essere riutilizzato in più applicazioni, metti le costanti in un file per modulo e nominalo come il modulo. Fintanto che tale necesstià non si presenti, tieni le constanti nel modulo principale in un file `constants.js`. @@ -2789,13 +2883,13 @@ Usa file template o snippet che ti aiutino a seguire stili e schemi consistentem ### Sublime Text ###### [Stile [Y250](#stile-y250)] - - Snippet Angular che seguono questi stili e linee guida. + - Snippet Angular che seguono questi stili e linee guida. - - Scarica gli [snippet di Angular per Sublime](assets/sublime-angular-snippets.zip?raw=true) + - Scarica gli [snippet di Angular per Sublime](assets/sublime-angular-snippets.zip?raw=true) - Mettili nella tua cartella Packages - - Riavvia Sublime + - Riavvia Sublime - In un file JavaScript digita questi comandi seguiti da `TAB` - + ```javascript ngcontroller // crea un controller Angular ngdirective // crea una directive Angular @@ -2817,20 +2911,32 @@ 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)] @@ -2842,7 +2948,7 @@ Usa file template o snippet che ti aiutino a seguire stili e schemi consistentem - Apri Atom, poi apri il Package Manager (Packages -> Settings View -> Install Packages/Themes) - Cerca il pacchetto 'angularjs-styleguide-snippets' - Clicca 'Install' per installare il pacchetto - + - In un file di JavaScript digita i seguenti comandi seguiti da un `TAB` ```javascript @@ -2882,7 +2988,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 ``` @@ -2894,7 +3000,12 @@ Usa file template o snippet che ti aiutino a seguire stili e schemi consistentem - Scarica gli [snippet vim per Angular](assets/vim-angular-snippets?raw=true) - setta [neosnippet.vim](https://github.com/Shougo/neosnippet.vim) - copia gli snippets nella directory snippet + + - snippet vim UltiSnips che seguono questi stilili e linee guida. + - Scarica gli [snippet vim Angular UltiSnips](assets/vim-angular-ultisnips?raw=true) + - setta [UltiSnips](https://github.com/SirVer/ultisnips) + - copia gli snippet nella directory UltiSnips ```javascript ngcontroller // crea un controller Angular ngdirective // crea una directive Angular @@ -2903,6 +3014,24 @@ Usa file template o snippet che ti aiutino a seguire stili e schemi consistentem ngservice // crea un service Angular ngfilter // crea un filter Angular ``` + +### Visual Studio Code + +###### [Stile [Y256](#stile-y256)] + + - Snippet [Visual Studio Code](http://code.visualstudio.com) che seguono questi stili e linee guida. + + - Scarica gli [snippet VS Code Angular](assets/vscode-snippets/javascript.json?raw=true) + - copia gli snippet nella directory snippet o, in alternativa, copia ed incolla gli snippet in quella esistente. + + ```javascript + ngcontroller // crea un controller Angular + ngdirective // crea una directive Angular + ngfactory // crea una factory Angular + ngmodule // crea un modulo Angular + ngservice // crea un service Angular + ``` + **[Torna all'inizio](#tavola-dei-contenuti)** ## Generatore Yeoman @@ -2941,7 +3070,7 @@ Il routing del lato client è importante al fine di creare in flusso di navigazi *Perché?*: UI Router offre tutte le funzionalità del router di Angular più alcune funzionalità aggiuntive che includono route nidificate e stati. *Perché?*: la sintassi è piuttosto simile a quella del router di Angular ed è facile migrare a UI Router. - + - Nota: Puoi usare un provider quale `routerHelperProvider` mostrato sotto per aiutarti a configurare gli stati tra i file durante la fase di esecuzione. ```javascript @@ -3070,7 +3199,7 @@ Apri prima una "issue" per discutere potenziali cambiamenti/aggiunte. Se hai dom ### Processo - 1. Discuti i cambiamenti in un issue di GitHub. + 1. Discuti i cambiamenti in un issue di GitHub. 2. Apri una Pull Request, fai riferimento all issue e specifica i cambiamenti e perché questi aggiungono valore. 3. La Pull Request sarà vagliata e quindi fatto un merge o declinata. diff --git a/i18n/ja-JP.md b/a1/i18n/ja-JP.md similarity index 99% rename from i18n/ja-JP.md rename to a1/i18n/ja-JP.md index 29218653..4b034ae4 100644 --- a/i18n/ja-JP.md +++ b/a1/i18n/ja-JP.md @@ -2,16 +2,14 @@ *[@john_papa](//twitter.com/john_papa)によるチームのための頑固なAngularスタイルガイド* -*Translation by [@noritamago](https://github.com/noritamago)* - ->The [original English version](http://jpapa.me/ngstyles) is the source of truth, as it is maintained and updated first. - もしあなたが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/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) をチェックすることをお勧めします。 @@ -444,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: もし関数がワンライナーであれば、可読性に影響が無い限り上に置いたままにすることを検討して下さい。 @@ -1483,9 +1481,9 @@ function Dashboard(common, dataservice) { } ``` - + このコードはMinifyされたときにマングルされた変数が生成され実行エラーになるかもしれません。 - + ```javascript /* avoid - not minification-safe*/ angular.module('app').controller('Dashboard', d);function d(a, b) { } @@ -1999,7 +1997,7 @@ ### Directive Component Names ###### [Style [Y126](#style-y126)] - - 全てのディレクトリにキャメルケースで一貫性を取れた名前を用いて下さい。そのディレクティブが属する範囲を表す短いプリフィックス(例としては会社やプロジェクトのプリフィックス)を用いて下さい。 + - 全てのディレクトリにキャメルケースで一貫性を取れた名前を用いて下さい。そのディレクティブが属する範囲を表す短いプリフィックス(例としては会社やプロジェクトのプリフィックス)を用いて下さい。 *なぜ ?*: 参照するべきコンポーネントと素早く特定する一貫性の取れた方法を与えます。 @@ -2174,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よりも難しいでしょう。 @@ -2268,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) *なぜ ?*: メインのアプリケーションのモジュールは、アプリケーションの機能を素早く特定可能なマニュフェストを含みます。 @@ -2404,7 +2402,7 @@ *なぜ ?*: Karmaは一度だけ実行するか、コードが変更されたときに自動的に実行するかを簡単に設定することができます。 - *なぜ ?*: Karmaは自前のテストランナーもしくはGruntやGulpを用いた継続的なインテグレーションのプロセスに容易に接続することができます。 + *なぜ ?*: Karmaは自前のテストランナーもしくはGruntやGulpを用いた継続的なインテグレーションのプロセスに容易に接続することができます。 *なぜ ?*: [WebStorm](http://www.jetbrains.com/webstorm/) や [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225)などのいくつかのIDEはKarmaを統合し始めています。 @@ -2454,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)] @@ -2910,7 +2908,7 @@ ## Yeoman Generator ###### [Style [Y260](#style-y260)] -このスタイルガイドに沿ったAngularのアプリケーションを作る良いスタートポイントとして、[HotTowel yeoman generator](http://jpapa.me/yohottowel)を使うことができます。 +このスタイルガイドに沿ったAngularのアプリケーションを作る良いスタートポイントとして、[HotTowel yeoman generator](http://jpapa.me/yohottowel)を使うことができます。 1. generator-hottowelをインストール @@ -3082,7 +3080,7 @@ _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 diff --git a/a1/i18n/ko-KR.md b/a1/i18n/ko-KR.md new file mode 100644 index 00000000..7d2a1d65 --- /dev/null +++ b/a1/i18n/ko-KR.md @@ -0,0 +1,3268 @@ +# Angular Style Guide + +## Angular Team의 지지를 받습니다. +Angular 팀의 리더인 Igor Minar 에게 특별히 감사합니다. 이 스타일 가이드를 위해 리뷰, 기여, 피드백을 해주었고 저를 믿어주고 이끌어 주었습니다. + +## Purpose +*팀환경을 위한 방향을 제시하는 Angular 스타일 가이드 by [@john_papa](//twitter.com/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) + + [![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). + +제 스타일의 많은 부분은 [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)** + +### Small Functions +###### [style [Y002](#style-y002)] + + - 작은 함수를 정의하세요, 75줄을 넘지 마세요 (적을수록 좋습니다). + + *이유*: 하나의 일을하고 하나의 목적을 수행하는 작은 함수는 테스트하기 쉽습니다. + + *이유*: 작은 함수는 재사용을 촉진합니다. + + *이유*: 작은 함수는 읽기 쉽습니다. + + *이유*: 작은 함수는 유지하기 쉽습니다. + + *이유*: 작은 함수는 외부 scpoe와 변수를 공유하거나, 원하지 않는 closure를 만들거나, 원하지 않는 의존성과의 결합을 통해 만들어지는 숨겨진 버그를 피하는데 도움이 됩니다. + +**[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/a1/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/a1/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); + } + } + ``` + + 위의 코드가 포함된 코드가 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)] + + - 캐멀 캐이스를 이용해서 디렉티브 이름을 일관적으로 지어주세요. 짧은 접두어를 사용하여 이 디렉티브가 어떤 프로젝트 혹은 회사에 소속되어 있는지 알려주세요. + + *이유*: 일관된 방식으로 컴포넌트를 찾아내고 참조할 수 있도록 합니다. + + ```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`코드를 쉽게 찾아낼 수 있음 (Locate) + 2. `I`첫눈에 구분할 수 있음 (Identify) + 3. `F`단순한 구조를 유지할 수 있음 (Flat) + 4. `T`반복작업을 피할 수 있음 (T-DRY) + +### 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/a1/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/a1/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)] + + - 모든 각각의 스토리를 위해서 테스트 세트를 작성하세요. 빈 테스트로 시작을 하고 스토리에 맞추어 코드를 작성하면서 채워나가세요. + + *이유*: 테스트 설명을 작성함으로 스스로의 스토리가 어떻게 작동해야 하고 어떻게 작동하지 말아야 하는지 투명하게 정의할 수 있습니다. 그리고 성공을 어떻게 측정해야 하는지도 포함됩니다. + + ```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/a1/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 +파일 템플릿이나 스니펫을 사용하면 일관적인 스타일과 패턴을 지킬수 있습니다. 웹 개발용 에디터와 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)를 사용하여 자동화 처리를 사용하세요. Gulp는 설정보다는 코드 자체에 무게를 더 주는 반면 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)** diff --git a/i18n/mk-MK.md b/a1/i18n/mk-MK.md similarity index 96% rename from i18n/mk-MK.md rename to a1/i18n/mk-MK.md index 872cf239..3ad49ea8 100644 --- a/i18n/mk-MK.md +++ b/a1/i18n/mk-MK.md @@ -2,18 +2,13 @@ *Своеволен Angular водич на кодирање за тимови од [@john_papa](//twitter.com/john_papa)* -*Преведено од [Александар Богатинов](https://github.com/Bogatinov)* - ->[Оригиналната Англиска верзија](http://jpapa.me/ngstyles) е изворот на вистината, одржувана и ажурирана прва. - -Доколку барате своеволен стил на кодирање за синтакса, конвенции и структурирање на Angular апликации, тогаш сте на правилното место. -Овие стилови се базирани на моето искуство во развој на [Angular](//angular.org), презентации, [Pluralsight тренинг курсеви](http://pluralsight.com/training/Authors/Details/john-papa) и работа во тимови. +Доколку барате своеволен стил на кодирање за синтакса, конвенции и структурирање на Angular апликации, тогаш сте на правилното место. Овие стилови се базирани на моето искуство во развој на [Angular](//angular.org), презентации, [Pluralsight тренинг курсеви](http://pluralsight.com/training/Authors/Details/john-papa) и работа во тимови. Целта на овој водич на кодирање е да овозможи насока во развој на Angular апликации преку конвенциите што јас ги користам, и уште поважно, зошто ги користам. >Ако ви се допаѓа овој водич, тогаш проверете ми го курсот [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) со цел да добиете осет за неговиот пристап и како се споредува. @@ -23,8 +18,7 @@ ## Погледнете ги водичите во пробната апликација Иако овој водич ги објаснува "што", "зошто" и "како", јас сметам дека е полезно да ги запазиме во практика. Овој водич е придружен од пробна апликација која ги следи овие стилови и модели. Можете да ја најдете [пробната апликација (наречена modular) тука](https://github.com/johnpapa/ng-demos) во папката 'modular'. Не се колебајте да ја земете, да ја клонирате и форкувате. [Инструкции за да ја започнете се во своете readme](https://github.com/johnpapa/ng-demos/tree/master/modular) - -##Преводи +##Преводи [Преводи од овој Angular водич на кодирање](https://github.com/johnpapa/angular-styleguide/tree/master/i18n) се одржувани од заедницата и можете да ги најдете тука. ## Table of contents @@ -48,7 +42,7 @@ 1. [Startup Logic](#startup-logic) 1. [Angular $ Wrapper Services](#angular--wrapper-services) 1. [Testing](#testing) - 1. [Animations](#animations) + 1. [Animations](#animations) 1. [Comments](#comments) 1. [JSHint](#js-hint) 1. [JSCS](#jscs) @@ -67,9 +61,9 @@ ### Правило од 1 ###### [Style [Y001](#style-y001)] - - Дефинирај 1 компонента во датотека. + - Дефинирај 1 компонента во датотека. - Следниот пример го дефинира `app` модулот и неговите зависности, дефинира контролер, и дефинира фабрика, сé во една датотека + Следниот пример го дефинира `app` модулот и неговите зависности, дефинира контролер, и дефинира фабрика, сé во една датотека ```javascript /* избегнувајте */ @@ -77,17 +71,17 @@ .module('app', ['ngRoute']) .controller('someController', someController) .factory('someFactory', someFactory); - + function SomeController() { } function someFactory() { } ``` - + Истите компоненти сега се разделени во свои датотеки. ```javascript /* препорачано */ - + // app.module.js angular .module('app', ['ngRoute']); @@ -95,7 +89,7 @@ ```javascript /* препорачано */ - + // someController.js angular .module('app') @@ -106,12 +100,12 @@ ```javascript /* препорачано */ - + // someFactory.js angular .module('app') .factory('someFactory', someFactory); - + function someFactory() { } ``` @@ -120,8 +114,8 @@ ## IIFE ### JavaScript Closures ###### [Style [Y010](#style-y010)] - - Вгнездете ги Angular компоненти во Immediately Invoked Function Expression (IIFE). - + - Вгнездете ги Angular компоненти во Immediately Invoked Function Expression (IIFE). + *Зошто?*: IIFE не ги покажува променливите на глобално ниво. Ова помага при спречување да променливите и декларациите на функциите да живеат подолго од очекуваното на глобалното ниво, што исто така помага во избегнување на судири на променливи со исто име. *Зошто?*: Кога вашиот код е минифициран и поставен во една датотека за да биде ажуриран на продукцискиот сервер тогаш може да се појават судири на локални и глобални променливи. IIFE ве заштитува од двата случаи овозможувајќи ниво на променливи во една датотека. @@ -133,7 +127,7 @@ .module('app') .factory('logger', logger); - // logger функцијата е додадена како глобална променлива + // logger функцијата е додадена како глобална променлива function logger() { } // storage.js @@ -145,10 +139,10 @@ function storage() { } ``` - + ```javascript /** - * препорачано + * препорачано * * нема заборавени глобални променливи */ @@ -156,7 +150,7 @@ // logger.js (function() { 'use strict'; - + angular .module('app') .factory('logger', logger); @@ -176,8 +170,8 @@ })(); ``` - - Забелешка: Со цел да се скрати кодот, остатокот од примерите во овој водич нема да го прикажат IIFE - + - Забелешка: Со цел да се скрати кодот, остатокот од примерите во овој водич нема да го прикажат IIFE + - Забелешка: IIFE's го спречува кодот за тестирање од пристап до приватните членови како регуларни изрази или помошни функции кои потребни се да бидат директно тестирани во изолација. Сепак можете да ги тестирате овие преку членови кои можат да се пристапат или да ги изложите преку своја компонента. На пример, со поставување тие помошни функции, регуларни изрази или константи во своја фабрика или константа. **[Назад кон содржината](#table-of-contents)** @@ -186,16 +180,16 @@ ### Избегнувајте судири во именување ###### [Style [Y020](#style-Y020)] - - Користите уникатни шаблони на именување со разделувачи за под-модулите. + - Користите уникатни шаблони на именување со разделувачи за под-модулите. *Зошто?*: Со уникатни имиња избегнувате судири на модули со исто име. Разделувачи ги одредуваат модулите и нивната хиерархија на подмодули. На пример `app` е главниот модул додека `app.dashboard` и `app.users` се модули кои можно е да се зависни од `app`. ### Дефинирање (познати како Setters) ###### [Style [Y021](#style-Y021)] - - Декларирај модули без променлива користејќи ја setter синтаксата. + - Декларирај модули без променлива користејќи ја setter синтаксата. *Зошто?*: Со 1 компонента во датотека, реткост е да поставиш нова променлива во модулот. - + ```javascript /* избегнувајте*/ var app = angular.module('app', [ @@ -229,7 +223,7 @@ /* избегнувајте */ var app = angular.module('app'); app.controller('SomeController', SomeController); - + function SomeController() { } ``` @@ -238,22 +232,22 @@ angular .module('app') .controller('SomeController', SomeController); - + function SomeController() { } ``` ### Setting vs Getting ###### [Style [Y023](#style-Y023)] - Само еднаш создади и земај го за сите други инстанци. - + *Зошто?*: Модул треба еднаш да биде создаден, и од тој момент да биде само превземен. - + - Употреби `angular.module('app', []);` за да создадеш модул. - - Употреби `angular.module('app');` за да го превземеш тој модул. + - Употреби `angular.module('app');` за да го превземеш тој модул. ### Именувани vs Анонимни функции ###### [Style [Y024](#style-Y024)] - - Употреби именувани функции наместо да проследиш анонимни функции како повратни повици (callback). + - Употреби именувани функции наместо да проследиш анонимни функции како повратни повици (callback). *Зошто?*: Ова создава читлив код, кој е полесен за дебагирање и го намалува бројот на вгнездени повратни повици. @@ -315,11 +309,11 @@ ### controllerAs синтакса во контролери ###### [Style [Y031](#style-Y031)] - - Употреби `controllerAs` наместо `класичен контролер со $scope` синтакса. + - Употреби `controllerAs` наместо `класичен контролер со $scope` синтакса. - `controllerAs` синтакса користи `this` во контролерите што се поврзува со `$scope` - *Зошто?*: `controllerAs` е синтаксички поубав од `$scope`. Вие сеуште може да се поврзете со Прегледот и да ги пристапите `$scope` методите. + *Зошто?*: `controllerAs` е синтаксички поубав од `$scope`. Вие сеуште може да се поврзете со Прегледот и да ги пристапите `$scope` методите. *Зошто?*: Ви помага да избегнете употреба на `$scope` методи во контролерот кога е подобро да ги избегнете или преместите во фабрика. `$scope` може да употребите во фабрика или во контролер само кога ви е потребен. На пример, кога објавувате/пријавувате настани со употреба на [`$emit`](https://docs.angular.org/api/ng/type/$rootScope.Scope#$emit), [`$broadcast`](https://docs.angular.org/api/ng/type/$rootScope.Scope#$broadcast), или [`$on`](https://docs.angular.org/api/ng/type/$rootScope.Scope#$on) подобро е да ги преместите во фабрика и да ја повикате неа во контролерот. @@ -342,7 +336,7 @@ ### controllerAs со vm ###### [Style [Y032](#style-Y032)] - Употребете нова променлива за `this` кога употребувате `controllerAs` синтакса. Одлучете се за постојано име на променлива како на пример `vm`, што е кратеница за ViewModel. - + *Зошто?*: `this` зборот е контекстуален и може да ја промени својата состојба кога се употребува во функција која е во контролерот. Со употреба на нова променлива за `this`, се избегнува овој проблем. ```javascript @@ -366,7 +360,7 @@ ```javascript /* jshint validthis: true */ var vm = this; - ``` + ``` Забелешка: Кога создавате watches во контролерот кога ја користите `controller as` синтаксата, тогаш можете да го надгледувате `vm.*` членот со следната синтакса. (Создавање watches претпазливо бидејќи можат да додадат товар на digest циклусот.) @@ -380,10 +374,10 @@ ### Поврзување на членовите на почеток ###### [Style [Y033](#style-Y033)] - Постави ги членовите кои ќе се поврзат според алфабетски редослед. Наместо да бидат поставени низ кодот во контролерот. - - *Зошто?*: Поставување на поврзувањата на почетокот го прави кодот полесен за читање и помага за да го најдеш кој член од контролерот е поврзан и употребуван во Прегледот. - *Зошто?*: Поставување на анонимни функции во ист ред е лесно, но доколку тие функции се подолги од 1 линија код тогаш може да ја намалат читливоста. Со дефинирање на функциите под поврзаните членови (функциите ќе бидат земени) горе и нивните имплементациите доле го прави кодот полесен за читање. + *Зошто?*: Поставување на поврзувањата на почетокот го прави кодот полесен за читање и помага за да го најдеш кој член од контролерот е поврзан и употребуван во Прегледот. + + *Зошто?*: Поставување на анонимни функции во ист ред е лесно, но доколку тие функции се подолги од 1 линија код тогаш може да ја намалат читливоста. Со дефинирање на функциите под поврзаните членови (функциите ќе бидат земени) горе и нивните имплементациите доле го прави кодот полесен за читање. ```javascript /* избегнувајте*/ @@ -429,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 линија код, можете да ја поставите горе се додека читливоста не се наруши. @@ -440,8 +434,8 @@ vm.gotoSession = gotoSession; vm.refresh = function() { - /** - * линии + /** + * линии * на * код * нарушуваат @@ -468,19 +462,19 @@ ### Декларација на функции за да ја скрие имплементацијата ###### [Style [Y034](#style-Y034)] - Употребу декларации на функции за да ги скриеш нивните имплементации. Нека членовите за поврзување останат горе. Кога треба да поврзеш функција во контролерот, посочи ја кон декларацијата на таа функција која се појавува подоле во датотеката. Ова е директно поврзано со секцијата Поврзување на членови на почеток. За повеќе детали, проверете го [овој пост](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - + *Зошто?*: Поставување членови за поврзување на почеток го прави кодот полесен за читање и лесно да забележиме кој член од контролерот може да биде повзан и искористен во Прегледот. (Исто како погоре.) *Зошто?*: Поставување на имплементацијата на функцијата подоле во датотеката ја поместува комплексноста надвор од преглед со цел да ги забележиме поважните работи на почетокот. *Зошто?*: Декларации на функции се превземени со цел да не се грижиме дали функцијата е декларирана пред да е дефинирана. (како што е со изрази на функции). - *Зошто?*: Нема потреба да се грижиме дали нашиот код ќе се скрши доколку ги приместиме `var a` пред `var b` доколку `a` зависи од `b`. + *Зошто?*: Нема потреба да се грижиме дали нашиот код ќе се скрши доколку ги приместиме `var a` пред `var b` доколку `a` зависи од `b`. *Зошто?*: Редоследот е значаен со функциски изрази. ```javascript - /** + /** * избегнувајте * употреба на функциски изрази */ @@ -573,7 +567,7 @@ .catch(function(error) { // Интерпретирајте ја грешката // Спремете се со истекување на време? пробување пак? друг сервис? - // Повторно одбиете за корисникот да ја забележи соодветната порака + // Повторно одбиете за корисникот да ја забележи соодветната порака }); }; } @@ -587,7 +581,7 @@ vm.isCreditOk; vm.total = 0; - function checkCredit() { + function checkCredit() { return creditService.isOrderTotalOk(vm.total) .then(function(isOk) { vm.isCreditOk = isOk; }) .catch(showServiceError); @@ -598,15 +592,15 @@ ### Контролерите треба да бидат фокусирани ###### [Style [Y037](#style-Y037)] - - Дефинирајте контролер за преглед, и пробајте да не го искористите истиот за други прегледи. Наместо тоа, преместете ја повторно употребливата логика во фабрики и поставете го тој контролер фокусиран за својот преглед. - + - Дефинирајте контролер за преглед, и пробајте да не го искористите истиот за други прегледи. Наместо тоа, преместете ја повторно употребливата логика во фабрики и поставете го тој контролер фокусиран за својот преглед. + *Why?*: Повторно искористување на контролери со повеќе прегледи е лесно кршливо и тешко за одржување стабилност низ повеќе апликации во е2е тестирање. ### Назначување Контролери ###### [Style [Y038](#style-Y038)] - - Кога контролерот е во пар со прегледот и кога било кој од нив треба да биде повторно искористен од други контролери или прегледи тогаш дефинирај ги контролерите со нивните рута. - - Забелешка: Доколку Прегледот е вчитан преку други начини наместо рути, тогаш искористете ја `ng-controller="Avengers as vm"` синтаксата. + - Кога контролерот е во пар со прегледот и кога било кој од нив треба да биде повторно искористен од други контролери или прегледи тогаш дефинирај ги контролерите со нивните рута. + + Забелешка: Доколку Прегледот е вчитан преку други начини наместо рути, тогаш искористете ја `ng-controller="Avengers as vm"` синтаксата. *Зошто?*: Преку поставување на контролерот во пар во рутата се овозможува други рути да започнат други парови од контролери и прегледи. Кога контролерите се назначени со прегледот со [`ng-controller`](https://docs.angular.org/api/ng/directive/ngController), тогаш тој преглед е секогаш поврзан со истиот контролер. @@ -662,8 +656,8 @@ ### Singletons ###### [Style [Y040](#style-Y040)] - - Сервиси се инстанцирани со `new` зборот, и се употребуваат со `this` за јавни методи и променливи. Бидејќи се слични со фабрики, користете фабрика за конзистентност. - + - Сервиси се инстанцирани со `new` зборот, и се употребуваат со `this` за јавни методи и променливи. Бидејќи се слични со фабрики, користете фабрика за конзистентност. + Забелешка: [Сите Angular сервиси се singletons](https://docs.angular.org/guide/services). Тоа значи дека има само една инстанца од сервис за injector. ```javascript @@ -705,14 +699,14 @@ ### Singletons ###### [Style [Y051](#style-Y051)] - Фабрики се singletons и враќаат објект што ги содржини членовите од тој сервис. - + Забелешка: [Сите Angular сервиси се singletons](https://docs.angular.org/guide/services). ### Членовите за пристап на почеток ###### [Style [Y052](#style-Y052)] - - Изложи ги членовите на сервисот кои треба да се повикаат (неговиот интерфејс) на почетокот, користејќи ја техниката [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). + - Изложи ги членовите на сервисот кои треба да се повикаат (неговиот интерфејс) на почетокот, користејќи ја техниката [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - *Зошто?*: Поставување на повикувачките членови на почетокот го прави кодот полесен за читање и помага при лесно забележување на кои членови можат да се повикаат и мораат да бидат тестирани (и/или излажани при тестирање). + *Зошто?*: Поставување на повикувачките членови на почетокот го прави кодот полесен за читање и помага при лесно забележување на кои членови можат да се повикаат и мораат да бидат тестирани (и/или излажани при тестирање). *Зошто?*: Ова е посебно значајно кога датотеката станува подолга и ја намалува потребата од scrolling за да забележиме што е изложено. @@ -722,10 +716,10 @@ /* избегнувајте */ function dataService() { var someValue = ''; - function save() { + function save() { /* */ }; - function validate() { + function validate() { /* */ }; @@ -750,19 +744,19 @@ //////////// - function save() { + function save() { /* */ }; - function validate() { + function validate() { /* */ }; } ``` - + На овој начин поврзувањата се пресликуваат низ објектот, примитивните вредности не можат да се ажурираат самостојно со употреба на 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)] @@ -774,9 +768,9 @@ *Зошто?*: Декларации на функции се поврзани така што нема потреба од грижа доколку се користи функцијата пред да биде дефинирана (како што е примерот со функциски изрази). - *Зошто?*: Никогаш нема да има потреба од грижа со декларации на функции и дали поместување на `var a` пред `var b` ќе го скрши кодот доколку `a` зависи од `b`. + *Зошто?*: Никогаш нема да има потреба од грижа со декларации на функции и дали поместување на `var a` пред `var b` ќе го скрши кодот доколку `a` зависи од `b`. - *Зошто?*: Редоследот е значаен за функциски изрази. + *Зошто?*: Редоследот е значаен за функциски изрази. ```javascript /** @@ -905,7 +899,7 @@ } } ``` - + Забелешка: Податочниот сервис е повикан од потрошувачите како контролери, криејќи ја имплементацијата од нив, како што е покажано подоле. ```javascript @@ -937,7 +931,7 @@ return vm.avengers; }); } - } + } ``` ### Вратете Promise од податочни повици @@ -989,9 +983,9 @@ ## Directives ### Ограничувајте се на 1 на датотека ###### [Style [Y070](#style-Y070)] - - Создадете една директива по датотека. Именувајте ја според директивата. + - Создадете една директива по датотека. Именувајте ја според директивата. - *Зошто?*: Полесно е да ги споите повеќе директиви во една датотека, но потешко да ги разделите тие со цел да бидат споделени низ апликации, модули или само еден модул. + *Зошто?*: Полесно е да ги споите повеќе директиви во една датотека, но потешко да ги разделите тие со цел да бидат споделени низ апликации, модули или само еден модул. *Зошто?*: Една директива по датотека е полесна за одржување. @@ -1049,7 +1043,7 @@ /** * @desc sales директива која може да се користи низ апликацијата во компанијата Acme * @пример
- */ + */ angular .module('sales.widgets') .directive('acmeSalesCustomerInfo', salesCustomerInfo); @@ -1080,7 +1074,7 @@ ### Манипулирајте DOM во директивата ###### [Style [Y072](#style-Y072)] - - Кога манипулирате директно со DOM, употребете директива. Ако можат да се употребат други начини, како CSS за стилови или [анимациски сервиси](https://docs.angular.org/api/ngAnimate), Angular темплејти, [`ngShow`](https://docs.angular.org/api/ng/directive/ngShow) или [`ngHide`](https://docs.angular.org/api/ng/directive/ngHide), тогаш употребете ги тие. На пример, ако директивата само се појавува/исчезнува, тогаш употребете ngHide/ngShow. + - Кога манипулирате директно со DOM, употребете директива. Ако можат да се употребат други начини, како CSS за стилови или [анимациски сервиси](https://docs.angular.org/api/ngAnimate), Angular темплејти, [`ngShow`](https://docs.angular.org/api/ng/directive/ngShow) или [`ngHide`](https://docs.angular.org/api/ng/directive/ngHide), тогаш употребете ги тие. На пример, ако директивата само се појавува/исчезнува, тогаш употребете ngHide/ngShow. *Зошто?*: Манипулација на DOM е тешка да се тестира, дебагира и притоа постојат подобри начини. (на пример CSS, анимации, темплејти) @@ -1088,9 +1082,9 @@ ###### [Style [Y073](#style-Y073)] - Обезбедете краток, уникатен и описен префикс на директивата како `acmeSalesCustomerInfo` што е декларирана во HTML како `acme-sales-customer-info`. - *Зошто?*: Уникатниот краток префикс ја идентификува смислата на директивата и нејзиното потекло. На пример, префиксот `cc-` може да укажува дека директивата дел од CodeCamper апликацијата додека `acme-` може да укажува дека директивата е за компанијата Acme. + *Зошто?*: Уникатниот краток префикс ја идентификува смислата на директивата и нејзиното потекло. На пример, префиксот `cc-` може да укажува дека директивата дел од CodeCamper апликацијата додека `acme-` може да укажува дека директивата е за компанијата Acme. - Забелешка: Избегнувајте `ng-` бидејќи тие се резервирани за директивите на Angular. Проучете најчесто употребувани директиви со цел да избегнувате судири со имињата, како `ion-` за [Ionic Framework](http://ionicframework.com/). + Забелешка: Избегнувајте `ng-` бидејќи тие се резервирани за директивите на Angular. Проучете најчесто употребувани директиви со цел да избегнувате судири со имињата, како `ion-` за [Ionic Framework](http://ionicframework.com/). ### Ограничете се на Елементи и Атрибути ###### [Style [Y074](#style-Y074)] @@ -1132,7 +1126,7 @@
``` - + ```javascript /* препорачано */ angular @@ -1202,8 +1196,8 @@ // Внесување $scope за споредба var vm = this; - vm.min = 3; - + 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); @@ -1219,7 +1213,7 @@ ``` Забелешка: Можете исто така да го именувате Контролерот кога ќе го вметнете во link функција и пристапите до атрибутите на директивата како својства на контролерот. - + ```javascript // Алтернатива за примерот горе function linkFunc(scope, el, attr, vm) { @@ -1229,7 +1223,7 @@ console.log('LINK: vm.max = %s', vm.max); } ``` - + ###### [Style [Y076](#style-y076)] @@ -1237,7 +1231,7 @@ *Зошто?*: Полесно е да се поврзе надворешниот scope со тој на контролерот во директивата. - Забелешка: `bindToController` беше воведен во Angular 1.3.0. + Забелешка: `bindToController` беше воведен во Angular 1.3.0. ```html
@@ -1285,13 +1279,13 @@ ### Активација на Promises во контролерот ###### [Style [Y080](#style-Y080)] - Решете се со логиката за започнување на контролерот во `activate` функцијата. - + *Зошто?*: Поставување на почетна логика во согласно место во контролерот е полесно за лоцирање, полесно за тестирање и го оневозможува распределувањето на почетната логика низ целиот контролер. *Зошто?*: Функцијата `activate` во контролерот е погодна за повторно да се искористи логиката за освежување на контролер/Преглед, ја држи логиката на едно место, побрзо го прикажува Прегледот, ги олеснува анимациите на `ng-view` или `ui-view` и е елегантно за корисникот. Забелешка: Доколку условно требате да го прекинете решавањето на патеката пред да го користите контролерот, тогаш разгледајте го [route resolve](#style-y081). - + ```javascript /* избегнувајте */ function Avengers(dataservice) { @@ -1436,7 +1430,7 @@ ### Опасно за минифкација ###### [Style [Y090](#style-Y090)] - Избегнувајте употреба на кратенки за декларација на зависности без употреба на безбеден пристап за минификација. - + *Зошто?*: Параметрите на компонентата (e.g. контролер, фабрика, итн) ќе бидат претворени во исчезнати променливи. На пример, `common` и `dataservice` може да постанат `a` или `b` и да не бидат најдени од Angular. ```javascript @@ -1459,7 +1453,7 @@ ### Рачна идентификација на зависности ###### [Style [Y091](#style-Y091)] - Употребете `$inject` за рачна идентификација на вашиоте зависности во Angular компонентите. - + *Зошто?*: Оваа техника се користи во [`ng-annotate`](https://github.com/olov/ng-annotate), што ја препорачувам за автоматизација на создавање на безбедни зависности при минификација. Доколку `ng-annotate` забележи зависност доколку постои, нема да ја повтори. *Зошто?*: Ова ги заштитува вашите зависности од можноста да бидат изгубени при минификација. На пример, `common` и `dataservice` можат да постанат `a` or `b` и да не можат да бидат најдени од Angular. @@ -1470,19 +1464,19 @@ /* избегнувајте */ angular .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', + .controller('Dashboard', + ['$location', '$routeParams', 'common', 'dataservice', function Dashboard($location, $routeParams, common, dataservice) {} - ]); + ]); ``` ```javascript /* избегнувајте */ angular .module('app') - .controller('Dashboard', + .controller('Dashboard', ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - + function Dashboard($location, $routeParams, common, dataservice) { } ``` @@ -1494,7 +1488,7 @@ .controller('Dashboard', Dashboard); Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - + function Dashboard($location, $routeParams, common, dataservice) { } ``` @@ -1536,7 +1530,7 @@ ### Рачна идентификација на зависностите преку решавање на патеки ###### [Style [Y092](#style-Y092)] - Употребете `$inject` за рачна идентификација на зависностите преку решавање на патеки во Angular компоненти. - + *Зошто?*: Оваа техника ги разделува анонимните функции за решавање на патеката, кое е полесно за читање. *Зошто?*: `$inject` линијата може лесно да го претходи решавачот, подобрувајќи ја безбедноста при минификација. @@ -1568,10 +1562,10 @@ ### ng-annotate ###### [Style [Y100](#style-Y100)] - Употребете [ng-annotate](//github.com/olov/ng-annotate) за [Gulp](http://gulpjs.com) или [Grunt](http://gruntjs.com) и поставете го коментарот `/** @ngInject */` над функциите кои им се потребни автоматизиран dependency injection. - + *Зошто?*: Го заштитува вашиот код од загуба на зависности при минификација. - *Зошто?*: Употреба на [`ng-min`](https://github.com/btford/ngmin) е застарено. + *Зошто?*: Употреба на [`ng-min`](https://github.com/btford/ngmin) е застарено. >Јас преферирам Gulp бидејќи сметам е полесен за пишување, читање и дебагирање. @@ -1644,7 +1638,7 @@ ### Употребете Gulp или Grunt за ng-annotate ###### [Style [Y101](#style-Y101)] - Употребете [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) или [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) во автоматизиран build task. Внесете `/* @ngInject */` пред функцијата која има зависности. - + *Зошто?*: ng-annotate ќе ги фати повеќето зависности, но понекогаш потребни се навестувања во својата `/* @ngInject */` синтакса. Следниот код е припер за gulp задача која употребува ngAnnotate @@ -1677,7 +1671,7 @@ ### Декоратори ###### [Style [Y110](#style-Y110)] - Употребете [decorator](https://docs.angular.org/api/auto/service/$provide#decorator), при конфигурација со употреба на [`$provide`](https://docs.angular.org/api/auto/service/$provide) сервис, на [`$exceptionHandler`](https://docs.angular.org/api/ng/service/$exceptionHandler) сервисот за да извршите лични акции кога ќе се случи исклучок. - + *Зошто?*: Овозможува постојан начин да се справи со исклучоци кои Angular не може да ги фати во development-time или run-time. Забелешка: Друга опција е да се прескокне сервисот наместо да се користи декоратор. Ова е добра опција, но доколку сакате да го задржите стандардното однесување и проширите, тогаш препорачливо е да користите декоратор. @@ -1699,9 +1693,9 @@ function extendExceptionHandler($delegate, toastr) { return function(exception, cause) { $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause + var errorData = { + exception: exception, + cause: cause }; /** * Може да ја додадете грешката на колекцијата на сервиси, @@ -1795,10 +1789,10 @@ - Употребете самостојни имиња за сите компоненти следејќи го шаблонот кој ја опишува функцијата и после (опционално) неговиот тип. Мојот препорачан шаблон е `feature.type.js`. Постојат две имиња за повеќето датотеки: * името на датотеката (`avengers.controller.js`) * регистрираната компонента во Angular (`AvengersController`) - + *Зошто?*: Конвенциите при именување им помага на тимот за конзистентен начин да ги најдат компонентите при прв поглед. Конзистентност во проект е значајно. Конзистентност во тимот е важно. Конзистентност низ компанијата обезбедува огромна ефикасност. - *Зошто?*: Конвенцијата при именување треба едностанво да ви помогне побрзо да го најдете кодот и полесно да го разберете. + *Зошто?*: Конвенцијата при именување треба едностанво да ви помогне побрзо да го најдете кодот и полесно да го разберете. ### Имиња на датотеки по функција ###### [Style [Y121](#style-Y121)] @@ -1839,7 +1833,7 @@ // Константи constants.js - + // Дефиниција на модули avengers.module.js @@ -1849,7 +1843,7 @@ // Конфигурација avengers.config.js - + // Директиви avenger-profile.directive.js avenger-profile.directive.spec.js @@ -1868,7 +1862,7 @@ ### Имиња на датотеки за тестови ###### [Style [Y122](#style-Y122)] - - Имињата на тестовите се слични со компонентата што ја тестираат со додавање на суфикс `spec`. + - Имињата на тестовите се слични со компонентата што ја тестираат со додавање на суфикс `spec`. *Зошто?*: Обезбедува конзистентен начин за лесно препознавање компоненти. @@ -1904,7 +1898,7 @@ function HeroAvengersController(){ } ``` - + ### Суфикс на името на контролерот ###### [Style [Y124](#style-Y124)] - Додадете суфикс `Controller` на името на Контролерот. @@ -1956,7 +1950,7 @@ * препорачано */ - // avenger-profile.directive.js + // avenger-profile.directive.js angular .module .directive('xxAvengerProfile', xxAvengerProfile); @@ -1969,7 +1963,7 @@ ### Модули ###### [Style [Y127](#style-Y127)] - Кога постојат повеќе модули, името на главниот модул е `app.module.js` додека другите зависни модули се именувани според тоа што претставуваат. На пример, админ модул е именувам `admin.module.js`. Соодветните регистрирани имиња на модули би биле `app` и `admin`. - + *Зошто?*: Обезбедува конзистентност за повеќе модуларни апликации како и проширување до огромни апликации. *Зошто?*: Обезбедува лесен начин да се автоматизираат задачите за создавање на дефинициите на модулите, па потоа сите останати angular датотеки (пакување на датотеки). @@ -1991,12 +1985,12 @@ ## Application Structure LIFT Principle ### LIFT ###### [Style [Y140](#style-Y140)] - - Поставете ја пликацијата така што `L` (брзо лоцирање на кодот), `I` (идентификација на кодот со поглед), `F` (најрамна структура што можете) и `T` (обидете се да останете DRY). Структурата треба да ги задоволува овие 4 основни водичи. + - Поставете ја пликацијата така што `L` (брзо лоцирање на кодот), `I` (идентификација на кодот со поглед), `F` (најрамна структура што можете) и `T` (обидете се да останете DRY). Структурата треба да ги задоволува овие 4 основни водичи. *Зошто LIFT?*: Обезбедува конзистентна структура која лесно се скалира, која е модуларна, и лесно може да ја зголеми ефикасноста на програмерите преку брзо лоцирање на кодот. Друг начин да ја проверите вашата структура е да се запрашате: Колку брзо можете да ги отворите и работите во сите датотека со одредена функција? Кога забележувам дека мојата структура е незадоволителна, се навраќам на LIFT водичите - + 1. `L` (лоцирање на вашиот код е лесно) 2. `I` (идентифицирање на кодот со поглед) 3. `F` (рамна структура колку што можеме) @@ -2056,7 +2050,7 @@ ### Изглед ###### [Style [Y151](#style-Y151)] - - Поставете компоненти која го отсликуваат целокупниот изглед на апликацијата во папката `layout`. Овде може да имаме поделеност на посебни секции од прегледот и контролер кој ќе биде контејнер за неговата апликација, навигација, мениа, области со содржина и други региони. + - Поставете компоненти која го отсликуваат целокупниот изглед на апликацијата во папката `layout`. Овде може да имаме поделеност на посебни секции од прегледот и контролер кој ќе биде контејнер за неговата апликација, навигација, мениа, области со содржина и други региони. *Зошто?*: Го организира целиот изглед во единствено место кое може да се употребува низ целата апликација. @@ -2064,7 +2058,7 @@ ###### [Style [Y152](#style-Y152)] - Создадете папки според нивната функција. Кога папката ќе порасне 7+ датотеки, почнете да размислувате за нови папки. Вашиот праг може да биде различен, така да поставете како што е препорачано. - *Зошто?*: Програмерот може да го лоцира кодот, идентификува секоја датотека што претставува, структурата останува рамна и не постојат повторувачки или непотребни имиња. + *Зошто?*: Програмерот може да го лоцира кодот, идентификува секоја датотека што претставува, структурата останува рамна и не постојат повторувачки или непотребни имиња. *Зошто?*: LIFT водичите се покриени. @@ -2080,84 +2074,84 @@ app/ app.module.js app.config.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html + components/ + calendar.directive.js + calendar.directive.html + user-profile.directive.js + user-profile.directive.html layout/ - shell.html + shell.html shell.controller.js - topnav.html - topnav.controller.js + topnav.html + topnav.controller.js people/ attendees.html - attendees.controller.js + attendees.controller.js people.routes.js speakers.html speakers.controller.js speaker-detail.html speaker-detail.controller.js - services/ - data.service.js + services/ + data.service.js localstorage.service.js - logger.service.js + logger.service.js spinner.service.js sessions/ - sessions.html + sessions.html sessions.controller.js sessions.routes.js session-detail.html - session-detail.controller.js + 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 прегледи и контролери (за други функционалности), а со тоа и потешко за лоцирање на датотеките на таа функционалност. ```javascript - /* + /* * избегнувајте * алтернативен папка-по-тип * Препорачаувам "папка-по-функционалност" */ - + app/ app.module.js app.config.js app.routes.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 + 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 + logger.js spinner.js views/ - attendees.html + attendees.html session-detail.html - sessions.html - shell.html - speakers.html + sessions.html + shell.html + speakers.html speaker-detail.html - topnav.html - ``` + topnav.html + ``` **[Назад кон содржината](#table-of-contents)** ## Modularity - + ### Многу мали, само содржани модулу ###### [Style [Y160](#style-Y160)] - Создадете мали модули кои содржат една одговорност. @@ -2198,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) *Зошто?*: Главниот модул на апликацијата содржи манифест од брзо идентифицирани функционалности на апликацијата. @@ -2208,7 +2202,7 @@ Забелешка: Ова е стратегија за конзистентност. Постојат многу добри опции. Одберете една што е конзистентна, што ги следи Angular правилата за зависности, а лесно за одржување и скалабилност. - > Моите структури се разликуваат малку низ проекти, но сите ги запазуваат правилата за структура и модуларност. Имплементацијата може да се разликува во зависност од функционалностите и тимот. Со други зборови, не се засегајте на буквалната структура се додека ја оправдува вашата структура за конзистентност, одржливост и ефикасност. + > Моите структури се разликуваат малку низ проекти, но сите ги запазуваат правилата за структура и модуларност. Имплементацијата може да се разликува во зависност од функционалностите и тимот. Со други зборови, не се засегајте на буквалната структура се додека ја оправдува вашата структура за конзистентност, одржливост и ефикасност. > Во мали апликации можете да ги поставите сите заеднички зависности во апликацискиот модул каде функционалните зависности немаат директни зависности. Ова овозможува полесно одржување кај мали апликации, но станува потешко да се реискористуват тие модули надвор од оваа апликација. @@ -2227,7 +2221,7 @@ .module('app') .config(configure); - configure.$inject = + configure.$inject = ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { @@ -2347,13 +2341,13 @@ ###### [Style [Y194](#style-Y194)] - Употребете [PhantomJS](http://phantomjs.org/) за да ги извршувате тестовите на вашиот сервер. - *Зошто?*: PhantomJS е пребарувач кој ги започнува тестовите без потреба од кориснички интерфејс. Така што нема потреба да инсталирате Chrome, Safari, IE, или други пребарувачи на вашиот сервер. + *Зошто?*: PhantomJS е пребарувач кој ги започнува тестовите без потреба од кориснички интерфејс. Така што нема потреба да инсталирате Chrome, Safari, IE, или други пребарувачи на вашиот сервер. Забелешка: Сепак потребно е да ги извршите тестовите на сите пребарувачи во вашата околина како и вашата целна група. ### Анализа на код ###### [Style [Y195](#style-Y195)] - - Извршете JSHint на вашиот код. + - Извршете JSHint на вашиот код. *Зошто?*: Тестовите се код. JSHint лесно ги прикажува грешките во квалитетот на кодот што може да предизвика неправилно извршување на тестовите. @@ -2373,14 +2367,14 @@ "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)] - Поставете ги датотеките за unit тестови (спецификација) една поред друга во вашиот клиентски код. Поставете ги спецификациите кои ја покриваат интеграцијата со серверот или тестираат повеќе компоненти во посебна `tests` папка. - *Зошто?*: Unit тестови имаат директна поврзаност со специфична компонента и датотека во изворниот код. + *Зошто?*: Unit тестови имаат директна поврзаност со специфична компонента и датотека во изворниот код. *Зошто?*: Полесно е временски да ги ажурирате бидејќи секогаш се забележителни. Кога кодирате, дали TDD или тестирате пред или после развојот, специкациите се секогаш една поред друга и никогаш нема да се изгубат, што значи дека треба да се одржуваат притоа подобрувајќи ја покриеноста на кодот. @@ -2468,7 +2462,7 @@ /** * @name logError * @desc Logs errors - * @param {String} msg Message to log + * @param {String} msg Message to log * @returns {String} * @memberOf Factories.Logger */ @@ -2700,13 +2694,13 @@ angular ### Sublime Text ###### [Style [Y250](#style-Y250)] - - Angular кратки кодови кои ги следат овие водичи и стилови на код. + - Angular кратки кодови кои ги следат овие водичи и стилови на код. - - Симнете ги [Sublime Angular кратки кодови](assets/sublime-angular-snippets?raw=true) + - Симнете ги [Sublime Angular кратки кодови](assets/sublime-angular-snippets?raw=true) - Поставете ги во вашата Packages папка - - Рестартирајте го Sublime + - Рестартирајте го Sublime - Во JavaScript датотека напишете ја следната команда и потоа кликнете на `TAB` - + ```javascript ngcontroller // создава Angular контролер ngdirective // создава Angular директива @@ -2731,7 +2725,7 @@ angular ###### [Style [Y252](#style-Y252)] - Angular кратки кодови и датотечни шаблони кои ги следат овие стилови и водичи на код. Можете да ги внесете во вашите WebStorm подесувања: - - Симнете ги [WebStorm Angular датотечни шаблони и кратки кодови](../assets/webstorm-angular-file-template.settings.jar?raw=true) + - Симнете ги [WebStorm Angular датотечни шаблони и кратки кодови](../assets/webstorm-angular-file-template.settings.jar?raw=true) - Отворете го WebStorm и одберете го `File` менито - Одберете го `Import Settings` - Одберете ја датотеката и кликнете `OK` @@ -2756,7 +2750,7 @@ angular - Отворете го Atom, потоа отворете го Package Manager (Packages -> Settings View -> Install Packages/Themes) - Побарајте го пакетот 'angular-styleguide-snippets' - Кликнете на 'Install' за да го инсталирате пакетот - + - Во JavaScript датотека напишете ги следните команди и потоа кликнете `TAB` ```javascript @@ -2865,7 +2859,7 @@ angular - Употребете автоматизирање на тестови за вклучување на сите `*.module.js` датотеки пред останатите JavaScript датотеки од апликацијата. - *Зошто?*: На Angular му требаат дефинициите на модулите да бидат регистрирани пред да бидат употребени. + *Зошто?*: На Angular му требаат дефинициите на модулите да бидат регистрирани пред да бидат употребени. *Зошто?*: Именување на модули со слична шема како `*.module.js` е полесно за нивно превземање со glob и да ги постави први. @@ -2901,7 +2895,7 @@ angular *Со придонесување до ова складиште, се придржувате вашата содржина да подлежи на лиценцата на ова складиште.* ### Процес - 1. Дискусирајте за промените во Github Issue. + 1. Дискусирајте за промените во Github Issue. 2. Отворете Pull Request, поставете референца до Issue и објаснете ја промената и како додава на вредност. 3. Pull Request ќе биде оценето и биде или споено или одбиено. @@ -2932,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/i18n/PT-BR.md b/a1/i18n/pt-BR.md similarity index 60% rename from i18n/PT-BR.md rename to a1/i18n/pt-BR.md index 0289257e..2c5d8fb5 100644 --- a/i18n/PT-BR.md +++ b/a1/i18n/pt-BR.md @@ -1,12 +1,10 @@ # 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)* -*Traduzido por [Eric Douglas](https://github.com/ericdouglas), [Ciro Nunes](https://github.com/cironunes), [Jean Lucas de Carvalho](https://github.com/jlcarvalho) e [Vinicius Sabadim Fernandes](https://github.com/vinicius-sabadim)* +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. ->The [original English version](http://jpapa.me/ngstyles) is the source of truth, as it is maintained and updated first. -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. > Se você gostar deste guia, confira meu curso [Angular Patterns: Clean Code](http://jpapa.me/ngclean) na Pluralsight. @@ -22,11 +20,11 @@ Vários de meus estilos vieram de várias sessões de pair-programming (programa Embora este guia explique o **o quê**, **porque** e **como**, acho útil ver tudo isso em prática. Este guia é acompanhado de uma aplicação de exemplo que segue estes estilos e padrões. Você pode encontrar a [aplicação de exemplo (chamada "modular") aqui](https://github.com/johnpapa/ng-demos) na pasta `modular`. Sinta-se livre para pegá-la, cloná-la e *forká-la*. [Instruções de como rodar o aplicativo estão em seu README](https://github.com/johnpapa/ng-demos/tree/master/modular). -> **Nota de tradução**: Os títulos originais de cada seção será mantido, pois caso você queira buscar mais sobre estes assuntos futuramente, fazendo tal busca em inglês será obtido um resultado **imensamente** melhor. +> **Nota de tradução**: Os títulos originais de cada seção serão mantidos, pois caso você queira buscar mais sobre estes assuntos futuramente, fazendo tal busca em inglês será obtido um resultado **imensamente** melhor. > > Após o título, estará a tradução auxiliar, quando necessária, visto que alguns termos são mais facilmente entendidos quando não traduzidos, por fazerem parte do núcleo do estudo em questão. > -> Para eventuais erros de digitação e/ou tradução, favor enviar um pull-request! +> Para eventuais erros de digitação e/ou tradução, favor enviar um pull-request! ## Tabela de Conteúdo @@ -48,7 +46,7 @@ Embora este guia explique o **o quê**, **porque** e **como**, acho útil ver tu 1. [Modularity](#modularity) 1. [Angular $ Wrapper Services](#angular--wrapper-services) 1. [Testing](#testing) - 1. [Animations](#animations) + 1. [Animations](#animations) 1. [Comments](#comments) 1. [JSHint](#js-hint) 1. [Constants](#constants) @@ -60,11 +58,11 @@ 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 um factory, todos no mesmo arquivo. + O exemplo seguinte define um módulo `app` e suas dependências, define um controller e define uma factory, todos no mesmo arquivo. ```javascript /* evite */ @@ -72,7 +70,7 @@ ou *Responsabilidade Única* .module('app', ['ngRoute']) .controller('SomeController' , SomeController) .factory('someFactory' , someFactory); - + function SomeController() { } function someFactory() { } @@ -82,7 +80,7 @@ ou *Responsabilidade Única* ```javascript /* recomendado */ - + // app.module.js angular .module('app', ['ngRoute']); @@ -90,7 +88,7 @@ ou *Responsabilidade Única* ```javascript /* recomendado */ - + // someController.js angular .module('app') @@ -101,12 +99,12 @@ ou *Responsabilidade Única* ```javascript /* recomendado */ - + // someFactory.js angular .module('app') .factory('someFactory' , someFactory); - + function someFactory() { } ``` @@ -140,18 +138,18 @@ ou *Responsabilidade Única* function storage() { } ``` - + ```javascript /** - * recomendado + * recomendado * - * nenhuma global é deixada para trás + * nada global é deixado para trás */ // logger.js (function() { 'use strict'; - + angular .module('app') .factory('logger', logger); @@ -171,9 +169,9 @@ ou *Responsabilidade Única* })(); ``` - - **Nota**: Apenas para agilizar, o resto dos exemplos neste guia omitirão a sintaxe IIFE. + - **Nota**: Apenas para agilizar, o resto dos exemplos neste guia omitirão a sintaxe IIFE. - - **Nota**: IIFE impede que códigos de teste alcancem membros privados como expressões regulares ou funções auxiliares que são frequentemente boas para testes unitários. Entretanto, você pode testá-las através de membros acessíveis ou expondo-os pelo próprio componente. Por exemplo, colocando funções auxiliares, expressões regulares ou constantes em sua própria *factory* ou constante. + - **Nota**: IIFE impede que códigos de teste alcancem membros privados como expressões regulares ou funções auxiliares que são frequentemente boas para testes unitários. Entretanto, você pode testá-las através de membros acessíveis ou expondo-os pelo próprio componente. Por exemplo, colocando funções auxiliares, expressões regulares ou constantes em sua própria *factory* ou constante. **[De volta ao topo](#tabela-de-conte%C3%BAdo)** @@ -185,7 +183,7 @@ ou *Evitando Colisão de Nomes* - Use uma única convenção de nomes com separadores para sub-módulos. - **Por que?** Nomes únicos ajudam a evitar colisão de nomes no módulo. Separadores ajudam a definir a hierarquia de módulos e submódulos. Por exemplo, `app` pode ser seu módulo raiz, enquanto `app.dashboard` e `app.users` podem ser módulos que são usados como dependências de `app`. + **Por que?** Nomes únicos ajudam a evitar colisão de nomes no módulo. Separadores ajudam a definir a hierarquia de módulos e submódulos. Por exemplo, `app` pode ser seu módulo raiz, enquanto `app.dashboard` e `app.users` podem ser módulos que são usados como dependências de `app`. ### Definições (*aka Setters*) @@ -194,7 +192,7 @@ ou *Evitando Colisão de Nomes* - Declare os módulos sem uma variável usando a sintaxe *setter*. **Por que?** Com 1 componente por arquivo, raramente será necessário criar uma variável para o módulo. - + ```javascript /* evite */ var app = angular.module('app', [ @@ -220,7 +218,7 @@ ou *Evitando Colisão de Nomes* ### *Getters* - - Quando usando um módulo, evite usar as variáveis e então use o encadeamento com a sintaxe *getter*. + - Ao usar um módulo, evite usar uma variável. Em vez disso, use encadeamento com a sintaxe *getter*. **Por que?** Isso produz um código mais legível e evita colisão de variáveis ou vazamentos. @@ -228,7 +226,7 @@ ou *Evitando Colisão de Nomes* /* evite */ var app = angular.module('app'); app.controller('SomeController' , SomeController); - + function SomeController() { } ``` @@ -237,24 +235,24 @@ ou *Evitando Colisão de Nomes* angular .module('app') .controller('SomeController' , SomeController); - + function SomeController() { } ``` -### *Setting* vs *Getting* +### *Setting* vs *Getting* ou *Definindo* vs *Obtendo* - Apenas *set* (configure) uma vez e *get* (receba) em todas as outras instâncias. - - **Por que?** Um módulo deve ser criado somente uma vez, então recupere-o deste ponto em diante. - + + **Por que?** Um módulo deve ser criado somente uma vez, então recupere-o deste ponto em diante. + - Use `angular.module('app', []);` para definir (*set*) um módulo. - - Use `angular.module('app');` para pegar (*get*) este módulo. + - Use `angular.module('app');` para pegar (*get*) este módulo. ### Named vs Anonymous Functions ou *Funções Nomeadas vs Funções Anônimas* - - Use funções nomeadas ao invés de passar uma função anônima como um callback. + - Use funções nomeadas ao invés de passar uma função anônima como um callback. **Por que?** Isso produz um código mais legível, é muito fácil de *debugar*, e reduz a quantidade de callbacks aninhados no código. @@ -293,13 +291,13 @@ ou *Controladores* ### controllerAs View Syntax - - 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`. + - 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 `controlerAs` é 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 @@ -317,13 +315,13 @@ ou *Controladores* ### controllerAs Controller Syntax - - Utilize a sintaxe `controllerAs` ao invés invés da sintaxe `clássica controller com $scope`. + - Utilize a sintaxe `controllerAs` ao invés da sintaxe `clássica controller com $scope`. - 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 o 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 */ @@ -344,8 +342,8 @@ ou *Controladores* ### controllerAs with vm - 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 */ @@ -364,13 +362,13 @@ ou *Controladores* } ``` - Nota: Você pode evitar qualquer [jshint](http://www.jshint.com/) warnings colocando o comentário abaixo acima da linha de código. - + Nota: Você pode evitar qualquer [jshint](http://www.jshint.com/) warnings colocando o comentário abaixo acima da linha de código. + ```javascript /* jshint validthis: true */ var vm = this; ``` - + Nota: Quando watches são criados no controller utilizando o `controller as`, você pode observar o objeto `vm.*` utilizando a seguinte sintaxe. (Crie watches com cuidado pois eles deixam o ciclo de digest mais "carregado".) ```javascript @@ -383,10 +381,10 @@ ou *Controladores* ### Bindable Members Up Top - 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?**: 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?** 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. ```javascript /* evite */ @@ -434,7 +432,7 @@ ou *Controladores* ![Controller Using "Above the Fold"](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/above-the-fold-1.png) - Nota: Se a função possuir apenas 1 linha considere matê-la no topo, desde que a legibilidade não seja afetada. + Nota: Se a função possuir apenas 1 linha considere mantê-la no topo, desde que a legibilidade não seja afetada. ```javascript /* evite */ @@ -443,8 +441,8 @@ ou *Controladores* vm.gotoSession = gotoSession; vm.refresh = function() { - /** - * linhas + /** + * linhas * de * código * afetam @@ -472,20 +470,20 @@ ou *Controladores* ### Function Declarations to Hide Implementation Details - 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 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 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?**: 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?** 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?**: 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?** 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?**: A ordenação é crítica em 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?** A ordenação é crítica em expressões de função. ```javascript - /** - * evite + /** + * evite * Usar expressões de funções. */ function Avengers(dataservice, logger) { @@ -547,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 */ @@ -560,7 +558,7 @@ ou *Controladores* vm.checkCredit = checkCredit; vm.total = 0; - function checkCredit() { + function checkCredit() { var orderTotal = vm.total; return $http.get('api/creditcheck').then(function(data) { var remaining = data.remaining; @@ -577,7 +575,7 @@ ou *Controladores* vm.checkCredit = checkCredit; vm.total = 0; - function checkCredit() { + function checkCredit() { return creditService.check(); }; } @@ -585,17 +583,17 @@ 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. - - **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. + - 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. ### Assigning Controllers - - Quando um controller deve ser pareado com sua view e algum componente pode ser reutilizado por outros controllers ou views, defina controllers juntamente de suas rotas. - - 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"`. + - Quando um controller deve ser pareado com sua view e algum componente pode ser reutilizado por outros controllers ou views, defina controllers juntamente de suas rotas. + + 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 */ @@ -650,8 +648,8 @@ ou *Serviços* ### Singletons - - Services são instanciados com a palavra-chave `new`, use `this` para métodos públicos e variáveis. Services são bastante similares a factories, use um factory para consistência. - + - Services são instanciados com a palavra-chave `new`, use `this` para métodos públicos e variáveis. Services são bastante similares a factories, use um factory para consistência. + Nota: [Todos services em Angular são singletons](https://docs.angularjs.org/guide/services). Isso significa que há apenas uma instância do serviço para cada injetor. ```javascript @@ -695,28 +693,28 @@ ou *Responsabilidade Única* ### Singletons - Factories são singletons e retornam um objeto que contém os membros do serviço. - + Nota: [Todos services em Angular são singletons](https://docs.angularjs.org/guide/services). ### Accessible Members Up Top 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). + - 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 */ function dataService() { var someValue = ''; - function save() { + function save() { /* */ }; - function validate() { + function validate() { /* */ }; @@ -741,11 +739,11 @@ ou *Membros acessíveis no topo* //////////// - function save() { + function save() { /* */ }; - function validate() { + function validate() { /* */ }; } @@ -760,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 /** @@ -863,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 */ @@ -899,7 +897,7 @@ ou *Chamadas de dados separadas* } } ``` - + Nota: O serviço de dados (data service) é chamado pelos consumidores, como um controlador (controller), escondendo a implementação dos consumidores, como mostrado abaixo. ```javascript @@ -931,7 +929,7 @@ ou *Chamadas de dados separadas* return vm.avengers; }); } - } + } ``` ### Return a Promise from Data Calls @@ -939,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 */ @@ -989,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 */ @@ -1010,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 @@ -1047,7 +1045,7 @@ ou *Limite 1 por arquivo* /** * @desc diretiva de spinner que pode ser usada em qualquer lugar de um aplicativo de vendas em uma companhia chamada Acme * @example
- */ + */ angular .module('sales.widgets') .directive('acmeSalesCustomerInfo', salesCustomerInfo); @@ -1074,32 +1072,32 @@ ou *Limite 1 por arquivo* } ``` - Nota: Há diferentes opções de nomear diretivas (directives), especialmente quando elas podem ser usadas em escopes (scopes) variados. Escolha uma que faça a diretiva e o nome do arquivo distinto e simples. Alguns exemplos são mostrados abaixo, mas veja a seção de nomeação para mais recomendações. + Nota: Há diferentes opções de nomear diretivas (directives), especialmente quando elas podem ser usadas em escopos (scopes) variados. Escolha uma que faça a diretiva e o nome do arquivo distinto e simples. Alguns exemplos são mostrados abaixo, mas veja a seção de nomeação para mais recomendações. ### Limit DOM Manipulation 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. + - 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. Pesquisa largamente as diretivas utilizadas para evitar conflitos de nomes, como `ion-` que são utilizadas para o [Ionic Framework](http://ionicframework.com/). + 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/). ### Restrict to Elements and Attributes 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 + @@ -1133,7 +1131,7 @@ ou *Restringir para elementos e atributos*
``` - + ```javascript /* recomendado */ angular @@ -1159,9 +1157,9 @@ 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. + 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. ```html
@@ -1191,8 +1189,8 @@ ou *Diretivas e "ControladorComo"* /* jshint validthis:true */ var vm = this; - vm.min = 3; - vm.max = $scope.max; + vm.min = 3; + vm.max = $scope.max; console.log('CTRL: $scope.max = %i', $scope.max); console.log('CTRL: vm.min = %i', vm.min); console.log('CTRL: vm.max = %i', vm.max); @@ -1207,7 +1205,7 @@ ou *Diretivas e "ControladorComo"* ``` ```html - /* example.directive.html */ +
hello world
max={{vm.max}}
min={{vm.min}}
@@ -1222,11 +1220,11 @@ ou *Resolvendo promessas para um controlador* 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). - Nota: Se vocẽ precisa cancelar a rota condicionalmente antes de utilizar o controlador (controller), utilize uma resolução de rota (route resolve). - + **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). + ```javascript /* evite */ function Avengers(dataservice) { @@ -1264,9 +1262,9 @@ ou *Ativação de promessas no controlador* ### Route Resolve Promises 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). + - 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). Utilizando [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 */ @@ -1331,8 +1329,8 @@ ou *Anotação Manual para Injeção de Dependência* 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*/ @@ -1355,30 +1353,30 @@ ou *Não seguro para Minificação* 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?*: 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?** 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?** 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 */ angular .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', + .controller('Dashboard', + ['$location', '$routeParams', 'common', 'dataservice', function Dashboard($location, $routeParams, common, dataservice) {} - ]); + ]); ``` ```javascript /* evite */ angular .module('app') - .controller('Dashboard', + .controller('Dashboard', ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - + function Dashboard($location, $routeParams, common, dataservice) { } ``` @@ -1390,12 +1388,12 @@ ou *Identifique Dependências Manualmente* .controller('Dashboard', Dashboard); Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - + function Dashboard($location, $routeParams, common, dataservice) { } ``` - Nota: Quando sua função estiver abaixo de um return o $inject pode ficar inacessível (isso pode acontecer em uma diretiva). Você pode resolver isso movendo o $inject para acima do return ou usando a sintaxe alternativa de injeção de array. + Nota: Quando sua função estiver abaixo de um return o $inject pode ficar inacessível (isso pode acontecer em uma diretiva). Você pode resolver isso movendo o $inject para acima do return ou usando a sintaxe alternativa de injeção de array. Nota: [`ng-annotate 0.10.0`](https://github.com/olov/ng-annotate) introduziu um comportamento em que ele move o `$inject` para onde ele possa ser acessado. @@ -1406,7 +1404,7 @@ ou *Identifique Dependências Manualmente* controller: DashboardPanel, }; - DashboardPanel.$inject = ['logger']; // Unreachable + DashboardPanel.$inject = ['logger']; // inacessível function DashboardPanel(logger) { } } @@ -1415,7 +1413,7 @@ ou *Identifique Dependências Manualmente* ```javascript // dentro da definição de diretiva function outer() { - DashboardPanel.$inject = ['logger']; // reachable + DashboardPanel.$inject = ['logger']; // acessível return { controller: DashboardPanel, }; @@ -1429,10 +1427,10 @@ ou *Identifique Dependências Manualmente* 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?*: Uma chamada a `$inject` pode facilmente preceder o resolvedor para fazer qualquer dependência segura para minificação. + **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. ```javascript /* recomendado */ @@ -1462,10 +1460,10 @@ ou *Minificação e Anotação* ### ng-annotate - 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?*: [`ng-min`](https://github.com/btford/ngmin) está deprecated. + **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. > Eu prefiro Gulp pois sinto que é mais fácil de escrever, de ler, e de debugar. @@ -1532,14 +1530,14 @@ ou *Minificação e Anotação* } ``` - > Nota: Apartir do Angular 1.3 use o parâmetro `ngStrictDi` da diretiva [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp). Quando presente, o injetor será criado no modo "strict-di" fazendo com que a aplicação falhe ao tentar invocar funções que não usem anotação explícita de função (elas podem não ser seguras para minificação). Informação de debug será logada no console para ajudar a rastrear o código ofensivo. + > Nota: A partir do Angular 1.3 use o parâmetro `ngStrictDi` da diretiva [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp). Quando presente, o injetor será criado no modo "strict-di" fazendo com que a aplicação falhe ao tentar invocar funções que não usem anotação explícita de função (elas podem não ser seguras para minificação). Informação de debug será logada no console para ajudar a rastrear o código ofensivo. `` ### Utilize Gulp ou Grunt para o ng-annotate - 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 @@ -1573,8 +1571,8 @@ ou *Tratamento de exceção* 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. @@ -1595,9 +1593,9 @@ ou *decoradores* function extendExceptionHandler($delegate, toastr) { return function(exception, cause) { $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause + var errorData = { + exception: exception, + cause: cause }; /** * Pode adicionar o erro para um serviço de coleções, @@ -1613,9 +1611,9 @@ ou *decoradores* ### Exception Catchers ou *Coletores de exceção* - - Criar um factory que expôe uma interface para capturar e tratar adequadamente as exceções. + - 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. @@ -1642,20 +1640,20 @@ ou *Coletores de exceção* ``` ### Route Errors - + - 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?*: 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?** 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. ```javascript /* recomendado */ function handleRoutingErrors() { /** - * Route cancellation: - * On routing error, go to the dashboard. - * Provide an exit clause if it tries to do it twice. + * Cancelamento de rota: + * Quando houver erro no roteamento, vá para o dashboard. + * Forneça uma cláusula de saída se ele tentar fazer isso 2 vezes. */ $rootScope.$on('$routeChangeError', function(event, current, previous, rejection) { @@ -1663,8 +1661,8 @@ ou *Coletores de exceção* '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) + * Opcionalmente log usando um serviço customizado ou $log. + * (Não se esqueça de injetar o serviço customizado) */ logger.warning(msg, [current]); } @@ -1679,26 +1677,26 @@ ou *Nomenclatura* ### Diretrizes de Nomenclatura - - Use nomes consistentes para todos os componentes seguindo um padrão que descreve a funcionalidade do componente e (opcionalmente) seu tipo. Meu padrão recomendado é `característica.tipo.js`. Existem dois nomes para a maioria dos componentes: - * o nome do arquivo (`avengers.controllers.js`) - * o nome de componente registrado pelo Angular (`AvengersController`) + - Use nomes consistentes para todos os componentes seguindo um padrão que descreva a funcionalidade do componente e (opcionalmente) seu tipo. Meu padrão recomendado é `característica.tipo.js`. Existem dois nomes para a maioria dos componentes: + * 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 e 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 /** - * opções comuns + * opções comuns */ // Controllers @@ -1727,7 +1725,7 @@ ou *Nome para funcionalidades* // constants constants.js - + // module definition avengers.module.js @@ -1737,13 +1735,13 @@ ou *Nome para funcionalidades* // configuration avengers.config.js - + // directives avenger-profile.directive.js avenger-profile.directive.spec.js ``` - Nota: Outra convenção comum é nomear arquivos dos controllers sem a palavra `controller` no nome do arquivo como` avengers.js` em vez de `avengers.controller.js`. Todas as outras convenções ainda mantem o uso de um sufixo do tipo. Controllers são o tipo mais comum de componente, portanto isso só economiza digitação e ainda é facilmente identificável. Eu recomendo que você escolha uma convenção que seja mais coerente para sua equipe. + Nota: Outra convenção comum é nomear arquivos dos controllers sem a palavra `controller` no nome do arquivo como` avengers.js` em vez de `avengers.controller.js`. Todas as outras convenções ainda mantêm o uso de um sufixo do tipo. Controllers são o tipo mais comum de componente, portanto isso só economiza digitação e ainda é facilmente identificável. Eu recomendo que você escolha uma convenção que seja mais coerente para sua equipe. ```javascript /** @@ -1758,10 +1756,10 @@ ou *Nome para funcionalidades* 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 padrões de correspondência para o [karma](http://karma-runner.github.io/) ou outros test runners. + **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. ```javascript /** @@ -1774,13 +1772,13 @@ ou *Nome para aquivos de testes* ``` ### Controller Names -ou *Nome para controllers* +ou *Nomes para controller* - - Use nomes consistentes para todos os controlers 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. + - 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?*: O UpperCamelCase é o modo mais comum para identificar objetos que serão instanciados através de construtores. + **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. ```javascript /** @@ -1794,14 +1792,14 @@ ou *Nome para controllers* function HeroAvengers(){ } ``` - + ### Controller Name Suffix ou *sufixo "Controllers"* - - Complemente o nome do controller com ou sem o sufixo `Controller`. Escolha uma opção, não ambas. + - 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 /** * recomendado: Opção 1 @@ -1829,13 +1827,13 @@ ou *sufixo "Controllers"* ``` ### Factory Names -ou *Nome para factory* +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, e evite prefixos com `$`. - - *Por que?*: Fornece um modo consistende de identificar e referenciar rapidamente as factories. + - 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?*: Evite colisão de nomes com factories e services pré-programadas que usam o prefixo `$`. + **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 `$`. ```javascript /** @@ -1851,18 +1849,18 @@ ou *Nome para factory* ``` ### Directive Component Names -ou *Nome para directive* +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 /** * recomendado */ - // avenger.profile.directive.js + // avenger.profile.directive.js angular .module .directive('xxAvengerProfile', xxAvengerProfile); @@ -1877,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`. + - 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?*: Separe 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* @@ -1898,24 +1896,27 @@ ou *Rotas* **[De volta ao topo](#tabela-de-conte%C3%BAdo)** ## Application Structure LIFT Principle +ou *Princípio da estrutura LIFT na aplicação* + ### LIFT - - 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. + - Estruture a sua aplicação de um modo onde você possa: `L`ocate (Localizar) seu código rapidamente, `I`dentify (Identificar) o código facilmente, manter a estrutura a mais `F`lattest (Plana) que você conseguir, e `T`ry (Tentar) seguir o conceito de DRY (Don't Repeat Yourself - Não repita a si mesmo). A estrutura deve seguir essas 4 regras básicas. + + **Por que LIFT?**: Fornece uma estrutura consistente que escala bem, é modular, e torna mais fácil para aumentar a eficiência ao desenvolver, pois encontra-se o código rapidamente. Outra forma de verificar a estrutura da sua aplicação é se perguntar: Quão rápido é para você abrir e trabalhar em todos os arquivos relacionados com uma funcionalidade? - *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? + Quando estou sentindo que não estou confortável com a minha estrutura, eu volto e revisito as regras do LIFT - 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 + 1. `L`ocating (Localizar) nosso código é fácil + 2. `I`dentify (Identificar) o código rapidamente + 3. `F`lat (Plano) - Deixar a estrutura a mais plana que conseguirmos + 4. `T`ry (Tentar) se manter DRY (Don’t Repeat Yourself - Não repita a si mesmo) ou T-DRY ### Locate +ou *Localizar* - - Make locating your code intuitive, simple and fast. + - Torne a localização do seu código: intuitiva, simples e rápida. - *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. + **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 @@ -1936,212 +1937,229 @@ ou *Rotas* ``` ### Identify +ou *Identificar* - - When you look at a file you should instantly know what it contains and represents. + - Quando você olhar para um arquivo, prontamente você deve saber o que ele contém e o que representa. - *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. + **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* - - Keep a flat folder structure as long as possible. When you get to 7+ files, begin considering separation. + - 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. - *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. + **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* - - Be DRY, but don't go nuts and sacrifice readability. + - Mantenha-se DRY, mas não fique louco e sacrifique a legibilidade. - *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. + **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)** ## Application Structure +ou *Estrutura da aplicação* ### Overall Guidelines +ou *Orientações gerais* - - Have a near term view of implementation and a long term vision. In other words, start small and 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`). + - Tenha uma visão de curto prazo da implementação e uma visão de longo prazo. Em outras palavras, comece pequeno, mas tenha em mente o caminho que o aplicativo pode tomar. Todo o código do aplicativo vai em uma pasta raiz chamada `app`. Todo o conteúdo é feito com um recurso por arquivo. Cada controlador (controller), serviço (service), módulo (module), visão (view) está em seu próprio arquivo. Todos os scripts de terceiros são armazenados em uma outra pasta raiz e não na pasta `app`. Não fui eu quem escreveu esses scripts, então eu não quero que eles baguncem meu aplicativo (`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/). + Nota: Encontre mais detalhes sobre essa estrutura em [esse post original sobre a estrutura da aplicação](http://www.johnpapa.net/angular-app-structuring-guidelines/). ### Layout - - 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. + - 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. - *Why?*: Organizes all layout in a single place re-used throughout the application. + **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* - - 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. + - 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. - *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. + **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. - *Why?*: The LIFT guidelines are all covered. + **Por que?** As orientações LIFT estão todas sendo respeitadas. - *Why?*: Helps reduce the app from becoming cluttered through organizing the content and keeping them aligned with the LIFT guidelines. + **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. - *Why?*: When there are a lot of files (10+) locating them is easier with a consistent folder structures and more difficult in flat structures. + **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 /** - * recommended + * recomendado */ app/ app.module.js app.config.js app.routes.js - components/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html + components/ + calendar.directive.js + calendar.directive.html + user-profile.directive.js + user-profile.directive.html layout/ - shell.html + shell.html shell.controller.js - topnav.html - topnav.controller.js + topnav.html + topnav.controller.js people/ attendees.html - attendees.controller.js + attendees.controller.js speakers.html speakers.controller.js speaker-detail.html speaker-detail.controller.js - services/ - data.service.js + services/ + data.service.js localstorage.service.js - logger.service.js + logger.service.js spinner.service.js sessions/ - sessions.html + sessions.html sessions.controller.js session-detail.html - session-detail.controller.js + session-detail.controller.js ``` - ![Sample App Structure](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/modularity-2.png) + ![Exemplo de estrutura na aplicação](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/modularity-2.png) - Note: Do not use structuring 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. + Nota: Não estruture seu aplicativo usando pastas-por-tipo. Isto requer alternar entre várias pastas ao trabalhar em um recurso e fica difícil de manejar quando o aplicativo cresce rapidamente para 5, 10 ou 25+ views e controllers (e outros recursos), o que torna mais difícil do que pasta-por-recurso para localizar arquivos. ```javascript - /* - * avoid - * Alternative folders-by-type. - * I recommend "folders-by-feature", instead. + /* + * evite + * Alternativa pastas-por-tipo. + * Eu recomendo "pastas-por-recurso". */ - + app/ app.module.js app.config.js app.routes.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 + 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 + logger.js spinner.js views/ - attendees.html + attendees.html session-detail.html - sessions.html - shell.html - speakers.html + sessions.html + shell.html + speakers.html speaker-detail.html - topnav.html - ``` + topnav.html + ``` **[De volta ao topo](#tabela-de-conte%C3%BAdo)** ## Modularity - +ou *Modularidade* + ### Many Small, Self Contained Modules +ou *Muitos módulos pequenos e independentes* - - Create small modules that encapsulate one responsibility. + - Crie pequenos módulos que encapsulem uma única responsabilidade. - *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. + **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* - - Create an application root module whose role is pull together all of the modules and features of your application. Name this for your application. +- 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. - *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. + **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. - - Only put logic for pulling together the app in the application module. Leave features in their own modules. + **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. - *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?** 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* - - Create modules that represent feature areas, such as layout, reusable and shared services, dashboards, and app specific features (e.g. customers, admin, sales). + - 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). - *Why?*: Self contained modules can be added to the application will little or no friction. + **Por que?** Módulos independentes podem ser adicionados na aplicação com pouco ou nenhum esforço. - *Why?*: Sprints or iterations can focus on feature areas and turn them on at the end of the sprint or iteration. + **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. - *Why?*: Separating feature areas into modules makes it easier to test the modules in isolation and reuse code. + **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. ### Reusable Blocks are Modules +ou *Blocos reutilizáveis são módulos* - - Create modules that represent reusable application blocks for common services such as exception handling, logging, diagnostics, security, and local data stashing. +- 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. - *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. + **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* - - The application root module depends on the app specific feature modules, the feature modules have no direct dependencies, the cross-application modules depend on all generic modules. + - 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. - ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angularjs-styleguide/master/assets/modularity-1.png) + ![Moduluaridade e Dependências](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. + **Por que?** O módulo principal do aplicativo contém um rápido manifesto para identificar os recursos da aplicação. - *Why?*: Cross application features become easier to share. The features generally all rely on the same cross application modules, which are consolidated in a single module (`app.core` in the image). + **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. - *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). + **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). - Note: This is a strategy for consistency. There are many good options here. Choose one that is consistent, follows AngularJS's dependency rules, and is easy to maintain and scale. + 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. - > 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. + > As minhas estruturas podem variar ligeiramente entre os projetos, mas todas elas seguem estas diretrizes para estrutura e modularidade. A implementação pode variar dependendo dos recursos e do time. Em outras palavras, não fique preso somente a uma estrutura mas justifique sua estrutura usando consistência, facilidade de manutenção e eficiência em mente. + + > Em um aplicativo pequeno, você também pode considerar colocar todas as dependẽncias compartilhadas no módulo principal do aplicativo, onde os módulos de recursos não tem dependências diretas. Isso torna mais fácil de manter a aplicação, mas torna mais difícil de reutilizar os módulos fora dessa aplicação. **[De volta ao topo](#tabela-de-conte%C3%BAdo)** ## 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() { @@ -2163,11 +2181,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). @@ -2175,41 +2193,41 @@ 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. ### Análise de Código - - Execute JSHint no seus testes. + - 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 */ @@ -2220,44 +2238,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 /** @@ -2285,7 +2305,7 @@ Testes unitários ajudam a manter o código limpo, tal como, eu inclui algumas r /** * @name logError * @desc Logs errors - * @param {String} msg Message to log + * @param {String} msg Message to log * @returns {String} * @memberOf Factories.Logger */ @@ -2302,13 +2322,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 { @@ -2384,7 +2404,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 @@ -2403,67 +2423,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 [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` - - 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` - ```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) 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 diff --git a/i18n/ru-RU.md b/a1/i18n/ru-RU.md similarity index 93% rename from i18n/ru-RU.md rename to a1/i18n/ru-RU.md index 5a0b09d4..f007a796 100644 --- a/i18n/ru-RU.md +++ b/a1/i18n/ru-RU.md @@ -2,17 +2,14 @@ *Angular соглашения по стилям для команд разработчиков, предложенные [@john_papa](//twitter.com/john_papa)* -*Перевел на русский язык [Василий Мажекин](https://github.com/mazhekin)* +Если вам нужны стандарты написания кода, соглашения и руководства структурирования приложений AngularJS, то вы находитесь в правильном месте. Эти соглашения основаны на моем опыте программирования на [AngularJS](//angularjs.org), на моих презентациях [Pluralsight training courses](http://pluralsight.com/training/Authors/Details/john-papa), а также на совместной работе в командах разработчиков. ->The [original English version](http://jpapa.me/ngstyles) is the source of truth, as it is maintained and updated first. +Главной целью этого документа является желание предоставить вам наиболее полные инструкции для построения приложений AngularJS. Рекомендуя данные соглашения, я стараюсь акцентировать ваше внимание на цели и причины, зачем их нужно придерживаться. -Если вам нужны стандарты написания кода, соглашения, и руководства структурирования приложений AngularJS, то вы находитесь в правильном месте. Эти соглашения основаны на моем опыте программирования на [AngularJS](//angularjs.org), на моих презентациях [Pluralsight training courses](http://pluralsight.com/training/Authors/Details/john-papa), а также на совместной работе в командах разработчиков. - -Главной целью этого документа является желание предоставить вам наиболее полные инструкции для построения приложений AngularJS. Рекомендуя данные соглашения, я стараюсь акцентировать ваше внимание на цели и причины, зачем их нужно придерживаться. >Если это руководство вам понравится, то вы можете также оценить мой курс [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) дабы почувствовать разницу подходов. @@ -21,7 +18,7 @@ ## Смотрите стили и шаблоны в приложении-примере Пока данное руководство объясняет *что*, *почему* и *как*, я сразу же показываю, как это работает на практике. Все, что я предлагаю и описываю сопровождается примером-приложения, которое соблюдает и демонстрирует приведенные стили и шаблоны. Вы можете найти [пример приложения (имя modular) здесь](https://github.com/johnpapa/ng-demos) в папке `modular`. Свободно скачивайте, клонируйте и перемещайте эти примеры в свои хранилища. [Инструкциии по запуску примеров находятся в файлах readme](https://github.com/johnpapa/ng-demos/tree/master/modular). -## Переводы +## Переводы [Переводы данного руководства по Angular-стилям](https://github.com/johnpapa/angularjs-styleguide/tree/master/i18n) поддерживаются соообществом разработчиков и могут быть найдены здесь. ## Table of Contents @@ -45,7 +42,7 @@ 1. [Startup Logic (Логика Запуска Приложения)](#startup-logic) 1. [Angular $ Wrapper Services (Angular и Интерфейсные Сервисы)](#angular--wrapper-services) 1. [Testing (Тестирование)](#testing) - 1. [Animations (Анимации)](#animations) + 1. [Animations (Анимации)](#animations) 1. [Comments (Комментарии)](#comments) 1. [JSHint](#js-hint) 1. [Constants (Константы)](#constants) @@ -59,7 +56,7 @@ ## Single Responsibility -### Правило 1 +### Правило 1 ###### [Style [Y001](#style-y001)] - Определяйте 1 компонент в одном файле. @@ -72,7 +69,7 @@ .module('app', ['ngRoute']) .controller('SomeController', SomeController) .factory('someFactory', someFactory); - + function SomeController() { } function someFactory() { } @@ -106,17 +103,17 @@ angular .module('app') .factory('someFactory', someFactory); - + function someFactory() { } ``` **[К Содержанию](#table-of-contents)** ## IIFE -### Замыкания JavaScript +### Замыкания JavaScript ###### [Style [Y010](#style-y010)] - - Оборачивайте компоненты Angular в Немедленно Исполняемые Функции(IIFE - Immediately Invoked Function Expression). + - Оборачивайте компоненты Angular в Немедленно Исполняемые Функции(IIFE - Immediately Invoked Function Expression). *Зачем?*: IIFE удаляют переменные из глобальной области видимости. Этот прием не дает существовать переменным и функциям дольше, чем это необходимо в глобальной области видимости. Иначе это может вызвать непредсказуемые коллизии во время исполнения всего приложения. @@ -143,9 +140,9 @@ ```javascript /** - * рекомендовано + * рекомендовано * - * больше нет глобальных переменных + * больше нет глобальных переменных */ // logger.js @@ -171,7 +168,7 @@ })(); ``` - - Замечание: Только для краткости, в остальных примерах мы не будем прописывать синтаксис с функциями IIFE. + - Замечание: Только для краткости, в остальных примерах мы не будем прописывать синтаксис с функциями IIFE. - Замечание: IIFE не дает тестировать приватный код, так как предотвращает доступ к нему, например, регулярные выражения или вспомогательные функции, которые нужно оттестировать в модульных тестах (unit test) напрямую. Как выход, вы можете тестировать через специальные методы, используемые только в тестах (accessible members) или выставить нужные внутренние функции в собственном компоненте. Например, поместите вспомогательные функции, регулярные выражения или константы в собственную фабрику(factory) или константу(constant). @@ -189,10 +186,10 @@ ### Сеттеры (Setters) ###### [Style [Y021](#style-y021)] - - Объявляйте модули без переменных, используйте сеттеры (setters). + - Объявляйте модули без переменных, используйте сеттеры (setters). *Почему?*: Так как обычно в файле 1 компонент, поэтому вряд ли потребуется вводить переменную для модуля. - + ```javascript /* избегайте этого */ var app = angular.module('app', [ @@ -248,12 +245,12 @@ *Почему?*: Модуль должен быть определен только один раз, а потом используйте его во всех остальных местах. - Используйте `angular.module('app', []);` для определения модуля. - - Используйте `angular.module('app');` чтобы получить модуль. + - Используйте `angular.module('app');` чтобы получить модуль. ### Именованные или Анонимные Функции ###### [Style [Y024](#style-y024)] - - Используйте именованные функции, не передавайте анонимные функции обратного вызова в качестве параметров. + - Используйте именованные функции, не передавайте анонимные функции обратного вызова в качестве параметров. *Почему?*: Так вы производите более читаемый код, его легче отлаживать, и это уменьшает число вложенных функций обратного вызова. @@ -292,9 +289,9 @@ ### Синтаксис controllerAs в представлении ###### [Style [Y030](#style-y030)] - - Используйте синтаксис [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/), который работает поверх синтаксиса `классический контроллер со $scope`. + - Используйте синтаксис [`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`), что является более концептуальным, проще для чтения, и помогает избегать многих ссылочных проблем, которые могут возникнуть без использования "точки". @@ -317,11 +314,11 @@ ### Синтаксис controllerAs в контроллере ###### [Style [Y031](#style-y031)] - - Используйте синтаксис `controllerAs` поверх синтаксиса `классический контроллер со $scope`. + - Используйте синтаксис `controllerAs` поверх синтаксиса `классический контроллер со $scope`. - Синтаксис `controllerAs` использует внутри контроллеров ключевую переменную `this`, которая привязывается к `$scope`. - *Почему?*: `controllerAs` - это только синтаксический сахар поверх `$scope`. Вы все равно можете использовать связывание в представлениях и все равно имеете доступ к методам `$scope`. + *Почему?*: `controllerAs` - это только синтаксический сахар поверх `$scope`. Вы все равно можете использовать связывание в представлениях и все равно имеете доступ к методам `$scope`. *Почему?*: Избавляет от искушения использования методов `$scope` внутри контроллера, когда это не требуется явно, и это позволяет перенести методы в фабрику(factory). Предпочтительнее использовать `$scope` в фабрике, а в контроллере только если это действительно необходимо. Например, когда мы подписываемся и рассылаем события с помощью [`$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) перенесите эти сервисы в фабрику, и вызывайте методы фабрики в контроллере. @@ -345,7 +342,7 @@ ###### [Style [Y032](#style-y032)] - Сохраните `this` в переменную, когда используете синтаксис `controllerAs`. Выберите постоянное имя для переменной, такое как `vm`, что будет значить ViewModel. - + *Почему?*: Ключевое слово `this` контекстное, и когда его потребуется использовать внутри любой другой функции контроллера, то его содержимое будет другим. Сохранение `this` в переменной `vm` позволит избежать этих проблем. ```javascript @@ -365,14 +362,14 @@ } ``` - Замечание: Вы можете избежать любые [jshint](http://www.jshint.com/) предупреждения, если разместите над строкой кода комментарий, как приведенный ниже в примере. Это не требуется, если функция названа с помощью ВерхнегоРегистра(UpperCasing), так как согласно этой конвециии, это значит, что функция является контруктором контроллера Angular. + Замечание: Вы можете избежать любые [jshint](http://www.jshint.com/) предупреждения, если разместите над строкой кода комментарий, как в приведенном ниже примере. Это не требуется, если функция названа с помощью ВерхнегоРегистра(UpperCasing), так как согласно этой конвенциии, это означает, что функция является конструктором контроллера Angular. ```javascript /* jshint validthis: true */ var vm = this; ``` - Замечание: Когда создаете наблюдающие функции(watcher) в контроллерах типа `controller as`, вы можете наблюдать за переменной следующего вида `vm.*`. (Создавайте наблюдающие функции с предупреждением, что они создают дополнительную нагрузку на цикл digest.) + Замечание: Когда создаете наблюдающие функции(watcher) в контроллерах типа `controller as`, вы можете наблюдать за переменной следующего вида `vm.*`. (Создавайте наблюдающие функции с осторожностью, т.к. они добавляют дополнительную нагрузку на цикл digest.) ```html @@ -397,7 +394,7 @@ *Почему?*: Размещение привязываемых членов наверху, упрощает чтение и позволяет мгновенно определить, какие члены контроллера привязаны и используются в представлении. - *Почему?*: Написание анонимных функций по месту использования может конечно и проще, но когда такие функции содержат много строк кода, то это значительно снижает читабельность кода. Определяйте функции ниже привязываемых членов, тем самым вы перенесете детали реализации вниз отдельно. Функции определяйте как hoisted, то есть они будут подняты наверх области видимости. А привязываемые члены наверху повысят читабельность кода. + *Почему?*: Написание анонимных функций по месту использования может конечно и проще, но когда такие функции содержат много строк кода, то это значительно снижает читабельность кода. Определяйте функции ниже привязываемых членов, тем самым вы перенесете детали реализации вниз отдельно. Функции определяйте как hoisted, то есть они будут подняты наверх области видимости. А привязываемые члены наверху повысят читабельность кода. ```javascript /* избегайте этого */ @@ -443,9 +440,9 @@ } ``` - ![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) - Замечание: Если функция однострочная, то разместите ее и наверху, но так чтобы это не усложняло читабельность. + Замечание: Если функция однострочная, то разместите и ее наверху, но так чтобы это не усложняло читабельность. ```javascript /* избегайте этого */ @@ -455,8 +452,8 @@ vm.gotoSession = gotoSession; vm.refresh = function() { /** - * эти - * строки + * эти + * строки * кода * ухудшают * читабельность @@ -484,19 +481,19 @@ - Используйте определения функций для скрытия деталей реализации. Держите свои привязываемые члены наверху. Если нужно в контроллере сделать функцию привязываемой, укажите это в группе привязываемых членов и ссылайтесь на данную функцию, которая реализована ниже. Это подробно описано в секции Привязываемые Члены Сверху. Подробнее смотрите [здесь](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - *Почему?*: Размещение привязываемых членов наверху делает код читабельнее, и позволяет мгновенно определить, какие члены привязаны и используются в представлении. (Выше описано тоже самое.) + *Почему?*: Размещение привязываемых членов наверху делает код читабельнее, и позволяет мгновенно определить, какие члены привязаны и используются в представлении. (Выше описано тоже самое.) *Почему?*: Размещение деталей реализации функции внизу скрывает эту сложность ниже и таким образом все важные вещи находятся на видном месте сверху. *Почему?*: Функции определены как hoisted (определены в самом верху области видимости), поэтому не надо заботиться об их использовании перед объявлением, так как это было бы с объявлениями выражений функций (function expressions). - *Почему?*: Вам не надо волноваться, о том в каком порядке объявлены функции. Также как и изменение порядка функций не будет ломать код из-за зависимостей. + *Почему?*: Вам не надо волноваться, о том в каком порядке объявлены функции. Также как и изменение порядка функций не будет ломать код из-за зависимостей. *Почему?*: С выражениями функций(function expressions) порядок будет критичен. ```javascript - /** - * избегайте этого + /** + * избегайте этого * Использование выражений функций (function expressions). */ function Avengers(dataservice, logger) { @@ -561,7 +558,7 @@ *Почему?*: Логика может использоваться несколькими контроллерами, если она помещена в сервис и выставлена в виде функции. - *Почему?*: Вся логика в сервисе может быть легко изолирована в модульном тесте, а вызовы этой логики в контроллере могут фиктивно реализованы (mocked). + *Почему?*: Вся логика в сервисе может быть легко изолирована в модульном тесте, а вызовы этой логики в контроллере могут быть фиктивно реализованы (mocked). *Почему?*: Из контроллера удаляются зависимости и скрываются подробности реализации. @@ -604,7 +601,7 @@ vm.isCreditOk; vm.total = 0; - function checkCredit() { + function checkCredit() { return creditService.isOrderTotalOk(vm.total) .then(function(isOk) { vm.isCreditOk = isOk; }) .catch(showServiceError); @@ -617,7 +614,7 @@ - Определяйте контроллер для одного представления, и не пытайтесь использовать этот контроллер для других представлений. Вместо этого, выносите повторно используемую логику в фабрики. Старайтесь держать контроллер простым и сфокусированным только на свое представление. - *Почему?*: Использование контроллера с несколькими представлениями хрупко и ненадежно. А для больших приложений требуется хорошее покрытие тестами end to end (e2e) для проверки стабильности. + *Почему?*: Использование контроллера с несколькими представлениями хрупко и ненадежно. А для больших приложений требуется хорошее покрытие тестами end to end (e2e) для проверки стабильности. ### Получение Контроллеров ###### [Style [Y038](#style-y038)] @@ -626,7 +623,7 @@ Замечание: Если представление загружается не через маршрут, тогда используйте синтаксис `ng-controller="Avengers as vm"`. - *Почему?*: Указание экземпляра контроллера в определении маршрута позволяет различным маршрутам вызывать различные пары контроллеров и представлений. А когда контроллеры указаны в представлении с помощью [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), то представление будет всегда ассоциировано с одним и тем же контроллером. + *Почему?*: Указание экземпляра контроллера в определении маршрута позволяет различным маршрутам вызывать различные пары контроллеров и представлений. А когда контроллеры указаны в представлении с помощью [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), то представление будет всегда ассоциировано с одним и тем же контроллером. ```javascript /* избегайте этого - когда используется маршрут и необходимо динамическое назначение контроллера и представления */ @@ -720,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)] @@ -732,22 +729,22 @@ ### Доступные Члены Наверх ###### [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). - *Почему?*: Размещение вызываемых членов в верхней части улучшает читабельность и дает вам возможность быстро определить, какие члены сервиса могут быть вызваны и должны быть модульно оттестированы (либо фиктивно имплементированы - mocked). - *Почему?*: Это особенно полезно, когда файл становится очень длинным, и вынуждает прокручивать текст кода, чтобы посмотреть, какие методы или свойства сервис предоставляет. - *Почему?*: Размещение функций в обычном прямом порядке может быть и проще, но когда эти функции становятся многострочными, это снижает читабельность и вынуждает много скроллить. Определяя вызываемый интерфейс(в виде возвращаемого сервиса), вы убираете детали реализации вниз. А размещенный сверху интерфейс улучшает читабельность. + *Почему?*: Размещение функций в обычном прямом порядке может быть и проще, но когда эти функции становятся многострочными, это снижает читабельность и вынуждает много скроллить. Определяя вызываемый интерфейс(в виде возвращаемого сервиса), вы убираете детали реализации вниз. А размещенный сверху интерфейс улучшает читабельность. ```javascript /* избегайте этого */ function dataService() { var someValue = ''; - function save() { + function save() { /* */ }; - function validate() { + function validate() { /* */ }; @@ -772,26 +769,26 @@ //////////// - function save() { + function save() { /* */ }; - function validate() { + function validate() { /* */ }; } ``` -Такой способ привязки реализовывается во всем объекте, и приватные члены не моут быть изменены из-за примененного паттерна Revealing Module. +Такой способ привязки реализовывается во всем объекте, и приватные члены не моут быть изменены из-за примененного паттерна 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)] - - Используйте определения функций, чтобы скрыть детали реализации. Держите вызываемые члены фабрики в верхней части. Свяжите их с определениями функций, которые распололжены ниже в файле. Подробную информацию смотрите [здесь](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). + - Используйте определения функций, чтобы скрыть детали реализации. Держите вызываемые члены фабрики в верхней части. Свяжите их с определениями функций, которые расположены ниже в файле. Подробную информацию смотрите [здесь](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - *Почему?*: Размещение вызываемых членов в верхней части улучшает читабельность и помогает быстро определить, какие функции фабрики могут быть вызваны извне. + *Почему?*: Размещение вызываемых членов в верхней части улучшает читабельность и помогает быстро определить, какие функции фабрики могут быть вызваны извне. *Почему?*: Размещение функций с деталями реализации в нижней части файла выносит сложные вещи из поля зрения, так что только важные детали вы видите в верхней части файла. @@ -891,13 +888,13 @@ ### Отделите вызовы данных ###### [Style [Y060](#style-y060)] - - Делайте рефакторинг логики работы с данными и взаимодействия этих данных с фабрикой. Создавайте сервисы данных, ответственных за вызовы XHR, локальное хранилище(local storage), работу с памятью или за любые другие операции с данными. + - Делайте рефакторинг логики работы с данными и взаимодействия этих данных с фабрикой. Создавайте сервисы данных, ответственных за вызовы XHR, локальное хранилище(local storage), работу с памятью или за любые другие операции с данными. *Почему?*: Ответственность контроллера - это предоставление или сбор информации для представления. Он не должен заботиться о том, как работать с данными, он только должен знать кого попросить об этом. В сервисы для данных переносится вся логика работы с данными. Это делает контроллер более простым и более сфокусированным для работы с представлением. - *Почему?*: Это позволяет более проще тестировать (фиктивно или реально) вызовы данных в контроллере, который использует сервис для данных. + *Почему?*: Это позволяет более проще тестировать (фиктивно или реально) вызовы данных в контроллере, который использует сервис для данных. - *Почему?*: Реализация сервиса данных может иметь очень специфичный код для операций с хранилищем данных. Могут использоваться заголовки для взаимодействовия с данными, или другие сервисы типа $http. Логика в сервисе данных инкапсулируется в одном месте и скрывает реализацию для внешних потребителей (контроллеров), также будет гораздо проще изменить реализацию в случае необходимости. + *Почему?*: Реализация сервиса данных может иметь очень специфичный код для операций с хранилищем данных. Могут использоваться заголовки для взаимодействовия с данными, или другие сервисы типа `$http`. Логика в сервисе данных инкапсулируется в одном месте и скрывает реализацию для внешних потребителей (контроллеров), также будет гораздо проще изменить реализацию в случае необходимости. ```javascript /* рекомендовано */ @@ -929,7 +926,7 @@ } } ``` - + Замечание: Сервис данных вызывается потребителем (контроллером), и скрывает реализацию от потребителя. Это показано ниже. ```javascript @@ -961,7 +958,7 @@ return vm.avengers; }); } - } + } ``` ### Возвращение Объекта Promise для Контроллера @@ -969,7 +966,7 @@ - Если сервис данных возвращает promise типа $http, то в вызывающей функции возвращайте promise тоже. - *Почему?*: Вы можете объединить в цепочку объекты promise, и после того как вызов данных закончится, выполнить дальнейшие действия для принятия или отклонения объекта promise. + *Почему?*: Вы можете объединить в цепочку объекты promise, и после того как вызов данных закончится, выполнить дальнейшие действия для принятия или отклонения объекта promise. ```javascript /* рекомендовано */ @@ -979,8 +976,8 @@ function activate() { /** * Шаг 1 - * Запрашиваем функцию getAvengers function - * для получения данных и ждем promise + * Запрашиваем функцию getAvengers function + * для получения данных и ждем promise */ return getAvengers().then(function() { /** @@ -994,7 +991,7 @@ function getAvengers() { /** * Шаг 2 - * Запрашиваем сервис для данных + * Запрашиваем сервис для данных * и ждем promise */ return dataservice.getAvengers() @@ -1012,7 +1009,7 @@ **[К Содержанию](#table-of-contents)** ## Directives -### Одна Директива - Один Файл +### Одна Директива - Один Файл ###### [Style [Y070](#style-y070)] - Создавайте только одну директиву в файле. Называйте файл именем директивы. @@ -1074,7 +1071,7 @@ /** * @desc директива продажи, которая может быть использована везде в модуле продаж компании Acme * @example
- */ + */ angular .module('sales.widgets') .directive('acmeSalesCustomerInfo', salesCustomerInfo); @@ -1101,12 +1098,12 @@ } ``` - Замечание: Существует много способов для именования директив, особенно это зависит от широты области использования (локально или глобально). Выбирайте тот способ, который определяет директиву и ее файл четко и ясно. Несколько примеров будет ниже, но в основном смотрите рекомендации в секции именований. + Замечание: Существует много способов для именования директив, особенно это зависит от широты области использования (локально или глобально). Выбирайте тот способ, который определяет директиву и ее файл четко и ясно. Несколько примеров будет ниже, но в основном смотрите рекомендации в секции именований. ### Манипулирование Элементами DOM в Директиве ###### [Style [Y072](#style-y072)] - - Используйте директивы, если нужно манипулировать элементами DOM напрямую. Но если существуют альтернативные способы, то используйте лучше их. Например, для изменения стилей используйте CSS, для эффектов [сервисы анимации](https://docs.angularjs.org/api/ngAnimate), для управления видимостью используйте [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) и [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide). + - Используйте директивы, если нужно манипулировать элементами DOM напрямую. Но если существуют альтернативные способы, то используйте лучше их. Например, для изменения стилей используйте CSS, для эффектов [сервисы анимации](https://docs.angularjs.org/api/ngAnimate), для управления видимостью используйте [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) и [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide). *Почему?*: Манипулирование элементами DOM тяжело тестить, отлаживать, и зачастую существуют более лучшие способы для реализации поставленной задачи (например CSS, анимации, шаблоны) @@ -1115,14 +1112,14 @@ - Добавляйте директивам короткий, уникальный, пояснительный префикс, такой как `acmeSalesCustomerInfo`, директива будет объявлена в HTML как `acme-sales-customer-info`. - *Почему?*: Уникальный короткий префикс говорит о контексте и происхождении директивы. Например, префикс `cc-` может рассказать нам, что директива является частью приложения CodeCamper, а `acme-` это директива для компании Acme. + *Почему?*: Уникальный короткий префикс говорит о контексте и происхождении директивы. Например, префикс `cc-` может рассказать нам, что директива является частью приложения CodeCamper, а `acme-` это директива для компании Acme. Замечание: Не используйте префикс `ng-` для своих директив, так как он зарезервирован для директив AngularJS. Также исследуйте префиксы широко используемых директив для избежания конфликтов имен, например, префикс `ion-` используется для директив [Ionic Framework](http://ionicframework.com/). - + ### Ограничивайте Элементы и Атрибуты ###### [Style [Y074](#style-y074)] - - При создании директивы, которая планируется как самостоятельный элемент, применяйте ограничение `E` (разработано, как элемент) или по необходимости ограничение `A` (разработано, как атрибут). В основном, если директива разрабатывается как элемент, ограничения `E` вполне достаточно. Хотя Angular позволяет использовать `EA`, но все же лучше определится как реализовывать директиву, либо как самостоятельный отдельный элемент, либо как атрибут для улучшения функциональности существующего DOM-элемента. + - При создании директивы, которая планируется как самостоятельный элемент, применяйте ограничение `E` (разработано, как элемент) или по необходимости ограничение `A` (разработано, как атрибут). В основном, если директива разрабатывается как элемент, ограничения `E` вполне достаточно. Хотя Angular позволяет использовать `EA`, но все же лучше определится как реализовывать директиву, либо как самостоятельный отдельный элемент, либо как атрибут для улучшения функциональности существующего DOM-элемента. *Почему?*: Это имееет смысл. @@ -1160,7 +1157,7 @@
``` - + ```javascript /* рекомендовано */ angular @@ -1192,7 +1189,7 @@ Замечание: Что касается внедренной зависимости, смотрите [Определение зависимостей вручную](#manual-annotating-for-dependency-injection). - Замечание: Заметьте, что контроллер директивы находится снаружи самой директивы. Такой подход исключает проблемы, когда инжектор создается в недосягаемом код после, например, 'return'. + Замечание: Заметьте, что контроллер директивы находится снаружи самой директивы. Такой подход исключает проблемы, когда инжектор создается в недосягаемом кодe после, например, 'return'. ```html
@@ -1214,7 +1211,7 @@ controller: ExampleController, controllerAs: 'vm' }; - + return directive; function linkFunc(scope, el, attr, ctrl) { @@ -1230,8 +1227,8 @@ // Внедрение $scope сразу для параметра сравнения var vm = this; - vm.min = 3; - vm.max = $scope.max; + vm.min = 3; + vm.max = $scope.max; console.log('CTRL: $scope.max = %i', $scope.max); console.log('CTRL: vm.min = %i', vm.min); console.log('CTRL: vm.max = %i', vm.max); @@ -1252,14 +1249,14 @@ ### Активация объектов promise в контроллере ###### [Style [Y080](#style-y080)] - - Размещайте стартовую начальную логику для контроллера в функции `activate`. - - *Почему?*: Размещение стартовой логики в согласованом месте контроллера позволяет ее быстрее находить, более согласованно тестить, и позволить не разбразывать по всему контроллеру логику активации. + - Размещайте стартовую начальную логику для контроллера в функции `activate`. - *Почему?*: Функция `activate` делает удобным повторное использование логики для обновления контроллера/представления, держит все логику вместе, делает более быстрой работу пользователя с представлением, делает анимацию более простой в директивами `ng-view` и `ui-view`, ну и делает ориентирование разработчика в коде более энергичным и быстрым. + *Почему?*: Размещение стартовой логики в согласованном месте контроллера позволяет ее быстрее находить, более согласованно тестировать и позволит не расбрасывать по всему контроллеру логику активации. + + *Почему?*: Функция `activate` делает удобным повторное использование логики для обновления контроллера/представления, держит всю логику вместе, делает более быстрой работу пользователя с представлением, делает анимацию более простой с директивами `ng-view` и `ui-view`, ну и делает ориентирование разработчика в коде более энергичным и быстрым. Замечание: Если вам нужно при каком-то условии отменить маршрут перед началом использования контроллера, используйте для этого [route resolve](#style-y081). - + ```javascript /* избегайте этого */ function Avengers(dataservice) { @@ -1297,7 +1294,7 @@ ### Работа с Объектами Promise в Маршрутах ###### [Style [Y081](#style-y081)] - - Если контроллер зависит от объекта promise, от результата которого зависит активация контроллера, то разрешайте/получайте эти зависимости в `$routeProvider`, перед тем как логика контроллера будет выполена. Если вам нужно по какому-то условию отменить маршрут перед активацией контроллера, то используйте обработчик маршрутов. + - Если контроллер зависит от объекта promise, от результата которого зависит активация контроллера, то разрешайте/получайте эти зависимости в `$routeProvider`, перед тем как логика контроллера будет выполена. Если вам нужно по какому-то условию отменить маршрут перед активацией контроллера, то используйте обработчик маршрутов. - Используйте обработчик маршрутов, если вы решили в последствии отменить маршут до перехода к представлению. @@ -1358,7 +1355,7 @@ } ``` - Замечание: В примере ниже показано, как разрешение маршрута указывает на именованную функцию, которую легче отлаживать и легче оперировать встроенной зависимостью. + Замечание: В примере ниже показано, как разрешение маршрута указывает на именованную функцию, которую легче отлаживать и легче оперировать встроенной зависимостью. ```javascript /* еще лучше */ @@ -1404,8 +1401,8 @@ ### Уязвимости от Минификации ###### [Style [Y090](#style-y090)] - - Избегайте объявления зависимостей без использования безопасного для минификации подхода. - + - Избегайте объявления зависимостей без использования безопасного для минификации подхода. + *Почему?*: Параметры для компонент (типа контроллер, фабрика и т.п.) будут преобразованы в усеченные переменные. Например, `common` и `dataservice` превратятся `a` или `b` и не будут найдены средой AngularJS. ```javascript @@ -1418,7 +1415,7 @@ } ``` - После минификации будут производится усеченные переменные и это будет вызывать ошибки выполнения. + После минификации будут производится усеченные переменные и это будет вызывать ошибки выполнения. ```javascript /* избегайте этого- не безопасно для минификации */ @@ -1428,20 +1425,20 @@ ### Определяйте Зависимости Вручную ###### [Style [Y091](#style-y091)] - - Используйте `$inject` для ручного определения ваших зависимостей для AngulaJS. - - *Почему?*: Этот техника отражает технику использованную в [`ng-annotate`](https://github.com/olov/ng-annotate), которую я рекомендую для автоматического создания зависимостей, безопасных для минификации. Если `ng-annotate` обнаруживает вставку(injection), которая уже была, то она не будет продублирована. + - Используйте `$inject` для ручного определения ваших зависимостей для AngularJS. + + *Почему?*: Этот прием отражает технику использованную в [`ng-annotate`](https://github.com/olov/ng-annotate), которую я рекомендую для автоматического создания зависимостей, безопасных для минификации. Если `ng-annotate` обнаруживает вставку(injection), которая уже была, то она не будет продублирована. *Почему?*: Это гарантирует вашим зависимостям защиту от повреждений, которую может нанести минификация, путем урезания и укорачивания переменных. Например, `common` и `dataservice` превратятся `a` и `b`, и не будут найдены средой AngularJS. - *Почему?*: Избегайте создания однострочных зависимостей в виде длинных списков, которые трудно читаемы в массиве. Еще в вводят в конфуз, то что такие массивы состоят из элементов строк, а последний элемент - это функция. + *Почему?*: Избегайте создания однострочных зависимостей в виде длинных списков, которые трудно читаемы в массиве. Еще вводят в заблуждение то, что такие массивы состоят из элементов строк, а последний элемент - это функция. ```javascript /* избегайте этого */ angular .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', + .controller('Dashboard', + ['$location', '$routeParams', 'common', 'dataservice', function Dashboard($location, $routeParams, common, dataservice) {} ]); ``` @@ -1450,9 +1447,9 @@ /* избегайте этого */ angular .module('app') - .controller('Dashboard', + .controller('Dashboard', ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - + function Dashboard($location, $routeParams, common, dataservice) { } ``` @@ -1469,7 +1466,7 @@ } ``` - Замечание: Если функция снизу является возращаемым значением, то $inject может быть недостижымым (это может случится в директиве). Это можно решить перемещением $inject выше, чем возращаемое значение, либо использовать альтернативный синтаксис массива вставок. + Замечание: Если функция снизу является возвращаемым значением, то $inject может быть недостижим (это может случится в директиве). Это можно решить перемещением $inject выше, чем возращаемое значение, либо использовать альтернативный синтаксис массива вставок. Замечание: [`ng-annotate 0.10.0`](https://github.com/olov/ng-annotate) ввело возможность, когда `$inject` переносится туда, где оно доступно. @@ -1499,12 +1496,12 @@ } ``` -### Определяйте Маршрутных Обработчиков Зависимостей Вручную +### Определяйте Маршрутные Обработчики Зависимостей Вручную ###### [Style [Y092](#style-y092)] - Используйте $inject, чтобы вручную определить ваш маршрутный обработчик зависимостей для компонентов AngularJS. - *Почему?*: Эта техника убирает анонимные функции для маршрутных обработчиков, делая чтение такого код проще. + *Почему?*: Эта техника убирает анонимные функции для маршрутных обработчиков, делая чтение такого кода проще. *Почему?*: Оператор $inject` может предшествовать обработчику для того, чтобы сделать любую минификацию зависимостей безопасной. @@ -1536,15 +1533,15 @@ ###### [Style [Y100](#style-y100)] - Используйте [ng-annotate](//github.com/olov/ng-annotate) для [Gulp](http://gulpjs.com) или [Grunt](http://gruntjs.com) и комментируйте функции, которые нуждаются в автоматической вставке зависимостей, используйте `/** @ngInject */`. - + *Почему?*: Это гарантирует, что в вашем коде нет зависимостей, которые не используют защиту для повреждений от минификации. - *Почему?*: [`ng-min`](https://github.com/btford/ngmin) не рекомендуется для применения, выводится из употребления + *Почему?*: [`ng-min`](https://github.com/btford/ngmin) не рекомендуется для применения, выводится из употребления >Я предпочитаю Gulp, так как для меня он проще для чтения, написания кода и отладки. Следующий код не использует защиту зависимостей от минификации. - + ```javascript angular .module('app') @@ -1563,7 +1560,7 @@ } ``` - Если код выше запустить через ng-annotate, то будет произведен код с аннотацией `$inject`, и код станет устойчив к минификации. + Если код выше запустить через ng-annotate, то будет произведен код с аннотацией `$inject`, и код станет устойчив к минификации. ```javascript angular @@ -1587,7 +1584,7 @@ Замечание: Если `ng-annotate` обнаруживает вставки которые уже сделаны (например `@ngInject` был обнаружен), он не будет дублирован в коде `$inject`. - Замечание: Если используется маршрутный обработчик, то вы можете перед встраиваемой функцией подставить `/* @ngInject */` и это будет производить корректный аннотационный код, делающий каждую вставленную зависимость безопасной для минификации. + Замечание: Если используется маршрутный обработчик, то вы можете перед встраиваемой функцией подставить `/* @ngInject */` и это будет производить корректный аннотационный код, делающий каждую вставленную зависимость безопасной для минификации. ```javascript // Используем @ngInject аннотацию @@ -1613,10 +1610,10 @@ ###### [Style [Y101](#style-y101)] - Используйте [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 */`. - Следующий код является примером gulp задания с использованием ngAnnotate. + *Почему?*: ng-annotate будет отлавливать большинство зависимостей, но иногда требуется вставлять подсказки в виде синтаксиса `/* @ngInject */`. + + Следующий код является примером gulp-задания с использованием ngAnnotate. ```javascript gulp.task('js', ['jshint'], function() { @@ -1646,11 +1643,11 @@ ### декораторы ###### [Style [Y110](#style-y110)] - - Используйте [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator), во время конфигурации, применяя сервис [`$provide`](https://docs.angularjs.org/api/auto/service/$provide), пользовательские действия будут происходить в сервисе [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler), если произойдут исключения. - + - Используйте [decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator), во время конфигурации, применяя сервис [`$provide`](https://docs.angularjs.org/api/auto/service/$provide), пользовательские действия будут происходить в сервисе [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler), если произойдут исключения. + *Почему?*: Это дает постоянный надежный способ обработки необработанных исключений Angular во время разработки и во время выполнения. - Замечание: Другим способом является переопределение сервиса, вместо использования декоратора. Это прекрасный способ, но если вы хотите сохранить поведение по умолчанию, и просто дополнить это поведение, то декоратоор крайне рекомендуем. + Замечание: Другим способом является переопределение сервиса, вместо использования декоратора. Это прекрасный способ, но если вы хотите сохранить поведение по умолчанию, и просто дополнить это поведение, то декоратор крайне рекомендуем. ```javascript /* рекомендовано */ @@ -1669,9 +1666,9 @@ function extendExceptionHandler($delegate, toastr) { return function(exception, cause) { $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause + var errorData = { + exception: exception, + cause: cause }; /** * Здесь можно добавить ошибку в сервисную коллекцию, @@ -1687,7 +1684,7 @@ ### Обработчики Исключений ###### [Style [Y111](#style-y111)] - - Создайте фабрику, которая предоставит интерфейс для перехвата и изящной обработки исключений. + - Создайте фабрику, которая предоставит интерфейс для перехвата и изящной обработки исключений. *Почему?*: Это дает постоянный и надежный способ для перехвата исключений, которые могут возникнуть в вашем коде (например, во время вызовов объекта XHR или сбоев в работе объектов promise). @@ -1757,14 +1754,14 @@ - Используйте согласованные имена для всех компонентов по шаблону, который описывает особенность(feature) компонента, а затем (опционально) его тип. Я рекомендую шаблон - `feature.type.js`. Существует два типа имен для большинства случаев: * имя файла (`avengers.controller.js`) * имя компонента, которое зарегистрировано Angular (`AvengersController`) - - *Почему?*: Соглашения об именованиях дает постояннный надежный способ поиска содержимого быстрым беглым взглядом. Согласованность в проекте жизненно важна. Согласованность в команде очень важна. Согласованность между компаниями дает огромную эффективность. - *Почему?*: Соглашения об именованиях просто должны помочь найти вам свой код быстрее, и сделать его проще для понимания. + *Почему?*: Соглашения об именованиях дает постояннный надежный способ поиска содержимого быстрым беглым взглядом. Согласованность в проекте жизненно важна. Согласованность в команде очень важна. Согласованность между компаниями дает огромную эффективность. + + *Почему?*: Соглашения об именованиях просто должны помочь найти вам свой код быстрее, и сделать его проще для понимания. ### Характерные Имена Файлов ###### [Style [Y121](#style-y121)] - - Используйте согласованные имена для всех компонентов используя шаблон, который описывает компонентную особенность, затем опционально его тип. Я рекомендую шаблон - `feature.type.js`. + - Используйте согласованные имена для всех компонентов используя шаблон, который описывает компонентную особенность, затем опционально его тип. Я рекомендую шаблон - `feature.type.js`. *Почему?*: Это надежный способ для быстрой идентификации компонентов. @@ -1801,7 +1798,7 @@ // константы constants.js - + // определение модуля avengers.module.js @@ -1811,14 +1808,14 @@ // конфигурация avengers.config.js - + // директивы avenger-profile.directive.js avenger-profile.directive.spec.js ``` Замечание: Другим общим соглашением является именование файлов контроллера без слова `controller`, например, называем файл `avengers.js` вместо `avengers.controller.js`. Все остальные соглашения все же должны содержать суффикс типа. Просто контроллеры наиболее общий тип компонентов, и таким образом, мы экономим время печатая имя, но контроллеры все равно прекрасно идентифицируются. Я рекомендую выбрать один тип соглашения и быть на одной волне со своей командой. - + ```javascript /** * рекомендовано @@ -1831,7 +1828,7 @@ ### Имена Тестовых Файлов ###### [Style [Y122](#style-y122)] - - Имя тестовой спецификации подобно имени компонента, которая его тестит, только к ней еще добавляется суффикс `spec`. + - Имя тестовой спецификации подобно имени компонента, которая его тестит, только к ней еще добавляется суффикс `spec`. *Почему?*: Это надежный способ для быстрой идентификации компонентов. @@ -1850,7 +1847,7 @@ ### Имена Контроллеров ###### [Style [Y123](#style-y123)] - - Используйте согласованные имена для всех контроллеров, именованных по их характерной особенности. Используйте UpperCamelCase (ВерхнийВерблюжийРегистр) для контроллеров, так как они являются конструкторами. + - Используйте согласованные имена для всех контроллеров, именованных по их характерной особенности. Используйте UpperCamelCase (ВерхнийВерблюжийРегистр) для контроллеров, так как они являются конструкторами. *Почему?*: Это дает надежный и понятный способ для быстрой идентификации и применения контроллеров. @@ -1872,11 +1869,11 @@ ### Суффикс Имени Контроллера ###### [Style [Y124](#style-y124)] - - Добавляйте к имени контроллера суффикс `Controller` или не добавляйте. Выберите одно правило т придерживайтесь его везде. + - Добавляйте к имени контроллера суффикс `Controller` или не добавляйте. Выберите одно правило и придерживайтесь его везде. *Почему?*: Суффикс `Controller` более общеупотребим и наиболее явно описателен. - *Почему?*: Если не указывать суффикс, то получатся более краткие имена, но контроллеры все равно будут легко идентифицируемы, даже без суффикса. + *Почему?*: Если не указывать суффикс, то получатся более краткие имена, но контроллеры все равно будут легко идентифицируемы, даже без суффикса. ```javascript /** @@ -1949,10 +1946,10 @@ ### Модули ###### [Style [Y127](#style-y127)] - - Если разрабатываются несколько модулей, файл главного модуля будет называться `app.module.js`, а другие модули получат свое название по своему назначению (то что они представляют). Например, модуль администрирования будет назван `admin.module.js`. Соответствующие зарегистрированные имена модулей будут `app` и `admin`. + - Если разрабатываются несколько модулей, файл главного модуля будет называться `app.module.js`, а другие модули получат свое название по своему назначению (то что они представляют). Например, модуль администрирования будет назван `admin.module.js`. Соответствующие зарегистрированные имена модулей будут `app` и `admin`. - *Почему?*: Это дает согласованность для многих модулей приложения, а также позволяет расширяться в огромные приложения. - *Почему?*: Получаем простой способ автоматизировать работу для первоначальной загрузки всех модульных определений, а затем уже остальных файлов angular. + *Почему?*: Это дает согласованность для многих модулей приложения, а также позволяет расширяться в огромные приложения. + *Почему?*: Получаем простой способ автоматизировать работу для первоначальной загрузки всех модульных определений, а затем уже остальных файлов angular. ### Конфигурация ###### [Style [Y128](#style-y128)] @@ -1966,7 +1963,7 @@ ### Маршруты ###### [Style [Y129](#style-y129)] - - Выделяйте конфигурацию маршрута в свой собственный файл. Примеры могут быть такими: `app.route.js` для главного модуля и `admin.route.js` для модуля `admin`. Даже в маленьких приложениях я предпочитаю такое разделение от остальной конфигурации. + - Выделяйте конфигурацию маршрута в свой собственный файл. Примеры могут быть такими: `app.route.js` для главного модуля и `admin.route.js` для модуля `admin`. Даже в маленьких приложениях я предпочитаю такое разделение от остальной конфигурации. **[К Содержанию](#table-of-contents)** @@ -1976,10 +1973,10 @@ - Структура вашего приложения должна быть построена таким образом, чтобы вы могли: `L` - размещать ваш код быстро (`L`ocate your code quickly), `I` - идентифицировать код практически с первого взгляда (`I`dentify the code at a glance), `F` - держать структуру плоской, насколько это возможно (keep the `F`lattest structure you can), и стараться оставаться DRY (Don’t Repeat Yourself) - Не Повторяйте Себя - (`T`ry to stay DRY - Don’t Repeat Yourself). Структура должна придерживаться этим основным 4 правилам. - *Почему LIFT?*: Получаем согласованную структуру, которая хорошо масштабируется, разбита на модули, и легко позволяет увеличить эффективность разработчика, путем быстрого нахождения кода. Другой способ проверить структуру вашего приложения - это спросить себя: Как быстро я могу открыть все сооответствующие файлы, чтобы работать над нужной функциональностью? - + *Почему LIFT?*: Получаем согласованную структуру, которая хорошо масштабируется, разбита на модули, и легко позволяет увеличить эффективность разработчика, путем быстрого нахождения кода. Другой способ проверить структуру вашего приложения - это спросить себя: Как быстро я могу открыть все сооответствующие файлы, чтобы работать над нужной функциональностью? + Когда я чувствую, что работать с моей структурой некомфортно, я возвращаюсь к принципам LIFT. - + 1. `L`Размещать наш код легко (`L`ocating our code is easy) 2. `I`Идентифицировать код быстро (`I`dentify code at a glance) 3. `F`Держать структуру ровной как можно дольше (`F`lat structure as long as we can) @@ -2027,8 +2024,8 @@ ### T-DRY (Try to Stick to DRY) T-DRY (Старайтесь придерживаться принципов DRY) ###### [Style [Y144](#style-y144)] - - Примечание переводчика: Аббревиатура DRY значит Don`t Repeat Yourself (Не повторяйте себя). - + - Примечание переводчика: Аббревиатура DRY значит Don`t Repeat Yourself (Не повторяйте себя). + - Придерживайтесь DRY, но не сходите с ума и не жертвуйте читабельностью. *Почему?*: Быть DRY - это важно, но не критично, если в жертву приносятся другие принципы LIFT. Поэтому я и назвал это T-DRY (Try DRY - попытайтесь быть DRY). Напрмер, я не хочу печатать session-view.html для представления, потому что и так понятно, что это представление(view). Но если это не очевидно или это определено соглашением, тогда нужно давать полное имя. @@ -2047,7 +2044,7 @@ ### Общие элементы ###### [Style [Y151](#style-y151)] - - Размещайте компоненты, которые определяют общий каркас приложения в папке `layout`. Там могут быть представление-оболочка и контроллер, которые являются контейнером для приложения, навигация, меню, регионы содержимого (content areas) и другие общие элементы. + - Размещайте компоненты, которые определяют общий каркас приложения в папке `layout`. Там могут быть представление-оболочка и контроллер, которые являются контейнером для приложения, навигация, меню, регионы содержимого (content areas) и другие общие элементы. *Почему?*: Так организуются все элементы общего назначения, которые размещаются в одном месте и используются во всем приложении. @@ -2102,17 +2099,17 @@ 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+ представлений и контроллеров (и других компонентов), то работа становится очень сложной, в отличиии от структуры папки-по-функциональностям. - Замечание: Не используйте структуру папки-по-типу. Так файлы одной функциональности разбрасываются по нескольким папкам, и далее все быстро становится очень громоздким. Как только в приложении создаются 5, 10, или 25+ представлений и контроллеров (и других компонентов), то работа становится очень сложной, в отличиии от структуры папки-по-функциональностям. - ```javascript - /* + /* * избегайте этого * Альтернативный способ "папки-по-типу". * Я рекомендую "папки-по-функциональностям". */ - + app/ app.module.js app.config.js @@ -2143,7 +2140,7 @@ speakers.html speaker-detail.html topnav.html - ``` + ``` **[К Содержанию](#table-of-contents)** @@ -2181,21 +2178,21 @@ *Почему?*: Спринты или итерации могут сосредоточиться на функциональных областях, и включить их по завершению спринта или итерации. - *Почему?*: Оформление функциональных областей в модули позволяет проще их тестировать в изолированном или повторно используемом коде. + *Почему?*: Оформление функциональных областей в модули позволяет проще их тестировать в изолированном или повторно используемом коде. ### Повторно Используемые Блоки и Модули ###### [Style [Y164](#style-y164)] - Создавайте модули, которые представляют из себя повторно используемые блоки приложения для общих сервисов таких как обработка исключений, логгирование, диагностика, безопасность и локальная работа с данными. - *Почему?*: Эти типы функциональностей нужны во многих приложениях. Поэтому держите их отдельно в своих собственных модулях, применяйте универсально и повторно используйте во многих приложениях. + *Почему?*: Эти типы функциональностей нужны во многих приложениях. Поэтому держите их отдельно в своих собственных модулях, применяйте универсально и повторно используйте во многих приложениях. ### Зависимости модулей ###### [Style [Y165](#style-y165)] - Корневой модуль приложения зависит от специфичных функциональных модулей и от любых общих или повторно используемых модулей. - ![Модульность и Зависимости](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) *Почему?*: Главный модуль приложения содержит хорошо читаемый манифест функциональностей приложения. @@ -2203,11 +2200,11 @@ *Почему?*: Функциональности приложения для внутреннего пользования (intra-app) такие как общие сервисы данных становится проще размещать и делится из модуля `app.core` (выберите это имя для такого модуля). - Замечание: Это стратегия для согласованности. Здесь очень много хороших вариантов. Выберите тот, который следует правилам зависимостей AngularJS, и его проще поддерживать и масштабировать. + Замечание: Это стратегия для согласованности. Здесь очень много хороших вариантов. Выберите тот, который следует правилам зависимостей AngularJS, и его проще поддерживать и масштабировать. > Мои структуры слегка отличаются от проекта к проекту, но они всегда следовали этим руководствам структуры и модульности. Реализация может менятся в зависимости от особенностей проекта и команды. Другими словами, не зацикливайтесь на точных воспроизведениях структуры. Регулируйте свою структуру учитывая согласованность, способность поддерживать код и эффективность. - > В небольшом приложении вы можете конечно объединить все общие зависимости в модуль приложения, где функциональные модули не имеют прямых зависимостей. Это проще поддерживать в маленьких приложениях, использовать это вне приложения будет очень затруднительно. + > В небольшом приложении вы можете конечно объединить все общие зависимости в модуль приложения, где функциональные модули не имеют прямых зависимостей. Это проще поддерживать в маленьких приложениях, использовать это вне приложения будет очень затруднительно. **[К Содержанию](#table-of-contents)** @@ -2225,7 +2222,7 @@ .module('app') .config(configure); - configure.$inject = + configure.$inject = ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { @@ -2250,7 +2247,7 @@ - Весь код, который должен запуститься, во время старта приложения, должен быть объявлен в фабрике, предоставлен в виде функции, и вставлен в [блок run](https://docs.angularjs.org/guide/module#module-loading-dependencies). - *Почему?*: Если код поместить сразу в блок run, то его будет тяжело тестить. Размещение кода в фабрике облегчает абстрагирование и использование фиктивных объектов для тестов. + *Почему?*: Если код поместить сразу в блок run, то его будет тяжело тестить. Размещение кода в фабрике облегчает абстрагирование и использование фиктивных объектов для тестов. ```javascript angular @@ -2274,7 +2271,7 @@ - Используйте [`$document`](https://docs.angularjs.org/api/ng/service/$document) и [`$window`](https://docs.angularjs.org/api/ng/service/$window) вместо `document` и `window`. - *Почему?*: Эти сервисы являются оберткой среды Angular, они более проще тестируются, чем объекты document и window. Это дает вам возможность не создавать самим фиктивные объекты document and window. + *Почему?*: Эти сервисы являются оберткой среды Angular, они более проще тестируются, чем объекты document и window. Это дает вам возможность не создавать самим фиктивные объекты document and window. ### $timeout and $interval ###### [Style [Y181](#style-y181)] @@ -2331,7 +2328,7 @@ *Почему?*: Karma просто конфигурируется, она просто запускается вручную или автоматически (как только вы измените код). - *Почему?*: Karma просто внедряется в ваш процесс Continuous Integration, как самостоятельно, так и через Grunt или Gulp. + *Почему?*: Karma просто внедряется в ваш процесс Continuous Integration, как самостоятельно, так и через Grunt или Gulp. *Почему?*: Некоторые средства разработки (IDE) начали интегрировать в себя библиотеку Karma, это - [WebStorm](http://www.jetbrains.com/webstorm/) и [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). @@ -2351,14 +2348,14 @@ - Используйте [PhantomJS](http://phantomjs.org/) для запуска тестов на сервере. - *Почему?*: PhantomJS - невизуальный браузер, который помогает запускать тесты, не используя обычные визуальные браузеры. Таким образом, вам не нужно устанавливать на ваш сервер Chrome, Safari, IE, или другие браузеры. + *Почему?*: PhantomJS - невизуальный браузер, который помогает запускать тесты, не используя обычные визуальные браузеры. Таким образом, вам не нужно устанавливать на ваш сервер Chrome, Safari, IE, или другие браузеры. Замечание: Все же проведите тесты на всех браузерах, особенно на браузерах вашей целевой аудитории. ### Анализ Кода ###### [Style [Y195](#style-y195)] - - Запустите JSHint на ваших тестах. + - Запустите JSHint на ваших тестах. *Почему?*: Тесты это код. JSHint помогает идентифицировать проблемы качества кода, которые могут причиной некорректной работы тестов. @@ -2367,13 +2364,13 @@ - Смягчите правила для кода ваших тестов, чтобы разрешить общие глобальные переменные такие как `describe` и `expect`. - *Почему?*: Ваши тесты - это код и он требует того же самого внимания и соблюдения правил качества, как и весь ваш рабочий код. Все же, глобальные переменные используются средой тестирования, и правила для них нужно ослабить в спецификации тестов. + *Почему?*: Ваши тесты - это код и он требует того же самого внимания и соблюдения правил качества, как и весь ваш рабочий код. Все же, глобальные переменные используются средой тестирования, и правила для них нужно ослабить в спецификации тестов. ```javascript /* 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)] @@ -2386,9 +2383,9 @@ *Почему?*: Когда вы изменяете исходный код, всегда проще сразу обновить тесты. - *Почему?*: Размещение файла кода и теста рядом упрощает их поиск и в случае необходимости, перенос в другое место обоих файлов не составляет большого труда. + *Почему?*: Размещение файла кода и теста рядом упрощает их поиск и в случае необходимости, перенос в другое место обоих файлов не составляет большого труда. - *Почему?*: Отделить тестовые файлы(specs), так чтобы они не попали в рабочую сборку можно с помощью grunt или gulp. + *Почему?*: Отделить тестовые файлы(specs), так чтобы они не попали в рабочую сборку можно с помощью grunt или gulp. ``` /src/client/app/customers/customer-detail.controller.js @@ -2416,8 +2413,8 @@ ### Длительность Анимаций ###### [Style [Y211](#style-y211)] - - Используйте короткую длительность анимаций. Я в основном начинаю с 300 миллисекунд и регулирую до нужного состояния. - *Почему?*: Долгие анимации могут иметь обратный эффект для пользователя, приложение будет восприниматься как медленно работающее. + - Используйте короткую длительность анимаций. Я в основном начинаю с 300 миллисекунд и регулирую до нужного состояния. + *Почему?*: Долгие анимации могут иметь обратный эффект для пользователя, приложение будет восприниматься как медленно работающее. ### animate.css ###### [Style [Y212](#style-y212)] @@ -2471,7 +2468,7 @@ /** * @name logError * @desc Logs errors - * @param {String} msg Message to log + * @param {String} msg Message to log * @returns {String} * @memberOf Factories.Logger */ @@ -2565,7 +2562,7 @@ ## Constants -### Глобальные Переменные Сторонних Производителей (Vendors) +### Глобальные Переменные Сторонних Производителей (Vendors) ###### [Style [Y240](#style-y240)] - Создайте константы Angular для глобальных переменных из библиотек сторонних производителей. @@ -2585,16 +2582,16 @@ .constant('moment', moment); })(); ``` - + ###### [Style [Y241](#style-y241)] - - Используйте константы для значений, которые не изменяются и не приходят из другого сервиса. Если константы используются в модуле, который может быть использован в нескольких приложениях, то поместите константу в файле, названному по имени модуля. В противном случае держите константы в главном модуле в файле `constants.js`. + - Используйте константы для значений, которые не изменяются и не приходят из другого сервиса. Если константы используются в модуле, который может быть использован в нескольких приложениях, то поместите константу в файле, названному по имени модуля. В противном случае держите константы в главном модуле в файле `constants.js`. *Почему?*: Значение может измениться, возможно это редкая ситуация, но допустим сервис возвращает нам значение, и таким образом мы не будем менять наш рабочий код, использующий этот сервис. Напрмер, url для сервиса данных мог бы быть помещен в константы, но лучше загружать его из веб сервиса. - + *Почему?*: Константы могут быть инжектированы в любой angular-компонент, включая провайдеры. - *Почему?*: Когда приложение разделено на модули, которые могут быть использованы в других приложениях, каждый отдельный модуль должен быть способен оперировать своими собственными зависимыми константами. + *Почему?*: Когда приложение разделено на модули, которые могут быть использованы в других приложениях, каждый отдельный модуль должен быть способен оперировать своими собственными зависимыми константами. ```javascript // Константы используются во всем приложении @@ -2619,18 +2616,20 @@ ### Sublime Text ###### [Style [Y250](#style-y250)] - - Angular сниппеты, которые соблюдают приведенные здесь стили и руководства. + - 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 + - Перезапустите Sublime - В файле JavaScript напечатайте следующие команды после клавиши `TAB` - + ```javascript + ngmodule // создает модуль Angular ngcontroller // создает контроллер Angular - ngdirective // создает директиву Angular + ngservice // создает сервис Angular ngfactory // создает фабрику Angular - ngmodule // создает модуль Angular + ngdirective // создает директиву Angular + ngfilter // создает фильтр Angular ``` ### Visual Studio @@ -2706,7 +2705,7 @@ *Почему?*: Когда мы удаляем или добавляем модуль, то приложение должно содержать только те маршруты, которые указывают на существующие представления. - *Почему?*: Так мы можем включать или исключать части приложения, не заботясь о том, что у нас останутся маршруты на несуществующие представления. + *Почему?*: Так мы можем включать или исключать части приложения, не заботясь о том, что у нас останутся маршруты на несуществующие представления. **[К Содержанию](#table-of-contents)** @@ -2719,7 +2718,7 @@ *Почему?*: Angular должен зарегистрировать все определения модулей, перед тем как их использовать. - *Почему?*: Именование модулей по специальному шаблону `*.module.js` упрощает их поиск и сборку в единую группу, для того чтобы подключить их первыми. + *Почему?*: Именование модулей по специальному шаблону `*.module.js` упрощает их поиск и сборку в единую группу, для того чтобы подключить их первыми. ```javascript var clientApp = './src/client/app/'; @@ -2738,12 +2737,12 @@ ## Contributing -Сначала откройте issie и объясните вопрос/проблему для того, чтобы обсудить потенциальные изменения/добавления. Если у вас есть вопросы по этому руководству, вы можете свободно задавать их, создавая в хранилище issues. Если вы нашли опечатку, создайте pull request. Идея в том, чтобы содержимое всегда дежать актуальным и используя возможности системы Github, помочь рассказать историю с вопросами или проблемами и распространить ее, чтобы она была доступна через поиск Google. Почему? Потому что, если у вас был такой вопрос, то вполне вероятно, что кто-то тоже ищет решение на этот же вопрос! Вы можете узнать больше здесь о том как можно сотрудничать. +Сначала откройте issue и объясните вопрос/проблему для того, чтобы обсудить потенциальные изменения/добавления. Если у вас есть вопросы по этому руководству, вы можете свободно задавать их, создавая в хранилище issues. Если вы нашли опечатку, создайте pull request. Идея в том, чтобы содержимое всегда держать актуальным и используя возможности системы Github, помочь рассказать историю с вопросами или проблемами и распространить ее, чтобы она была доступна через поиск Google. Почему? Потому что, если у вас был такой вопрос, то вполне вероятно, что кто-то тоже ищет решение на этот же вопрос! Вы можете узнать больше здесь о том как можно сотрудничать. *Добавляя материал в данное хранилище вы согласны с тем, ваше содержимое будет доступно согласно приведенной ниже лицензии.* ### Процесс - 1. Обсудите изменения в Issue. + 1. Обсудите изменения в Issue. 2. Откройте Pull Request, сделайте ссылку на issue, объясните изменения и их ценность. 3. Pull Request будет проверен и далее принят или отклонен. diff --git a/a1/i18n/tr-TR.md b/a1/i18n/tr-TR.md new file mode 100644 index 00000000..7d2b973c --- /dev/null +++ b/a1/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/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 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. + +## Ö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/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. + + ```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/a1/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/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. + + ```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/a1/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/a1/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)** diff --git a/i18n/zh-CN.md b/a1/i18n/zh-CN.md similarity index 88% rename from i18n/zh-CN.md rename to a1/i18n/zh-CN.md index 5ed3cf0f..e3de9a82 100644 --- a/i18n/zh-CN.md +++ b/a1/i18n/zh-CN.md @@ -1,31 +1,31 @@ -# Angular风格指南 +# Angular规范 -*Angular风格指南[@john_papa](//twitter.com/john_papa)* +## Angular Team Endorsed +非常感谢领导Angular团队的Igor Minar对本指南做出的审查和贡献,并且委托我继续打理本指南。 -*由[kerncink](https://github.com/natee)翻译* +## 目的 +*Angular规范[@john_papa](//twitter.com/john_papa)* ->The [original English version](http://jpapa.me/ngstyles) is the source of truth, as it is maintained and updated first. +如果你正在寻找一些关于语法、约定和结构化的Angular应用的一个有建设性的规范,那么你来对地方了。这里所包含的内容是基于我在团队中使用[Angular](//angularjs.org)的一些经验、一些演讲和[Pluralsight培训课程](http://pluralsight.com/training/Authors/Details/john-papa)。 -如果你正在寻找一些关于语法、约定和结构化的Angular应用的一个有建设性的风格指南,那么你来对地方了。这里所包含的风格是基于我在团队中使用[Angular](//angularjs.org)的一些经验、一些演讲和[Pluralsight培训课程](http://pluralsight.com/training/Authors/Details/john-papa)。 +这个规范的目的是为构建Angular应用提供指导,当然更加重要的是让大家知道我为什么要选择它们。 -这个风格指南的目的是为构建Angular应用提供指导,当然更加重要的是让大家知道我为什么要选择它们。 +>如果你喜欢这个规范,请在Pluralsight看看[Angular Patterns: Clean Code](http://jpapa.me/ngclean)。 ->如果你喜欢这个指南,请在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),在那里你能看到我们之间的区别。 +Angular社区是一个热衷于分享经验的令人难以置信的社区,尽管Todd Motto(他是我的一个朋友,也是Angular专家)和我合作了多种规范和惯例,但是我们也存在着一些分歧。我鼓励你去看看[Todd的指南](https://github.com/toddmotto/angularjs-styleguide),在那里你能看到我们之间的区别。 -我的许多风格都是从大量的程序会话[Ward Bell](http://twitter.com/wardbell)和我所拥有的而来的,我的好友Ward也影响了本指南的最终演变。 +我的许多规范都是从大量的程序会话[Ward Bell](http://twitter.com/wardbell)和我所拥有的而来的,我的好友Ward也影响了本规范的最终演变。 -## 在示例App中了解这些风格 +## 在示例App中了解这些规范 看示例代码有助于你更好地理解,你可以在`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. [单一职责](#单一职责) 1. [IIFE](#iife) 1. [Modules](#modules) @@ -39,13 +39,13 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 1. [压缩和注释](#压缩和注释) 1. [异常处理](#异常处理) 1. [命名](#命名) - 1. [应用程序结构LIFT原则](#应用程序结构lift原则) + 1. [应用程序结构的LIFT准则](#应用程序结构的lift准则) 1. [应用程序结构](#应用程序结构) 1. [模块化](#模块化) 1. [启动逻辑](#启动逻辑) 1. [Angular $包装服务](#angular-包装服务) 1. [测试](#测试) - 1. [动画](#动画) + 1. [动画](#动画) 1. [注释](#注释) 1. [JSHint](#js-hint) 1. [JSCS](#jscs) @@ -60,25 +60,25 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 1. [许可](#许可) ## 单一职责 -###规则一 +### 规则一 ###### [Style [Y001](#style-y001)] - 一个文件只定义一个组件。 - 下面的例子在同一个文件中定义了一个`app`的module和它的一些依赖、一个controller和一个factory。 + 下面的例子在同一个文件中定义了一个`app`的module和它的一些依赖、一个controller和一个factory。 ```javascript - /* avoid */ + /* avoid */ angular .module('app', ['ngRoute']) .controller('SomeController', SomeController) .factory('someFactory', someFactory); - + function SomeController() { } function someFactory() { } ``` - + 推荐以下面的方式来做,把上面相同的组件分割成单独的文件。 ```javascript /* recommended */ @@ -105,7 +105,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 angular .module('app') .factory('someFactory', someFactory); - + function someFactory() { } ``` @@ -115,12 +115,12 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ### JavaScript闭包 ###### [Style [Y010](#style-y010)] - - 把Angular组件包装到一个立即调用函数表达式中(IIFE)。 - + - 把Angular组件包装到一个立即调用函数表达式中(IIFE)。 + *为什么?*:把变量从全局作用域中删除了,这有助于防止变量和函数声明比预期在全局作用域中有更长的生命周期,也有助于避免变量冲突。 *为什么?*:当你的代码为了发布而压缩了并且被合并到同一个文件中时,可能会有很多变量发生冲突,使用了IIFE(给每个文件提供了一个独立的作用域),你就不用担心这个了。 - + ```javascript /* avoid */ // logger.js @@ -128,7 +128,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 .module('app') .factory('logger', logger); - // logger function会被当作一个全局变量 + // logger function会被当作一个全局变量 function logger() { } // storage.js @@ -136,15 +136,15 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 .module('app') .factory('storage', storage); - // storage function会被当作一个全局变量 + // storage function会被当作一个全局变量 function storage() { } ``` ```javascript /** - * recommended + * recommended * - * 再也不存在全局变量了 + * 再也不存在全局变量了 */ // logger.js @@ -170,7 +170,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 })(); ``` - - 注:为了简洁起见,本指南余下的示例中将会省略IIFE语法。 + - 注:为了简洁起见,本规范余下的示例中将会省略IIFE语法。 - 注:IIFE阻止了测试代码访问私有成员(正则表达式、helper函数等),这对于自身测试是非常友好的。然而你可以把这些私有成员暴露到可访问成员中进行测试,例如把私有成员(正则表达式、helper函数等)放到factory或是constant中。 @@ -178,20 +178,20 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## Modules -###避免命名冲突 +### 避免命名冲突 ###### [Style [Y020](#style-y020)] - 每一个独立子模块使用唯一的命名约定。 *为什么*:避免冲突,每个模块也可以方便定义子模块。 -###定义(aka Setters) +### 定义(aka Setters) ###### [Style [Y021](#style-y021)] - - 不使用任何一个使用了setter语法的变量来定义modules。 + - 不使用任何一个使用了setter语法的变量来定义modules。 *为什么?*:在一个文件只有一个组件的条件下,完全不需要为一个模块引入一个变量。 - + ```javascript /* avoid */ var app = angular.module('app', [ @@ -215,7 +215,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ]); ``` -###Getters +### Getters ###### [Style [Y022](#style-y022)] - 使用module的时候,避免直接用一个变量,而是使用getter的链式语法。 @@ -226,7 +226,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 /* avoid */ var app = angular.module('app'); app.controller('SomeController', SomeController); - + function SomeController() { } ``` @@ -235,24 +235,24 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 angular .module('app') .controller('SomeController', SomeController); - + function SomeController() { } ``` -###Setting vs Getting +### Setting vs Getting ###### [Style [Y023](#style-y023)] - 只能设置一次。 - + *为什么?*:一个module只能被创建一次,创建之后才能被检索到。 - + - 设置module,`angular.module('app', []);`。 - - 获取module,`angular.module('app');`。 + - 获取module,`angular.module('app');`。 -###命名函数 vs 匿名函数 +### 命名函数 vs 匿名函数 ###### [Style [Y024](#style-y024)] - - 回调函数使用命名函数,不要用匿名函数。 + - 回调函数使用命名函数,不要用匿名函数。 *为什么?*:易读,方便调试,减少嵌套回调函数的数量。 @@ -288,12 +288,12 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## Controllers -###controllerAs在View中的语法 +### controllerAs在View中的语法 ###### [Style [Y030](#style-y030)] - - 使用[`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) 语法代替直接用经典的$scope定义的controller的方式。 + - 使用[`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) 语法代替直接用经典的$scope定义的controller的方式。 - *为什么?*:congtroller被构建的时候,就会有一个新的实例,`controllerAs` 的语法比`经典的$scope语法`更接近JavaScript构造函数。 + *为什么?*:controller被构建的时候,就会有一个新的实例,`controllerAs` 的语法比`经典的$scope语法`更接近JavaScript构造函数。 *为什么?*:这促进在View中对绑定到“有修饰”的对象的使用(例如用`customer.name` 代替`name`),这将更有语境、更容易阅读,也避免了任何没有“修饰”而产生的引用问题。 @@ -313,16 +313,16 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽
``` -###controllerAs在controller中的语法 +### controllerAs在controller中的语法 ###### [Style [Y031](#style-y031)] - - 使用 `controllerAs` 语法代替 `经典的$scope语法` 语法。 + - 使用 `controllerAs` 语法代替 `经典的$scope语法` 语法。 - 使用`controllerAs` 时,controller中的`$scope`被绑定到了`this`上。 - *为什么?*:`controllerAs` 是`$scope`的语法修饰,你仍然可以绑定到View上并且访问 `$scope`的方法。 + *为什么?*:`controllerAs` 是`$scope`的语法修饰,你仍然可以绑定到View上并且访问 `$scope`的方法。 - *为什么?*:避免在controller中使用 `$scope`,最好不用它们或是把它们移到一个factory中。factory中可以考虑使用`$scope`,controller中只在需要时候才使用`$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)来发布和订阅事件时,可以考虑把这些调用挪到factory当中,并从controller中调用。 + *为什么?*:避免在controller中使用 `$scope`,最好不用它们或是把它们移到一个factory中。factory中可以考虑使用`$scope`,controller中只在需要时候才使用`$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 */ @@ -340,11 +340,12 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 } ``` -###controllerAs with vm +### controllerAs with vm + ###### [Style [Y032](#style-y032)] - 使用`controllerAs`语法时把`this` 赋值给一个可捕获的变量,选择一个有代表性的名称,例如`vm`代表ViewModel。 - + *为什么?*:`this`在不同的地方有不同的语义(就是作用域不同),在controller中的一个函数内部使用`this`时可能会改变它的上下文。用一个变量来捕获`this`的上下文从而可以避免遇到这样的坑。 ```javascript @@ -365,7 +366,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ``` - 注:你可以参照下面的做法来避免 [jshint](http://www.jshint.com/)的警告。但是构造函数(函数名首字母大写)是不需要这个的. - + ```javascript /* jshint validthis: true */ var vm = this; @@ -380,7 +381,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 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); @@ -388,15 +389,13 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 } ``` - -###可绑定成员放到顶部 +### 置顶绑定成员 + ###### [Style [Y033](#style-y033)] - 把可绑定的成员放到controller的顶部,按字母排序,并且不要通过controller的代码传播。 - - *为什么?*:易读,可以让你立即识别controller中的哪些成员可以在View中绑定和使用。 - *为什么?*:虽然设置单行匿名函数很容易,但是当这些函数的代码超过一行时,这将极大降低代码的可读性。在可绑定成员下面定义函数(这些函数被提出来),把具体的实现细节放到下面,可绑定成员放到顶部,这会提高代码的可读性。 + *为什么?*:虽然设置单行匿名函数很容易,但是当这些函数的代码超过一行时,这将极大降低代码的可读性。在可绑定成员下面定义函数(这些函数被提出来),把具体的实现细节放到下面,可绑定成员置顶,这会提高代码的可读性。 ```javascript /* avoid */ @@ -442,7 +441,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) 注:如果一个函数就是一行,那么只要不影响可读性就把它放到顶部。 @@ -453,8 +452,8 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 vm.gotoSession = gotoSession; vm.refresh = function() { - /** - * lines + /** + * lines * of * code * affects @@ -478,24 +477,24 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 vm.title = 'Sessions'; ``` -###函数声明隐藏实现细节 +### 函数声明隐藏实现细节 ###### [Style [Y034](#style-y034)] - - 使用函数声明来隐藏实现细节,把绑定成员放到顶部,当你需要在controller中绑定一个函数时,把它指向一个在文件的后面会出现函数声明。更多详情请看[这里](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code)。 - + - 使用函数声明来隐藏实现细节,置顶绑定成员,当你需要在controller中绑定一个函数时,把它指向一个在文件的后面会出现函数声明。更多详情请看[这里](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code)。 + *为什么?*:易读,易识别哪些成员可以在View中绑定和使用。 *为什么?*:把函数的实现细节放到后面,你可以更清楚地看到重要的东西。 - *为什么?*:由于函数声明会被提到顶部,所以没有必要担心在声明它之前就使用函数的问题。 + *为什么?*:由于函数声明会被置顶,所以没有必要担心在声明它之前就使用函数的问题。 - *为什么?*:你再也不用担心当 `a`依赖于 `b`时,把`var a`放到`var b`之前会中断你的代码的函数声明问题。 + *为什么?*:你再也不用担心当 `a`依赖于 `b`时,把`var a`放到`var b`之前会中断你的代码的函数声明问题。 *为什么?*:函数表达式中顺序是至关重要的。 ```javascript - /** - * avoid + /** + * avoid * Using function expressions. */ function Avengers(dataservice, logger) { @@ -554,7 +553,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 } ``` -###把Controller中的逻辑延迟到Service中 +### 把Controller中的逻辑延迟到Service中 ###### [Style [Y035](#style-y035)] - 通过委派到service和factory中来延迟controller中的逻辑。 @@ -575,7 +574,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 vm.isCreditOk; vm.total = 0; - function checkCredit () { + function checkCredit () { var settings = {}; // Get the credit service base URL from config // Set credit service required headers @@ -605,7 +604,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 vm.isCreditOk; vm.total = 0; - function checkCredit () { + function checkCredit () { return creditService.isOrderTotalOk(vm.total) .then(function(isOk) { vm.isCreditOk = isOk; }) .catch(showServiceError); @@ -613,19 +612,19 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 } ``` -###保持Controller的专一性 +### 保持Controller的专一性 ###### [Style [Y037](#style-y037)] - - 一个view定义一个controller,尽量不要在其它view中使用这个controller。把可重用的逻辑放到factory中,保证controller只服务于当前视图。 - + - 一个view定义一个controller,尽量不要在其它view中使用这个controller。把可重用的逻辑放到factory中,保证controller只服务于当前视图。 + *为什么?*:不同的view用同一个controller是非常不科学的,良好的端对端测试覆盖率对于保证大型应用稳定性是必需的。 -###分配Controller +### 分配Controller ###### [Style [Y038](#style-y038)] - - 当一个controller必须匹配一个view时或者任何一个组件可能被其它controller或是view重用时,连同controller的route一起定义。 - - 注:如果一个view是通过route外的其它形式加载的,那么就用`ng-controller="Avengers as vm"`语法。 + - 当一个controller必须匹配一个view时或者任何一个组件可能被其它controller或是view重用时,连同controller的route一起定义。 + + 注:如果一个view是通过route外的其它形式加载的,那么就用`ng-controller="Avengers as vm"`语法。 *为什么?*:在route中匹配controller允许不同的路由调用不同的相匹配的controller和view,当在view中通过[`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController)分配controller时,这个view总是和相同的controller相关联。 @@ -679,11 +678,11 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## Services -###单例 +### 单例 ###### [Style [Y040](#style-y040)] - 用`new`实例化service,用`this`实例化公共方法和变量,由于这和factory是类似的,所以为了保持统一,推荐用facotry来代替。 - + 注意:[所有的Angular services都是单例](https://docs.angularjs.org/guide/services),这意味着每个injector都只有一个实例化的service。 ```javascript @@ -718,28 +717,28 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## Factories -###单一职责 +### 单一职责 ###### [Style [Y051](#style-y051)] - factory应该是[单一职责](http://en.wikipedia.org/wiki/Single_responsibility_principle),这是由其上下文进行封装的。一旦一个factory将要处理超过单一的目的时,就应该创建一个新的factory。 -###单例 +### 单例 ###### [Style [Y051](#style-y051)] - facotry是一个单例,它返回一个包含service成员的对象。 - + 注:[所有的Angular services都是单例](https://docs.angularjs.org/guide/services),这意味着每个injector都只有一个实例化的service。 -###可访问的成员放到顶部### +### 可访问的成员置顶 ###### [Style [Y052](#style-y052)] - 使用从[显露模块模式](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript)派生出来的技术把service(它的接口)中可调用的成员暴露到顶部, - *为什么?*:易读,并且让你可以立即识别service中的哪些成员可以被调用,哪些成员必须进行单元测试(或者被别人嘲笑)。 + *为什么?*:易读,并且让你可以立即识别service中的哪些成员可以被调用,哪些成员必须进行单元测试(或者被别人嘲笑)。 *为什么?*:当文件内容很长时,这可以避免需要滚动才能看到暴露了哪些东西。 - *为什么?*:虽然你可以随意写一个函数,但当函数代码超过一行时就会降低可读性并造成滚动。通过把实现细节放下面、可调用接口放到顶部的返回service的方式来定义可调用的接口,从而使代码更加易读。 + *为什么?*:虽然你可以随意写一个函数,但当函数代码超过一行时就会降低可读性并造成滚动。通过把实现细节放下面、把可调用接口置顶的形式返回service的方式来定义可调用的接口,从而使代码更加易读。 ```javascript /* avoid */ @@ -748,7 +747,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 function save () { /* */ }; - function validate () { + function validate () { /* */ }; @@ -773,11 +772,11 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 //////////// - function save () { + function save () { /* */ }; - function validate () { + function validate () { /* */ }; } @@ -785,22 +784,22 @@ 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)] - - 函数声明隐藏实现细节,把绑定成员放到顶部,当你需要在controller中绑定一个函数时,把它指向一个函数声明,这个函数声明在文件的后面会出现。 + - 函数声明隐藏实现细节,置顶绑定成员,当你需要在controller中绑定一个函数时,把它指向一个函数声明,这个函数声明在文件的后面会出现。 *为什么?*:易读,易识别哪些成员可以在View中绑定和使用。 *为什么?*:把函数的实现细节放到后面,你可以更清楚地看到重要的东西。 - *为什么?*:由于函数声明会被提到顶部,所以没有必要担心在声明它之前就使用函数的问题。 + *为什么?*:由于函数声明会被置顶,所以没有必要担心在声明它之前就使用函数的问题。 - *为什么?*:你再也不用担心当 `a`依赖于 `b`时,把`var a`放到`var b`之前会中断你的代码的函数声明问题。 + *为什么?*:你再也不用担心当 `a`依赖于 `b`时,把`var a`放到`var b`之前会中断你的代码的函数声明问题。 - *为什么?*:函数表达式中顺序是至关重要的。 + *为什么?*:函数表达式中顺序是至关重要的。 ```javascript /** @@ -889,7 +888,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## Data Services -###独立的数据调用 +### 独立的数据调用 ###### [Style [Y060](#style-y060)] - 把进行数据操作和数据交互的逻辑放到factory中,数据服务负责XHR请求、本地存储、内存存储和其它任何数据操作。 @@ -962,10 +961,10 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 return vm.avengers; }); } - } + } ``` -###数据调用返回一个Promise +### 数据调用返回一个Promise ###### [Style [Y061](#style-y061)] - 就像`$http`一样,调用数据时返回一个promise,在你的调用函数中也返回一个promise。 @@ -1013,16 +1012,16 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 **[返回顶部](#目录)** ## Directives -###一个dirctive一个文件 +### 一个directive一个文件 ###### [Style [Y070](#style-y070)] - - 一个文件中只创建一个directive,并依照directive来命名文件。 + - 一个文件中只创建一个directive,并依照directive来命名文件。 *为什么?*:虽然把所有directive放到一个文件中很简单,但是当一些directive是跨应用的,一些是跨模块的,一些仅仅在一个模块中使用时,想把它们独立出来就非常困难了。 *为什么?*:一个文件一个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 */ @@ -1077,7 +1076,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 /** * @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); @@ -1106,14 +1105,14 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 注:由于directive使用条件比较广,所以命名就存在很多的选项。选择一个让directive和它的文件名都清楚分明的名字。下面有一些例子,不过更多的建议去看[命名](#命名)章节。 -###在directive中操作DOM +### 在directive中操作DOM ###### [Style [Y072](#style-y072)] - 当需要直接操作DOM的时候,使用directive。如果有替代方法可以使用,例如:使用CSS来设置样式、[animation services](https://docs.angularjs.org/api/ngAnimate)、Angular模板、[`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow)或者[`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide),那么就直接用这些即可。例如,如果一个directive只是想控制显示和隐藏,用ngHide/ngShow即可。 *为什么?*:DOM操作的测试和调试是很困难的,通常会有更好的方法(CSS、animations、templates)。 -###提供一个唯一的Directive前缀 +### 提供一个唯一的Directive前缀 ###### [Style [Y073](#style-y073)] - 提供一个短小、唯一、具有描述性的directive前缀,例如`acmeSalesCustomerInfo`在HTML中声明为`acme-sales-customer-info`。 @@ -1122,7 +1121,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 注:避免使用`ng-`为前缀,研究一下其它广泛使用的directive避免命名冲突,例如[Ionic Framework](http://ionicframework.com/)的`ion-`。 -###限制元素和属性 +### 限制元素和属性 ###### [Style [Y074](#style-y074)] - 当创建一个directive需要作为一个独立元素时,restrict值设置为`E`(自定义元素),也可以设置可选值`A`(自定义属性)。一般来说,如果它就是为了独立存在,用`E`是合适的做法。一般原则是允许`EA`,但是当它是独立的时候这更倾向于作为一个元素来实施,当它是为了增强已存在的DOM元素时则更倾向于作为一个属性来实施。 @@ -1163,7 +1162,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽
``` - + ```javascript /* recommended */ angular @@ -1184,7 +1183,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 } ``` -###Directives和ControllerAs +### Directives和ControllerAs ###### [Style [Y075](#style-y075)] - directive使用`controller as`语法,和view使用`controller as`保持一致。 @@ -1218,9 +1217,9 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 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); @@ -1228,7 +1227,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 console.log('LINK: scope.vm.max = %s', scope.vm.max); } } - + ExampleController.$inject = ['$scope']; function ExampleController($scope) { @@ -1247,12 +1246,12 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ```html
hello world
-
max={{vm.max}}
-
min={{vm.min}}
+
max={{vm.max}}
+
min={{vm.min}}
``` 注意:当你把controller注入到link的函数或可访问的directive的attributes时,你可以把它命名为控制器的属性。 - + ```javascript // Alternative to above example function linkFunc(scope, el, attr, vm) { // 和上面例子的区别在于把vm直接传递进来 @@ -1313,17 +1312,17 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 **[返回顶部](#目录)** ## 解决Controller的Promises -###Controller Activation Promises +### Controller Activation Promises ###### [Style [Y080](#style-y080)] - 在`activate`函数中解决controller的启动逻辑。 - + *为什么?*:把启动逻辑放在一个controller中固定的位置可以方便定位、有利于保持测试的一致性,并能够避免controller中到处都是激活逻辑。 *为什么?*:`activate`这个controller使得重用刷新视图的逻辑变得很方便,把所有的逻辑都放到了一起,可以让用户更快地看到视图,可以很轻松地对`ng-view` 或 `ui-view`使用动画,用户体验更好。 注意:如果你需要在开始使用controller之前有条件地取消路由,那么就用[route resolve](#style-y081)来代替。 - + ```javascript /* avoid */ function Avengers(dataservice) { @@ -1358,14 +1357,14 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 } ``` -###Route Resolve Promises +### Route Resolve Promises ###### [Style [Y081](#style-y081)] - 当一个controller在激活之前,需要依赖一个promise的完成时,那么就在controller的逻辑执行之前在`$routeProvider`中解决这些依赖。如果你需要在controller被激活之前有条件地取消一个路由,那么就用route resolver。 - 当你决定在过渡到视图之前取消路由时,使用route resolve。 - *为什么?*:controller在加载前可能需要一些数据,这些数据可能是从一个通过自定义factory或是[$http](https://docs.angularjs.org/api/ng/service/$http)的promise而来的。[route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider)允许promise在controller的逻辑执行之前解决,因此它可能对从promise中来的数据做一些处理。 + *为什么?*:controller在加载前可能需要一些数据,这些数据可能是从一个通过自定义factory或是[$http](https://docs.angularjs.org/api/ng/service/$http)的promise而来的。[route resolve](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider)允许promise在controller的逻辑执行之前解决,因此它可能对从promise中来的数据做一些处理。 *为什么?*:这段代码将在路由后的controller的激活函数中执行,视图立即加载,promise resolve的时候将会开始进行数据绑定,可以(通过`ng-view`或`ui-view`)在视图的过渡之间加个loading状态的动画。 @@ -1444,7 +1443,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 }); } - function moviePrepService(movieService) { + function moviesPrepService(movieService) { return movieService.getMovies(); } @@ -1469,7 +1468,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ###### [Style [Y090](#style-y090)] - 声明依赖时避免使用缩写语法。 - + *为什么?*:组件的参数(例如controller、factory等等)将会被转换成各种乱七八糟错误的变量。例如,`common`和`dataservice`可能会变成`a`或者`b`,但是这些转换后的变量在Angular中是找不到的。 ```javascript @@ -1489,34 +1488,34 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 angular.module('app').controller('Dashboard', d);function d(a, b) { } ``` -###手动添加依赖 +### 手动添加依赖 ###### [Style [Y091](#style-y091)] - 用`$inject`手动添加Angular组件所需的依赖。 - + *为什么?*:这种技术反映了使用[`ng-annotate`](https://github.com/olov/ng-annotate)的技术,这就是我推荐的对依赖关系进行自动化创建安全压缩的方式,如果`ng-annotate`检测到已经有了注入,那么它就不会再次重复执行。 *为什么?*:可以避免依赖变成其它Angular找不到的变量,例如,`common`和`dataservice`可能会变成`a`或者`b`。 - *为什么?*:避免创建内嵌的依赖,因为一个数组太长不利于阅读,此外,内嵌的方式也会让人感到困惑,比如数组是一系列的字符串,但是最后一个却是组件的function。 + *为什么?*:避免创建内嵌的依赖,因为一个数组太长不利于阅读,此外,内嵌的方式也会让人感到困惑,比如数组是一系列的字符串,但是最后一个却是组件的function。 ```javascript /* avoid */ angular .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', + .controller('Dashboard', + ['$location', '$routeParams', 'common', 'dataservice', function Dashboard($location, $routeParams, common, dataservice) {} - ]); + ]); ``` ```javascript /* avoid */ angular .module('app') - .controller('Dashboard', + .controller('Dashboard', ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - + function Dashboard($location, $routeParams, common, dataservice) { } ``` @@ -1528,7 +1527,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 .controller('Dashboard', Dashboard); Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - + function Dashboard($location, $routeParams, common, dataservice) { } ``` @@ -1567,11 +1566,11 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 } ``` -###手动确定路由解析器依赖 +### 手动确定路由解析器依赖 ###### [Style [Y092](#style-y092)] - 用`$inject`手动给Angular组件添加路由解析器依赖。 - + *为什么?*:这种技术打破了路由解析的匿名函数的形式,易读。 *为什么?*:`$inject`语句可以让任何依赖都可以安全压缩。 @@ -1585,13 +1584,13 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 controller: 'AvengersController', controllerAs: 'vm', resolve: { - moviesPrepService: moviePrepService + moviesPrepService: moviesPrepService } }); } - moviePrepService.$inject = ['movieService']; - function moviePrepService(movieService) { + moviesPrepService.$inject = ['movieService']; + function moviesPrepService(movieService) { return movieService.getMovies(); } ``` @@ -1600,11 +1599,11 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## 压缩和注释 -###ng-annotate +### ng-annotate ###### [Style [Y100](#style-y100)] - 在[Gulp](http://gulpjs.com)或[Grunt](http://gruntjs.com)中使用[ng-annotate](//github.com/olov/ng-annotate),用`/** @ngInject */`对需要自动依赖注入的function进行注释。 - + *为什么?*:可以避免代码中的依赖使用到任何不安全的写法。 *为什么?*:不推荐用[`ng-min`](https://github.com/btford/ngmin)。 @@ -1677,11 +1676,11 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 > 注意:从Angular 1.3开始,你就可以用[`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp)指令的 `ngStrictDi`参数来检测任何可能失去依赖的地方,当以“strict-di”模式创建injector时,会导致应用程序无法调用不使用显示函数注释的函数(这也许无法安全压缩)。记录在控制台的调试信息可以帮助追踪出问题的代码。我只在需要调试的时候才会用到`ng-strict-di`。 `` -###使用Gulp或Grunt结合ng-annotate +### 使用Gulp或Grunt结合ng-annotate ###### [Style [Y101](#style-y101)] - 在自动化任务中使用[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 */`语法提示。 下面的代码是gulp任务使用ngAnnotate的例子。 @@ -1705,18 +1704,17 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 .pipe(sourcemaps.write('./')) .pipe(gulp.dest(pkg.paths.dev)); }); - - ``` + ``` **[返回顶部](#目录)** ## 异常处理 -###修饰符 +### 修饰符 ###### [Style [Y110](#style-y110)] - 使用一个[decorator](https://docs.angularjs.org/api/auto/service/$provide#decorator),在配置的时候用[`$provide`](https://docs.angularjs.org/api/auto/service/$provide)服务,当发生异常时,在[`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler)服务中执行自定义的处理方法。 - + *为什么?*:在开发时和运行时提供了一种统一的方式来处理未被捕获的Angular异常。 注:另一个选项是用来覆盖service的,这个可以代替decorator,这是一个非常nice的选项,但是如果你想保持默认行为,那么推荐你扩展一个decorator。 @@ -1738,9 +1736,9 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 function extendExceptionHandler($delegate, toastr) { return function(exception, cause) { $delegate(exception, cause); - var errorData = { - exception: exception, - cause: cause + var errorData = { + exception: exception, + cause: cause }; /** * Could add the error to a service's collection, @@ -1753,7 +1751,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 } ``` -###异常捕获器 +### 异常捕获器 ###### [Style [Y111](#style-y111)] - 创建一个暴露了一个接口的factory来捕获异常并以合适方式处理异常。 @@ -1784,7 +1782,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 } ``` -###路由错误 +### 路由错误 ###### [Style [Y112](#style-y112)] - 用[`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError)来处理并打印出所有的路由错误信息。 @@ -1833,18 +1831,18 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## 命名 -###命名原则 +### 命名原则 ###### [Style [Y120](#style-y120)] - 遵循以描述组件功能,然后是类型(可选)的方式来给所有的组件提供统一的命名,我推荐的做法是`feature.type.js`。大多数文件都有2个名字。 * 文件名 (`avengers.controller.js`) * 带有Angular的注册组件名 (`AvengersController`) - + *为什么?*:命名约定有助于为一目了然地找到内容提供一个统一的方式,在项目中和团队中保持统一性是非常重要的,保持统一性对于跨公司来说提供了巨大的效率。 - *为什么?*:命名约定应该只为代码的检索和沟通提供方便。 + *为什么?*:命名约定应该只为代码的检索和沟通提供方便。 -###功能文件命名 +### 功能文件命名 ###### [Style [Y121](#style-y121)] - 遵循以“描述组件功能.类型(可选)”的方式来给所有的组件提供统一的命名,我推荐的做法是`feature.type.js`。 @@ -1855,7 +1853,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ```javascript /** - * common options + * common options */ // Controllers @@ -1884,7 +1882,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 // constants constants.js - + // module definition avengers.module.js @@ -1894,14 +1892,14 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 // configuration avengers.config.js - + // directives avenger-profile.directive.js avenger-profile.directive.spec.js ``` 注意:另外一种常见的约定就是不要用`controller`这个词来给controller文件命名,例如不要用`avengers.controller.js`,而是用`avengers.js`。所有其它的约定都坚持使用类型作为后缀,但是controller是组件中最为常用的类型,因此这种做法的好处貌似仅仅是节省了打字,但是仍然很容易识别。我建议你为你的团队选择一种约定,并且要保持统一性。我喜欢的命名方式是`avengers.controller.js`。 - + ```javascript /** * recommended @@ -1911,10 +1909,10 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 avengers.spec.js ``` -###测试文件命名 +### 测试文件命名 ###### [Style [Y122](#style-y122)] - - 和组件命名差不多,带上一个`spec`后缀。 + - 和组件命名差不多,带上一个`spec`后缀。 *为什么?*:为快速识别组件提供统一的方式。 @@ -1930,7 +1928,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 avenger-profile.directive.spec.js ``` -###Controller命名 +### Controller命名 ###### [Style [Y123](#style-y123)] - 为所有controller提供统一的名称,先特征后名字,鉴于controller是构造函数,所以要采用UpperCamelCase(每个单词首字母大写)的方式。 @@ -1949,10 +1947,10 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 .module .controller('HeroAvengersController', HeroAvengersController); - function HeroAvengers(){ } + function HeroAvengers(){ } ``` -###Controller命名后缀 +### Controller命名后缀 ###### [Style [Y124](#style-y124)] - 使用`Controller`。 @@ -1971,8 +1969,8 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 function AvengersController(){ } ``` - -###Factory命名 + +### Factory命名 ###### [Style [Y125](#style-y125)] - 一样要统一,对service和factory使用camel-casing(驼峰式,第一个单词首字母小写,后面单词首字母大写)方式。避免使用`$`前缀。 @@ -1980,7 +1978,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 *为什么?*:可以快速识别和引用factory。 *为什么?*:避免与内部使用`$`前缀的服务发生冲突。 - + ```javascript /** * recommended @@ -1994,7 +1992,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 function logger(){ } ``` -###Directive组件命名 +### Directive组件命名 ###### [Style [Y126](#style-y126)] - 使用camel-case方式,用一个短的前缀来描述directive在哪个区域使用(一些例子中是使用公司前缀或是项目前缀)。 @@ -2006,7 +2004,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 * recommended */ - // avenger-profile.directive.js + // avenger-profile.directive.js angular .module .directive('xxAvengerProfile', xxAvengerProfile); @@ -2016,7 +2014,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 function xxAvengerProfile(){ } ``` -###模块 +### 模块 ###### [Style [Y127](#style-y127)] - 当有很多的模块时,主模块文件命名成`app.module.js`,其它依赖模块以它们代表的内容来命名。例如,一个管理员模块命名成`admin.module.js`,它们各自的注册模块名字就是`app`和`admin`。 @@ -2025,7 +2023,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 *为什么?*:对使用任务来自动化加载所有模块的定义(先)和其它所有的angular文件(后)提供了一种简单的方式。 -###配置 +### 配置 ###### [Style [Y128](#style-y128)] - 把一个模块的配置独立到它自己的文件中,以这个模块为基础命名。`app`模块的配置文件命名成`app.config.js`(或是`config.js`),`admin.module.js`的配置文件命名成`admin.config.js`。 @@ -2034,7 +2032,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 *为什么?*:为设置模块的配置提供了一个可识别的地方。 -###路由 +### 路由 ###### [Style [Y129](#style-y129)] - 把路由的配置独立到单独的文件。主模块的路由可能是`app.route.js`,`admin`模块的路由可能是`admin.route.js`。即使是在很小的应用中,我也喜欢把路由的配置从其余的配置中分离出来。 @@ -2042,7 +2040,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 **[返回顶部](#目录)** ## 应用程序结构的LIFT准则 -###LIFT +### LIFT ###### [Style [Y140](#style-y140)] - 构建一个可以快速定位(`L`ocate)代码、一目了然地识别(`I`dentify)代码、拥有一个平直(`F`lattest)的结构、尽量(`T`ry)坚持DRY(Don’t Repeat Yourself)的应用程序,其结构应该遵循这4项基本准则。 @@ -2050,13 +2048,13 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 *为什么是LIFT?*: 提供一个有良好扩展的结构,并且是模块化的,更快的找到代码能够帮助开发者提高效率。另一种检查你的app结构的方法就是问你自己:你能多块地打开涉及到一个功能的所有相关文件并开始工作? 当我发现我的的代码结构很恶心的时候,我就重新看看LIFT准则。 - + 1. 轻松定位代码(L) 2. 一眼识别代码(I) 3. 平直的代码结构(层级不要太多)(F) 4. 尽量保持不要写重复代码(T) -###Locate +### Locate ###### [Style [Y141](#style-y141)] - 更直观、更简单、更快捷地定位代码 @@ -2081,21 +2079,21 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 .bower.json ``` -###Identify +### Identify ###### [Style [Y142](#style-y142)] - + - 当你看到一个文件时你应该能够立即知道它包含了什么、代表了什么。 *为什么?*:你花费更少的时间来了解代码代表了什么,并且变得更加高效。如果这意味着你需要更长的名字,那么就这么干吧。文件名一定要具有描述性,保持和文件内容互为一体。避免文件中有多个controller,多个service,甚至是混合的。 -###Flat +### Flat ###### [Style [Y143](#style-y143)] - 尽可能长时间地保持一个平直的文件夹结构,如果你的文件夹层级超过7+,那么就开始考虑分离。 *为什么?*:没有谁想在一个7级文件夹中寻找一个文件,你可以考虑一下网页导航栏有那么多层。文件夹结构没有硬性规则,但是当一个文件夹下的文件有7-10个,那么就是时候创建子文件夹了,文件夹的层级一定要把握好。一直使用一个平直的结构,直到确实有必要(帮助其它的LIFT)创建一个新的文件夹。 -###T-DRY(尽量坚持DRY) +### T-DRY(尽量坚持DRY) ###### [Style [Y144](#style-y144)] - 坚持DRY,但是不要疯了一样的做却牺牲了可读性。 @@ -2106,28 +2104,28 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## 应用程序结构 -###总指南 +### 总规范 ###### [Style [Y150](#style-y150)] - 有实施的短期看法和长远的目标,换句话说,从小处入手,但是要记住app的走向。app的所有代码都在一个叫做`app`的根目录下,所有的内容都遵循一个功能一个文件,每一个controller、service、module、view都是独立的文件。第三方脚本存放在另外的根文件夹中(`bower_components`、`scripts`、`lib`)。 注:了解实例结构的具体信息看[Angular应用结构](http://www.johnpapa.net/angular-app-structuring-guidelines/)。 -###Layout +### Layout ###### [Style [Y151](#style-y151)] - 把定义应用程序总体布局的组件放到`layout`文件夹中,如导航、内容区等等。 *为什么?*:复用。 - -###按功能划分文件夹结构 + +### 按功能划分文件夹结构 ###### [Style [Y152](#style-y152)] - 按照它们代表的功能来给创建的文件夹命名,当文件夹包含的文件超过7个(根据需要自行调整数量限制),就考虑新建文件夹。 - *为什么?*:开发者可以快速定位代码、快速识别文件代表的意思,结构尽可能平直,没有重复,没有多余名字。 + *为什么?*:开发者可以快速定位代码、快速识别文件代表的意思,结构尽可能平直,没有重复,没有多余名字。 - *为什么?*:LIFT指南都包括在内。 + *为什么?*:LIFT规范都包括在内。 *为什么?*:通过组织内容和让它们保持和LIFT指导准则一致,帮助降低应用程序变得混乱的可能性。 @@ -2141,21 +2139,21 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 app/ app.module.js app.config.js - directives/ - calendar.directive.js - calendar.directive.html - user-profile.directive.js - user-profile.directive.html - services/ - dataservice.js + directives/ + calendar.directive.js + calendar.directive.html + user-profile.directive.js + user-profile.directive.html + services/ + dataservice.js localstorage.js - logger.js + logger.js spinner.js layout/ - shell.html + shell.html shell.controller.js - topnav.html - topnav.controller.js + topnav.html + topnav.controller.js people/ attendees.html attendees.controller.js @@ -2165,69 +2163,69 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 speaker-detail.html speaker-detail.controller.js sessions/ - sessions.html + sessions.html sessions.controller.js sessions.routes.js session-detail.html - session-detail.controller.js + 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)时,这种方式将迅速变得不实用,这就使得它定位文件比按功能分文件夹的方式要困难的多。 ```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 + 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 + services/ + dataservice.js localstorage.js - logger.js - spinner.js + logger.js + spinner.js views/ - attendees.html + attendees.html session-detail.html - sessions.html - shell.html - speakers.html + sessions.html + shell.html + speakers.html speaker-detail.html - topnav.html - ``` - + topnav.html + ``` + **[返回顶部](#目录)** ## 模块化 - -###许多小的、独立的模块 + +### 许多小的、独立的模块 ###### [Style [Y160](#style-y160)] - 创建只封装一个职责的小模块。 *为什么?*:模块化的应用程序很容易添加新的功能。 -###创建一个App Module +### 创建一个App Module ###### [Style [Y161](#style-y161)] - 创建一个应用程序的根模块,它的职责是把应用程序中所有的模块和功能都放到一起。 @@ -2236,14 +2234,14 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 *为什么?*:应用程序模块变成了一个描述哪些模块有助于定义应用程序的清单。 -###保持App Module的精简 +### 保持App Module的精简 ###### [Style [Y162](#style-y162)] - app module中只放聚合其它模块的逻辑,具体功能在它们自己的模块中实现。 *为什么?*:添加额外的代码(获取数据、展现视图、其它和聚合模块无关的代码)到app module中使app module变得很糟糕,也使得模块难以重用和关闭。 -###功能区域就是模块 +### 功能区域就是模块 ###### [Style [Y163](#style-y163)] - 创建代表功能区的模块,例如布局、可重用、共享服务、仪表盘和app的特殊功能(例如客户、管理、销售)。 @@ -2254,19 +2252,19 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 *为什么?*:把功能拆分成不同模块方便测试。 -###可重用的块就是模块 +### 可重用的块就是模块 ###### [Style [Y164](#style-y164)] - 为通用service创建代表可重用的应用程序块的模块,例如异常处理、日志记录、诊断、安全性和本地数据储藏等模块。 *为什么?*:这些类型的功能在很多应用程序中都需要用到,所以把它们分离到自己的模块中,它们可以变成通用的应用程序,也能被跨应用地进行重用。 - -###模块依赖 + +### 模块依赖 ###### [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) *为什么?*:主程序模块包含一个能快速识别应用程序功能的清单。 @@ -2296,9 +2294,9 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 .module('app') .config(configure); - configure.$inject = + configure.$inject = ['routerHelperProvider', 'exceptionHandlerProvider', 'toastr']; - + function configure (routerHelperProvider, exceptionHandlerProvider, toastr) { exceptionHandlerProvider.configure(config.appErrorPrefix); configureStateHelper(); @@ -2338,16 +2336,16 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 **[返回顶部](#目录)** -##Angular $包装服务 +## Angular $包装服务 -###$document和$window +### $document和$window ###### [Style [Y180](#style-y180)] - 用[`$document`](https://docs.angularjs.org/api/ng/service/$document)和[`$window`](https://docs.angularjs.org/api/ng/service/$window)代替`document`和`window`。 *为什么?*:使用内部包装服务将更容易测试,也避免了你自己去模拟document和window。 -###$timeout和$interval +### $timeout和$interval ###### [Style [Y181](#style-y181)] - 用[`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout)和[`$interval`](https://docs.angularjs.org/api/ng/service/$interval)代替`setTimeout`和`setInterval` 。 @@ -2359,7 +2357,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## 测试 单元测试有助于保持代码的清晰,因此我加入一些关于单元测试的基础和获取更多信息的链接。 -###用故事来编写测试 +### 用故事来编写测试 ###### [Style [Y190](#style-y190)] - 给每一个故事都写一组测试,先创建一个空的测试,然后用你给这个故事写的代码来填充它。 @@ -2386,7 +2384,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 // and so on ``` -###测试库 +### 测试库 ###### [Style [Y191](#style-y191)] - 用[Jasmine](http://jasmine.github.io/)或者[Mocha](http://mochajs.org)进行单元测试。 @@ -2395,7 +2393,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 注意:使用Mocha时你可以考虑选择一个类似[Chai](http://chaijs.com)的提示库。 -###测试运行器 +### 测试运行器 ###### [Style [Y192](#style-y192)] - [Karma](http://karma-runner.github.io)。 @@ -2408,7 +2406,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 *为什么?*:Karma可以很好的和自动化任务工具如[Grunt](http://www.gruntjs.com)(带有[grunt-karma](https://github.com/karma-runner/grunt-karma))和[Gulp](http://www.gulpjs.com)(带有[gulp-karma](https://github.com/lazd/gulp-karma))合作。 -###Stubbing和Spying +### Stubbing和Spying ###### [Style [Y193](#style-y193)] - 用[Sinon](http://sinonjs.org/)。 @@ -2419,23 +2417,23 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 *为什么?*:测试失败Sinon有一个具有描述性的信息。 -###Headless Browser +### Headless Browser ###### [Style [Y194](#style-y194)] - 在服务器上使用[PhantomJS](http://phantomjs.org/)来运行你的测试。 - *为什么?*:PhantomJS是一个headless browser,无需一个“可视”的浏览器来帮助你运行测试。因此你的服务器上不需要安装Chrome、Safari、IE或是其它浏览器。 + *为什么?*:PhantomJS是一个headless browser,无需一个“可视”的浏览器来帮助你运行测试。因此你的服务器上不需要安装Chrome、Safari、IE或是其它浏览器。 注意:你仍然需要在你的环境下测试所有浏览器,来满足用户的需求。 -###代码分析 +### 代码分析 ###### [Style [Y195](#style-y195)] -在你的测试上运行JSHint。 *为什么?*:测试也是代码,JSHint能够帮你识别代码中可能导致测试无法正常工作的的质量问题。 -###对测试降低全局JSHint规则 +### 对测试降低全局JSHint规则 ###### [Style [Y196](#style-y196) - 对你的测试代码放宽规则,这样可以允许使用`describe`和`expect`等类似通用的全局方法。对表达式放宽规则,就行Mocha一样。 @@ -2452,12 +2450,12 @@ 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)] - - 把单元测试的文件放到一个独立的`tests`文件夹中,它和你的客户端代码并列。 + - 将单元测试文件(specs)同被测试客户端代码并列放在同一个文件夹下,将多个组件共用的测试文件以及服务器集成测试的文件放到一个单独的`tests`文件夹下。 *为什么?*:单元测试和源代码中的每一个组件和文件都有直接的相关性。 @@ -2474,8 +2472,8 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ``` /src/client/app/customers/customer-detail.controller.js /customer-detail.controller.spec.js + /customers.controller.js /customers.controller.spec.js - /customers.controller-detail.spec.js /customers.module.js /customers.route.js /customers.route.spec.js @@ -2485,7 +2483,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## 动画 -###用法 +### 用法 ###### [Style [Y210](#style-y210)] - 在页面过渡时使用[Angular动画](https://docs.angularjs.org/guide/animations),包括[ngAnimate模块](https://docs.angularjs.org/api/ngAnimate)。三个关键点是细微、平滑、无缝。 @@ -2494,14 +2492,14 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 *为什么?*:当视图过渡时,微小的动画可以提高感知性。 -###Sub Second +### Sub Second ###### [Style [Y211](#style-y211)] - - 使用短持续性的动画,我一般使用300ms,然后调整到合适的时间。 + - 使用短持续性的动画,我一般使用300ms,然后调整到合适的时间。 *为什么?*:长时间的动画容易造成用户认为程序性能太差的影响。 -###animate.css +### animate.css ###### [Style [Y212](#style-y212)] - 传统动画使用[animate.css](http://daneden.github.io/animate.css/)。 @@ -2553,7 +2551,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 /** * @name logError * @desc Logs errors - * @param {String} msg Message to log + * @param {String} msg Message to log * @returns {String} * @memberOf Factories.Logger */ @@ -2570,12 +2568,12 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## JS Hint -###使用一个Options文件 +### 使用一个Options文件 ###### [Style [Y230](#style-y230)] - 用JS Hint来分析你的JavaScript代码,确保你自定义了JS Hint选项文件并且包含在源控制里。详细信息:[JS Hint文档](http://www.jshint.com/docs/)。 - *为什么?*:提交代码到原版本之前先发出警告。 + *为什么?*:提交代码到原版本之前先发出警告。 *为什么?*:统一性。 @@ -2650,7 +2648,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ### 用一个Options文件 ###### [Style [Y235](#style-y235)] - - 使用JSCS检查代码风格,确保你的代码控制中有定制的JSCS options文件,在这里[JSCS docs](http://www.jscs.info)查看更多信息。 + - 使用JSCS检查代码规范,确保你的代码控制中有定制的JSCS options文件,在这里[JSCS docs](http://www.jscs.info)查看更多信息。 *为什么?*:提交代码前第一时间提供一个预警。 @@ -2735,7 +2733,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## 常量 -###供应全局变量 +### 供应全局变量 ###### [Style [Y240](#style-y240)] - 为供应库中的全局变量创建一个Angular常量。 @@ -2784,16 +2782,16 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 **[返回顶部](#目录)** ## 文件模板和片段 -使用文件模板和片段帮助保持一致性的风格,这里有针对一些web开发的编辑器和IDE的模板和(或)片段。 +为了遵循一致的规范和模式,使用文件模板和片段,这里有针对一些web开发的编辑器和IDE的模板和(或)片段。 -###Sublime Text +### Sublime Text ###### [Style [Y250](#style-y250)] - - Angular片段遵循这些风格指南。 + - Angular片段遵循这些规范。 - - 下载[Sublime Angular snippets](assets/sublime-angular-snippets?raw=true) + - 下载[Sublime Angular snippets](assets/sublime-angular-snippets?raw=true) - 把它放到Packages文件夹中 - - 重启Sublime + - 重启Sublime - 在JavaScript文件中输入下面的命令然后按下`TAB`键即可: ```javascript @@ -2805,26 +2803,26 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ngfilter // creates an Angular filter ``` -###Visual Studio +### Visual Studio ###### [Style [Y251](#style-y251)] - - Angular文件遵循[SideWaffle](http://www.sidewaffle.com)所介绍的风格指南。 + - Angular文件遵循[SideWaffle](http://www.sidewaffle.com)所介绍的规范。 - 下载Visual Studio扩展文件[SideWaffle](http://www.sidewaffle.com) - 运行下载的vsix文件 - 重启Visual Studio -###WebStorm +### WebStorm ###### [Style [Y252](#style-y252)] - 你可以把它们导入到WebStorm设置中: - - 下载[WebStorm Angular file templates and snippets](assets/webstorm-angular-file-template.settings.jar?raw=true) + - 下载[WebStorm Angular file templates and snippets](https://github.com/johnpapa/angular-styleguide/blob/master/assets/webstorm-angular-file-template.settings.jar?raw=true) - 打开WebStorm点击`File`菜单 - 选择`Import Settings`菜单选项 - 选择文件点击`OK` - 在JavaScript文件中输入下面的命令然后按下`TAB`键即可: - + ```javascript ng-c // creates an Angular controller ng-f // creates an Angular factory @@ -2834,7 +2832,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ### Atom ###### [Style [Y253](#style-y253)] - - Angular片段遵循以下指南。 + - Angular片段遵循以下规范。 ``` apm install angularjs-styleguide-snippets ``` @@ -2857,7 +2855,7 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ### Brackets ###### [Style [Y254](#style-y254)] - - Angular代码片段遵循以下风格指南。 + - Angular代码片段遵循以下规范。 - 下载[Brackets Angular snippets](assets/brackets-angular-snippets.yaml?raw=true) - 拓展管理器( File > Extension manager ) @@ -2886,12 +2884,29 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ngroute // creates an Angular routeProvider ``` +### vim +###### [Style [Y255](#style-y255)] + + - vim代码片段遵循以下规范。 + + - 下载[vim Angular代码段](assets/vim-angular-snippets?raw=true) + - 设置[neosnippet.vim](https://github.com/Shougo/neosnippet.vim) + - 粘贴到snippet路径下 + + ```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 + ``` **[返回顶部](#目录)** ## Yeoman Generator ###### [Style [Y260](#style-y260)] -你可以使用[HotTowel yeoman generator](http://jpapa.me/yohottowel)来创建一个遵循本指南的Angular起步应用。 +你可以使用[HotTowel yeoman generator](http://jpapa.me/yohottowel)来创建一个遵循本规范的Angular入门应用。 1. 安装generator-hottowel @@ -2925,6 +2940,76 @@ 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)] - Define routes for views in the module where they exist,Each module should contain the routes for the views in the module. @@ -2938,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和自动化任务的信息 @@ -2977,18 +3062,18 @@ Angular社区是一个热衷于分享经验的令人难以置信的社区,尽 ## 贡献 -先打开一个问题讨论潜在的变化和增加。如果你对这篇指南有问题,随时在仓库中提出问题。如果你发现了一个错字,创建一个pull request。这样做是为了保持内容的更新,使用github的原生功能通过问题和PR来帮助讲述这个故事,具体做法可以google一下。为什么?因为如果你有问题,其他人可能有同样的问题,你在这里可以学到如何贡献。 +先打开一个问题讨论潜在的变化和增加。如果你对这篇规范有任何疑惑,随时在仓库中提出问题。如果你发现了一个错字,创建一个pull request。这样做是为了保持内容的更新,使用github的原生功能通过问题和PR来帮助讲述这个故事,具体做法可以google一下。为什么?因为如果你有问题,其他人可能有同样的问题,你在这里可以学到如何贡献。 *贡献代码到这个仓库就意味着你同意了本仓库的许可证内容* -###过程 +### 过程 1. 在Github Issue中讨论这个问题。 2. 拉取一个pull request,引用这个问题,解释你做的修改和为什么要这样做。 3. pull request将会被进行评估,结果就是合并或是拒绝。 -## 许可证 +## 许可 - - **tldr;** 如果可以的话,使用本指南的时候还是指明归属吧。 + - **tldr;** 如果可以的话,使用本规范的时候还是指明归属吧。 ### Copyright diff --git a/a2/README.md b/a2/README.md new file mode 100644 index 00000000..dbda1e90 --- /dev/null +++ b/a2/README.md @@ -0,0 +1,25 @@ +# 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 :) + +## 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 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. + +## Relocating the Angular 2 Style Guide +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 + +>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) + +**[Back to top](#table-of-contents)** diff --git a/a2/notes.md b/a2/notes.md new file mode 100644 index 00000000..393606b5 --- /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 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 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. + +## 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)** diff --git a/assets/above-the-fold-1.png b/assets/above-the-fold-1.png deleted file mode 100644 index 71b97780..00000000 Binary files a/assets/above-the-fold-1.png and /dev/null differ diff --git a/assets/webstorm-angular-file-template.settings.jar b/assets/webstorm-angular-file-template.settings.jar deleted file mode 100644 index 38763068..00000000 Binary files a/assets/webstorm-angular-file-template.settings.jar and /dev/null differ diff --git a/i18n/README.md b/i18n/README.md deleted file mode 100644 index ca99715e..00000000 --- a/i18n/README.md +++ /dev/null @@ -1,39 +0,0 @@ -#Translations - -The [original English version](http://jpapa.me/ngstyles) is the source of truth, as it is maintained and updated first. - -*All translations are created by and maintained by the community.* - -1. [French](fr-FR.md) -2. [German](de-DE.md) -3. [Italian](it-IT.md) -4. [Japanese](ja-JP.md) -5. [Macedonian](mk-MK.md) -6. [Portuguese-Brazil](PT-BR.md) -7. [Russian](ru-RU.md) -8. [Simplified Chinese](zh-CN.md) -9. [Spanish](es-ES.md) - -## Contributing -Language translations are welcomed and encouraged. The succcess of these translations depends on the community. I highly encourage new translation contributions and help to keep them up to date. - -All translations must preserve the intention of the original document. - -> All contributions fall under the [MIT License of this repository](https://github.com/johnpapa/angularjs-styleguide#license). In other words, you would be providing these free to the community. - -### New Translations -1. Fork the repository -2. Create a translation file and name it using the 118n standard format. -3. Put this file in the i18n folder -4. Translate the original English version to be current with the latest changes -3. Make a Pull Request - -Once you do these I will merge, point the translation links to it, and enter the translation credit to you. - -### Updated Translations -1. Fork the repository -2. Make the translation changes -3. Make a Pull Request - -Once you do these I will merge, point the translation links to it, and enter the translation credit to you. - diff --git a/i18n/fr-FR.md b/i18n/fr-FR.md deleted file mode 100644 index ece93b52..00000000 --- a/i18n/fr-FR.md +++ /dev/null @@ -1,2938 +0,0 @@ -# Le Guide de Style Angular - -*Le guide d'un point de vue personnel sur le style Angular par [@john_papa](//twitter.com/john_papa)* - -*Translated by [Eric Lemerdy](https://github.com/ericlemerdy)* - ->The [original English version](http://jpapa.me/ngstyles) is the source of truth, as it is maintained and updated first. - -Si vous cherchez un guide de style pour la syntaxe, les conventions, et la structuration d'application Angular, alors vous êtes au bon endroit. Ces styles sont basés sur mon expérience de dévelopement avec [Angular](//angularjs.org), mes présentations, [mes cours sur Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) et mon travail au sein des équipes. - -Le but de ce guide de style est de proposer des conseils sur le développement d'applications Angular en montrant les conventions que j'utilise et, plus important encore, les raisons des choix que j'ai pris. - ->Si vous appréciez ce guide, visitez mon cours [Angular Patterns: Clean Code](http://jpapa.me/ngclean) sur Pluralsight. - - [![Angular Patterns: Clean Code](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/ng-clean-code-banner.png)](http://jpapa.me/ngclean) - -## Suprématie de la Communauté et Remerciements -Ne jamais travailler dans le vide. J'ai trouvé que la communauté Angular est un incroyable groupe dont les membres ont à coeur de partager leurs expériences. Ainsi, un ami et expert Angular Todd Motto et moi 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 [les guideslines de Todd](https://github.com/toddmotto/angularjs-styleguide) pour vous faire un sentiment sur son approche et en quoi elle est comparable. - -De nombreux de mes styles proviennent des maintes scéances de pair programming que [Ward Bell](http://twitter.com/wardbell) et moi avons eu. Même si nous n'étions pas toujours d'accord, mon ami Ward a assurément contribué à influencer l'évolution ultime de ce guide. - -## Visualiser les Styles dans une Application d'Exemple -Alors que ce guide explique le *quoi*, le *pourquoi* et le *comment*, il m'a été utile de les visualiser dans la pratique. Ce guide est accompagné par une application d'exemple qui suit ces styles et ces motifs. Vous pouvez trouver l'[application d'exemple (appellé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 dupliquer pour le modifier. [Les instructions pour l'éxécuter sont contenues dans ce readme](https://github.com/johnpapa/ng-demos/tree/master/modular). - -## Traductions -[Des traductions de ce guide stylistique Angular](https://github.com/johnpapa/angular-styleguide/tree/master/i18n) sont maintenues par la communauté et peuvent être trouvées ici. - -## Table des matières - - 1. [Responsabilité Unique](#responsabilité-unique) - 1. [IIFE](#iife) - 1. [Modules](#modules) - 1. [Controlleurs](#controlleurs) - 1. [Services](#services) - 1. [Factories](#factories) - 1. [Services de données](#services-de-données) - 1. [Directives](#directives) - 1. [Résolution des Promesses pour un controlleur](#résolution-des-promesses-pour-un-controlleur) - 1. [Annoter Manuellement pour l'Injection de Dépendances](#annoter-manuellement-pour-linjection-de-dépendances) - 1. [Minification et Annotation](#minification-et-annotation) - 1. [Gestion des Exceptions](#gestion-des-exceptions) - 1. [Nommage](#nommage) - 1. [Le Principe LIFT de Structuration de l'Application](#le-principe-lift-de-structuration-de-lapplication) - 1. [Structure de l'Application](#structure-de-lapplication) - 1. [Modularité](#modularité) - 1. [Logique de Démarrage](#logique-de-démarrage) - 1. [Les Services de Wrapper $ de Angular](#les-services-de-wrapper--de-angular) - 1. [Le Test](#le-test) - 1. [Animations](#animations) - 1. [Commentaires](#commentaires) - 1. [JSHint](#js-hint) - 1. [JSCS](#jscs) - 1. [Constantes](#constantes) - 1. [Templates de Fichiers et Fragments](#templates-de-fichiers-et-fragments) - 1. [Générateur Yeoman](#générateur-yeoman) - 1. [Routage](#routage) - 1. [Automatisation des Tâches](#automatisation-des-taches) - 1. [Filtres](#filtres) - 1. [Documentation Angular](#documentation-angular) - 1. [Contribuer](#contribuer) - 1. [License](#license) - -## Responsabilité Unique - -### La règle de l'unicité -###### [Style [Y001](#style-y001)] - - - Définir 1 composant par fichier. - - L'exemple suivant définit le module `app` et ses dépendances, définit un controlleur, et définit une factory le tout dans le même fichier. - - ```javascript - /* à éviter */ - angular - .module('app', ['ngRoute']) - .controller('SomeController', SomeController) - .factory('someFactory', someFactory); - - function SomeController() { } - - function someFactory() { } - ``` - - Les même composants sont maintenant séparés dans leurs propres fichiers. - - ```javascript - /* recommandé */ - - // app.module.js - angular - .module('app', ['ngRoute']); - ``` - - ```javascript - /* recommandé */ - - // someController.js - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommandé */ - - // someFactory.js - angular - .module('app') - .factory('someFactory', someFactory); - - function someFactory() { } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## IIFE -### Les Closures JavaScript -###### [Style [Y010](#style-y010)] - - - Encapsuler les composants Angular dans une Immediately Invoked Function Expression (IIFE) ou Expression de Fonction Immédiatement Invoquée. - - *Pourquoi ?* : Une IIFE supprime les variables du scope global. Cela aide à éviter que les déclarations de variables et de fonctions ne vivent plus longtemps qu'attendu dans le scope global, ce qui aide aussi à éviter les collisions de variables. - - *Pourquoi ?* : Lorsque votre code est minifié et embarqué dans un unique fichier pour le déploiement dans un serveur de production, vous pouvez avoir des collisions de variables et de nombreuses variables globales. Une IIFE protège contre ces derniers en fournissant un scope de variable pour chaque fichier. - - ```javascript - /* à éviter */ - // logger.js - angular - .module('app') - .factory('logger', logger); - - // la fonction de logger est ajoutée en tant que variable globale - function logger() { } - - // storage.js - angular - .module('app') - .factory('storage', storage); - - // la fonction storage est ajoutée en tant que variable globale - function storage() { } - ``` - - ```javascript - /** - * recommandé - * - * plus aucune variable globale ne reste après. - */ - - // 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 : Pour des raisons de concision seulement, le reste des examples de ce guide peuvent avoir omis la syntaxe IIFE. - - - Note : Les IIFE empêchent le code code de test d'atteindre des membres privés comme des expressions régulières ou des fonctions helper ce qui est bon pour tester unitairement directement indépendamment. Cependant, vous pouvez les tester à travers des membres accessibles ou en les exposant à travers leur propre composant. Par exemple en plaçant des fonctions helper, des expressions régulières ou des constantes dans leur propre factory ou contante. - -**[Retour en haut de page](#table-des-matières)** - -## Modules - -### Éviter les Collisions de Nommage -###### [Style [Y020](#style-y020)] - - - Utilisez des conventions de nommages uniques avec des séparations pour les sous-modules. - - *Pourquoi ?* : Les noms uniques aident à éviter les collisions de nom de module. Les séparateurs aident à définir les modules et leur hiérarchie de sous-modules. Par exemple, `app` pourrait être le module racine tandis que `app.dashboard` et `app.users` serait les modules qui sont utilisés en tant que dépendances de `app`. - -### Définitions (i.e. Setters) -###### [Style [Y021](#style-y021)] - - - Déclarer des modules sans variable en utilisant la syntaxe setter. - - *Pourquoi ?* : Avec 1 composant par fichier, on ne devrait pas avoir besoin d'introduire une variable pour le module. - - ```javascript - /* à éviter */ - var app = angular.module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - - Utilisez à la place la syntaxe setter simple. - - ```javascript - /* recommandé */ - angular - .module('app', [ - 'ngAnimate', - 'ngRoute', - 'app.shared', - 'app.dashboard' - ]); - ``` - -### Getters -###### [Style [Y022](#style-y022)] - - - Lorsque vous utilisez un module, évitez d'utiliser une variable en utilisant plutôt le chaînage avec la syntaxe du getter. - - *Pourquoi ?* : Cela produit du code plus lisible et évite les collisions de variable ou les fuites. - - ```javascript - /* à éviter */ - var app = angular.module('app'); - app.controller('SomeController', SomeController); - - function SomeController() { } - ``` - - ```javascript - /* recommandé */ - angular - .module('app') - .controller('SomeController', SomeController); - - function SomeController() { } - ``` - -### Setting ou Getting -###### [Style [Y023](#style-y023)] - - - Ne settez qu'une fois et gettez pour toutes les autres instances. - - *Pourquoi ?* : Un module ne devrait être créé qu'une seule fois, et ensuite récupéré à partir de ce point et après. - - - Utilisez `angular.module('app', []);` pour setter un module. - - Utilisez `angular.module('app');` pour getter un module. - -### Fonctions Nommées ou Anonymes -###### [Style [Y024](#style-y024)] - - - Utilisez des fonctions nommées au lieu de passer une fonction anonyme comme callback. - - *Pourquoi ?* : Celà produit du code plus lisible, est plus facile à débugguer, et réduit la quantité de code callback imbriqué. - - ```javascript - /* à éviter */ - angular - .module('app') - .controller('Dashboard', function() { }) - .factory('logger', function() { }); - ``` - - ```javascript - /* recommendé */ - - // dashboard.js - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard() { } - ``` - - ```javascript - // logger.js - angular - .module('app') - .factory('logger', logger); - - function logger() { } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Controlleurs - -### La Syntaxe Vue controllerAs -###### [Style [Y030](#style-y030)] - - - Utilisez la syntaxe [`controllerAs`](http://www.johnpapa.net/do-you-like-your-angular-controllers-with-or-without-sugar/) au lieu de la syntaxe de `controlleur classique avec $scope`. - - *Pourquoi ?* : Les controlleurs sont construits, recréés, et fournissent une unique nouvelle instance, et la syntaxe `controllerAs` est plus proche de celle d'un contructeur Javascript que la `syntaxe $scope classique`. - - *Pourquoi ?* : Il encourage l'usage du binding entre un objet avec "point" et la Vue (ex. `customer.name` au lieu de `name`), ce qui est plus contextuel, plus facile à lire, et évite tout problème de référence qui peut arriver sans "point". - - *Pourquoi ?* : Permet d'éviter l'usage des appels à `$parent` dans les Vues avec des controlleurs imbriqués. - - ```html - -
- {{ name }} -
- ``` - - ```html - -
- {{ customer.name }} -
- ``` - -### La Syntaxe de Controlleur controllerAs -###### [Style [Y031](#style-y031)] - - - Utilisez la syntaxe `controllerAs` au lieu de la syntaxe de `controlleur classique avec $scope`. - - - La syntaxe `controllerAs` utilise `this` à l'intérieur des controlleurs qui se font relier au `$scope`. - - *Pourquoi ?* : `controllerAs` est un sucre syntaxique sur le `$scope`. Vous pouvez toujours vous relier à la Vue et toujours accéder aux métodes du `$scope`. - - *Pourquoi ?* : Permet d'éviter la tentation d'utiliser les méthodes du `$scope` à l'intérieur d'un controlleur alors qu'il serait par ailleurs meilleur de les éviter ou de les déplacer dans une factory. Considérez utiliser le `$scope` dans une factory, ou seulement si nécessaire dans un controlleur. Par exemple lorsqu'il faut publier ou souscrire des événements en utilisant [`$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) considérez déplacer ces usages dans une factory et les invoquer depuis le controlleur. - - ```javascript - /* à éviter */ - function Customer($scope) { - $scope.name = {}; - $scope.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommandé - mais voir la section suivante */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - -### controllerAs avec vm -###### [Style [Y032](#style-y032)] - - - Utilisez une variable de capture pour `this` quand vous utilisez la syntaxe `controllerAs`. Choisissez un nom de variable consistant tel que `vm`, qui signifie ViewModel. - - *Pourquoi ?* : Le mot clé `this` est contextuel et son utilisation au sein d'une fonction à l'intérieur d'un controlleur pourrait changer son contexte. Capturer le contexte de `this` évite de rencontrer ce problème. - - ```javascript - /* à éviter */ - function Customer() { - this.name = {}; - this.sendMessage = function() { }; - } - ``` - - ```javascript - /* recommandé */ - function Customer() { - var vm = this; - vm.name = {}; - vm.sendMessage = function() { }; - } - ``` - - Note : Vous pouvez évitez n'importe quel avertissement [jshint](http://www.jshint.com/) en plaçant le commentaire ci-dessous au dessus de la ligne de code. Cependant, ce n'est pas nécesaire lorsque la fonction est nommée en utilisant la CasseEnMajuscule, puisque cette convention signifie que c'est une fonction constructeur, ce qu'est un controlleur en Angular. - - ```javascript - /* jshint validthis: true */ - var vm = this; - ``` - - Note : Lors de la création de watchs dans un controlleur en utilisant `controller as`, vous pouvez watcher les membres `vm.*` en utilisant la syntaxe suivante. (Créez des watchs avec prudence puisqu'ils ajoutent plus de charge au cycle 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 was %s', original); - $log.info('vm.title is now %s', current); - }); - } - ``` - -### Les Membres Bindable au Début -###### [Style [Y033](#style-y033)] - - - Placez les membres bindables au début du controlleur, par ordre alphabétique, et non pas dispersés à travers le code du controlleur. - - *Pourquoi ?* : Placer les membres bindables au début permet de faciliter la lecture et vous aide à identifier instantanément quels membres du controlleur peut être bindé et utilisés dans la Vue. - - *Pourquoi ?* : Placer les fonction anonymes sur la même ligne peut être facile, mais lorsque ces fonctions ont plus d'une ligne de code elles peuvent réduire la lisibilité. Définir les fonctions sous les membres bindables (les fonctions seront hissées) déplace les détails d'implémentation en bas, guardant les membres bindables en haut, - 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 Sessions() { - var vm = this; - - vm.gotoSession = function() { - /* ... */ - }; - vm.refresh = function() { - /* ... */ - }; - vm.search = function() { - /* ... */ - }; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - - ```javascript - /* recommended */ - 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() { - /* */ - } - ``` - - ![Les Controlleur Utilisant "Au dessus de la Réduction"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-1.png) - - Note : Si la fonction est un one-liner, considérez de la garder bien en haut, tant que la lisibilité n'est pas affectée. - - ```javascript - /* à éviter */ - 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 - /* recommandé */ - function Sessions(dataservice) { - var vm = this; - - vm.gotoSession = gotoSession; - vm.refresh = dataservice.refresh; // Le one-liner est acceptable - vm.search = search; - vm.sessions = []; - vm.title = 'Sessions'; - ``` - -### Déclaration de Fonctions pour Cacher les Détails d'Implémentation -###### [Style [Y034](#style-y034)] - - - Utilisez les déclarations de fonctions pour cacher les détails d'implémentation. Gardez vos membres bindables tout en haut. Quand vous avez besoin de binder une fonction dans un controlleur, faites-la pointer vers la déclaration de la fonction plus bas dans le fichier. Ceci est directement lié à la section des Membres Bindable au Début. Pour plus de détails, vous pouvez lire [cet article](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *Pourquoi ?* : Placer les membres bindables en haut rend plus facile la lecture et vous aide instantanément à identifier quels membres du controlleur peuvent être bindés et utilisés dans la Vue. (Même chose que plus haut.) - - *Pourquoi ?* : Placer les détails d'implémentation d'une fonction plus bas dans le fichier déplace cette complexité en dehors du regard ainsi vous pouvez ne voir que les choses importantes en haut. - - *Pourquoi ?* : Les déclarations de fichiers sont remontées donc il n'y a aucun problème à utiliser une fonction avant qu'elle ne soit définie (alors que ça le serait avec les expressions de fonction). - - *Pourquoi ?* : Vous ne vous préocuperez plus des déclarations de fonctions déplaçant `var a` avant `var b` cassant ainsi votre code car `a` dépend de `b`. - - *Pourquoi ?* : L'ordre est critique avec les expressions de fonction - - ```javascript - /** - * à éviter - * L'utilisation des expressions de fonction. - */ - function Avengers(dataservice, 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 dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - - vm.getAvengers = getAvengers; - - activate(); - } - ``` - - Remarquez que les choses importantes sont dispersées dans l'exemple précédent. Dans l'exemple ci-dessous, remarquez que les choses importantes sont tout en haut. Par exemple, les membres bindés au controlleur tels que `vm.avengers` et `vm.title`. Les détails d'implémentation sont plus bas dessous. C'est simplement plus facile à lire. - - ```javascript - /* - * recommandé - * L'utilisation des déclarations de fonction - * et les membres bindables tout en haut. - */ - function Avengers(dataservice, 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 dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - } - ``` - -### Déplacer la Logique des Controlleurs -###### [Style [Y035](#style-y035)] - - - Déplacer la logique d'un controlleur en la déléguant à des services et des factories. - - *Pourquoi ?* : La logique peut être ré-utilisée par plusieurs controlleurs lorsqu'elle est placée au sein d'un service et exposée via une fonction. - - *Pourquoi ?* : La logique d'un service peut plus facilement être isolée dans un test unitaire, tandis que la logique d'appel dans le controlleur peut facilement être mockée. - - *Pourquoi ?* : Cela supprime des dépendances et cache les détails d'implémentation au controlleur. - - ```javascript - - /* à éviter */ - function Order($http, $q, config, userInfo) { - var vm = this; - vm.checkCredit = checkCredit; - vm.isCreditOk; - vm.total = 0; - - function checkCredit() { - var settings = {}; - // Obtenir l'URL de base du service credit à partir de la config - // Positionne les headers requis pour le service credit - // Prépare l'URL query string ou l'objet de données avec les données de requête. - // Ajoute les infos d'identification de l'utilsiateur afin que le service obtienne - // les bons droits de limite credit pour cet utilisateur. - // Utilise JSONP pour ce navigateur s'il ne supporte pas CORS - return $http.get(settings) - .then(function(data) { - // Décompresse les données JSON dans l'objet réponse - // afin de rechercher maxRemainingAmount - vm.isCreditOk = vm.total <= maxRemainingAmount - }) - .catch(function(error) { - // Interpréter l'erreur - // Gérer le timeout ? Réessayer ? Essayer un service alternatif ? - // Re-rejetter avec une erreur appropriée à la vue de l'utilisateur - }); - }; - } - ``` - - ```javascript - /* recommandé */ - 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(showServiceError); - }; - } - ``` - -### Gardez des Controlleurs Focalisés -###### [Style [Y037](#style-y037)] - - - Définir un controlleur pour une vue, et n'essayez pas de ré-utiliser le controlleur pour d'autres vues. Au lieu de cela, déplacez la logique réutilisable vers des factories et gardez le controlleur simple et focalisé sur sa vue. - - *Pourquoi ?*: La réutilisation de controlleurs sur plusieurs vues est fragile et une bonne couverture de tests de bout en bout ("end to end" ou "e2e") est requise afin d'assurer la stabilité dans les grosses applications. - -### Assigner les Controlleurs -###### [Style [Y038](#style-y038)] - - - Lorsqu'un controlleur doit être associé à une vue et qu'un composant pourrait être ré-utilisé par d'autres controlleurs ou vues, définissez les controlleurs avec leurs routes. - - Note : Si une Vue est chargée via d'autres moyens que la route, alors utilisez la syntaxe `ng-controller="Avengers as vm"`. - - *Pourquoi ?* : Associer le controlleur dans la route permet que différentes routes invoquent d'autres paires de controlleurs et vues. Lorsque les controlleurs sont assignés dans la vue avec [`ng-controller`](https://docs.angularjs.org/api/ng/directive/ngController), cette vue est toujours associée avec le même controlleur. - - ```javascript - /* à éviter - lorsque l'utilisation avec une route et une association dynamique est voulue */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html' - }); - } - ``` - - ```html - -
-
- ``` - - ```javascript - /* recommandé */ - - // route-config.js - angular - .module('app') - .config(config); - - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm' - }); - } - ``` - - ```html - -
-
- ``` - -**[Retour en haut de page](#table-des-matières)** - -## Services - -### Singletons -###### [Style [Y040](#style-y040)] - - - Les Services sont instanciés avec le mot clé `new`, utilisez `this` pour les méthodes publiques et les variables. Puisque ces derniers sont tellement similaires aux factories, utilisez à la place une factory pour la cohérence. - - Note : [Tous les serices Angular services sont des singletons](https://docs.angularjs.org/guide/services). Celà signifie qu'il n'y a qu'une seule instance d'un service donné par injecteur. - - ```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) { - /* */ - } - }; - } - ``` - -**[Retour en faut de page](#table-des-matières)** - -## Factories - -### Responsabilité Unique -###### [Style [Y050](#style-y050)] - - - Les factories ne devraient avoir qu'une [unique responsabilité](http://en.wikipedia.org/wiki/Single_responsibility_principle), c'est-à-dire encapsulé par son contexte. Une fois qu'une factory commence à dépasser ce but unique, une nouvelle factory devrait être créée. - -### Singletons -###### [Style [Y051](#style-y051)] - - - Les factories sont des singletons et renvoient un objet qui contient les membres du service. - - Note : [Tous les services Angular sont des singletons](https://docs.angularjs.org/guide/services). - -### Membres Accessibles Tout en Haut -###### [Style [Y052](#style-y052)] - - - Exposer les membres appellables du services (son interface) en haut, utilisant une technique dérivée du Principe du Module Révélateur ou [Revealing Module Pattern](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript). - - *Pourquoi ?* : Placer les membres appellables tout en haut le rend facile à lire et vous aide à identifier instantanément quels membres du service peut être appellé et doit être testé unitairement (et/ou mocké). - - *Pourquoi ?* : Ceci est spécialement conseillé lorsque le fichier devient un peu long et celà aide à éviter le besoin de faire défiler pour voir ce qui est exposé. - - *Pourquoi ?* : Placer les fonctions au fil de l'écriture semble être facile, mais quand ces fonctions ont plus d'une ligne de code, elles peuvent réduire la lisibilité et causer plus de défilement. Définir l'interface à appeller via le service renvoyé déplace les détails d'implémentation plus bas, garde l'interface appellante tout en haut, et rend le tout plus facile à lire. - - ```javascript - /* à éviter */ - function dataService() { - var someValue = ''; - function save() { - /* */ - }; - function validate() { - /* */ - }; - - return { - save: save, - someValue: someValue, - validate: validate - }; - } - ``` - - ```javascript - /* recommandé */ - function dataService() { - var someValue = ''; - var service = { - save: save, - someValue: someValue, - validate: validate - }; - return service; - - //////////// - - function save() { - /* */ - }; - - function validate() { - /* */ - }; - } - ``` - - De cette façon, les bindings sont dupliqués à travers l'objet hôte, les valeurs primitives ne peuvant se mettre à jour toutes seules grâce au principe du module révélateur. - - ![Factories Utilisants "Au dessus du Pliage"](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/above-the-fold-2.png) - -### Déclaration de Fonctions pour Cacher les Détails d'Implémentation -###### [Style [Y053](#style-y053)] - - - Utilisez les déclarations de fonction pour cacher les détails d'implémentation. Gardez les membres accessibles de la factory tout en haut. Faites-les pointer vers les déclarations de fonction qui apparaissent plus loin dans le fichier. Pour plus de détails, voir [ce post](http://www.johnpapa.net/angular-function-declarations-function-expressions-and-readable-code). - - *Pourquoi ?* : Placer les membres accessibles tout en haut permet de le rendre facile à lire et vous aide à identifier instantanément quelles fonctions de la factory vous pouvez accéder de l'extérieur. - - *Pourquoi ?* : Placer les détails d'implémentation d'une fonction plus loin dans le fichier déplace cette complexité en dehors de la vue afin de voir les choses importantes tout en haut. - - *Pourquoi ?* : Les déclarations de fonctions sont hissées de telle sorte qu'il n'y ait aucun soucis à utiliser une fonction avant qu'elle ne soit définie (comme il serait de mise avec les expressions fonctionnelles). - - *Pourquoi ?* : Vous n'aurez plus jamais à vous en faire avec les déclarations de fonction dont le déplacement de `var a` avant `var b` pourrait casser votre code à cause d'une dépendance de `a` vers `b`. - - *Pourquoi ?* : L'ordre est critique avec les expressions fonctionnelles. - - ```javascript - /** - * À éviter - * L'utilisation des expressions fonctionnelles - */ - function dataservice($http, $location, $q, exception, logger) { - var isPrimed = false; - var primePromise; - - var getAvengers = function() { - // avec les détails d'implémentation ici - }; - - var getAvengerCount = function() { - // avec les détails d'implémentation ici - }; - - var getAvengersCast = function() { - // avec les détails d'implémentation ici - }; - - var prime = function() { - // avec les détails d'implémentation ici - }; - - var ready = function(nextPromises) { - // avec les détails d'implémentation ici - }; - - var service = { - getAvengersCast: getAvengersCast, - getAvengerCount: getAvengerCount, - getAvengers: getAvengers, - ready: ready - }; - - return service; - } - ``` - - ```javascript - /** - * recommandé - * L'utilisation des déclararions de fonction - * et des membres accessibles tout en haut. - */ - 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() { - // avec les détails d'implémentation ici - } - - function getAvengerCount() { - // avec les détails d'implémentation ici - } - - function getAvengersCast() { - // avec les détails d'implémentation ici - } - - function prime() { - // avec les détails d'implémentation ici - } - - function ready(nextPromises) { - // avec les détails d'implémentation ici - } - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Services de Données - -### Séparer les Appels de Données -###### [Style [Y060](#style-y060)] - - - Refactorer la logique pour faire les opérations sur les données et les interactions avec la donnée dans une factory. Rendez les services de données responsables des appels ajax, du local storage, du stockage en mémoire, ou toute autre opérations sur les données. - - *Pourquoi ?* : La responsabilité du controlleur est la présentation et l'assemblage des informations pour la vue. Il ne devrait pas se soucier de la façon dont la donnée est récupérée, mais seulement de la façon de la demander. Séparer des services de données déplace la logique du 'comment récupérer une donnée' dans ce service de donnée, et laisse le controlleur plus simple et plus focalisé sur la vue. - - *Pourquoi ?* : Cela le rend plus facile à tester (en mockant ou avec le vrai) les appels aux données lorsque l'on teste un controlleur qui utilise un service de données. - - *Pourquoi ?* : L'implémentation d'un service de données peut avoir du code très spécifique pour gérer le référentiel des données. Celà peut inclure des entêtes, la façon de dialoguer avec la donnée, ou des dépendances vers d'autres services tels que $http. La séparation de la logique vers un service de données encapsule cette logique dans un unique endroit en cachant les détails d'implémentation du consommateur externe (peut-être un controlleur), en rendant également plus simple les changements d'implémentation. - - ```javascript - /* Recommandé */ - - // une factory service de données - 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 : Le service de données est appellé depuis des consommateurs, tels que des controlleurs, en leur cachant l'implémentation, comme le montre l'éxemple ci-dessous. - - ```javascript - /* Recommandé */ - - // un controlleur qui appelle la factory du service de données - 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; - }); - } - } - ``` - -### Retourner une promesse depuis un appel de donnée -###### [Style [Y061](#style-y061)] - - - Lorsqu'un service de données retourne une promesse telle que $http, retournez une promesse dans votre fonction appelée. - - *Pourquoi ?* : Vous pouvez chainer les promesses entre elles et ajouter des actions après que l'appel des données soit terminé puis résoudre ou rejeter la promesse. - - ```javascript - /* recommandé */ - - activate(); - - function activate() { - /** - * Etape 1 - * Appel la fonction getAvengers pour récupérer - * les données avenger et attend la promesse - */ - return getAvengers().then(function() { - /** - * Etape 4 - * Exécute une action à la résolution de la promesse finale - */ - logger.info('Activated Avengers View'); - }); - } - - function getAvengers() { - /** - * Etape 2 - * Appel du service de données pour récupérer les données - * et attend la promesse - */ - return dataservice.getAvengers() - .then(function(data) { - /** - * Etape 3 - * Défini les donnée et résoue la promesse - */ - vm.avengers = data; - return vm.avengers; - }); - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Directives -### Limite de 1 Par Fichier -###### [Style [Y070](#style-y070)] - - - Créer une directive par fichier. Nommer le fichier en fonction de la directive. - - *Pourquoi ?* : C'est facile de placer toutes les directives dans un fichier, mais difficile de les séparer, certains sont partagés par toute l'application, certain par modules, et certain juste par un module. - - *Pourquoi ?* : Une directive par fichier est plus facilement maintenable. - - > Note : "**Bonne pratique** : Les directives devraient pouvoir s'auto-nettoyer. Vous pouvez utiliser `element.on('$destroy', ...)` ou `scope.$on('$destroy', ...)` pour lancer une fonction de nettoyage quand une directive est enlevée" ... - Documentation d'Angular - - ```javascript - /* à éviter */ - /* directives.js */ - - angular - .module('app.widgets') - - /* directive order spécifique pour le module order */ - .directive('orderCalendarRange', orderCalendarRange) - - /* directive sales pouvant être utilisée n'importe où dans l'application sales */ - .directive('salesCustomerInfo', salesCustomerInfo) - - /* directive spinner pouvant être utilisée n'importe où dans l'application */ - .directive('sharedSpinner', sharedSpinner); - - function orderCalendarRange() { - /* détails de l'implémentation */ - } - - function salesCustomerInfo() { - /* détails de l'implémentation */ - } - - function sharedSpinner() { - /* détails de l'implémentation */ - } - ``` - - ```javascript - /* recommandé */ - /* calendarRange.directive.js */ - - /** - * @desc directive order spécifique pour le module order pour la compagnie Acme - * @example
- */ - angular - .module('sales.order') - .directive('acmeOrderCalendarRange', orderCalendarRange); - - function orderCalendarRange() { - /* détails de l'implémentation */ - } - ``` - - ```javascript - /* recommandé */ - /* customerInfo.directive.js */ - - /** - * @desc directive sales pouvant être utilisée n'importe où dans l'application sales pour la compagnie Acme - * @example
- */ - angular - .module('sales.widgets') - .directive('acmeSalesCustomerInfo', salesCustomerInfo); - - function salesCustomerInfo() { - /* détails de l'implémentation */ - } - ``` - - ```javascript - /* recommandé */ - /* spinner.directive.js */ - - /** - * @desc directive spinner pouvant être utilisée n'importe où dans l'application pour la compagnie Acme - * @example
- */ - angular - .module('shared.widgets') - .directive('acmeSharedSpinner', sharedSpinner); - - function sharedSpinner() { - /* détails de l'implémentation */ - } - ``` - - Note : Il y a plusieurs options de nommage pour les directives, puisqu'elles peuvent être utilisé par des scopes plus ou moins larges. Choisissez un nom qui rend la directive et son fichier clair et distinct. Des exemples sont définis plus bas, mais regarder la section des noms pour plus de recommandations. - -### Manipuler le DOM dans une Directive -###### [Style [Y072](#style-y072)] - - - Quand le DOM est directement manipulé, utilisez une directive. Si une alternative peut être utilisé avec le CSS pour définir le style ou les [services d'animation](https://docs.angularjs.org/api/ngAnimate), les templates Angular , [`ngShow`](https://docs.angularjs.org/api/ng/directive/ngShow) ou [`ngHide`](https://docs.angularjs.org/api/ng/directive/ngHide), mieux vaut les utiliser à la place. Par exemple, si la directive affiche ou cache un élément, utilisez ngHide/ngShow. - - *Pourquoi ?* : La manipulation du DOM peut être difficile à tester, debugger, et il y a souvent une meilleure façon de faire (e.g. CSS, animations, templates) - -### Fournir un Unique Préfixe de Directive -###### [Style [Y073](#style-y073)] - - - Definissez un préfixe de directive court, unique et descriptif tel que `acmeSalesCustomerInfo` et déclaré en HTML comme `acme-sales-customer-info`. - - *Pourquoi ?* : Le préfixe court et unique identifie le contexte et l'origine de la directive. Par exemple, un préfixe `cc-` peut indiquer que la directive fait partie de l'application CodeCamper alors que `acme-` peut indiquer une directive de la companie Acme. - - Note : Evitez `ng-` car il est réservé pour les directives Angular. Cherchez les directives largement utilisées pour éviter les conflits de nom, tel que `ion-` pour le [Framework Ionic](http://ionicframework.com/). - -### Restreindre aux Éléments et aux Attributs -###### [Style [Y074](#style-y074)] - - - Lors de la création d'une directive qui fait du sens comme élément indépendant, permettez la restriction `E` (élément personnalisé) et éventuellement la restriction `A` (attribut personnalisé). En général, s'il devrait avoir son propre contrôle, `E` est le plus indiqué. Le conseil le plus général est de permettre `EA` mais se dirige vers une implémentation en tant qu'élément lorsqu'il est indépendant et en tant qu'attribut lorsqu'il améliore un élément DOM existant. - - *Pourquoi ?* : Çela a du sens. - - *Pourquoi ?* : Même s'il est autorisé d'utiliser une directive en tant que classe, si la directive agit vraiement comme un élément, elle fait plus de sens en tant qu'élément ou au moins en tant qu'attribut. - - Note : EA est la valeur par défaut avec Angular 1.3 + - - ```html - -
- ``` - - ```javascript - /* à éviter */ - 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 - /* recommandé */ - 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 et ControllerAs -###### [Style [Y075](#style-y075)] - - - Utilisez la syntaxe `controller as` avec une directive pour être cohérent avec l'utilisation de `controller as` pour l'association de la vue et du controlleur. - - *Pourquoi ?* : Ça fait sens et ce n'est pas difficile. - - Note : La directive ci-dessous démontre une façon parmis d'autres d'utiliser le scope à l'intérieur de la fonction link et dans un controlleur de directive, par l'utilisation de controllerAs. J'ai in-liné le template que pour le mettre au même endroit. - - Note : Concernant l'injection de dépendance, voir [Identifier Manuellement les Dépendances](#manual-annotating-for-dependency-injection). - - Note : Remarquez que le controlleur de la directive est à l'extérieur de la closure de la directive. Cette façon de faire évite le problème des injections plus disponibles après le `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, - controllerAs: 'vm', - bindToController: true // parce que le scope est isolé - }; - - 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) { - // Injection du $scope seuelement pour la comparaison - 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}}
- ``` - -###### [Style [Y076](#style-y076)] - - - Utilisez `bindToController = true` lorsque vous utilisez la syntaxe `controller as` avec une directive quand vous voulez binder le scope externe au scope du controlleur de la directive. - - *Pourquoi ?* : Cela rend plus facile de binder le scope externe au scope du controlleur de la directive. - - Note : `bindToController` a été introduit à partir de 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}}
- ``` - -**[Retour en haut de page](#table-des-matières)** - -## Résolution des Promesses pour un Controlleur - -### Promesses d'Activation du Controller -###### [Style [Y080](#style-y080)] - - - Résolvez la logique de démarrage d'un controlleur dans une fonction `activate`. - - *Pourquoi ?* : Placer la logique de démarrage toujours au même endroit permet de le rendre plus facile à localiser, plus cohérent à tester, et permet d'éviter la dispersion de la logique d'activation à travers le controlleur. - - *Pourquoi ?* : La fonction `activate` d'un controlleur rend pratique la ré-utilisation de la logique pour un refraichissement du controlleur ou de la vue, garde cette logique à un seul endroit, envoie l'utilisateur plus rapidement à la Vue, rend les animations faciles sur la `ng-view` ou l'`ui-view`, et c'est rendu plus vif à l'utilisateur. - - Note : Si vous avez besoin d'annuler sous conditions la route avant de vous mettre à utiliser le controlleur, utilisez une [résolution de route](#style-y081) à la place. - - ```javascript - /* à éviter */ - function Avengers(dataservice) { - var vm = this; - vm.avengers = []; - vm.title = 'Avengers'; - - dataservice.getAvengers().then(function(data) { - vm.avengers = data; - return vm.avengers; - }); - } - ``` - - ```javascript - /* recommandé */ - 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; - }); - } - } - ``` - -### Promesses de Résolution de Route -###### [Style [Y081](#style-y081)] - - - Lorsqu'un controlleur dépend d'une promesse qui doit être résolue avant qu'un controlleur soit activé, résolvez ces dépendances dans le `$routeProvider`. Si vous avez besoin d'annuler une route sous certaines conditions avant que le controlleur soit activé, utilisez un resolver de route. - - - Utilisez un resolver de route dès lors que vous voulez décider d'annuler la route avant même de commencer à naviguer vers la Vue. - - *Pourquoi ?* : Un controlleur pourrait avoir besoin de données avant qu'il se charge. Cette donnée pourrait venir d'une promesse via une factory personnalisée ou de [$http](https://docs.angularjs.org/api/ng/service/$http). Utiliser une [resolution de route](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) permet à la promesse de se résoudre avant que la logique du controlleur s'éxécute, ainsi on peut prendre des actions basées sur cette donnée à partir de la promesse. - - *Pourquoi ?* : Le code s'éxécute après la route et dans la fonction activate du controlleur. La Vue commence à se charger tout de suite. Le data binding démarre quand la promesse d'activation se résoud. Une animation de "chargement" peut être affichée pendant que la vue opère la transition (via `ng-view` ou `ui-view`). - - Note : Le code s'éxécute avant la route via une promesse. Le rejet de la promesse annule le routage. Sa résolution met la nouvelle vue en attente de la résolution du routage. Une animation de "chargement" peut être affichée avant la résolution et lorsque la vue entre en transition. Si vous voulez aller à la Vue plus vite et que vous n'avez pas besoin d'un point pour décider si vous voulez atteindre la Vue, il est conseillé d'utiliser la [technique de l'activation de controlleur](#style-y080) à la place. - - ```javascript - /* à éviter */ - angular - .module('app') - .controller('Avengers', Avengers); - - function Avengers(movieService) { - var vm = this; - // non-résolue - vm.movies; - // résolue de façon asynchrone - movieService.getMovies().then(function(response) { - vm.movies = response.movies; - }); - } - ``` - - ```javascript - /* mieux */ - - // 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; - } - ``` - - Note : L'exemple ci-dessous montre que la résolution de routage pointe vers une fonction nommée, laquelle est plus facile à débugguer et dont l'injection de dépendance est plus facile à gérer. - - ```javascript - /* encore mieux */ - - // 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 moviePrepService(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 : Les dépendances dans l'exemple de code sur `movieService` ne sont pas directement compatibles avec la minification. Pour les détails sur la façon de rendre ce code compatible avec la minification, voir la section sur l'[injection de dépendance](#manual-annotating-for-dependency-injection) et sur [la minification et les annotations](#minification-and-annotation). - -**[Retour en haut de page](#table-des-matières)** - -## Annoter Manuellement pour l'Injection de Dépendances - -### Non Sur pour la Minification -###### [Style [Y090](#style-y090)] - - - Eviter l'utilisation de la syntaxe raccourcie de déclaration des dépendances sans utiliser une approche sûre pour la minification. - - *Pourquoi ?* : Les paramètres du composant (ex: controlleur, factory, etc.) seront converties en variables mutilées. Par exemple, ˋcommonˋet ˋdataserviceˋ deviendraient ˋaˋet ˋbˋ et ne seraient pas trouvées par Angular. - - ```javascript - /* à éviter - non sûr pour la minification */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - function Dashboard(common, dataservice) { - } - ``` - - Ce code pourrait produire des variables mutilées après minification et en cela provoquer des erreurs à l'éxécution. - - ```javascript - /* à éviter - non sûr pour la minification */ - angular.module('app').controller('Dashboard', d);function d(a, b) { } - ``` - -### Identifier Manuellement les Dépendances -###### [Style [Y091](#style-y091)] - - - Utilisez ˋ$injectˋpour identifier manuellement vos dépendances de composants Angular. - - *Pourquoi ? * : Cette technique est la même que celle utilisée par [`ng-annotate`](https://github.com/olov/ng-annotate), que je recommande pour automatiser la création de dépendances sûres pour la minification. Si ˋng-annotateˋ détecte que l'injection a déjà été faite, celà ne la dupliquera pas. - - *Pourquoi ?* : Ca préserve vos dépendances d'être vulnérable aux problèmes de minification lorsque les paramètres sont mutilés. Par exemple, ˋcommonˋet ˋdataserviceˋpourraient devenir ˋaˋ et ˋbˋ et ne pas être trouvés par Angular. - - *Pourquoi ?* : Eviter de créer en ligne une longue liste de dépendances peut rendre le tableau difficile à lire. De même, il peut entraîner une confusion dans la mesure où le tableau est une série de chaînes de caratères alors que le dernier élément est le nom de la fonction du composant. - - ```javascript - /* à éviter */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', - function Dashboard($location, $routeParams, common, dataservice) {} - ]); - ``` - - ```javascript - /* à éviter */ - angular - .module('app') - .controller('Dashboard', - ['$location', '$routeParams', 'common', 'dataservice', Dashboard]); - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - ```javascript - /* recommandé */ - angular - .module('app') - .controller('Dashboard', Dashboard); - - Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; - - function Dashboard($location, $routeParams, common, dataservice) { - } - ``` - - Note : Lorsque votre fonction est sous une instruction de return, le $inject peut ne pas être accessible (cela pourrait arriver dans une directive). Vous pouvez vous en sortir en bougeant le controlleur en dehors de la directive. - - ```javascript - /* À éviter */ - // à l'intérieur d'une définition de directive - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo - - DashboardPanelController.$inject = ['logger']; // Inatteignable - function DashboardPanelController(logger) { - } - } - ``` - - ```javascript - /* recommandé */ - // A l'exterieur d'une définition de directive - function outer() { - var ddo = { - controller: DashboardPanelController, - controllerAs: 'vm' - }; - return ddo; - } - DashboardPanelController.$inject = ['logger']; - function DashboardPanelController(logger) { - } - ``` - -### Identifier Manuellement les Dépendances du Route Resolver -###### [Style [Y092](#style-y092)] - - - Utilisez `$inject pour identifier manuellement vos dépendances du route resolver pour les composants Angular. - - *Pourquoi ?* : Cette technique divise la fonction anonyme pour le route resolver, la rendant plsu facile à lire. - - *Pourquoi ?* : Une instruction `$inject` peut facilement précéder le resolver à manipuler rendant n'importe quelle dépendance sûre à la minification. - - ```javascript - /* recommandé */ - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'AvengersController', - controllerAs: 'vm', - resolve: { - moviesPrepService: moviePrepService - } - }); - } - - moviePrepService.$inject = ['movieService']; - function moviePrepService(movieService) { - return movieService.getMovies(); - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Minification et Annotation - -### ng-annotate -###### [Style [Y100](#style-y100)] - - - Utilisez [ng-annotate](//github.com/olov/ng-annotate) pour [Gulp](http://gulpjs.com) ou [Grunt](http://gruntjs.com) et commentez les fonctions qui nécessitent l'injection de dépendances automatique en utilisant `/** @ngInject */`. - - *Pourquoi ?* : Ca préserve votre code de n'importe quelle dépendance qui pourrait ne pas utiliser les pratiques sûres à la minification. - - *Pourquoi ?*: [`ng-min`](https://github.com/btford/ngmin) est déprécié. - - >Je préfère Gulp car ça me paraît plus facile à écrire, lire et débugger. - - Le code suivant n'utilise pas de dépendances sûres à la minification. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero(){ - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - ``` - - Lorsque le code ci-dessus est éxécuté par ng-annotate il produira le résultat suivant avec l'annotation ˋ$injectˋ et deviendra sûr à la minification. - - ```javascript - angular - .module('app') - .controller('Avengers', Avengers); - - /* @ngInject */ - function Avengers(storageService, avengerService) { - var vm = this; - vm.heroSearch = ''; - vm.storeHero = storeHero; - - function storeHero(){ - var hero = avengerService.find(vm.heroSearch); - storageService.save(hero.name, hero); - } - } - - Avengers.$inject = ['storageService', 'avengerService']; - ``` - - Note : Si ˋng-annotateˋ détecte que l'injection a déjà été faite (ex : ˋ@ngInjectˋ a été détécté), il ne dupliquera pas le code ˋ$injectˋ. - - Note : Lors de l'utilisation d'un route resolver, vous pouvez préfixer la fonction de résolution avec `/* @ngInject */` et cela produira le code proprepement annoté, en gardant toute dépendance injectée sûre à la minification. - - ```javascript - // En utilisant les annotations @ngInject - function config($routeProvider) { - $routeProvider - .when('/avengers', { - templateUrl: 'avengers.html', - controller: 'Avengers', - controllerAs: 'vm', - resolve: { /* @ngInject */ - moviesPrepService: function(movieService) { - return movieService.getMovies(); - } - } - }); - } - ``` - - > Note : A partir d'Angular 1.3, utilisez le paramètre ˋngStrictDiˋ de la directive [`ngApp`](https://docs.angularjs.org/api/ng/directive/ngApp). Avec ce paramètre, l'injecteur sera créé en mode "strict-di" qui rendra fera échouer les invocations de fonctions de l'application qui n'utilisent pas explicitement les annotation de fonction (ceci peut ne pas être sûr à la minification). Débugger les informations qui seront logguées dans la console peut aider à débusquer le code à l'origine. - `` - -### Utilisation de Gulp ou Grunt pour ng-annotate -###### [Style [Y101](#style-y101)] - - - Utilisez [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate) ou [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) dans une tâche de build automatisée. Injectez `/* @ngInject */` avant toute fonction qui possède des dépendances. - - *Pourquoi ?* : ng-annotate va intercepter la plupart des dépendances, mais parfois va nécessiter des indices grâce à l'utilisation de la syntaxe `/* @ngInject */ˋ. - - Le code ci-dessous est un exemple d'une tâche gulp qui utilise 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 est avant uglify pour que le code soit minifié correctement. - .pipe(ngAnnotate({ - // true permet de l'ajouter là où @ngInject n'est pas utilisé. C'est inféré. - // Ne fonctionne pas avec resolve, donc nous devons être explicite ici. - add: true - })) - .pipe(bytediff.start()) - .pipe(uglify({mangle: true})) - .pipe(bytediff.stop()) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(pkg.paths.dev)); - }); - - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Gestion des Exceptions - -### decorateurs -###### [Style [Y110](#style-y110)] - - - Utilisez un [decorateur](https://docs.angularjs.org/api/auto/service/$provide#decorator), au moment de la configuration en utilisant le service [`$provide`](https://docs.angularjs.org/api/auto/service/$provide), sur le service [`$exceptionHandler`](https://docs.angularjs.org/api/ng/service/$exceptionHandler) pour effecture des actions personnalisées lorsque des exceptions se produisent. - - *Pourquoi ?* : Fournir un moyen cohérent pour gérer les exceptions non interceptées d'Angular pendant le développement ou à l'éxécution. - - Note : Une autre possibilité serait de surcharger le service au lieu d'utiliser un décorateur. C'est une bonne possibilité, mais si vou voulez garder le comportement par défaut et l'étendre, un décorateur est plus approprié. - - ```javascript - /* recommandé */ - 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 - }; - /** - * On pourrait ajouter l'erreur à une collection d'un service, - * ajouter les erreurs au $rootScope, loguer les erreurs vers un serveur distant, - * ou loguer locallement. Ou rejetter directement. C'est entièrement votre choix. - * throw exception; - */ - toastr.error(exception.msg, errorData); - }; - } - ``` - -### Catcher d'Exceptions -###### [Style [Y111](#style-y111)] - - - Créer une factory qui expose une interface pour attraper et gérer correctement les exceptions. - - *Pourquoi ?* : Fournir un moyen cohérent pour gérer les exception qui peuvent être déclenchées dans votre code (par example, pendant un appel Ajax ou lors d'un échec d'une promesse). - - Note : Le catcher d'axception est bon pour attraper et réagir à des exceptions spécifiques provenant d'appels dont vous savez qu'elles sont déclenchées. Par exemple, lorsque on fait un appel Ajax pour récuppérer des données d'un serveur distant et que vous voulez attraper n'importe quelles exceptions provenant de ce service uniquement et réagir seulement à celui-ci. - - ```javascript - /* recommandé */ - 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); - }; - } - } - ``` - -### Erreurs de Routage -###### [Style [Y112](#style-y112)] - - - Gérez et loguez toute erreur de routage en utilisant [`$routeChangeError`](https://docs.angularjs.org/api/ngRoute/service/$route#$routeChangeError). - - *Pourquoi ?* : Fournir un moyen cohérent de gérer les erreurs de routage. - - *Pourquoi ?* : Fournir potentiellement une meilleure expérience utilisateur si une erreur de routage se produit et les rediriger vers un écran convivial avec plus de détails ou les possibilités de s'en sortir. - - ```javascript - /* recommandé */ - function handleRoutingErrors() { - /** - * Annulation du routage: - * Sur une erreur de routage, aller au dashboard. - * Fournir une clause de sortie s'il essaye de le faire deux fois. - */ - $rootScope.$on('$routeChangeError', - function(event, current, previous, rejection) { - var destination = (current && (current.title || current.name || current.loadedTemplateUrl)) || - 'unknown target'; - var msg = 'Error routing to ' + destination + '. ' + (rejection.msg || ''); - /** - * Loguer éventuellement en utilisant un service personnalisé ou $log. - * (N'oubliez pas d'injecter votre service personnalisé) - */ - logger.warning(msg, [current]); - } - ); - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Nommage - -### Règles de Nommage -###### [Style [Y120](#style-y120)] - - - Utilisez des noms cohérents pour tous les composants en utilisant un pattern qui décrit la fonctionnalité de ce composant puis (éventuellement) son type. Le pattern que je recommande est `fonctionnalité.type.js`. Il y a 2 noms pour la plupart des resources: - * le nom du fichier (`avengers.controller.js`) - * le nom du composant déclaré à Angular (`AvengersController`) - - *Pourquoi ?* : Les conventions de nommage donnent une façon cohérente de s'y retrouver en un clin d'oeil. La cohérence dans tout le projet est vitale. La cohérence au sein de l'équipe est importante. La cohérence dans l'entreprise apporte une efficacité redoutable. - - *Pourquoi ?* : Les conventions de nommage doivent simplement vous aider à naviguer dans le code plus vite et le rendre plus facile à comprendre. - -### Nom des Fichiers de Fonctionnalités -###### [Style [Y121](#style-y121)] - - - Utilisez des noms cohérents pour tous les composants qui suivent un pattern qui décrit la fonctionnalité d'un composant et (éventuellent) son type. Le pattern que je recommande est `fonctionnalité.type.js`. - - *Pourquoi ?* : Offre une façon cohérente d'identifier rapidement les composants. - - *Pourquoi ?* : Fournit un pattern matching pour automatiser des tâches. - - ```javascript - /** - * possibilités couramment rencontrées. - */ - - // Controlleurs - avengers.js - avengers.controller.js - avengersController.js - - // Services/Factories - logger.js - logger.service.js - loggerService.js - ``` - - ```javascript - /** - * recommandé - */ - - // controlleurs - avengers.controller.js - avengers.controller.spec.js - - // services/factories - logger.service.js - logger.service.spec.js - - // constantes - constants.js - - // definition de module - 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 : Une autre convention courante consiste à nommer les fichiers de controlleurs sans le mot `controller` dans le nom de fichier comme `avengers.js`au lieu de `avengers.controller.js`. Toutes les autres conventions étant maintenues avec un suffixe par type. Les controlleurs étant les les types de composant les plus courants, ça permet d'économiser la frappe au clavier tout en étant facilement identifiable. Je vous conseille de choisir une convention et de vous y tenir dans toute l'équipe. Ma préference est `avengers.controller.js`. - - ```javascript - /** - * recommandé - */ - // Controlleurs - avengers.js - avengers.spec.js - ``` - -### Nommage des Fichiers de Test -###### [Style [Y122](#style-y122)] - - - Les spécifications du nommage des tests est similaire à celui du composant qu'il teste avec un suffixe `spec`. - - *Pourquoi ?* : Fournit une façon cohérente d'identifier rapidement les composants. - - *Pourquoi ?* : Permet le pattern matching pour [karma](http://karma-runner.github.io/) ou d'autres moteurs de tests. - - ```javascript - /** - * recommandé - */ - avengers.controller.spec.js - logger.service.spec.js - avengers.routes.spec.js - avenger-profile.directive.spec.js - ``` - -### Nommage des Controlleurs -###### [Style [Y123](#style-y123)] - - - Utilisez des noms cohérents pour tous les controlleurs nommés d'après leur fonctionnalité. Utilisez le CamelCaseEnMajuscule, puisque ce sont des constructeurs. - - *Pourquoi ?* : Fournit une façon cohérente d'identifier rapidement et de référencer les controlleurs. - - *Pourquoi ?* : Le CamelCaseEnMajuscules est la convention pour identifier les objets qui peuvent être instanciés avec un controleur. - - ```javascript - /** - * recommandé - */ - - // avengers.controller.js - angular - .module - .controller('HeroAvengersController', HeroAvengersController); - - function HeroAvengersController() { } - ``` - -### Suffixe des Noms de Controlleurs -###### [Style [Y124](#style-y124)] - - - Ajoutez au nom du controlleur le suffixe ˋControllerˋ. - - *Pourquoi ?* : Le suffixe ˋControllerˋ est utilisé souvent et il est plus explicitement descriptif. - - ```javascript - /** - * recommandé - */ - - // avengers.controller.js - angular - .module - .controller('AvengersController', AvengersController); - - function AvengersController(){ } - ``` - -### Nommage des Factory -###### [Style [Y125](#style-y125)] - - - Utilisez des noms cohérents pour toutes les foactories nommées d'après la fonctionnalitée. Utilisez le camel-case pour les services et les factories. - - *Pourquoi ?* : Fournit une façon cohérente d'identifier rapidement et de référencer les factories. - - ```javascript - /** - * recommandé - */ - - // logger.service.js - angular - .module - .factory('logger', logger); - - function logger(){ } - ``` - -### Nommage des Directives de Composants -###### [Style [Y126](#style-y126)] - - - Utilisez des noms cohérents pour toutes les directives en utilisant le camel-case. Utilisez un préfixe court pour décrire le domaine à laquelle les directives appartiennent (exemples : préfixe de la société ou préfixe du projet). - - *Pourquoi ?* : Fournit une façon cohérente d'identifier rapidement et de référencer les composants. - - ```javascript - /** - * recommandés - */ - - // avenger-profile.directive.js - angular - .module - .directive('xxAvengerProfile', xxAvengerProfile); - - // l'usage est - - function xxAvengerProfile(){ } - ``` - -### Modules -###### [Style [Y127](#style-y127)] - - - Lorqu'il y a de multiples modules, le fichier du module principal est nommé ˋapp.module.jsˋ tandis que les autres modules dépendants sont nommés d'après ce qu'ils représentent. Par exemple, un module d'admin est nommé ˋadmin.module.jsˋ. Les noms des modules déclarés seraient respectivement ˋappˋ et ˋadminˋ. - - *Pourquoi ?* : Fournit de la cohérence pour les applications multi-modules, et pour les applications qui grossissent. - - *Pourquoi ?* : Fournit une façon aisée d'utiliser l'automatisation des tâches afin de charger toutes les définitions de modules en premier, puis ensuite tous les autres fichiers angular (pour l'assemblage). - -### Configuration -###### [Style [Y128](#style-y128)] - - - Séparer la configuration d'un module dans son propre fichier nommé d'après le module. Un fichier de configuration du module principal ˋappˋ est nommé ˋapp.config.jsˋ (ou simplement ˋconfig.jsˋ). Une configuration pour un module nommé ˋadmin.module.jsˋ est nommé ˋadmin.config.jsˋ. - - *Pourquoi ?* : Sépare la configuration de la définition du module, du composant et du code actif. - - *Pourquoi ?* : Fournit un endroit bien identifié pour mettre la configuration d'un module. - -### Routes -###### [Style [Y129](#style-y129)] - - - Séparez la configuration de la route dans son propre fichier. Un exemple pourrait être ˋapp.route.jsˋ pour le module principal et ˋadmin.route.jsˋ pour le module d'ˋadminˋ. Même pour de petites applications, il est préférable de privilégier cette séparation du reste de la configuration. - -**[Retour en haut de page](#table-des-matières)** - -## Le Principe LIFT de Structuration de l'Application -### LIFT -###### [Style [Y140](#style-y140)] - - - Structurez votre application afin de pouvoir `L`ocaliser le code plus rapidement, `I`dentifier le code d'un seul coup, garder la structure la plus platte possible (`F`lattest), et essayez (`T`ry) de rester DRY (Don't Repeat Yourself). La structure doit suivre ces 4 règles de bases. - - *Pourquoi LIFT ?* : Fournit une structure cohérente qui passe bien à l'échelle, qui est modulaire, et facilite l'augmentation de l'efficacité du développeur. Une autre façon de valider la structure de votre application est de vous demander : à quelle vitesse vous pouvez ouvrir et travailler dans tous les fichiés liés à une fonctionnalité ? - - Lorsque je trouve que ma structure n'est pas confortable, je reviens en arrière et je revisite les règles LIFT. - - 1. `L`ocaliser le code est facile - 2. `I`dentifier le code d'un coup - 3. `F`lat (platte) structure autant que possible - 4. `T`ry (essayer) de rester DRY (Don’t Repeat Yourself, Ne Pas Se Répéter) ou T-DRY - -### Localisation -###### [Style [Y141](#style-y141)] - - - Rendez la localisation du code intuitive, simple et rapide. - - *Pourquoi ?* : Je trouve que c'est super important pour un projet. Si l'équipe ne peut pas trouver rapidement les fichiers sur lesquels elle doit travailler, ils ne vont pas être en mesure de travailler aussi efficacement que possible, et la structure devra changer. Vous ne connaissez peut-être pas le nom du fichier où la position des fichiers liés, alors les placer dans les endroits les plus intuitifs et proches les uns les autres permet de gagner un paquet de temps. Une structure de répertoire descriptive peut aider à ça. - - ``` - /bower_components - /client - /app - /avengers - /blocks - /exception - /logger - /core - /dashboard - /data - /layout - /widgets - /content - index.html - .bower.json - ``` - -### Identification -###### [Style [Y142](#style-y142)] - - - Lorsque vous regardez un fichier vous devriez instantanément savoir ce qu'il contient ce qu'il représente. - - *Pourquoi ?* : Vous passez moins de temps à fouiller et vous perdre pour cherche le code, et devenez de plus en plus efficient. Si ça implique des noms de fichier plus long, alors d'accord. Soyez descriptif avec les noms de fichier et leur contenu ne doit contenir exactement qu'un seul composant. Éviter les fichier avec plusieurs controlleurs, plusieurs services, ou un mélange. On pourrait admettre une exception à cette règle si j'ai un ensemble de fonctionnalités très petites qui sont toutes reliées entre elles, elles sont toujours facilement identifiables. - -### Plat -###### [Style [Y143](#style-y143)] - - - Gardez une structure de répertoire à plat le plus longtemps possible. Lorsque vous avez 7 fichiers ou plus, commencez à penser à séparer. - - *Pourquoi ?* : Personne ne souhaite rechercher dans 7 niveaux de répertoires pour trouver un fichier. Pensez aux menus des sites web… rien de plus profond que 2 niveaux ne devrait être sérieursement pris en considération. Dans une structure de répertoire, il n'y a pas de nombre d'or, mais lorsqu'un répertoire à entre 7 et 10 fichiers, il serait temps de créer des sous-répertoires. Basez cela sur votre niveau de confort. Utilisez une structure plus plate jusqu'à ce qu'il y ait un évident intérêt (pour respecter les autres principes LIFT) à créer un sous-répertoire. - -### T-DRY (Essayer de respecter DRY) -###### [Style [Y144](#style-y144)] - - - Ne vous répétez pas (DRY), mais pas bêtement à tout prix ni en sacrifiant la lisibilité. - - *Pourquoi ?* : Ne pas se répéter (DRY) est important, mais pas cricial si vous en êtesréduit à sacrifier les autres principes LIFT, c'est que qu'on peut appeller T-DRY (Essayer de ne pas se répéter). Je ne voudrai pas écrire session-view.html pour une vue, parce que, c'est évidemment une vue. Si ce n'est pas évident ou par convention, alors nommez-le. - -**[Retour en haut de page](#table-des-matières)** - -## Structure de l'Application - -### Règles Générales -###### [Style [Y150](#style-y150)] - - - Vous devez avoir une vue court terme et une vision à long terme. En d'autres mots, commencez petit et garder en tête là où en est votre application. Tout le code de l'appli va dans un répertoire racine nommé `app`. Tout contenu fonctionnel doit être rangé dans son propre fichier. Chaque controlleur, service, module, vue doit avoir son propre fichier. Tous les scripts provenant de fournisseurs extérieurs doivent être rangés dans un autre répertoire racine et non dans le répertoire `app`. Le code que l'on écrit pas soi-même ne doit pas se mélanger avec son appli (`bower_components`, `script`, `lib`). - - Note : Vous trouverez plus de détails et les justifications derrière la structure sur [ce post original sur la structure des applications](http://www.johnpapa.net/angular-app-structuring-guidelines/). - -### Layout -###### [Style [Y151](#style-y151)] - - - Placez les composants qui définissent le layout principal de l'application dans un répertoire nommé `layout`. Il devrait inclure une vue noyau et le controlleur devrait agir comme conteneur pour l'appli, la nivigation, les menus, les zones de contenu, et les autres régions. - - *Pourquoi ?* : Organise tout le layout à un seul endroit réutilisé dans l'application. - -### Structure en Répertoires-par-Fonctionnalités -###### [Style [Y152](#style-y152)] - - - Créez des répertoires nommés d'après les fonctionnalités qu'elles représentent. Lorsqu'un répertoire grossit jusqu'à atteindre plus de 7 fichiers, commencez à penser la création d'un répertoire pour ceux-ci. Si votre seuil est différent, ajustez-le au besoin. - - *Pourquoi ?* : Un développeur peut localiser, identifier ce que représente chaque fichier en sune seule fois, la structure est aussi plate que possible, et il n'y a ni répétitions ni noms redondants. - - *Pourquoi ?* : Les règles LIFT sont toutes couvertes. - - *Pourquoi ?* : Aide à diminuer l'entropie de l'appli en organizant le contenu et en le maintenant aligné avec les principes LIFT. - - *Pourquoi ?* : Lorsqu'il y a de nombreux fichiers (+ de 10) les repérer est plus facile avec une structure de répertoires cohérente et plus difficile dans une structure à plat. - - ```javascript - /** - * recommandé - */ - - 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 - ``` - - ![Structure d'une Appli Exemple](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-2.png) - - Note : N'utilisez pas une structuration de répertoires-par-type. Cela requiert de se déplacer entre de multiples répertoires lorsqu'on travaille sur une fonctionnalité et cela devient rapidement difficile à manier lorsque l'application grossit à 5, 10 ou plus de 25 vues et controlleurs (et autres), ce qui complique la localisation par rapport à des répertoires-par-fonctionnalité. - - ```javascript - /* - * à éviter - * Trouver une alternative aux répertoires-par-type. - * Je vous conseille les "répertoires-par-fonctionnalité", à la place. - */ - - app/ - app.module.js - app.config.js - app.routes.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 - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Modularité - -### De Nombreux Petits Modules Auto-Suffisants -###### [Style [Y160](#style-y160)] - - - Créez de petits modules qui encapsulent une responsabilité. - - *Pourquoi ?* : Les Applications modulaires rendent faciles le branchement rapide puisqu'elles permettent aux équipes de développement de construire des sections verticales de l'application et de livrer incrémentalement. Cela signifie que nous pouvons brancher de nouvelles fonctionnalités à mesure que nous les développons. - -### Création d'un Module Applicatif -###### [Style [Y161](#style-y161)] - - - Créez un module racine pour l'application dont le rôle est d'assembler tous les modules et fonctionnalités de votre application. Nommez-le comme votre application. - - *Pourquoi ?* : Angular encourage la modularité et la séparation des responsabilités. La création d'un module racine pour l'application dont le rôle est de lier ensemble les autres modules fournit un moyen très direct d'ajouter et de retirer des modules à votre application. - -### Garder le Module Applicatif Léger -###### [Style [Y162](#style-y162)] - - - Ne placez dans le module applicatif que la logique d'assemblage de l'application. Laissez les fonctionnalités dans leurs propres modules. - - *Pourquoi ?* : Ajouter des responsabilités supplémentaires à l'application racine pour récupérer des données, afficher des vues, ou toute autre logique non reliée à l'assemblage de l'application trouble le module applicatif rend plus difficile à réutiliser ou éteindre les ensembles de fonctionnalités. - - *Pourquoi ?* : Le module applicatif devient une déclaration qui montre quels modules aident à constituer l'application. - -### Les Macro Fonctionnalités en tant que Modules -###### [Style [Y163](#style-y163)] - - - Créez des modules qui représentent des macro fonctionnalités, comme le layout, les services ré-utilisables et partagés, les dashboards, et les fonctionnalités applicatives spécifiques (par exemple : clients, admin, ventes). - - *Pourquoi ?* : Les modules auto-suffisants peuvent être ajoutés à l'application avec peu ou pas de friction. - - *Pourquoi ?* : Les sprints ou itérations peuvent se focaliser sur les macro-fonctionnalités et les activer à la fin de l'itération. - - *Pourquoi ?* : Séparer les macros-fonctionnalités rend plus facile les tests des modules en isolation et la réutilisation du code. - -### Les Blocks Ré-Utilisables en tant que Modules -###### [Style [Y164](#style-y164)] - - - Créez des modules qui représentent des blocs d'application ré-utilisables pour les services en commun tels que la gestion d'exceptions, les logs, les diagnostics, la sécurité et la gestion des données en locale. - - *Pourquoi ?* : Ces types de fonctinnalités sont requises dans de nombreuses application, donc en les gardant séparées dans leur propres modules elles peuvent être génériques par rapport aux applications et peuvent être ré-utilisées pour d'autres applications. - -### Dependences Entre Modules -###### [Style [Y165](#style-y165)] - - - Le module racine de l'application dépend des modules des fonctionnalités spécifiques et de certains modules partagés et ré-utilisables. - - ![Modularity and Dependencies](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/modularity-1.png) - - *Pourquoi?* : Le module principal de l'appli continent une déclaration rapidement identifible des fonctionnalités de l'application. - - *Pourquoi ?* : Chaque groupe de fonctionnalité contient une déclaration de ce dont il dépend, de ce fait il peut être tiré comme dépendance dans d'autres applications et continuer à fonctionner. - - *Pourquoi ?* : Les fonctionalités propres à l'appli tels que les services de données partagées deviennent faciles à repérer et partager au sein d'un `app.core` (choisissez un nom de votre choix pour ce module. - - Note : C'est un stratégie pour la cohérence. Il y a ici beaucoup de bons choix. Choisisez-en une qui soit cohérente, suivez les règles des dépendances d'Angular, et la maintenance et la montée en charge sera facilitée. - - > Mes structures peuvent varier légèrement entre les projets mais elles suivent toutes ces règles pour la structure et la modularité. L'implémentation peut varier en fonction des fonctionnalités et de l'équipe. En d'autres termes, ne vous paralysez pas sur une structure exactement semblable mais soumettez votre structure aux critères de cohérence, maintenance, et efficacité. - - > Dans de petites applic, vous pouvez aussi mettre toutes vos dépendances partagées dans le module applicatif où les modules fonctionnels n'ont pas de dépendances directes. Cela pourra rendre la maintenance de cette petite application plus facile, mais rendez difficile la ré-utilisation de ces modules en dehors de cette application. - -**[Retour en haut de page](#table-des-matières)** - -## Logique de Démarrage - -### Configuration -###### [Style [Y170](#style-y170)] - - - Injectez le code à l'intérieur d'une [configuration de module](https://docs.angularjs.org/guide/module#module-loading-dependencies) qui doit être configuré avant l'éxécution de l'appli angular. Parmis les candidats idéaux, on trouve les providers et les constantes. - - *Pourquoi ?* : Ça rend les choses plus faciles d'avoir le moins d'endroits possible pour placer la 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: ' - }); - } - } - ``` - -### Blocs Run -###### [Style [Y171](#style-y171)] - - - Tout code qui nécessite de s'éxécuter lorsque l'application démarre devrait être déclaré dans une factory, exposé via une fonction, et injecté dans un [bloc run](https://docs.angularjs.org/guide/module#module-loading-dependencies). - - *Pourquoi ?* : Le code écrit directement dans un bloc run peut être difficile à tester. Le placer dans une factory le rend plus facile à abstraire et mocker. - - ```javascript - angular - .module('app') - .run(runBlock); - - runBlock.$inject = ['authenticator', 'translator']; - - function runBlock(authenticator, translator) { - authenticator.initialize(); - translator.initialize(); - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Les Services de Wrapper $ de Angular - -### $document et $window -###### [Style [Y180](#style-y180)] - - - Utilisez [`$document`](https://docs.angularjs.org/api/ng/service/$document) et [`$window`](https://docs.angularjs.org/api/ng/service/$window) au lieu de `document` et `window`. - - *Pourquoi ?* : Ces services sont wrappés par Angular et plus facilement testables qu'en utilisant document et window dans les tests. Ils vous aident à éviter d'avoir à mocker document et window vous-même. - -### $timeout et $interval -###### [Style [Y181](#style-y181)] - - - Utilisez [`$timeout`](https://docs.angularjs.org/api/ng/service/$timeout) et [`$interval`](https://docs.angularjs.org/api/ng/service/$interval) au lieu de `setTimeout` et `setInterval`. - - *Pourquoi ?* : Ces services sont wrappés par Angular et plus facilement testables et gèrent le cycle de digest d'Angular conservant un data binding à jour. - -**[Retour en haut de page](#table-des-matières)** - -## Le Test -Les tests unitaires aident à maintenir un code propre, ainsi, j'ai inclu quelques unes de mes recommandations sur les fondamentaux du test unitaire avec des liens pour plus déinformation. - -### Écriture des Tests avec les Stories -###### [Style [Y190](#style-y190)] - - - Écrivez un ensemble de tests pour chaque story. Commencer avec un test vide et complétez-les à mesure que vous écrivez le code pour la story. - - *Pourquoi ?* : Écrire les descriptions de tests aident à définir clairement ce que votre story devra faire, ne devra pas faire et comment mesurer l'avancement. - - ```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?) - }); - - // et ainsi de suite - ``` - -### Librairie de Test -###### [Style [Y191](#style-y191)] - - - Utilisez [Jasmine](http://jasmine.github.io/) or [Mocha](http://mochajs.org) pour les tests unitaires. - - *Pourquoi ?* : Jasmine et Mocha sont toutes deux largement utilisées dans la communauté Angular. Toutes les deux stables, bien maintenues, et fournissant des fonctionnalités robustes de test. - - Note : Lorsque vous utilisez Mocha, utilisez aussi une librairie d'assertion telle que [Chai](http://chaijs.com). Je prefère Mocha. - -### Lanceur de Test -###### [Style [Y192](#style-y192)] - - - Utilisez [Karma](http://karma-runner.github.io) comme lanceur de test. - - *Pourquoi ?* : Karma est facile à configurer pour lancer les tests une fois ou automatiquement lorsqu'un changement est fait dans le code. - - *Pourquoi ?* : Karma s'intègre facilement dans votre processus d'Intégration Continue soit tout seul ou par Grunt ou Gulp. - - *Pourquoi ?* : Quelques EDI commencent à s'intégrer avec Karma, c'est le cas de [WebStorm](http://www.jetbrains.com/webstorm/) et [Visual Studio](http://visualstudiogallery.msdn.microsoft.com/02f47876-0e7a-4f6c-93f8-1af5d5189225). - - *Pourquoi ?* : Karma fonctionne bien avec les leaders de l'automatisation de tâches tel que [Grunt](http://www.gruntjs.com) (avec [grunt-karma](https://github.com/karma-runner/grunt-karma)) ou [Gulp](http://www.gulpjs.com) (avec [gulp-karma](https://github.com/lazd/gulp-karma)). - -### Les Stubs et les Spy -###### [Style [Y193](#style-y193)] - - - Utilisez [Sinon](http://sinonjs.org/) pour les stubs et les spy. - - *Pourquoi ?* : Sinon fonctionne bien avec Jasmine et Mocha et étend les fonctionnalités de stub et de spy qu'ils offrent. - - *Pourquoi ?* : Sinon rend plus facile l'alternance entre Jasmine et Mocha, si vous voulez essayer les deux. - - *Pourquoi ?* : Sinon a des messages descriptifs quand les tests ne valident pas les assertions. - - -### Navigateur sans Interface Graphique -###### [Style [Y194](#style-y194)] - - - Utilisez [PhantomJS](http://phantomjs.org/) pour éxécuter les tests sur un serveur. - - *Pourquoi?* : PhantomJS est un navigateur sans interface graphique qui peut vous aider à éxécuter les tests sans avoir besoin d'un navigateur "visuel". Ainsi vous n'avez pas besoin d'installer Chrome, Safari, IE, ou d'autres navigateurs sur votre serveur. - - Note : Que cela ne vous dispense pas de tester sur tous les navigateurs dans votre environnement, d'après les clients que vous ciblez. - -### Analyse de Code -###### [Style [Y195](#style-y195)] - - - Exécutez JSHint sur vos tests. - - *Pourquoi ?* : Les tests sont du code. JSHint peut vous aider à identifier les problèmes de qualité de code qui pourrait amener les tests à fonctionner de façon incorrecte. - -### Atténuation des Règles JSHint avec les Golbales sur les Tests -###### [Style [Y196](#style-y196)] - - - Relaxez les règles sur votre code de test afin de permettre l'usage des globales telles que `describe` et `expect`. Relaxez les règles pour les expressions, de la même façon qu'avec Mocha. - - *Pourquoi ?* : Vos tests sont du code et requièrent à ce titre la même attention avec les mêmes règles de qualité de code que votre code de production. Cependant, les variables globales utilisées par les frameworks de test, par exemple, peuvent être relaxées en les incluants dans les spécifications de test. - - ```javascript - /* jshint -W117, -W030 */ - ``` - Ou laors vous pouvez rajouter ça à votre fichier d'option JSHint. - - ```javascript - "jasmine": true, - "mocha": true, - ``` - - - ![Outils de Test](https://raw.githubusercontent.com/johnpapa/angular-styleguide/master/assets/testing-tools.png) - -### Organizing Tests -###### [Style [Y197](#style-y197)] - - - Placez les fichiers des tests unitaires (specs) côte-à-côte du code client. Placez les specs qui couvrent l'intégration avec le serveur ou les celles qui testent plusieurs composants dans un répertoire `tests` séparé. - - *Pourquoi ?* : Les tests unitaires sont en corrélation directe avec un composant spécifique et un fichier dans le code source. - - *Pourquoi ?* : Il est plus facile de les mettre à jour pluisqu'ils sont toujours les uns en face des autres. Quand vous développez, que vous fassiez du TDD, des tests en meme temps que l'implémentation ou des tests après l'implémentation, les specs sont côte-à-côte et jamais loin ni des yeux ni de l'esprit, et ainsi ils ont plus de chance d'etre maintenus ce qui permet aussi de tenir une bonne couverture de code. - - *Pourquoi ?* : Quand vous mettez à jour le code source, il est plus facile de mettre à jour les tests en même temps. - - *Pourquoi ?* : Les placer côte-à-côte les rend plus facile à trouver et facile à déplacer si vous déplacez les sources. - - *Pourquoi ?* : Avoir les specs proches permet au lecteur du code source d'apprendre comment le composant est supposé être utilisé et découvrir les limitations connues. - - *Pourquoi ?* : La séparation des specs afin qu'ils ne soient pas inclus dans le build est facile avec grunt ou gulp. - - ``` - /src/client/app/customers/customer-detail.controller.js - /customer-detail.controller.spec.js - /customers.controller.spec.js - /customers.controller-detail.spec.js - /customers.module.js - /customers.route.js - /customers.route.spec.js - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Animations - -### Utilisation -###### [Style [Y210](#style-y210)] - - - Utilisez de subtiles [animations avec Angular](https://docs.angularjs.org/guide/animations) pour la transition entre les états pour les vues et les éléments visuels premiers. Incluez le [module ngAnimate](https://docs.angularjs.org/api/ngAnimate). Les trois clés sont la subtilité, la fluidité, l'homogénéïté. - - *Pourquoi ?* : Des animations subtiles peuvent améliorer l'Expérience Utilisateur lorsqu'elles sont utilisées de façon appropriéés. - - *Pourquoi ?* : Des animations subtiles peuvent améliorer la performance perçue lorsque les vues changent. - -### Moins d'une Seconde -###### [Style [Y211](#style-y211)] - - - Utilisez de courtes durées pour les animations. Je commence en général par 300 milli secondes et j'ajuste jusqu'à ce que ce soit bien. - - *Pourquoi ?* : Les animations longues peuvent avoir un effet inverse sur l'Expérience Utilisateur et la performance perçue en donnant l'apparence d'une application lente. - -### animate.css -###### [Style [Y212](#style-y212)] - - - Utilisez [animate.css](http://daneden.github.io/animate.css/) pour les animations conventionnelles. - - *Pourquoi ?* : Les animations que fournissent animate.css sont rapides, fluides et facile a ajouter à votre application. - - *Pourquoi ?* : Fournit de la cohérence dans vos animations. - - *Pourquoi ?* : animate.css est largement utilisée et testée. - - Note : Voir ce [super post par Matias Niemelä sur les animations d'Angular](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html) - -**[Retour en haut de page](#table-des-matières)** - -## Commentaires - -### jsDoc -###### [Style [Y220](#style-y220)] - - - Si vous prévoyez de produire de la documentation de code, utilisez la syntaxe [`jsDoc`](http://usejsdoc.org/) pour documenter les noms de fonction, leur description, paramètres et valeurs de renvoi. Utilisez `@namespace` et `memberOf` pour s'adapter à la structure de votre appli. - - *Pourquoi ?* : Vous pouvez générer (et re-générer) la documentation à partir de votre code, au lieu de l'écrire de zéro. - - *Pourquoi ?* : Fournit de la cohérence en utilisant un outil industriel commun. - - ```javascript - /** - * Factory de Logger - * @namespace Factories - */ - (function() { - angular - .module('app') - .factory('logger', logger); - - /** - * @namespace Logger - * @desc Logger de niveau applicatif - * @memberOf Factories - */ - function logger($log) { - var service = { - logError: logError - }; - return service; - - //////////// - - /** - * @name logError - * @desc Loggue les errors - * @param {String} msg Le message à logguer - * @returns {String} - * @memberOf Factories.Logger - */ - function logError(msg) { - var loggedMsg = 'Error: ' + msg; - $log.error(loggedMsg); - return loggedMsg; - }; - } - })(); - ``` - -**[Retour en haut de page](#table-des-matières)** - -## JS Hint - -### Utilisation d'un Fichier d'Options -###### [Style [Y230](#style-y230)] - - - Utilisez JS Hint pour éplucher votre JavaScript et assurez-vous d'avoir personnalisé le fichier d'options JS Hint et incluez le dans le système de gestion de versions. Voir la [doc de JS Hint](http://www.jshint.com/docs/) pour les détails de chaque option. - - *Pourquoi ?* : Fournit une première alerte avant de committer son code dans le système de gestion de version. - - *Pourquoi ?* : Fournit de la cohérence dans votre équipe. - - ```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 - } - } - ``` - -**[Retour en haut de page](#table-des-matières)** - -## JSCS - -### Use an Options File -###### [Style [Y235](#style-y235)] - - Utilisez JSCS pour valider votre style de code pour votre JavaScript et pensez à personnaliser vos options pour JSCS et de l'inclure dans votre gestionnaire de versionning. Vous pouvez consulter la [documentation de JSCS](http://www.jscs.info) pour voir les détails et les options. - - *Pourquoi ?* : Fournit une premiere alerte avant de commiter sur votre gestionnaire de versionning. - - *Pourquoi ?* : Permet d'assurer une cohérence au sein de votre équipe. - - ```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, - - "validateJSDoc": { - "checkParamNames": true, - "requireParamTypes": true - }, - - "disallowMultipleLineBreaks": true, - - "disallowCommaBeforeLineBreak": null, - "disallowDanglingUnderscores": null, - "disallowEmptyBlocks": null, - "disallowMultipleLineStrings": null, - "disallowTrailingComma": null, - "requireCommaBeforeLineBreak": null, - "requireDotNotation": null, - "requireMultipleVarDecl": null, - "requireParenthesesAroundIIFE": true - } - ``` - -**[Back to top](#table-of-contents)** - -## Constantes - -### Globales des Librairies Externes -###### [Style [Y240](#style-y240)] - - - Créez une Constante Angular pour les variables gobales des librairies externes. - - *Pourquoi ?* : Fournit un moyen d'injecter des librairies tierces qui seraient sinon des globales. Cela améliore la testabilité du code en vous permettant de savoir plus facilement quelles sont les dépendances de vos composants évite les abstractions qui fuient). Ça vous permet aussi de mocker ces dépendances, là où cela fait sens. - - ```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)] - - - Utilisez les constantes pour les valeurs qui ne changent pas et ne viennent pas d'un autre service. Quand des contantes ne sont utilisées que par un module qui peut être ré-utilisé dans d'autres applications, placez les constantes dans un seul fichier par module nommé comme le module. Tant que c'est possible, gardez les constantes dans le module principal dans un fichier `constants.js`. - - *Pourquoi ?* : Une valeur qui peut changer, même rarement, devrait être récupérée d'un service afin de ne pas avoir à changer le code source. Par exemple, une URL pour un service de données pourrait être définit comme constante mais il serait mieux de lire cette valeur par appel à un web service. - - *Pourquoi ?* : Les constantes peuvent être injectées dans un composant angular, y compris les providers. - - *Pourquoi ?* : Quand une application est divisée en modules qui peuvent être ré-utilisés dans d'autres applications, chacun de ces modules individiuel devrait pouvoir fonctioner tout seul, y compris avec les constantes dépendantes. - - ```javascript - // Constantes utilisées par toute l'appli - angular - .module('app.core') - .constant('moment', moment); - - // Constantes utilisées seulement par le module de vente - angular - .module('app.sales') - .constant('events', { - ORDER_CREATED: 'event_order_created', - INVENTORY_DEPLETED: 'event_inventory_depleted' - }); - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Templates de Fichiers et Fragments -Utilisez des templates de fichier ou des fragments pour vous aider à suivre des styles et des patterns cohérents. Voici des templates et/ou fragments pour quelques uns des éditeurs de texte pour le développement web et EDIs. - -### Sublime Text -###### [Style [Y250](#style-y250)] - - - Fragments Angular qui suivent ces styles et règles. - - - Téléchargez les [Fragments Angular pour Sublime](assets/sublime-angular-snippets.zip?raw=true) - - Placez-les dans votre répertoire Package - - Redémarrez Sublime - - Dans un fichier de type JavaScript, tapez ces commandes suivies par la touche `TAB` - - ```javascript - ngcontroller // crée un controlleur Angular - ngdirective // crée une directive Angular - ngfactory // crée une factory Angular - ngmodule // crée un module Angular - ``` - -### Visual Studio -###### [Style [Y251](#style-y251)] - - - Les templates de fichier qui suivent ces styles et règles peuvent être trouvées sur [SideWaffle](http://www.sidewaffle.com) - - - Téléchargez l'extension [SideWaffle](http://www.sidewaffle.com) pour Visual Studio (fichier vsix) - - Éxécutez le fichier vsix - - Re-démarrez Visual Studio - -### WebStorm -###### [Style [Y252](#style-y252)] - - - Les fragments Angular et templates de fichiers qui suivent le style et les règles. Vous pouvez les importer dans les paramètres de WebStorm : - - - Téléchargez les [templates de fichier et fragments WebStorm pour Angular](assets/webstorm-angular-file-template.settings.jar?raw=true) - - Ouvrez WebStorm et allez dans le menu `File` - - Choisissez le menu `Import Settings` - - Sélectionnez le fichier et clickez sur `OK` - - Dans un fichier de type JavaScript, tapez ces commandes suivies de la touche `TAB` : - - ```javascript - ng-c // crée un controlleur Angular - ng-f // crée une factory Angular - ng-m // crée un module Angular - ``` - - **[Retour en haut de page](#table-des-matières)** - -## Generateur Yeoman -###### [Style [Y260](#style-y260)] - -Vous pouvez utiliser le [générateur yeoman HotTowel](http://jpapa.me/yohottowel) pour créer une appli pour démarrer avec Angular en suivant ce guide de style. - -1. Installer generator-hottowel - - ``` - npm install -g generator-hottowel - ``` - -2. Créer un nouveau répertoire et aller dans ce répertoire - - ``` - mkdir myapp - cd myapp - ``` - -3. Éxécuter le générateur - - ``` - yo hottowel helloWorld - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Routage -Le routage côté client est important pour créer un flux de navigation entre les vues et la composition des vues constituées de nombreux plus petits templates et directives. - -###### [Style [Y270](#style-y270)] - - - Utilisez [Routeur AngularUI](http://angular-ui.github.io/ui-router/) pour faire le routage côté client. - - *Pourquoi ?* : UI Router offre toutes les fonctionnalités du routeur Angular plus quelques autres parmis lesquels les routes imbriquées et les états. - - *Pourquoi ?* : La syntaxe est quasiement similaire au routeur Angular et il est facile de migrer à UI Router. - -###### [Style [Y271](#style-y271)] - - - Définissez les routes pour les vues d'un module à l'endroit où elles existent. Chaque module devrait contenir le routage de ses vues. - - *Pourquoi ?* : Chaque module devrait avoir la cohérence de définir ses propres routes. - - *Pourquoi ?* : Si on ajoute ou enlève un module, on souhaite que l'appli ne contienne que les routes vers des vues existantes. - - *Pourquoi ?* : Cela rend facile l'activation ou la désactivation de portions de l'application sans se préoccuper des routes orphelines. - -**[Retour en haut de page](#table-des-matières)** - -## Automatisation des Tâches -Utilisez [Gulp](http://gulpjs.com) ou [Grunt](http://gruntjs.com) pour créer des tâches automatisées. Gulp favorise le code plutôt que la configuration tandis que Grunt tend vers la configuration plutôt que le code. Je préfère personnellement Gulp car il me semble plus facile à lire et écrire, mais les deux sont excellents. - -> Learn more about gulp and patterns for task automation in my [Gulp Pluralsight course(http://jpapa.me/gulpps) -> Vous pouvez en lire plus sur Gulp et ses patterns pour l'automatisation des taches dans mon cours sur [Pluralsight](http://jpapa.me/gulpps). - -###### [Style [Y400](#style-y400)] - - - Utilisez l'automatisation des tâches pour lister les fichiers de définition de module `*.module.js` avant tout autre fichier JavaScript de l'application. - - *Pourquoi ?* : Angular a besoin que la définition des modules soit déclarée avant qu'ils puissent être utilisés. - - *Pourquoi ?* : Nommer les modules avec un pattern spécifique tel que `*.module.js` les rends faciles à aller chercher avec une expression englobante et à les lister en premier. - - ```javascript - var clientApp = './src/client/app/'; - - // Toujours aller chercher les fichiers de module en premier - var files = [ - clientApp + '**/*.module.js', - clientApp + '**/*.js' - ]; - ``` - -**[Retour en haut de page](#table-des-matières)** - -## Filtres - -###### [Style [Y420](#style-y420)] - - - Évitez d'utiliser les filtres pour scanner toutes les propriété de l'arborescence d'un objet complexe. Utilisez les filtres pour sélectionner des propriétés. - - *Pourquoi ?*: les filtres peuvent être surutilisés et peuvent avoir des effets négatifs sur les performances s'ils ne sont pas utilisés de façon appropriée. Par exemple, quand un filtre touche un gros objet dont l'arborescence est profonde. - -**[Back to top](#table-of-contents)** - -## Documentation Angular -Pour tout le reste, la référence des API, allez voir la [documentation Angular](//docs.angularjs.org/api). - -## Contribuer - -Créez d'abord un problème pour discuter de potentiels changements ou ajouts. Si vous avez des questions sur le guide, je vous encourage à les rapporter comme problèmes dans le référentiel. Si vous trouvez une erreur de frappe, créez une pull request. L'idée est de garder le contenu à jour et d'utiliser les fonctionnalités natives de github pour aider à raporter les problèmes et les pull requests, lesquels sont recherchables via google. Pourquoi ? Parce que d'autres pourraient avoir la même question ! Vous en apprendrez plus ci-dessous pour savoir comment contribuer. - -*En contribuant à ce référentiel, vous acceptez de rendre votre contenu accessible le sujet de la licence de ce référentiel.* - -### Processus - 1. Discuter des changements dans une Issue GitHub. - 2. Ouvrir une Pull Request sur la branche develop, référencer l'Issue, et expliquer le changement et la raison pour laquelle ce changement est pertinent. - 3. La Pull Request sera évaluée et soit mergée ou abandonnée. - -## License - -_tldr; Utilisez ce guide. Les attributions sont appréciées._ - -### 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. - -**[Retour en haut de page](#table-des-matières)**