{"id":20549518,"url":"https://github.com/akullpp/styleguide","last_synced_at":"2026-03-19T16:20:58.546Z","repository":{"id":79174011,"uuid":"50683554","full_name":"akullpp/styleguide","owner":"akullpp","description":"AngularJS 1.5+ Enterprise Styleguide","archived":false,"fork":false,"pushed_at":"2017-11-03T11:55:02.000Z","size":13,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-16T16:42:40.527Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/akullpp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-01-29T18:49:29.000Z","updated_at":"2023-09-16T21:25:21.000Z","dependencies_parsed_at":"2023-05-29T23:45:49.508Z","dependency_job_id":null,"html_url":"https://github.com/akullpp/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/akullpp%2Fstyleguide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akullpp%2Fstyleguide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akullpp%2Fstyleguide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akullpp%2Fstyleguide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/akullpp","download_url":"https://codeload.github.com/akullpp/styleguide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242153820,"owners_count":20080566,"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":[],"created_at":"2024-11-16T02:18:17.798Z","updated_at":"2026-03-08T19:39:10.519Z","avatar_url":"https://github.com/akullpp.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# AngularJS 1.5+ Enterprise Styleguide\n\nLessons learned from dozens of Angular enterprise projects.\n\nA small glimpse of a concrete application can be found at [akSkeleton](https://github.com/akullpp/akSkeleton).\n\n## Table Of Contents\n\n* [EcmaScript 5](#ecmascript-5)\n* [HTML](#html)\n    * [HTML5 Conformity](#html5-conformity)\n    * [General](#general)\n* [AngularJS](#angularjs)\n    * [Folder Structure](#folder-structure)\n    * [Names](#names)\n        * [Modules](#modules)\n        * [Layers](#layers)\n        * [States](#states)\n        * [Components, Directives and Filters](#components-directives-and-filters)\n    * [Databinding](#databinding)\n    * [Module](#module)\n    * [Dependency Injection](#dependency-injection)\n    * [Components](components)\n        * [IIFE](#iife)\n        * [Constants](#constants)\n        * [Component](#component)\n        * [Directives](#directives)\n            * [Element Directives](#element-directives)\n            * [Attribute Directives](#attribute-directives)\n        * [Filter](#filter)\n        * [Controller](#controller)\n        * [Services](#services)\n            * [Service pattern](#service-pattern)\n            * [Resource Model Pattern](#resource-model-pattern)\n    * [Promises](#promises)\n    * [Scope, Watches, Emits, Broadcasts](#scope-watches-emits-broadcasts)\n    * [Wrapper](#wrapper)\n    * [Third-party libraries](third-party-libraries)\n    * [Software Engineering Principles](software-engineering-principles)\n\n## EcmaScript 5\n\n[Airbnb](https://github.com/airbnb/javascript/tree/es5-deprecated/es5).\n\n## HTML\n\n### HTML5 Conformity\n\nIf you want to reach conformity with HTML5 you will need to do the following:\n\n1. Non-HTML5 attributes must have a `data-` prefix.\n\n2. Non-HTML5 tags must contain a `-`. Normally, this is achieved via a project prefix `ui-` for Angular Bootstrap components.\n\nIf you don't care about HTML5 conformity, you won't need to do this.\n\n### General\n\n1. The order of attributes should be consistent and preferably:\n\n    ```\n    id \u003e class \u003e name \u003e custom \u003e third-party \u003e Angular \u003e validation\n    ```\n\n    For example:\n\n    ```html\n    \u003cinput id=\"foo\"\n        class=\"bar\"\n        data-ak-foo\n        data-ui-foo\n        data-ng-bind\n        required\u003e\n    ```\n\n2. Elements with attributes on multiple lines should follow this pattern:\n\n    ```html\n    \u003cinput data-ng-bind=\"\"\n        data-ng-required=\"\"\u003e\n    ```\n\n3. Empty elements should close on the same line:\n\n    ```html\n    \u003c!-- Wrong --\u003e\n    \u003cspan data-ng-bind=\"\"\n        data-ng-required=\"\"\u003e\n    \u003c/span\u003e\n\n    \u003c!-- Correct --\u003e\n    \u003cspan data-ng-bind=\"\"\n        data-ng-required=\"\"\u003e\u003c/span\u003e\n    ```\n\n4. Multiple elements on the same indentation level should be separated visually by newlines:\n\n    ```html\n    \u003cdiv\u003e\u003c/div\u003e\n\n    \u003cdiv\u003e\u003c/div\u003e\n\n    \u003cdiv\u003e\u003c/div\u003e\n    ```\n\n## AngularJS\n\n### Folder structure\n\nKeep a flat feature-based structure:\n\n```\naccount/\n    account.js\n    account.ui.js\n    account.html\n```\n\nDon't use a functional structure:\n\n```\nservices/\n    account.js\n```\n\nAvoid directories with general names if possible:\n\n```\ncommon/\nutility/\n```\n\n### Names\n\n#### Modules\n\nYou should decide on a project prefix, e.g. `ak` and apply the following hierarchical conventions:\n\n```js\nangular\n    .module('ak');\n\nangular\n    .module('ak.foo');\n\nangular\n    .module('ak.foo.bar');\n```\n\n#### Layers\n\nSplit your files into representation and logic containers.\n\n1. Submodules with view components are named `prefix.name.ui` located in `name.ui.js`, e.g. `ak.customer.ui` in `customer.ui.js`\n2. Submodules with model or logic components: `prefix.name` located in `name.js`, e.g. `ak.customer` in `customer.js`\n\nRepresentation components are typically `directive`, `component`, `filter` and States with the notable exception of `service` for modals.\n\nLogic components are typically `service` and do either plain logic or API communication or a mixture of both.\n\n#### States\n\nStates are very tied to modules, e.g.\n\n```js\n    angular\n        .module('ak.customers.ui', [\n            'ui.router'\n        ])\n        .config(route);\n\n    function route($stateProvider) {\n        $stateProvider\n            .state('ak.customers', {\n                url: '/customers'\n            });\n    }\n```\n\n#### Components, Directives and Filters\n\nThese components always have the project prefix, e.g. `akCustomers`:\n\n```js\nangular\n    .module('ak.footer.ui', [])\n    .component('akFooter', {\n        templateUrl: 'app/footer/footer.html'\n    });\n```\n\nso that it can be used in markup with `\u003cak-footer\u003e\u003c/ak-footer\u003e`.\n\n## Databinding\n\nPrefer `ngBind` instead of `{{}}`:\n\n```html\n\u003c!-- Wrong --\u003e\n\u003cspan\u003e{{::vm.foo}}\u003c/span\n\n\u003c!-- Correct --\u003e\n\u003cspan ng-bind=\"::vm.foo\"\u003e\u003c/span\u003e\n```\n\n## Module\n\nDon't bind the module to a variable since this leads to indirection. Instead use chaining:\n\n```js\nangular\n    .module('foo', [])\n    .constant('FOOBARBAZ' FOOBARBAZ)\n    .filter('fooBaz', fooBaz)\n    .controller('BarController', BarController)\n    .directive('fooBar', fooBar);\n```\n\nIdeally, you'd sort it in the order of usage.\n\n## Dependency Injection\n\nDon't use manual dependency injection since this can be done by `ngAnnotate`.\n\nIf there's an edge case where `ngAnnotate` won't inject the dependencies you can use the `ngInject` instruction for the plugin:\n\n```js\nvar foo = {\n    bar: function (Customer) {\n        'ngInject';\n    }\n}\n\n// Resolve in an UI-Router state\nresolve: foo\n```\n\n## Components\n\n### IIFE\n\nAll components are located in an IIFE with strict mode enabled:\n\n```js\n(function () {\n    'use strict';\n\n    // Code\n})();\n```\n\n### Constants\n\nTo be used to extract reusable groups of values instead of hardcoding them inside a component, e.g.\n\n```js\nangular.module('baz', [])\n    .constant('FOO', 'bar');\n```\n\nIf they have a sufficient complexity or are plain large, you should think about extracting them to a preceding variable:\n\n```js\nvar FOO = {\n    bar: {\n        baz: 'baz'\n    }\n};\n\nangular.module('baz', [])\n    .constant('FOO', FOO);\n```\n\n### Component\n\nComponents are simplified attributue directives with sensible defaults. They are implemented as objects and to be preferred since they enable a more componentized appraoch to development. Here are the defaults:\n\n1. `restrict` = `E`\n2. `scope` = `true`\n3. `controllerAs` = `$ctrl`\n4. `bindToController` = `true`\n\nThey are to be inlined according the following pattern:\n\n```js\nangular\n    .module('foo', [])\n    .component('bar', {\n        bindings: {\n            baz: '@'\n        },\n        templateUrl: 'app/foo/bar/baz.html'\n        controllerAs: 'vm'\n    });\n```\n\nEven if there's no explicit controller function, you should always set it to `vm` to be consistent.\n\n### Directives\n\n#### Element Directives\n\nUse `component`.\n\n#### Attribute Directives\n\n1. Used for DOM-manipulations and validation\n2. The parameters of the link function are always named `scope`, `elem`, `attrs`, `ctrl` or `ctrls` if you have an array.\n\n    ```js\n    angular\n        .module('foo.bar.ui', [])\n        .directive('fooBar', fooBar)\n\n    function fooBar() {\n        return {\n            restrict: 'A',\n            require: 'ngModel',\n            link:  function link(scope, elem, attrs, ctrl) {\n                // ...\n            }\n        };\n    }\n    ```\n\n### Filter\n\nUse sparingly since they are very costly.\n\n### Controller\n\n1. Always use the `Controller` suffix\n2. Implemented in a standalone function\n3. Minimal amount of logic\n4. Logic goes to the service to be testable\n5. No DOM-manipulations\n6. Try to get required data inside the `resolve` of the state\n7. Always bind `this` to `vm` and don't use `$scope`\n8. Use `extend` blocks to create meaningful semantic blocks\n\n```js\nfunction FooController() {\n    var vm = this;\n\n    _.extend(vm, {\n        // Variables\n    });\n\n    _.extend(vm, {\n        // Functions\n    });\n\n    _.extend(vm, {\n        // Another semantic block\n    });\n}\n```\n\n### Services\n\n1. Don't use technical suffixes\n2. Don't use `factory` the difference is confusing and `service` is more flexible\n\n#### Service Pattern\n\nThis is known as the Revealing Module Pattern and allows for a clean, testable API.\n\n```js\nfunction foo() {\n    return {\n        bar: bar\n    };\n\n    var baz = 'baz';\n\n    function bar() {\n        // ...\n    }\n}\n```\n\n#### Resource Model Pattern\n\nRepresents the model layer in the frontend and allows to have class-like constructs for REST and specifcally HAL resources.\n\n```js\nangular\n    .module(foo.bar, [])\n    .service('Bar', Bar);\n\nfunction Bar(\n    // Dependencies\n) {\n    function Bar(\n        // Parameters\n        resource\n    ) {\n        // Instance\n        var bar = _.extend({\n            // Variables\n        }, resource);\n\n        _.extend(bar, {\n            // Instance methods\n        });\n\n        return bar;\n    }\n    _.extend(Bar, {\n        // Static class methods\n    });\n\n    return Bar;\n}\n```\n\nAn concrete example:\n\n```js\nangular\n    .module(ak.customer, [])\n    .constant('CUSTOMER_URL', 'api/v1/customer')\n    .service('Customer', Customer);\n\nfunction Customer(\n    CUSTOMER_URL,\n    restClient\n) {\n    function Customer(\n        resource\n    ) {\n        var customer = _.extend({\n            fullname: resource.firstname + ' ' + resource.lastname\n        }, resource);\n\n        _.extend(Customer, {\n            delete: function () {\n                return restClient.delete(resource._links['delete'])\n            }\n        });\n\n        return customer;\n    }\n    _.extend(Customer, {\n        getAll: function () {\n            return restClient.get(CUSTOMER_URL)\n                .then(function(customers) {\n                    return _.map(customers, function (customer) {\n                        return new Customer(customer);\n                    });\n                });\n        }\n    });\n\n    return Customer;\n}\n```\n\nAnd then you could do things like:\n\n```js\nCustomer.getAll()\n    .then(function (customers) {\n        vm.customers = customers;\n    });\n\n_.first(vm.customers).delete();\n```\n\n## Promises\n\n1. Always `return` the promise for chaining\n2. Extract functions if it could add clarity\n\n```js\nreturn promise\n    .then(foo1)\n    .then(foo2)\n    .then(foo3)\n    .catch(bar)\n    .finally(baz);\n```\n\n## Scope, Watches, Emits, Broadcasts\n\nVery often you don't need it. Ask yourself twice why you injected it and if you can solve it via bindings.\n\n## Wrapper\n\nUse the Angular wrappers instead of the native ones.\n\n* `$timeout` instead `setTimeout`\n* `$interval` instead `setInterval`\n* `$window` instead `window`\n* `$document` instead `document`\n\n## Third-party libraries\n\nIt is advisable to wrap third-party libraries in a service if you need to modify them and delete them from global namespace in order to use Angular's dependency injection, e.g.\n\n```js\nangular\n    .module('moment',[])\n    .service('moment', moment);\n\nfunction ($window) {\n  var moment = $window.moment;\n\n  _.extend(moment, {\n      foo: /...\n  });\n\n  try {\n    delete $window.moment;\n  } catch (e) {\n      $window.moment = undefined;\n      return moment;\n    }\n}\n```\n\n## Software Engineering Principles\n\n* [SRP](https://en.wikipedia.org/wiki/Single_responsibility_principle)\n* [SOC](https://en.wikipedia.org/wiki/Separation_of_concerns)\n* [YAGNI](http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It)\n* [DRY](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself)\n* [KISS](http://en.wikipedia.org/wiki/KISS_principle)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakullpp%2Fstyleguide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fakullpp%2Fstyleguide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakullpp%2Fstyleguide/lists"}