Tujuan dari panduan ini adalah memberikan standar gaya dan cara-cara terbaik menulis kode untuk sebuah aplikasi AngularJS. Cara-cara ini dikumpulkan dari:
- Kode sumber AngularJS
- Kode-kode atau artikel yang pernah saya baca
- Pengalaman saya sendiri
Catatan 1: Panduan ini masih berupa konsep, tujuan utamanya adalah agar dikendalikan oleh komunitas, jadi menyumbangkan waktu anda untuk proyek ini akan sangat dihargai seluruh komunitas.
Catatan 2: Sebelum mengikuti panduan ini, pastikan bahwa panduan ini versi yang terbaru atau sama dengan versi dalam bahasa inggris (bisa anda cek dari waktu commit).
Dalam panduan ini, anda tidak akan menemukan panduan umum untuk menulis kode javascript. Seperti yang bisa anda temukan di:
- Panduan menulis kode javascript milik Google
- Panduan menulis kode javascript milik Mozilla
- Panduan menulis kode javascript milik GitHub
- Panduan menulis kode javascript milik Douglas
- Panduan menulis kode javascript milik Airbnb
Tapi setidaknya kami merekomendasikan anda untuk mengikuti Panduan menulis kode javascript milik Google.
Di halaman GitHub milik AngularJS ada sebuah wiki yang bagus seputar topik ini, kontribusi dari ProLoser, anda bisa melihatnya di sini.
- Umum
- Modules
- Controllers
- Directives
- Filters
- Services
- Templates
- Routing
- Testing
- Kontribusi
- Kontributor
Karena sebuah aplikasi bisa mempunyai banyak komponen, sangat baik untuk membuatnya terstruktur seperti struktur direktori. Ada 2 struktur yang biasa digunakan:
Struktur 1: Membagi menjadi 2 divisi, divisi atas disusun berdasarkan komponen, divisi bawah berdasarkan fungsionalitas.
Dengan cara ini, struktur akan terlihat seperti ini:
.
├── app
│ ├── app.js
│ ├── controllers
│ │ ├── home
│ │ │ ├── FirstCtrl.js
│ │ │ └── SecondCtrl.js
│ │ └── about
│ │ └── ThirdCtrl.js
│ ├── directives
│ │ ├── home
│ │ │ └── directive1.js
│ │ └── about
│ │ ├── directive2.js
│ │ └── directive3.js
│ ├── filters
│ │ ├── home
│ │ └── about
│ └── services
│ ├── CommonService.js
│ ├── cache
│ │ ├── Cache1.js
│ │ └── Cache2.js
│ └── models
│ ├── Model1.js
│ └── Model2.js
├── partials
├── lib
└── test
Struktur 2: Membagi menjadi 2 divisi, divisi atas disusun berdasarkan fungsionalitas, divisi bawah berdasarkan komponen.
Dengan cara ini, struktur akan terlihat seperti ini:
.
├── app
│ ├── app.js
│ ├── common
│ │ ├── controllers
│ │ ├── directives
│ │ ├── filters
│ │ └── services
│ ├── home
│ │ ├── controllers
│ │ │ ├── FirstCtrl.js
│ │ │ └── SecondCtrl.js
│ │ ├── directives
│ │ │ └── directive1.js
│ │ ├── filters
│ │ │ ├── filter1.js
│ │ │ └── filter2.js
│ │ └── services
│ │ ├── service1.js
│ │ └── service2.js
│ └── about
│ ├── controllers
│ │ └── ThirdCtrl.js
│ ├── directives
│ │ ├── directive2.js
│ │ └── directive3.js
│ ├── filters
│ │ └── filter3.js
│ └── services
│ └── service3.js
├── partials
├── lib
└── test
Struktur tambahan: Ketika membuat directives, akan sangat memudahkan apabila anda menempatkan semua file yang berhubungan (seperti: templates, CSS/SASS) di dalam direktori yang sama. Jika anda mengikuti cara ini, anda harus konsisten.
app
└── directives
├── directive1
│ ├── directive1.html
│ ├── directive1.js
│ └── directive1.sass
└── directive2
├── directive2.html
├── directive2.js
└── directive2.sass
Struktur tambahan ini bisa dikombinasikan dengan 2 struktur di atas.
Variasi Struktur: Seperti yang digunakan di proyek ng-boilerplate. Di dalam proyek tersebut, setiap unit testing ditempatkan dalam 1 direktori yang sama dengan komponen. Dengan cara tersebut, anda akan menemukannya dengan cepat dan juga bisa digunakan untuk dokumentasi dan cara penggunaan komponen tersebut.
services
├── cache
│ ├── cache1.js
│ └── cache1.spec.js
└── models
├── model1.js
└── model1.spec.js
- File
app.js
berisi definisi routing dan konfigurasi. - Setiap file javascript, hanya boleh berisi sebuah komponen. File tersebut harus diberi nama sesuai nama komponen.
- Alternatif lain, gunakan struktur seperti di Yeoman, ng-boilerplate.
Saya sendiri lebih menyukai struktur pertama, karena komponen lebih mudah dicari.
Untuk Aturan standar penamaan komponen bisa ditemukan di panduan tiap bagian.
TLDR; Letakan semua script di bagian bawah halaman.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyApp</title>
</head>
<body>
<div ng-app="myApp">
<div ng-view></div>
</div>
<script src="angular.js"></script>
<script src="app.js"></script>
</body>
</html>
Buat semuanya simpel dan susun file dari yang sangat umum hingga directive yang spesifik di bagian akhir. Dengan begitu lebih mudah untuk melihat, mencari dan mengubahnya.
<form class="frm" ng-submit="login.authenticate()">
<div>
<input class="ipt" type="text" placeholder="name" require ng-model="user.name">
</div>
</form>
Untuk atribut HTML yang lain, ikuti panduan ini
- Hanya 'watch' variabel vital (seperti: ketika menggunakan komunikasi real-time, jangan gunakan
$digest
loop disetiap pesan yang diterima). - Untuk konten yang diinisialisasi sekali dan tidak pernah berubah lagi, gunakan 'single-time watchers' seperti
bindonce
. - Buat komputasi di dalam
$watch
se-simpel mungkin. Karena komputasi berat atau lambat di sebuah$watch
akan memperlambat seluruh aplikasimu. - Set false parameter ke 3 di
$timeout
untuk melewatkan$digest
loop ketika tidak ada lagi variabel yang harus di 'watch'.
- Gunakan:
$timeout
daripadasetTimeout
$interval
daripadasetInterval
$window
daripadawindow
$document
daripadadocument
$http
daripada$.ajax
Ini akan mempermudah testing dan akan mencegah kejadian tidak terduga (seperti, jika anda melewatkan $scope.$apply
di setTimeout
).
-
Otomatiskan sistem kerja anda dengan tool bantuan seperti:
-
Gunakan promises (
$q
) daripada callbacks. Ini akan membuat kode anda lebih elegan dan bersih, dan menyelamatkan anda dari 'callback hell'. -
Gunakan
$resource
daripada$http
jika memungkinkan. Semakin tinggi level abstraksi, akan menyelamatkan anda dari redudansi. -
Gunakan AngularJS pre-minifier (seperti ngmin atau ng-annotate) untuk mencegah masalah setelah proses minifikasi.
-
Jangan gunakan variabel global. Selalu gunakan Dependency Injection.
-
Jangan kotori
$scope
. Hanya tambahkan fungsi dan variabel yang benar-benar akan digunakan. -
Lebih baik inisialisasi variabel di controllers daripada menggunakan
ngInit
. Hanya gunakanngInit
untuk aliasing dingRepeat
. -
Jangan gunakan awalan
$
untuk nama variabel, properti dan fungsi. Awalan ini digunakan oleh AngularJS. -
Ketika melakukan Depedency Injection, susun semuanya dimulai dari library AngularJS terlebih dahulu, setelah itu library kustom anda:
module.factory('Service', function ($rootScope, $timeout, MyCustomDependency1, MyCustomDependency2) {
return {
//Something
};
});
- Modules harus diberi nama dengan standar 'lowerCamelCase'. Apabila module
b
adalah submodule dari modulea
, maka anda bisa menumpuknya dengan namespace seperti:a.b
.
Ada 2 struktur umum untuk modul:
- Dari fungsionalitas
- Dari tipe komponen
Saat ini 2 struktur di atas tidak terlalu berbeda, tapi yang pertama lebih terlihat bersih. Namun, apabila module 'lazy-loading' sudah diimplementasikan (walaupun saat ini module tersebut tidak ada dalam roadmap tim AngularJS), struktur pertama dapat menambah performa aplikasi anda.
- Jangan memanipulasi DOM dari controller, akan membuat controller anda sulit di tes dan melawan prinsip Separation of Concerns. Lebih baik gunakan directive.
- Untuk penamaan controller gunakan standar 'UpperCamelCase' dan harus sesuai dengan fungsionalitasnya (seperti: shopping cart, homepage, admin panel) dan diakhiri dengan
Ctrl
(HomePageCtrl
,ShoppingCartCtrl
,AdminPanelCtrl
, dll.). - Controller tidak boleh didefinisikan secara global (meskipun AngularJS mengijinkan hal ini, namun sangatlah tidak baik mengotori namespace global).
- Gunakan syntax array untuk definisi controller:
module.controller('MyCtrl', ['dependency1', 'dependency2', ..., 'dependencyN', function (dependency1, dependency2, ..., dependencyN) {
//...body
}]);
Gunakan cara definisi seperti ini untuk menghindarkan masalah minifikasi. Anda dapat secara otomatis menghasilkan definisi seperti di atas dengan menggunakan tool seperti ng-annotate (dan grunt task grunt-ng-annotate).
- Gunakan nama asli dari Dependency Injection, ini akan memudahkanmu membaca kode:
module.controller('MyCtrl', ['$scope', function (s) {
//...body
}]);
Akan lebih susah dimengerti daripada:
module.controller('MyCtrl', ['$scope', function ($scope) {
//...body
}]);
Hal ini akan sangat berguna ketika file yang anda baca cukup panjang.
- Buat controller sesimpel mungkin dan abstraksikan semua logika bisnis ke dalam service.
- Komunikasi dengan controller yang berbeda menggunakan method invocation (memungkinkan ketika child controller ingin berkomunikasi dengan parent controller) atau
$emit
,$broadcast
dan$on
. Pesan yang disebarkan harus seminimum mungkin. - Buatlah daftar semua pesan yang digunakan
$emit
,$broadcast
dan atur secara hati-hati untuk menghindari nama yang sama. - Ketika anda ingin mengubah format tampilan data, gunakan filter:
module.filter('myFormat', function () {
return function () {
//body...
};
});
module.controller('MyCtrl', ['$scope', 'myFormatFilter', function ($scope, myFormatFilter) {
//body...
}]);
- Untuk controller yang bertumpuk, gunakan "nested scoping" dengan
controllerAs
:
app.js
module.config(function ($routeProvider) {
$routeProvider
.when('/route', {
templateUrl: 'partials/template.html',
controller: 'HomeCtrl',
controllerAs: 'home'
});
});
HomeCtrl
function HomeCtrl() {
this.bindingValue = 42;
}
template.html
<div ng-bind="home.bindingValue"></div>
- Gunakan standar penamaan 'lowerCamelCase'.
- Gunakan
scope
daripada$scope
di dalam link function. Pada saat proses kompilasi, post/pre link function yang telah didefinisikan dengan argumen, akan diberikan ketika fungsi tersebut dipanggil, anda tidak akan dapat mengubahnya dengan Dependency Injection. Cara ini juga digunakan di kode sumber AngularJS. - Gunakan awalan yang berbeda untuk directive, untuk mencegah error karena penggunaan nama yang sama dengan third-party library.
- Jangan gunakan awalan
ng
atauui
karena telah digunakan untuk AngularJS dan AngularJS UI. - Manipulasi DOM hanya dilakukan di dalam directive.
- Usahakan scope harus terisolasi ketika membuat komponen yang bisa digunakan kembali.
- Gunakan directive di atribut atau elemen daripada di komentar atau class, ini akan membuat kode lebih mudah dibaca.
- Gunakan
$scope.$on('$destroy', fn)
untuk membersihkan. Sangat berguna terutama ketika anda menggunakan third-party plugin sebagai directive. - Jangan lupa gunakan
$sce
untuk konten yang tidak dapat dipercaya.
- Gunakan standar penamaan 'lowerCamelCase'.
- Buat filter anda se-ringan mungkin karena sering dipanggil selama
$digest
loop. - Fokus lakukan 1 hal saja untuk setiap filter. Untuk manipulasi lebih kompleks, lebih baik menggabungkan beberapa filter sekaligus.
Bagian ini adalah informasi tentang komponen service secara umum di AngularJS dan tidak spesifik tentang (provider
, .factory
atau .service
), kecuali ditegaskan demikian.
-
Gunakan standar penamaan 'camelCase'.
-
UpperCamelCase (PascalCase) untuk nama service apabila digunakan sebagai konstruktor, seperti:
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; }; });
-
lowerCamelCase untuk semua service yang lain.
-
-
Enkapsulasikan semua logika bisnis di dalam service.
-
Service yang merepresentasikan domain lebih baik adalah
service
daripadafactory
. Dengan begitu kita dapat lebih mudah mendapatkan keuntungan dari "klassical" inheritance:
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);
- Untuk cache pada level session gunakan
$cacheFactory
. Ini harus digunakan untuk meng-cache hasil dari request atau komputasi yang berat. - Jika sebuah service membutuhkan konfigurasi, maka definisikan service tersebut sebagai provider dan konfigurasikan service itu di dalam
config
callback seperti:
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);
});
- Gunakan
ng-bind
ataung-cloak
daripada{{ }}
untuk mencegah konten yang berkedip (flashing content). - Hindari menulis ekspresi yang kompleks di dalam template.
- Gunakan
ng-src
dan{{ }}
daripada hanyasrc
- Gunakan
ng-href
daripadasrc
dan{{ }}
- Gunakan
ng-style
daripadastyle
dan{{ }}
<script>
...
$scope.divStyle = {
width: 200,
position: 'relative'
};
...
</script>
<div ng-style="divStyle">my beautifully styled div which will work in IE</div>;
- Gunakan
resolve
untuk resolve dependencies sebelum view ditampilkan.
TBD
Sampai bagian ini selesai, anda bisa menggunakan panduan ini terlebih dahulu.
Karena panduan ini dibuat oleh komunitas, kontribusi sangatlah dihargai. Contohnya, anda dapat berkontribusi dengan memperluas bagian testing atau dengan menerjemahkan panduan ini ke dalam bahasa anda.
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 |
guiltry |