{"id":13547213,"url":"https://github.com/toddmotto/angularjs-styleguide","last_synced_at":"2025-05-14T10:07:18.712Z","repository":{"id":19081912,"uuid":"22309429","full_name":"toddmotto/angularjs-styleguide","owner":"toddmotto","description":"AngularJS styleguide for teams","archived":false,"fork":false,"pushed_at":"2022-08-24T23:24:49.000Z","size":325,"stargazers_count":5965,"open_issues_count":23,"forks_count":699,"subscribers_count":326,"default_branch":"master","last_synced_at":"2024-10-29T15:18:55.815Z","etag":null,"topics":["angular","angularjs","es2015","typescript"],"latest_commit_sha":null,"homepage":"https://ultimateangular.com","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/toddmotto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-07-27T11:31:16.000Z","updated_at":"2024-10-20T16:52:17.000Z","dependencies_parsed_at":"2022-09-01T10:00:44.765Z","dependency_job_id":null,"html_url":"https://github.com/toddmotto/angularjs-styleguide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toddmotto%2Fangularjs-styleguide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toddmotto%2Fangularjs-styleguide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toddmotto%2Fangularjs-styleguide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toddmotto%2Fangularjs-styleguide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/toddmotto","download_url":"https://codeload.github.com/toddmotto/angularjs-styleguide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248335540,"owners_count":21086611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","angularjs","es2015","typescript"],"created_at":"2024-08-01T12:00:52.549Z","updated_at":"2025-04-11T03:32:54.124Z","avatar_url":"https://github.com/toddmotto.png","language":null,"readme":"# AngularJS styleguide (ES2015)\n\n### Up-to-date with AngularJS 1.6 best practices. Architecture, file structure, components, one-way dataflow, lifecycle hooks.\n\n\u003cdiv align=\"center\"\u003e\n\u003ca href=\"https://ultimatecourses.com/courses/angular\" target=\"_blank\"\u003e\u003cimg width=\"100%\" src=\"https://ultimatecourses.com/static/banners/ultimate-angular-github.svg\"\u003e\u003c/a\u003e\n\u003c/div\u003e\n\n---\n\n\u003e Want an example structure as reference? Check out my [component based architecture 1.5 app](https://github.com/toddmotto/angular-1-5-components-app).\n\n---\n\n*A sensible styleguide for teams by [@toddmotto](//twitter.com/toddmotto)*\n\nThis architecture and styleguide has been rewritten from the ground up for ES2015, the changes in AngularJS 1.5+ for future-upgrading your application to Angular. This guide includes new best practices for one-way dataflow, event delegation, component architecture and component routing.\n\nYou can find the old styleguide [here](https://github.com/toddmotto/angular-styleguide/tree/angular-old-es5), and the reasoning behind the new one [here](https://toddmotto.com/rewriting-angular-styleguide-angular-2).\n\n\u003e Join the Ultimate AngularJS learning experience to fully master beginner and advanced AngularJS features to build real-world apps that are fast, and scale.\n\n\u003ca href=\"https://ultimateangular.com\" target=\"_blank\"\u003e\u003cimg src=\"https://ultimateangular.com/assets/img/banners/ua-github.svg\"\u003e\u003c/a\u003e\n\n## Table of Contents\n\n1. [Modular architecture](#modular-architecture)\n    1. [Theory](#module-theory)\n    1. [Root module](#root-module)\n    1. [Component module](#component-module)\n    1. [Common module](#common-module)\n    1. [Low-level modules](#low-level-modules)\n    1. [File naming conventions](#file-naming-conventions)\n    1. [Scalable file structure](#scalable-file-structure)\n1. [Components](#components)\n    1. [Theory](#component-theory)\n    1. [Supported properties](#supported-properties)\n    1. [Controllers](#controllers)\n    1. [One-way dataflow and Events](#one-way-dataflow-and-events)\n    1. [Stateful Components](#stateful-components)\n    1. [Stateless Components](#stateless-components)\n    1. [Routed Components](#routed-components)\n1. [Directives](#directives)\n    1. [Theory](#directive-theory)\n    1. [Recommended properties](#recommended-properties)\n    1. [Constants or Classes](#constants-or-classes)\n1. [Services](#services)\n    1. [Theory](#service-theory)\n    1. [Classes for Service](#classes-for-service)\n1. [Styles](#styles)\n1. [ES2015 and Tooling](#es2015-and-tooling)\n1. [State management](#state-management)\n1. [Resources](#resources)\n1. [Documentation](#documentation)\n1. [Contributing](#contributing)\n\n# Modular architecture\n\nEach module in an Angular app is a module component. A module component is the root definition for that module that encapsulates the logic, templates, routing and child components.\n\n### Module theory\n\nThe design in the modules maps directly to our folder structure, which keeps things maintainable and predictable. We should ideally have three high-level modules: root, component and common. The root module defines the base module that bootstraps our app, and the corresponding template. We then import our component and common modules into the root module to include our dependencies. The component and common modules then require lower-level component modules, which contain our components, controllers, services, directives, filters and tests for each reusable feature.\n\n**[Back to top](#table-of-contents)**\n\n### Root module\n\nA root module begins with a root component that defines the base element for the entire application, with a routing outlet defined, example shown using `ui-view` from `ui-router`.\n\n```js\n// app.component.js\nexport const AppComponent = {\n  template: `\n    \u003cheader\u003e\n        Hello world\n    \u003c/header\u003e\n    \u003cdiv\u003e\n        \u003cdiv ui-view\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cfooter\u003e\n        Copyright MyApp 2016.\n    \u003c/footer\u003e\n  `\n};\n```\n\nA root module is then created, with `AppComponent` imported and registered with `.component('app', AppComponent)`. Further imports for submodules (component and common modules) are made to include all components relevant for the application. You'll notice styles are also being imported here, we'll come onto this in later chapters in this guide.\n\n```js\n// app.module.js\nimport angular from 'angular';\nimport uiRouter from 'angular-ui-router';\nimport { AppComponent } from './app.component';\nimport { ComponentsModule } from './components/components.module';\nimport { CommonModule } from './common/common.module';\nimport './app.scss';\n\nexport const AppModule = angular\n  .module('app', [\n    ComponentsModule,\n    CommonModule,\n    uiRouter\n  ])\n  .component('app', AppComponent)\n  .name;\n```\n\n**[Back to top](#table-of-contents)**\n\n### Component module\n\nA Component module is the container reference for all reusable components. See above how we import `ComponentsModule` and inject them into the Root module, this gives us a single place to import all components for the app. These modules we require are decoupled from all other modules and thus can be moved into any other application with ease.\n\n```js\n// components/components.module.js\nimport angular from 'angular';\nimport { CalendarModule } from './calendar/calendar.module';\nimport { EventsModule } from './events/events.module';\n\nexport const ComponentsModule = angular\n  .module('app.components', [\n    CalendarModule,\n    EventsModule\n  ])\n  .name;\n```\n\n**[Back to top](#table-of-contents)**\n\n### Common module\n\nThe Common module is the container reference for all application specific components, that we don't want to use in another application. This can be things like layout, navigation and footers. See above how we import `CommonModule` and inject them into the Root module, this gives us a single place to import all common components for the app.\n\n```js\n// common/common.module.js\nimport angular from 'angular';\nimport { NavModule } from './nav/nav.module';\nimport { FooterModule } from './footer/footer.module';\n\nexport const CommonModule = angular\n  .module('app.common', [\n    NavModule,\n    FooterModule\n  ])\n  .name;\n```\n\n**[Back to top](#table-of-contents)**\n\n### Low-level modules\n\nLow-level modules are individual component modules that contain the logic for each feature block. These will each define a module, to be imported to a higher-level module, such as a component or common module, an example below. Always remember to add the `.name` suffix to each `export` when creating a _new_ module, not when referencing one. You'll noticed routing definitions also exist here, we'll come onto this in later chapters in this guide.\n\n```js\n// calendar/calendar.module.js\nimport angular from 'angular';\nimport uiRouter from 'angular-ui-router';\nimport { CalendarComponent } from './calendar.component';\nimport './calendar.scss';\n\nexport const CalendarModule = angular\n  .module('calendar', [\n    uiRouter\n  ])\n  .component('calendar', CalendarComponent)\n  .config(($stateProvider, $urlRouterProvider) =\u003e {\n    'ngInject';\n    $stateProvider\n      .state('calendar', {\n        url: '/calendar',\n        component: 'calendar'\n      });\n    $urlRouterProvider.otherwise('/');\n  })\n  .name;\n```\n\n**[Back to top](#table-of-contents)**\n\n### File naming conventions\n\nKeep it simple and lowercase, use the component name, e.g. `calendar.*.js*`, `calendar-grid.*.js` - with the name of the type of file in the middle. Use `*.module.js` for the module definition file, as it keeps it verbose and consistent with Angular.\n\n```\ncalendar.module.js\ncalendar.component.js\ncalendar.service.js\ncalendar.directive.js\ncalendar.filter.js\ncalendar.spec.js\ncalendar.html\ncalendar.scss\n```\n\n**[Back to top](#table-of-contents)**\n\n### Scalable file structure\n\nFile structure is extremely important, this describes a scalable and predictable structure. An example file structure to illustrate a modular component architecture.\n\n```\n├── app/\n│   ├── components/\n│   │  ├── calendar/\n│   │  │  ├── calendar.module.js\n│   │  │  ├── calendar.component.js\n│   │  │  ├── calendar.service.js\n│   │  │  ├── calendar.spec.js\n│   │  │  ├── calendar.html\n│   │  │  ├── calendar.scss\n│   │  │  └── calendar-grid/\n│   │  │     ├── calendar-grid.module.js\n│   │  │     ├── calendar-grid.component.js\n│   │  │     ├── calendar-grid.directive.js\n│   │  │     ├── calendar-grid.filter.js\n│   │  │     ├── calendar-grid.spec.js\n│   │  │     ├── calendar-grid.html\n│   │  │     └── calendar-grid.scss\n│   │  ├── events/\n│   │  │  ├── events.module.js\n│   │  │  ├── events.component.js\n│   │  │  ├── events.directive.js\n│   │  │  ├── events.service.js\n│   │  │  ├── events.spec.js\n│   │  │  ├── events.html\n│   │  │  ├── events.scss\n│   │  │  └── events-signup/\n│   │  │     ├── events-signup.module.js\n│   │  │     ├── events-signup.component.js\n│   │  │     ├── events-signup.service.js\n│   │  │     ├── events-signup.spec.js\n│   │  │     ├── events-signup.html\n│   │  │     └── events-signup.scss\n│   │  └── components.module.js\n│   ├── common/\n│   │  ├── nav/\n│   │  │     ├── nav.module.js\n│   │  │     ├── nav.component.js\n│   │  │     ├── nav.service.js\n│   │  │     ├── nav.spec.js\n│   │  │     ├── nav.html\n│   │  │     └── nav.scss\n│   │  ├── footer/\n│   │  │     ├── footer.module.js\n│   │  │     ├── footer.component.js\n│   │  │     ├── footer.service.js\n│   │  │     ├── footer.spec.js\n│   │  │     ├── footer.html\n│   │  │     └── footer.scss\n│   │  └── common.module.js\n│   ├── app.module.js\n│   ├── app.component.js\n│   └── app.scss\n└── index.html\n```\n\nThe high level folder structure simply contains `index.html` and `app/`, a directory in which all our root, component, common and low-level modules live along with the markup and styles for each component.\n\n**[Back to top](#table-of-contents)**\n\n# Components\n\n### Component theory\n\nComponents are essentially templates with a controller. They are _not_ Directives, nor should you replace Directives with Components, unless you are upgrading \"template Directives\" with controllers, which are best suited as a component. Components also contain bindings that define inputs and outputs for data and events, lifecycle hooks and the ability to use one-way data flow and event Objects to get data back up to a parent component. These are the new defacto standard in AngularJS 1.5 and above. Everything template and controller driven that we create will likely be a component, which may be a stateful, stateless or routed component. You can think of a \"component\" as a complete piece of code, not just the `.component()` definition Object. Let's explore some best practices and advisories for components, then dive into how you should be structuring them via stateful, stateless and routed component concepts.\n\n**[Back to top](#table-of-contents)**\n\n### Supported properties\n\nThese are the supported properties for `.component()` that you can/should use:\n\n| Property | Support |\n|---|---|\n| bindings | Yes, use `'@'`, `'\u003c'`, `'\u0026'` only |\n| controller | Yes |\n| controllerAs | Yes, default is `$ctrl` |\n| require | Yes (new Object syntax) |\n| template | Yes |\n| templateUrl | Yes |\n| transclude | Yes |\n\n**[Back to top](#table-of-contents)**\n\n### Controllers\n\nControllers should only be used alongside components, never anywhere else. If you feel you need a controller, what you really need is likely a stateless component to manage that particular piece of behaviour.\n\nHere are some advisories for using `Class` for controllers:\n\n* Drop the name \"Controller\", i.e. use `controller: class TodoComponent {...}` to aid future Angular migration\n* Always use the `constructor` for dependency injection purposes\n* Use [ng-annotate](https://github.com/olov/ng-annotate)'s `'ngInject';` syntax for `$inject` annotations\n* If you need to access the lexical scope, use arrow functions\n* Alternatively to arrow functions, `let ctrl = this;` is also acceptable and may make more sense depending on the use case\n* Bind all public functions directly to the `Class`\n* Make use of the appropriate lifecycle hooks, `$onInit`, `$onChanges`, `$postLink` and `$onDestroy`\n  * Note: `$onChanges` is called before `$onInit`, see [resources](#resources) section for articles detailing this in more depth\n* Use `require` alongside `$onInit` to reference any inherited logic\n* Do not override the default `$ctrl` alias for the `controllerAs` syntax, therefore do not use `controllerAs` anywhere\n\n**[Back to top](#table-of-contents)**\n\n### One-way dataflow and Events\n\nOne-way dataflow was introduced in AngularJS 1.5, and redefines component communication.\n\nHere are some advisories for using one-way dataflow:\n\n* In components that receive data, always use one-way databinding syntax `'\u003c'`\n* _Do not_ use `'='` two-way databinding syntax anymore, anywhere\n* Components that have `bindings` should use `$onChanges` to clone the one-way binding data to break Objects passing by reference and updating the parent data\n* Use `$event` as a function argument in the parent method (see stateful example below `$ctrl.addTodo($event)`)\n* Pass an `$event: {}` Object back up from a stateless component (see stateless example below `this.onAddTodo`).\n  * Bonus: Use an `EventEmitter` wrapper with `.value()` to mirror Angular, avoids manual `$event` Object creation\n* Why? This mirrors Angular and keeps consistency inside every component. It also makes state predictable.\n\n**[Back to top](#table-of-contents)**\n\n### Stateful components\n\nLet's define what we'd call a \"stateful component\".\n\n* Fetches state, essentially communicating to a backend API through a service\n* Does not directly mutate state\n* Renders child components that mutate state\n* Also referred to as smart/container components\n\nAn example of a stateful component, complete with its low-level module definition (this is only for demonstration, so some code has been omitted for brevity):\n\n```js\n/* ----- todo/todo.component.js ----- */\nimport templateUrl from './todo.html';\n\nexport const TodoComponent = {\n  templateUrl,\n  controller: class TodoComponent {\n    constructor(TodoService) {\n      'ngInject';\n      this.todoService = TodoService;\n    }\n    $onInit() {\n      this.newTodo = {\n        title: '',\n        selected: false\n      };\n      this.todos = [];\n      this.todoService.getTodos().then(response =\u003e this.todos = response);\n    }\n    addTodo({ todo }) {\n      if (!todo) return;\n      this.todos.unshift(todo);\n      this.newTodo = {\n        title: '',\n        selected: false\n      };\n    }\n  }\n};\n\n/* ----- todo/todo.html ----- */\n\u003cdiv class=\"todo\"\u003e\n  \u003ctodo-form\n    todo=\"$ctrl.newTodo\"\n    on-add-todo=\"$ctrl.addTodo($event);\"\u003e\u003c/todo-form\u003e\n  \u003ctodo-list\n    todos=\"$ctrl.todos\"\u003e\u003c/todo-list\u003e\n\u003c/div\u003e\n\n/* ----- todo/todo.module.js ----- */\nimport angular from 'angular';\nimport { TodoComponent } from './todo.component';\nimport './todo.scss';\n\nexport const TodoModule = angular\n  .module('todo', [])\n  .component('todo', TodoComponent)\n  .name;\n```\n\nThis example shows a stateful component, that fetches state inside the controller, through a service, and then passes it down into stateless child components. Notice how there are no Directives being used such as `ng-repeat` and friends inside the template. Instead, data and functions are delegated into `\u003ctodo-form\u003e` and `\u003ctodo-list\u003e` stateless components.\n\n**[Back to top](#table-of-contents)**\n\n### Stateless components\n\nLet's define what we'd call a \"stateless component\".\n\n* Has defined inputs and outputs using `bindings: {}`\n* Data enters the component through attribute bindings (inputs)\n* Data leaves the component through events (outputs)\n* Mutates state, passes data back up on-demand (such as a click or submit event)\n* Doesn't care where data comes from - it's stateless\n* Are highly reusable components\n* Also referred to as dumb/presentational components\n\nAn example of a stateless component (let's use `\u003ctodo-form\u003e` as an example), complete with its low-level module definition (this is only for demonstration, so some code has been omitted for brevity):\n\n```js\n/* ----- todo/todo-form/todo-form.component.js ----- */\nimport templateUrl from './todo-form.html';\n\nexport const TodoFormComponent = {\n  bindings: {\n    todo: '\u003c',\n    onAddTodo: '\u0026'\n  },\n  templateUrl,\n  controller: class TodoFormComponent {\n    constructor(EventEmitter) {\n        'ngInject';\n        this.EventEmitter = EventEmitter;\n    }\n    $onChanges(changes) {\n      if (changes.todo) {\n        this.todo = Object.assign({}, this.todo);\n      }\n    }\n    onSubmit() {\n      if (!this.todo.title) return;\n      // with EventEmitter wrapper\n      this.onAddTodo(\n        this.EventEmitter({\n          todo: this.todo\n        })\n      );\n      // without EventEmitter wrapper\n      this.onAddTodo({\n        $event: {\n          todo: this.todo\n        }\n      });\n    }\n  }\n};\n\n/* ----- todo/todo-form/todo-form.html ----- */\n\u003cform name=\"todoForm\" ng-submit=\"$ctrl.onSubmit();\"\u003e\n  \u003cinput type=\"text\" ng-model=\"$ctrl.todo.title\"\u003e\n  \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n\u003c/form\u003e\n\n/* ----- todo/todo-form/todo-form.module.js ----- */\nimport angular from 'angular';\nimport { TodoFormComponent } from './todo-form.component';\nimport './todo-form.scss';\n\nexport const TodoFormModule = angular\n  .module('todo.form', [])\n  .component('todoForm', TodoFormComponent)\n  .value('EventEmitter', payload =\u003e ({ $event: payload }))\n  .name;\n```\n\nNote how the `\u003ctodo-form\u003e` component fetches no state, it simply receives it, mutates an Object via the controller logic associated with it, and passes it back to the parent component through the property bindings. In this example, the `$onChanges` lifecycle hook makes a clone of the initial `this.todo` binding Object and reassigns it, which means the parent data is not affected until we submit the form, alongside one-way data flow new binding syntax `'\u003c'`.\n\n**[Back to top](#table-of-contents)**\n\n### Routed components\n\nLet's define what we'd call a \"routed component\".\n\n* It's essentially a stateful component, with routing definitions\n* No more `router.js` files\n* We use Routed components to define their own routing logic\n* Data \"input\" for the component is done via the route resolve (optional, still available in the controller with service calls)\n\nFor this example, we're going to take the existing `\u003ctodo\u003e` component, refactor it to use a route definition and `bindings` on the component which receives data (the secret here with `ui-router` is the `resolve` properties we create, in this case `todoData` directly map across to `bindings` for us). We treat it as a routed component because it's essentially a \"view\":\n\n```js\n/* ----- todo/todo.component.js ----- */\nimport templateUrl from './todo.html';\n\nexport const TodoComponent = {\n  bindings: {\n    todoData: '\u003c'\n  },\n  templateUrl,\n  controller: class TodoComponent {\n    constructor() {\n      'ngInject'; // Not actually needed but best practice to keep here incase dependencies needed in the future\n    }\n    $onInit() {\n      this.newTodo = {\n        title: '',\n        selected: false\n      };\n    }\n    $onChanges(changes) {\n      if (changes.todoData) {\n        this.todos = Object.assign({}, this.todoData);\n      }\n    }\n    addTodo({ todo }) {\n      if (!todo) return;\n      this.todos.unshift(todo);\n      this.newTodo = {\n        title: '',\n        selected: false\n      };\n    }\n  }\n};\n\n/* ----- todo/todo.html ----- */\n\u003cdiv class=\"todo\"\u003e\n  \u003ctodo-form\n    todo=\"$ctrl.newTodo\"\n    on-add-todo=\"$ctrl.addTodo($event);\"\u003e\u003c/todo-form\u003e\n  \u003ctodo-list\n    todos=\"$ctrl.todos\"\u003e\u003c/todo-list\u003e\n\u003c/div\u003e\n\n/* ----- todo/todo.service.js ----- */\nexport class TodoService {\n  constructor($http) {\n    'ngInject';\n    this.$http = $http;\n  }\n  getTodos() {\n    return this.$http.get('/api/todos').then(response =\u003e response.data);\n  }\n}\n\n/* ----- todo/todo.module.js ----- */\nimport angular from 'angular';\nimport uiRouter from 'angular-ui-router';\nimport { TodoComponent } from './todo.component';\nimport { TodoService } from './todo.service';\nimport './todo.scss';\n\nexport const TodoModule = angular\n  .module('todo', [\n    uiRouter\n  ])\n  .component('todo', TodoComponent)\n  .service('TodoService', TodoService)\n  .config(($stateProvider, $urlRouterProvider) =\u003e {\n    'ngInject';\n    $stateProvider\n      .state('todos', {\n        url: '/todos',\n        component: 'todo',\n        resolve: {\n          todoData: TodoService =\u003e TodoService.getTodos()\n        }\n      });\n    $urlRouterProvider.otherwise('/');\n  })\n  .name;\n```\n\n**[Back to top](#table-of-contents)**\n\n# Directives\n\n### Directive theory\n\nDirectives gives us `template`, `scope` bindings, `bindToController`, `link` and many other things. The usage of these should be carefully considered now that `.component()` exists. Directives should not declare templates and controllers anymore, or receive data through bindings. Directives should be used solely for decorating the DOM. By this, it means extending existing HTML - created with `.component()`. In a simple sense, if you need custom DOM events/APIs and logic, use a Directive and bind it to a template inside a component. If you need a sensible amount of DOM manipulation, there is also the `$postLink` lifecycle hook to consider, however this is not a place to migrate all your DOM manipulation to, use a Directive if you can for non-Angular things.\n\nHere are some advisories for using Directives:\n\n* Never use templates, scope, bindToController or controllers\n* Always `restrict: 'A'` with Directives\n* Use compile and link where necessary\n* Remember to destroy and unbind event handlers inside `$scope.$on('$destroy', fn);`\n\n**[Back to top](#table-of-contents)**\n\n### Recommended properties\n\nDue to the fact directives support most of what `.component()` does (template directives were the original component), I'm recommending limiting your directive Object definitions to only these properties, to avoid using directives incorrectly:\n\n| Property | Use it? | Why |\n|---|---|---|\n| bindToController | No | Use `bindings` in components |\n| compile | Yes | For pre-compile DOM manipulation/events |\n| controller | No | Use a component |\n| controllerAs | No | Use a component |\n| link functions | Yes | For pre/post DOM manipulation/events |\n| multiElement | Yes | [See docs](https://docs.angularjs.org/api/ng/service/$compile#-multielement-) |\n| priority | Yes | [See docs](https://docs.angularjs.org/api/ng/service/$compile#-priority-) |\n| require | No | Use a component |\n| restrict | Yes | Defines directive usage, always use `'A'` |\n| scope | No | Use a component |\n| template | No | Use a component |\n| templateNamespace | Yes (if you must) | [See docs](https://docs.angularjs.org/api/ng/service/$compile#-templatenamespace-) |\n| templateUrl | No | Use a component |\n| transclude | No | Use a component |\n\n**[Back to top](#table-of-contents)**\n\n### Constants or Classes\n\nThere are a few ways to approach using ES2015 and directives, either with an arrow function and easier assignment, or using an ES2015 `Class`. Choose what's best for you or your team, keep in mind Angular uses `Class`.\n\nHere's an example using a constant with an Arrow function an expression wrapper `() =\u003e ({})` returning an Object literal (note the usage differences inside `.directive()`):\n\n```js\n/* ----- todo/todo-autofocus.directive.js ----- */\nimport angular from 'angular';\n\nexport const TodoAutoFocus = ($timeout) =\u003e {\n  'ngInject';\n  return {\n    restrict: 'A',\n    link($scope, $element, $attrs) {\n      $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) =\u003e {\n        if (!newValue) {\n          return;\n        }\n        $timeout(() =\u003e $element[0].focus());\n      });\n    }\n  }\n};\n\n/* ----- todo/todo.module.js ----- */\nimport angular from 'angular';\nimport { TodoComponent } from './todo.component';\nimport { TodoAutofocus } from './todo-autofocus.directive';\nimport './todo.scss';\n\nexport const TodoModule = angular\n  .module('todo', [])\n  .component('todo', TodoComponent)\n  .directive('todoAutofocus', TodoAutoFocus)\n  .name;\n```\n\nOr using ES2015 `Class` (note manually calling `new TodoAutoFocus` when registering the directive) to create the Object:\n\n```js\n/* ----- todo/todo-autofocus.directive.js ----- */\nimport angular from 'angular';\n\nexport class TodoAutoFocus {\n  constructor($timeout) {\n    'ngInject';\n    this.restrict = 'A';\n    this.$timeout = $timeout;\n  }\n  link($scope, $element, $attrs) {\n    $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) =\u003e {\n      if (!newValue) {\n        return;\n      }\n      this.$timeout(() =\u003e $element[0].focus());\n    });\n  }\n}\n\n/* ----- todo/todo.module.js ----- */\nimport angular from 'angular';\nimport { TodoComponent } from './todo.component';\nimport { TodoAutofocus } from './todo-autofocus.directive';\nimport './todo.scss';\n\nexport const TodoModule = angular\n  .module('todo', [])\n  .component('todo', TodoComponent)\n  .directive('todoAutofocus', ($timeout) =\u003e new TodoAutoFocus($timeout))\n  .name;\n```\n\n**[Back to top](#table-of-contents)**\n\n# Services\n\n### Service theory\n\nServices are essentially containers for business logic that our components shouldn't request directly. Services contain other built-in or external services such as `$http`, that we can then inject into component controllers elsewhere in our app. We have two ways of doing services, using `.service()` or `.factory()`. With ES2015 `Class`, we should only use `.service()`, complete with dependency injection annotation using `$inject`.\n\n**[Back to top](#table-of-contents)**\n\n### Classes for Service\n\nHere's an example implementation for our `\u003ctodo\u003e` app using ES2015 `Class`:\n\n```js\n/* ----- todo/todo.service.js ----- */\nexport class TodoService {\n  constructor($http) {\n    'ngInject';\n    this.$http = $http;\n  }\n  getTodos() {\n    return this.$http.get('/api/todos').then(response =\u003e response.data);\n  }\n}\n\n/* ----- todo/todo.module.js ----- */\nimport angular from 'angular';\nimport { TodoComponent } from './todo.component';\nimport { TodoService } from './todo.service';\nimport './todo.scss';\n\nexport const TodoModule = angular\n  .module('todo', [])\n  .component('todo', TodoComponent)\n  .service('TodoService', TodoService)\n  .name;\n```\n\n**[Back to top](#table-of-contents)**\n\n# Styles\n\nUsing [Webpack](https://webpack.github.io/) we can now use `import` statements on our `.scss` files in our `*.module.js` to let Webpack know to include that file in our stylesheet. Doing this lets us keep our components isolated for both functionality and style; it also aligns more closely to how stylesheets are declared for use in Angular. Doing this won't isolate our styles to just that component like it does with Angular; the styles will still be usable application wide but it is more manageable and makes our applications structure easier to reason about.\n\nIf you have some variables or globally used styles like form input elements then these files should still be placed into the root `scss` folder. e.g. `scss/_forms.scss`. These global styles can then be `@imported` into your root module (`app.module.js`) stylesheet like you would normally do.\n\n**[Back to top](#table-of-contents)**\n\n# ES2015 and Tooling\n\n##### ES2015\n\n* Use [Babel](https://babeljs.io/) to compile your ES2015+ code and any polyfills\n* Consider using [TypeScript](http://www.typescriptlang.org/) to make way for any Angular upgrades\n\n##### Tooling\n* Use `ui-router` [latest alpha](https://github.com/angular-ui/ui-router) (see the Readme) if you want to support component-routing\n  * Otherwise you're stuck with `template: '\u003ccomponent\u003e'` and no `bindings`/resolve mapping\n* Consider preloading templates into `$templateCache` with `angular-templates` or `ngtemplate-loader`\n  * [Gulp version](https://www.npmjs.com/package/gulp-angular-templatecache)\n  * [Grunt version](https://www.npmjs.com/package/grunt-angular-templates)\n  * [Webpack version](https://github.com/WearyMonkey/ngtemplate-loader)\n* Consider using [Webpack](https://webpack.github.io/) for compiling your ES2015 code and styles\n* Use [ngAnnotate](https://github.com/olov/ng-annotate) to automatically annotate `$inject` properties\n* How to use [ngAnnotate with ES6](https://www.timroes.de/2015/07/29/using-ecmascript-6-es6-with-angularjs-1-x/#ng-annotate)\n\n**[Back to top](#table-of-contents)**\n\n# State management\n\nConsider using Redux with AngularJS 1.5 for data management.\n\n* [Angular Redux](https://github.com/angular-redux/ng-redux)\n\n**[Back to top](#table-of-contents)**\n\n# Resources\n\n* [Stateful and stateless components, the missing manual](https://toddmotto.com/stateful-stateless-components)\n* [Understanding the .component() method](https://toddmotto.com/exploring-the-angular-1-5-component-method/)\n* [Using \"require\" with $onInit](https://toddmotto.com/on-init-require-object-syntax-angular-component/)\n* [Understanding all the lifecycle hooks, $onInit, $onChanges, $postLink, $onDestroy](https://toddmotto.com/angular-1-5-lifecycle-hooks)\n* [Using \"resolve\" in routes](https://toddmotto.com/resolve-promises-in-angular-routes/)\n* [Redux and Angular state management](http://blog.rangle.io/managing-state-redux-angular/)\n* [Sample Application from Community](https://github.com/chihab/angular-styleguide-sample)\n\n**[Back to top](#table-of-contents)**\n\n# Documentation\nFor anything else, including API reference, check the [AngularJS documentation](//docs.angularjs.org/api).\n\n# Contributing\n\nOpen an issue first to discuss potential changes/additions. Please don't open issues for questions.\n\n## License\n\n#### (The MIT License)\n\nCopyright (c) 2016-2018 Todd Motto\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","funding_links":[],"categories":["Others","代码规范\u0026设计模式","AngularJS","JavaScript","AngularJS ##"],"sub_categories":["AngularJS","Version 1.x ###"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoddmotto%2Fangularjs-styleguide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftoddmotto%2Fangularjs-styleguide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoddmotto%2Fangularjs-styleguide/lists"}