Nhờ vào một lượng lớn các nguồn có giá trị, tôi đã nhận được một số khuyến nghị chung để triển khai các thành phần trong ứng dụng AngularJS:
Bộ điều khiển
Bộ điều khiển chỉ nên là một lớp xen kẽ giữa mô hình và khung nhìn. Cố gắng làm cho nó mỏng nhất có thể.
Rất khuyến khích để tránh logic kinh doanh trong bộ điều khiển. Nó nên được chuyển đến mô hình.
Bộ điều khiển có thể giao tiếp với các bộ điều khiển khác bằng cách gọi phương thức (có thể khi trẻ muốn giao tiếp với cha mẹ) hoặc $ emit , $ Broadcast và $ trên các phương thức. Các tin nhắn được phát ra và phát sóng nên được giữ ở mức tối thiểu.
Trình điều khiển không nên quan tâm đến việc trình bày hoặc thao tác DOM.
Cố gắng tránh các bộ điều khiển lồng nhau . Trong trường hợp này, bộ điều khiển cha được hiểu là mô hình. Thay vào đó, mô hình tiêm như dịch vụ chia sẻ.
Phạm vi trong bộ điều khiển nên được sử dụng cho mô hình liên kết với chế độ xem và
đóng gói Mô hình xem như đối với mẫu thiết kế Mô hình trình bày .
Phạm vi
Xử lý phạm vi là chỉ đọc trong các mẫu và chỉ ghi trong bộ điều khiển . Mục đích của phạm vi là đề cập đến mô hình, không phải là mô hình.
Khi thực hiện liên kết hai chiều (ng-model), hãy đảm bảo bạn không liên kết trực tiếp với các thuộc tính phạm vi.
Mô hình
Mô hình trong AngularJS là một singleton được xác định bởi dịch vụ .
Mô hình cung cấp một cách tuyệt vời để phân tách dữ liệu và hiển thị.
Các mô hình là ứng cử viên chính cho thử nghiệm đơn vị, vì chúng thường có chính xác một phụ thuộc (một số dạng trình phát sự kiện, trong trường hợp phổ biến là $ rootScope ) và chứa logic miền có thể kiểm tra cao .
Mô hình nên được coi là một thực hiện của đơn vị cụ thể. Nó dựa trên nguyên tắc trách nhiệm duy nhất. Đơn vị là một thể hiện chịu trách nhiệm cho phạm vi logic liên quan riêng của nó có thể đại diện cho một thực thể duy nhất trong thế giới thực và mô tả nó trong thế giới lập trình về dữ liệu và trạng thái .
Mô hình nên đóng gói dữ liệu của ứng dụng của bạn và cung cấp API
để truy cập và thao tác dữ liệu đó.
Mô hình nên có thể mang theo để có thể dễ dàng vận chuyển đến ứng dụng tương tự.
Bằng cách cô lập logic đơn vị trong mô hình của bạn, bạn đã giúp việc định vị, cập nhật và bảo trì dễ dàng hơn.
Mô hình có thể sử dụng các phương thức của các mô hình toàn cầu chung hơn, phổ biến cho toàn bộ ứng dụng.
Cố gắng tránh thành phần của các mô hình khác vào mô hình của bạn bằng cách sử dụng phép nội xạ phụ thuộc nếu nó không thực sự phụ thuộc vào việc giảm khớp nối các thành phần và tăng khả năng kiểm tra đơn vị và khả năng sử dụng .
Cố gắng tránh sử dụng trình nghe sự kiện trong các mô hình. Nó làm cho chúng khó kiểm tra hơn và thường giết chết các mô hình theo nguyên tắc đơn trách nhiệm.
Mô hình thực hiện
Vì mô hình nên gói gọn một số logic về mặt dữ liệu và trạng thái, nên về mặt kiến trúc sẽ hạn chế quyền truy cập vào các thành viên của nó để chúng tôi có thể đảm bảo khớp nối lỏng lẻo.
Cách để làm điều đó trong ứng dụng AngularJS là xác định nó bằng cách sử dụng loại dịch vụ của nhà máy . Điều này sẽ cho phép chúng tôi xác định các thuộc tính và phương thức riêng tư rất dễ dàng và cũng trả lại các thuộc tính có thể truy cập công khai ở một nơi sẽ giúp nó thực sự dễ đọc cho nhà phát triển.
Một ví dụ :
angular.module('search')
.factory( 'searchModel', ['searchResource', function (searchResource) {
var itemsPerPage = 10,
currentPage = 1,
totalPages = 0,
allLoaded = false,
searchQuery;
function init(params) {
itemsPerPage = params.itemsPerPage || itemsPerPage;
searchQuery = params.substring || searchQuery;
}
function findItems(page, queryParams) {
searchQuery = queryParams.substring || searchQuery;
return searchResource.fetch(searchQuery, page, itemsPerPage).then( function (results) {
totalPages = results.totalPages;
currentPage = results.currentPage;
allLoaded = totalPages <= currentPage;
return results.list
});
}
function findNext() {
return findItems(currentPage + 1);
}
function isAllLoaded() {
return allLoaded;
}
// return public model API
return {
/**
* @param {Object} params
*/
init: init,
/**
* @param {Number} page
* @param {Object} queryParams
* @return {Object} promise
*/
find: findItems,
/**
* @return {Boolean}
*/
allLoaded: isAllLoaded,
/**
* @return {Object} promise
*/
findNext: findNext
};
});
Tạo phiên bản mới
Cố gắng tránh việc một nhà máy trả lại chức năng mới có thể vì điều này bắt đầu phá vỡ sự tiêm phụ thuộc và thư viện sẽ hành xử lúng túng, đặc biệt là đối với bên thứ ba.
Một cách tốt hơn để thực hiện điều tương tự là sử dụng nhà máy làm API để trả về một bộ sưu tập các đối tượng với các phương thức getter và setter gắn liền với chúng.
angular.module('car')
.factory( 'carModel', ['carResource', function (carResource) {
function Car(data) {
angular.extend(this, data);
}
Car.prototype = {
save: function () {
// TODO: strip irrelevant fields
var carData = //...
return carResource.save(carData);
}
};
function getCarById ( id ) {
return carResource.getById(id).then(function (data) {
return new Car(data);
});
}
// the public API
return {
// ...
findById: getCarById
// ...
};
});
Mô hình toàn cầu
Nói chung, cố gắng tránh các tình huống như vậy và thiết kế mô hình của bạn đúng cách để nó có thể được đưa vào bộ điều khiển và được sử dụng trong chế độ xem của bạn.
Trong trường hợp cụ thể, một số phương pháp yêu cầu khả năng tiếp cận toàn cầu trong ứng dụng. Để làm cho nó có thể, bạn có thể định nghĩa thuộc tính ' chung ' trong $ rootScope và liên kết nó với commonModel trong ứng dụng bootstrap:
angular.module('app', ['app.common'])
.config(...)
.run(['$rootScope', 'commonModel', function ($rootScope, commonModel) {
$rootScope.common = 'commonModel';
}]);
Tất cả các phương pháp toàn cầu của bạn sẽ sống trong tài sản ' chung '. Đây là một số loại không gian tên .
Nhưng không định nghĩa bất kỳ phương thức nào trực tiếp trong $ rootScope của bạn . Điều này có thể dẫn đến hành vi không mong muốn khi được sử dụng với chỉ thị ngModel trong phạm vi chế độ xem của bạn, nói chung là xả rác phạm vi của bạn và dẫn đến các phương pháp phạm vi ghi đè các vấn đề.
Nguồn
Tài nguyên cho phép bạn tương tác với các nguồn dữ liệu khác nhau .
Nên được thực hiện bằng cách sử dụng nguyên tắc đơn trách nhiệm .
Trong trường hợp cụ thể, nó là một proxy có thể tái sử dụng cho các điểm cuối HTTP / JSON.
Tài nguyên được đưa vào trong các mô hình và cung cấp khả năng gửi / truy xuất dữ liệu.
Thực hiện tài nguyên
Một nhà máy tạo ra một đối tượng tài nguyên cho phép bạn tương tác với các nguồn dữ liệu phía máy chủ RESTful.
Đối tượng tài nguyên được trả về có các phương thức hành động cung cấp các hành vi cấp cao mà không cần phải tương tác với dịch vụ $ http cấp thấp.
Dịch vụ
Cả mô hình và tài nguyên đều là dịch vụ .
Các dịch vụ không liên kết, các đơn vị chức năng kết hợp lỏng lẻo được khép kín.
Dịch vụ là một tính năng mà Angular mang đến cho các ứng dụng web phía máy khách từ phía máy chủ, nơi các dịch vụ đã được sử dụng phổ biến trong một thời gian dài.
Các dịch vụ trong ứng dụng Angular là các đối tượng thay thế được nối với nhau bằng cách sử dụng phép nội xạ phụ thuộc.
Angular đi kèm với các loại dịch vụ khác nhau. Mỗi người có trường hợp sử dụng riêng của mình. Vui lòng đọc Hiểu các loại dịch vụ để biết chi tiết.
Cố gắng xem xét các nguyên tắc chính của kiến trúc dịch vụ trong ứng dụng của bạn.
Nói chung theo Thuật ngữ dịch vụ web :
Dịch vụ là một tài nguyên trừu tượng thể hiện khả năng thực hiện các nhiệm vụ hình thành chức năng mạch lạc theo quan điểm của các thực thể nhà cung cấp và các thực thể người yêu cầu. Để được sử dụng, một dịch vụ phải được thực hiện bởi một đại lý cung cấp cụ thể.
Cấu trúc phía khách hàng
Nói chung, phía máy khách của ứng dụng được chia thành các mô-đun . Mỗi mô-đun nên được kiểm tra như một đơn vị.
Cố gắng xác định các mô-đun tùy thuộc vào tính năng / chức năng hoặc chế độ xem , không phải theo loại. Xem phần trình bày của Misko để biết chi tiết.
Các thành phần mô-đun có thể được nhóm theo quy ước theo các loại như bộ điều khiển, mô hình, khung nhìn, bộ lọc, chỉ thị, v.v.
Nhưng bản thân mô-đun vẫn có thể tái sử dụng , chuyển nhượng và kiểm tra được .
Nó cũng dễ dàng hơn nhiều cho các nhà phát triển để tìm thấy một số phần của mã và tất cả các phụ thuộc của nó.
Vui lòng tham khảo Tổ chức mã trong các ứng dụng AngularJS và JavaScript lớn để biết chi tiết.
Một ví dụ về cấu trúc thư mục :
|-- src/
| |-- app/
| | |-- app.js
| | |-- home/
| | | |-- home.js
| | | |-- homeCtrl.js
| | | |-- home.spec.js
| | | |-- home.tpl.html
| | | |-- home.less
| | |-- user/
| | | |-- user.js
| | | |-- userCtrl.js
| | | |-- userModel.js
| | | |-- userResource.js
| | | |-- user.spec.js
| | | |-- user.tpl.html
| | | |-- user.less
| | | |-- create/
| | | | |-- create.js
| | | | |-- createCtrl.js
| | | | |-- create.tpl.html
| |-- common/
| | |-- authentication/
| | | |-- authentication.js
| | | |-- authenticationModel.js
| | | |-- authenticationService.js
| |-- assets/
| | |-- images/
| | | |-- logo.png
| | | |-- user/
| | | | |-- user-icon.png
| | | | |-- user-default-avatar.png
| |-- index.html
Ví dụ điển hình về cấu trúc ứng dụng góc được triển khai bởi angular-app - https://github.com/angular-app/angular-app/tree/master/client/src
Điều này cũng được xem xét bởi các trình tạo ứng dụng hiện đại - https://github.com/yeoman/generator-angular/issues/109