Das Ziel dieses Style Guides ist, eine Sammlung von Best Practices und Gestaltungsrichtlinien für AngularJS-Anwendungen aufzuzeigen. Sie wurden aus den folgenden Quellen zusammengestellt:
Hinweis 1: Hierbei handelt es sich noch um einen Entwurf des Style Guides, dessen vorrangiges Ziel es ist, gemeinschaftlich von der Community entwickelt zu werden. Die gesamte Community wird es daher begrüßen, wenn Lücken gefüllt werden. Hinweis 2: Bevor du den Richtlinien in einer der Übersetzungen des englischsprachigen Dokuments folgst, vergewissere dich, dass diese aktuell sind. Die jüngste Version des AngularJS Style Guide ist im Dokument README.md.
Du wirst in diesem Style Guide keine allgemeinen Richtlinien für die JavaScript-Entwicklung finden. Solche finden sich unter:
Für die AngularJS-Entwicklung ist Googles JavaScript-Style-Guide empfehlenswert.
Im GitHub-Wiki von AngularJS gibt es einen ähnlichen Abschnitt von ProLoser, den du dir hier ansehen kannst.
Da eine große AngularJS-Anwendung viele Komponenten hat, sollten diese mit Hilfe einer Verzeichnishierarchie strukturiert werden. Es gibt zwei Basis-Herangehensweisen:
Die Verzeichnisstruktur wird in diesem Fall folgendermaßen aussehen:
.
├── app
│  ├── app.js
│  ├── controllers
│  │  ├── page1
│  │  │  ├── FirstCtrl.js
│  │  │  └── SecondCtrl.js
│  │  └── page2
│  │  └── ThirdCtrl.js
│  ├── directives
│  │  ├── page1
│  │  │  └── directive1.js
│  │  └── page2
│  │  ├── directive2.js
│  │  └── directive3.js
│  ├── filters
│  │  ├── page1
│  │  └── page2
│  └── services
│  ├── CommonService.js
│  ├── cache
│  │  ├── Cache1.js
│  │  └── Cache2.js
│  └── models
│  ├── Model1.js
│  └── Model2.js
├── partials
├── lib
└── test
Hier ist das entsprechende Layout:
.
├── app
│  ├── app.js
│  ├── common
│  │  ├── controllers
│  │  ├── directives
│  │  ├── filters
│  │  └── services
│  ├── page1
│  │  ├── controllers
│  │  │  ├── FirstCtrl.js
│  │  │  └── SecondCtrl.js
│  │  ├── directives
│  │  │  └── directive1.js
│  │  ├── filters
│  │  │  ├── filter1.js
│  │  │  └── filter2.js
│  │  └── services
│  │  ├── service1.js
│  │  └── service2.js
│  └── page2
│  ├── controllers
│  │  └── ThirdCtrl.js
│  ├── directives
│  │  ├── directive2.js
│  │  └── directive3.js
│  ├── filters
│  │  └── filter3.js
│  └── services
│  └── service3.js
├── partials
├── lib
└── test
app
└── directives
├── directive1
│  ├── directive1.html
│  ├── directive1.js
│  └── directive1.sass
└── directive2
├── directive2.html
├── directive2.js
└── directive2.sass
Dieser Ansatz kann mit beiden der oben genannten Verzeichnisstrukturen kombiniert werden.
services
├── cache
│  ├── cache1.js
│  └── cache1.spec.js
└── models
├── model1.js
└── model1.spec.js
app.js
enthält die Routendefinitionen, die Konfiguration und/oder das manuelle Bootstrapping (falls benötigt).Ich bevorzuge die erste Struktur, weil bei ihr die üblichen Komponenten einfacher gefunden werden können.
Konventionen über die Benennung der Komponenten stehen in jedem Abschnitt über die jeweilige Komponente.
Auch die HTML-Markup ist wichtig und sollte in einem Team so geschrieben werden, als sei sie von derselben Person.
TLDR; Scripts sollten am Ende einer Seite eingefügt werden.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Meine App</title>
</head>
<body>
<div ng-app="myApp">
<div ng-view></div>
</div>
<script src="angular.js"></script>
<script src="app.js"></script>
</body>
</html>
Um den Code nicht unnötig zu verkomplizieren, füge AngularJS-spezifische Direktiven hinter Standard-Attributen ein. Dadurch ist es einfacher, sich den Code anzusehen und durch das Framework erweitertes HTML zu erkennen (was die Wartbarkeit verbessert).
<form class="frm" ng-submit="login.authenticate()">
<div>
<input class="ipt" type="text" placeholder="name" require ng-model="user.name">
</div>
</form>
Andere HTML-Attribute sollten den Empfehlungen des Code Guides folgen.
$digest
-Loop ausgelöst werden).bindonce
verwendet werden.$watch
so weit wie möglich. Komplexe und langsame Berechnungen in einem einzigen $watch
verlangsamen die gesamte Applikation (der $digest
-Loop wird in einem einzelnen Thread ausgeführt, weil JavaScript single-threaded ist).$timeout
keine gewatchten Variablen geändert werden, setze den dritten Parameter der $timeout
-Funktion auf false
, um nicht automatisch einen $digest
-Zyklus durch den Aufruf des Callbacks auszulösen.##Namensgebung Die Namensgebung für alle Elemente sind in folgender Tabelle wieder zu finden:
Element | Style | Beispiel | Verwendung bei |
---|---|---|---|
Module | lowerCamelCase | angularApp | |
Controller | funktionalität + 'Ctrl' | adminCtrl | |
Direktiven | lowerCamelCase | userInfo | |
Filter | lowerCamelCase | userFilter | |
Services | UpperCamelCase | User | Konstruktor |
Services | lowerCamelCase | dataFactory | sonstige |
$timeout
statt setTimeout
$interval
statt setInterval
$window
statt window
$document
statt document
$http
statt $.ajax
Dadurch werden deine Tests einfacher und in manchen Fällen wird einem unerwarteten Verhalten vorgebeugt (zum Beispiel wenn du ein $scope.$apply()
in setTimeout
vergessen hast).
Automatisiere deinen Workflow mit Tools wie:
Verwende Promises ($q
) statt Callbacks. Dadurch sieht dein Code eleganter und sauberer aus und du wirst nicht in der Callback-Hölle landen.
Verwende, wenn möglich, $resource
statt $http
. Das höhere Abstraktionslevel schützt dich vor Redundanz.
Verwende einen Angular Pre-Minifier (wie ngmin oder ng-annotate), um Probleme nach einer Minification zu vermeiden.
Verwende keine Globalen. Löse alle Abhängigkeiten durch Dependency Injection auf.
Mülle deinen $scope
nicht zu. Füge ihm nur Funktionen und Variablen hinzu, die in den Templates verwendet werden.
Bevorzuge Controller gegenüber ngInit
. ngInit
eignet sich nur, um Aliase für spezielle Eigenschaften von ngRepeat
zu erstellen. Hiervon abgesehen solltest du immer Controller statt ngInit
verwenden, um Werte in einem Scope zu initialisieren.
Verwende kein $
als Präfix für die Namen von Variablen, Eigenschaften oder Methoden. Dieser Präfix ist für AngularJS reserviert.
b
ein Untermodul von a
ist, kannst du sie durch Namespaces verschachteln, z. B.: a.b
.Es gibt zwei verbreitete Wege, nach denen Module strukturiert werden können:
Derzeit gibt es keinen großen Unterschied, aber die erste Variante sieht sauberer aus. Außerdem wird - wenn lazy-loading für die Module implementiert ist (momentan nicht auf der AngularJS-Roadmap) - die Performance der App verbessert.
Ctrl
benannt werden. Controller werden in UpperCamelCase benannt (HomePageCtrl
, ShoppingCartCtrl
, AdminPanelCtrl
usw.).module.controller('MyCtrl', ['dependency1', 'dependency2', ..., 'dependencyn', function(dependency1, dependency2, ..., dependencyn) {
// body
}]);
Durch die Verwendung dieses Definitionstyps werden Probleme bei der Minification vermieden. Die Array-Definition kann aus der Standardnotation automatisch generiert werden, indem Werkzeuge wie ng-annotate (und der Grunt-Task grunt-ng-annotate) verwendet werden.
module.controller('MyCtrl', ['$scope', function(s) {
// body
}]);
ist schlechter lesbar als:
module.controller('MyCtrl', ['$scope', function($scope) {
// body
}]);
Das gilt insbesondere für Dateien, die so viel Code enthalten, dass gescrollt werden muss. Dadurch vergisst du möglicherweise, welche Variable zu welcher Abhängigkeit gehört.
$emit
-, $broadcast
- und $on
-Methoden verwendest. Ãœber $emit
und $broadcast
gesendete Nachrichten sollten auf ein Minimum reduziert werden.$emit
und $broadcast
verschickt werden. Pflege diese Liste, um Kollisionen und Bugs zu vermeiden.module.filter('myFormat', function() {
return function() {
// body
};
});
module.controller('MyCtrl', ['$scope', 'myFormatFilter', function($scope, myFormatFilter) {
// body
}]);
scope
statt $scope
in deiner Link-Funktion. In den Compile- und Post-/Pre-Link-Funktionen hast du bereits Argumente angegeben, die verwendet werden sobald die Funktion aufgerufen wird. Diese kannst du nicht über eine Dependency Injection ändern. Dieser Stil wird auch im AngularJS-Sourcecode verwendet.ng
und ui
solltest du nicht verwenden, da diese für AngularJS und AngularUI reserviert sind.$scope.$on('$destroy', fn)
. Dies ist besonders nützlich wenn du Wrapper-Direktiven für Drittanbieter-Plug-ins entwickelst.$sce
zu verwenden, wenn du mit Inhalten arbeitest, die nicht vertrauenswürdig sind.$digest
-Schleife werden sie häufig aufgerufen, so dass langsame Filter die gesamte Anwendung verlangsamen.Dieser Abschnitt enthält Informationen über AngularJS' Service-Komponente. Er bezieht sich nicht auf eine spezielle Definitionsweise (d. h. als Provider, Factory oder Service), falls nicht ausdrücklich genannt.
module.controller('MainCtrl', function ($scope, User) {
$scope.user = new User('foo', 42);
});
module.factory('User', function () {
return function User(name, age) {
this.name = name;
this.age = age;
};
});
service
statt als factory
geschrieben werden. Auf diese Weise können die Vorteile der klassischen Vererbung einfacher genutzt werden:function Human() {
// body
}
Human.prototype.talk = function() {
return "I'm talking";
};
function Developer() {
// body
}
Developer.prototype = Object.create(Human.prototype);
Developer.prototype.code = function() {
return "I'm coding";
};
myModule.service('Human', Human);
myModule.service('Developer', Developer);
$cacheFactory
verwenden. Diesen solltest du nutzen, um die Ergebnisse von Anfragen oder aufwändigen Berechnungen zwischenzuspeichern.config
-Callback, wie hier:angular.module('demo', [])
.config(function ($provide) {
$provide.provider('sample', function () {
var foo = 42;
return {
setFoo: function (f) {
foo = f;
},
$get: function () {
return {
foo: foo
};
}
};
});
});
var demo = angular.module('demo');
demo.config(function (sampleProvider) {
sampleProvider.setFoo(41);
});
ng-bind
oder ng-cloak
statt einfachen {{ }}
, um flackernde Inhalte zu vermeiden.src
-Attribut eines Bilds dynamisch gesetzt werden soll, verwende ng-src
statt src
mit einem {{ }}
-Template.href
-Attribut eines Ankers dynamisch setzen musst, verwende ng-href
statt href
mit einem {{ }}
-Template.{{ }}
in ein style
-Attribut zu schreiben, benutze die ng-style
-Direktive, der als Parameter objektartige Strings und Scopevariablen übergeben werden können:$scope.divStyle = {
width: 200,
position: 'relative'
};
<div ng-style="divStyle">Mein wunderschön gestyltes div, das auch im IE funktioniert</div>;
resolve
, um Abhängigkeiten aufzulösen bevor die View angezeigt wird.#Testen
TBD
Du kannst diese Anleitung verwenden, solange dieser Abschnitt noch nicht fertig ist.
Da dieser Style Guide gemeinschaftlich durch die Community erstellt werden soll, sind Beiträge willkommen. Zum Beispiel kannst du etwas beitragen, indem du den Abschnitt über Tests erweiterst oder den Style Guide in deine Sprache übersetzt.
#Mitwirkende
mgechev | pascalockert | mainyaa | rubystream | lukaszklis |
cironunes | cavarzan | tornad | jmblog | bargaorobalo |
astalker | valgreens | bitdeli-chef | dchest | gsamokovarov |
ntaoo | hermankan | jesselpalmer | capaj | jordanyee |
nacyot | kirstein | mo-gr | cryptojuice | olov |
vorktanamobay | thomastuts | grapswiz | coderhaoxin | dreame4 |