多亏了大量有价值的资源,我有一些关于在AngularJS应用程序中实现组件的一般建议:
控制器
控制器应该只是模型和视图之间的夹层。尽量让它尽可能薄。
强烈建议避免在控制器中使用业务逻辑。应将其移动到模型。
控制器可以使用方法调用(当孩子想要与父节点通信时可能)或$emit,$broadcast和$on方法与其他控制器进行通信。发出和广播的消息应保持在最低限度。
控制器不应该关心演示或 DOM 操作。
尽量避免嵌套控制器。在这种情况下,父控制器被解释为模型。而是将模型作为共享服务注入。
控制器中的作用域应用于将模型与视图绑定,并
像用于表示模型设计模式一样封装视图模型。
范围
在模板中将作用域视为只读,在控制器中将其视为只写。范围的目的是引用模型,而不是成为模型。
执行双向绑定(ng 模型)时,请确保不要直接绑定到作用域属性。
型
AngularJS中的模型是由服务定义的单例。
模型提供了一种分离数据和显示的绝佳方法。
模型是单元测试的主要候选者,因为它们通常只有一个依赖项(某种形式的事件发射器,通常情况下$rootScope),并且包含高度可测试的域逻辑。
模型应被视为特定单元的实现。它基于单一责任原则。Unit 是一个实例,它负责自己的相关逻辑范围,这些逻辑可能表示现实世界中的单个实体,并在编程世界中根据数据和状态来描述它。
模型应封装应用程序的数据,并提供 API 来访问和操作该数据。
模型应该是可移植的,以便可以很容易地运输到类似的应用程序。
通过在模型中隔离单元逻辑,您可以更轻松地查找、更新和维护单元逻辑。
模型可以使用整个应用程序通用的更通用的全局模型的方法。
如果减少组件耦合并提高单元可测试性和可用性并不真正依赖于减少组件耦合并提高单元可测试性和可用性,请尝试使用依赖关系注入将其他模型组合到模型中。
尽量避免在模型中使用事件侦听器。这使得它们更难测试,并且通常会在单一责任原则方面杀死模型。
模型实现
由于模型应该在数据和状态方面封装一些逻辑,因此它应该在架构上限制对其成员的访问,因此我们可以保证松散耦合。
在AngularJS应用程序中执行此操作的方法是使用工厂服务类型定义它。这将使我们能够非常轻松地定义私有属性和方法,并在单个位置返回可公开访问的属性和方法,这将使开发人员真正可读。
例如:
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
};
});
创建新实例
尽量避免有一个工厂返回一个新的能够运行的函数,因为这开始分解依赖注入,库的行为会很尴尬,特别是对于第三方。
完成相同任务的更好方法是将工厂用作 API 来返回对象集合,并附加了 getter 和 setter 方法。
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
// ...
};
});
全球模式
通常,请尽量避免这种情况并正确设计模型,以便将其注入控制器并在视图中使用。
特别是在某些情况下,某些方法需要在应用程序中具有全局可访问性。为了做到这一点,您可以在$rootScope中定义“common”属性,并在应用程序引导期间将其绑定到commonModel:
angular.module('app', ['app.common'])
.config(...)
.run(['$rootScope', 'commonModel', function ($rootScope, commonModel) {
$rootScope.common = 'commonModel';
}]);
您的所有全局方法都将位于“公共”属性中。这是某种命名空间。
但不要直接在$rootScope中定义任何方法。当在视图范围内与 ngModel 指令一起使用时,这可能会导致意外行为,这通常会散落在您的范围内,并导致作用域方法覆盖问题。
资源
资源允许您与不同的数据源进行交互。
应使用单一责任原则实现。
在特定情况下,它是HTTP / JSON端点的可重用代理。
资源注入模型,并提供发送/检索数据的可能性。
资源实施
创建资源对象的工厂,允许您与 RESTful 服务器端数据源进行交互。
返回的资源对象具有操作方法,这些方法提供高级行为,而无需与低级$http服务进行交互。
服务业
模型和资源都是服务。
服务是自包含的未关联、松散耦合的功能单元。
服务是Angular从服务器端为客户端Web应用程序带来的一项功能,服务已经普遍使用了很长时间。
Angular 应用中的服务是使用依赖关系注入连接在一起的可替换对象。
Angular附带不同类型的服务。每个都有自己的用例。有关详细信息,请阅读了解服务类型。
尝试在应用程序中考虑服务体系结构的主要原则。
一般来说,根据Web服务术语表:
服务是一种抽象资源,它表示执行任务的能力,这些任务从提供者实体和请求者实体的角度形成一致的功能。要使用,服务必须由具体的提供者代理实现。
客户端结构
通常,应用程序的客户端被拆分为多个模块。每个模块都应该作为一个单元进行测试。
尝试根据特性/功能或视图(而不是类型)来定义模块。有关详细信息,请参阅Misko的演示文稿。
模块组件可以按类型(如控制器、模型、视图、筛选器、指令等)进行常规分组。
但模块本身仍然是可重用的,可转移的和可测试的。
开发人员也更容易找到代码的某些部分及其所有依赖项。
有关详细信息,请参阅大型 AngularJS 和 JavaScript 应用程序中的代码组织。
文件夹结构示例:
|-- 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
角度应用程序结构的好例子是由angular-app实现的 - https://github.com/angular-app/angular-app/tree/master/client/src
现代应用生成器也考虑了这一点 - https://github.com/yeoman/generator-angular/issues/109