感谢大量宝贵的资源,我为在AngularJS应用程序中实现组件提供了一些一般性建议:
控制者
控制器应该只是模型和视图之间的中间层。尝试使其尽可能薄。
强烈建议避免在控制器中使用业务逻辑。应该将其移至模型。
控制器可以使用方法调用(在孩子想与父母进行通信时)或$ emit,$ broadcast和$ on方法与其他控制器进行通信。发出和广播的消息应保持最少。
控制器不应该关心表示或DOM操作。
尽量避免嵌套控制器。在这种情况下,父控制器被解释为模型。而是将模型作为共享服务注入。
控制器的作用域应用于与视图绑定模型,并像表示模型设计模式一样
封装视图模型。
范围
如治疗范围只读模板和只写在控制器。范围的目的是引用模型,而不是模型。
在进行双向绑定(ng-model)时,请确保您不直接绑定到范围属性。
模型
AngularJS中的模型是service定义的单例。
模型提供了分离数据和显示的绝佳方法。
模型是单元测试的主要候选者,因为它们通常仅具有一种依赖关系(某种形式的事件发射器,在通常情况下为$ rootScope)并且包含高度可测试的域逻辑。
模型应被视为特定单元的实现。它基于单一职责原则。单元是一个实例,负责其自身的相关逻辑范围,该逻辑可以表示现实世界中的一个实体,并在编程世界中根据数据和状态对其进行描述。
模型应封装您的应用程序数据,并提供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';
}]);
您所有的全局方法都将位于“ common ”属性内。这是某种名称空间。
但是不要直接在$ rootScope中定义任何方法。在视图范围内与ngModel指令一起使用时,这可能导致意外的行为,通常使您的范围乱七八糟,并导致范围方法覆盖问题。
资源资源
资源使您可以与不同的数据源进行交互。
应该使用single-responsibility-principle实现。
在特定情况下,它是HTTP / JSON端点的可重用代理。
资源被注入模型中,并提供了发送/检索数据的可能性。
资源实施
工厂创建一个资源对象,使您可以与RESTful服务器端数据源进行交互。
返回的资源对象具有可提供高级行为的操作方法,而无需与低级$ http服务进行交互。
服务
模型和资源都是服务。
服务是独立的,独立的,松散耦合的功能单元。
服务是Angular从服务器端带到客户端Web应用程序的一项功能,在该服务中,服务已被长期使用。
Angular应用程序中的服务是可替换对象,这些对象使用依赖项注入连接在一起。
Angular带有不同类型的服务。每个人都有自己的用例。有关详细信息,请阅读了解服务类型。
尝试考虑应用程序中服务体系结构的主要原理。
通常根据Web服务词汇表:
服务是一种抽象资源,代表从提供者实体和请求者实体的角度来看,执行形成一致功能的任务的能力。要使用,服务必须由具体的提供者代理来实现。
客户端结构
通常,应用程序的客户端分为模块。每个模块应作为一个单元进行测试。
尝试根据功能/特性或视图(而不是类型)定义模块。有关详细信息,请参见Misko的演示。
传统上,模块组件可以按类型分组,例如控制器,模型,视图,过滤器,指令等。
但是模块本身仍然是可重用,可转让和可测试的。
对于开发人员来说,查找代码的某些部分及其所有依赖性也要容易得多。
有关详细信息,请参阅Large 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