模型数据和行为放在哪里?

我正在和AngularJS合作我的最新项目。 在文档和教程中,所有模型数据都被放入控制器范围。 我明白,必须在那里为控制器,因此在相应的意见。

但是我不认为模型应该实际上在那里实施。 例如,它可能比较复杂,具有私有属性。 此外,人们可能希望在另一个上下文/应用程序中重用它。 把所有东西都放到控制器中,完全打破了MVC模式。

任何模型的行为也是如此。 如果我将使用DCI体系结构和单独的行为从数据模型,我将不得不引入其他对象来保持行为。 这将通过引入angular色和上下文来完成。

当然,模型数据和行为可以用普通的javascript对象或任何“类”模式来实现。 但是AngularJS怎么做呢? 使用服务?

所以归结为这个问题:

遵循AngularJS最佳实践,您如何实现与控制器分离的模型?

如果你想让多个控制器可用,你应该使用服务。 这是一个简单的人为的例子:

myApp.factory('ListService', function() { var ListService = {}; var list = []; ListService.getItem = function(index) { return list[index]; } ListService.addItem = function(item) { list.push(item); } ListService.removeItem = function(item) { list.splice(list.indexOf(item), 1) } ListService.size = function() { return list.length; } return ListService; }); function Ctrl1($scope, ListService) { //Can add/remove/get items from shared list } function Ctrl2($scope, ListService) { //Can add/remove/get items from shared list } 

我目前正在尝试这种模式,尽pipe没有DCI,但它提供了一种经典的服务/模型解耦(用于与Web服务交互的服务(又名模型CRUD),以及定义对象属性和方法的模型)。

请注意,只有当模型对象需要使用自己的属性的方法时,我才会使用这种模式,我可能会在任何地方使用(例如改进的getter / setter)。 我主张为系统地为每个服务做这个。

编辑:我曾经认为这种模式会违背“angular模型是普通的旧JavaScript对象”的口头禅,但在我看来,这种模式是完全正常的。

编辑(2):为了更清楚,我使用一个模型类只是为了简单的getters / setters(例如:在视图模板中使用)。 对于大的业务逻辑,我build议使用“知道”模型的单独服务,但是要保持独立的服务,并且只包含业务逻辑。 如果你愿意,可以称之为“业务专家”服务层

service / ElementServices.js (注意Element是如何在声明中注入的)

 MyApp.service('ElementServices', function($http, $q, Element) { this.getById = function(id) { return $http.get('/element/' + id).then( function(response) { //this is where the Element model is used return new Element(response.data); }, function(response) { return $q.reject(response.data.error); } ); }; ... other CRUD methods } 

model / Element.js (使用angularjs Factory,用于创build对象)

 MyApp.factory('Element', function() { var Element = function(data) { //set defaults properties and functions angular.extend(this, { id:null, collection1:[], collection2:[], status:'NEW', //... other properties //dummy isNew function that would work on two properties to harden code isNew:function(){ return (this.status=='NEW' || this.id == null); } }); angular.extend(this, data); }; return Element; }); 

Angularjs文档明确指出:

与许多其他框架不同,Angular对模型没有限制或要求。 没有类可以inheritance或访问或更改模型的特殊访问器方法。 该模型可以是基元,对象哈希或完整的对象types。 简而言之,模型是一个普通的JavaScript对象。

所以这意味着如何声明一个模型是由你决定的。 这是一个简单的Javascript对象。

我个人不会使用Angular Services,因为它们的行为就像您可以使用的单例对象,例如,在您的应用程序中保持全局状态。

DCI是一个范例,因此没有这样做的方法,无论是语言支持DCI还是不支持DCI。 如果你愿意使用源代码转换,JS支持DCI,如果不是的话,JS会支持一些缺点。 再说,DCI没有更多的dependency injection,而不是说一个C#类,并且绝对不是服务。 所以用angulusJS来做DCI的最好方法就是用JS的方式来做DCI,这与DCI如何制定是非常接近的。 除非进行源代码转换,否则将无法完全实现,因为angular色方法即使在上下文之外也将成为对象的一部分,但这通常是基于方法注入的DCI的问题。 如果您查看DOO的权威站点fullOO.info ,您可以查看他们也使用方法注入的ruby实现,或者您可以在这里查看有关DCI的更多信息。 这主要是与RUby的例子,但DCI的东西是不可知的。 DCI的关键之一是系统所做的与系统是分开的。 所以数据对象非常笨拙,但是一旦绑定到上下文angular色中的angular色,方法就会使某些行为可用。 angular色只不过是一个标识符,没有更多,当通过该标识符访问对象时,angular色方法是可用的。 没有angular色对象/类。 通过注入方法,angular色方法的范围并不完全如描述,而是非常接近。 JS中的上下文的一个例子可能是

 function transfer(source,destination){ source.transfer = function(amount){ source.withdraw(amount); source.log("withdrew " + amount); destination.receive(amount); }; destination.receive = function(amount){ destination.deposit(amount); destination.log("deposited " + amount); }; this.transfer = function(amount){ source.transfer(amount); }; } 

这篇关于AngularJS模型的文章可能会有所帮助:

http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/

正如其他海报所述,Angular没有提供用于build模的开箱即用的基类,但是可以有效地提供几个function:

  1. 与RESTful API交互并创build新对象的方法
  2. build立模型之间的关系
  3. 在坚持到后端之前validation数据; 对于显示实时错误也很有用
  4. caching和延迟加载,以防止浪费HTTP请求
  5. 状态机挂钩(保存,更新,创build,新build等之前/之后)

一个完成所有这些工作的库是ngActiveResource( https://github.com/FacultyCreative/ngActiveResource )。 充分披露 – 我写了这个图书馆 – 我成功地使用它build立了几个企业级的应用程序。 它经过了很好的testing,并提供了Rails开发人员应该熟悉的API。

我和我的团队继续积极开发这个图书馆,我很希望看到更多的Angular开发人员为此作出贡献并进行战斗testing。

一个较老的问题,但我认为这个话题比以前更适合Angular 2.0的新方向。 我认为最好的做法是编写尽可能less的依赖于特定框架的代码。 只有在增加直接价值的地方才使用特定于框架的部分。

目前,Angular服务似乎是为了下一代Angular而devise的less数几个概念之一,所以遵循将所有逻辑转移到服务的一般原则可能是明智的。 但是,我认为即使没有对Angular服务的直接依赖,也可以创build解耦模型。 只用必要的依赖关系和责任来创build自包含的对象可能就是要走的路。 在进行自动化testing时,它也使生活变得更容易。 单一责任是近来的一个热门话题,但这确实有很大的意义!

下面是一个模式的例子,我认为这是一个很好的从dom中解耦对象模型。

http://www.syntaxsuccess.com/viewarticle/548ebac8ecdac75c8a09d58e

一个关键的目标是以一种使unit testing和视图一样易于使用的方式来构build代码。 如果你能达到这个目标,你就有能力写出真实而有用的testing。

我试图解决这个博客文章中的确切问题。

基本上,数据build模的最佳家园是在服务和工厂。 但是,根据您如何检索数据以及所需行为的复杂性,有很多不同的方法来执行。 Angular目前没有标准的方法或最佳实践。

这篇文章涵盖了三种方法,使用$ http$ resourceRestangular

以下是每个代码的一些示例代码,Job模型上有一个自定义的getResult()方法:

Restangular(容易peasy):

 angular.module('job.models', []) .service('Job', ['Restangular', function(Restangular) { var Job = Restangular.service('jobs'); Restangular.extendModel('jobs', function(model) { model.getResult = function() { if (this.status == 'complete') { if (this.passed === null) return "Finished"; else if (this.passed === true) return "Pass"; else if (this.passed === false) return "Fail"; } else return "Running"; }; return model; }); return Job; }]); 

$资源(稍微更复杂):

 angular.module('job.models', []) .factory('Job', ['$resource', function($resource) { var Job = $resource('/api/jobs/:jobId', { full: 'true', jobId: '@id' }, { query: { method: 'GET', isArray: false, transformResponse: function(data, header) { var wrapped = angular.fromJson(data); angular.forEach(wrapped.items, function(item, idx) { wrapped.items[idx] = new Job(item); }); return wrapped; } } }); Job.prototype.getResult = function() { if (this.status == 'complete') { if (this.passed === null) return "Finished"; else if (this.passed === true) return "Pass"; else if (this.passed === false) return "Fail"; } else return "Running"; }; return Job; }]); 

$ http(铁杆):

 angular.module('job.models', []) .service('JobManager', ['$q', '$http', 'Job', function($q, $http, Job) { return { getAll: function(limit) { var deferred = $q.defer(); $http.get('/api/jobs?limit=' + limit + '&full=true').success(function(data) { var jobs = []; for (var i = 0; i < data.objects.length; i ++) { jobs.push(new Job(data.objects[i])); } deferred.resolve(jobs); }); return deferred.promise; } }; }]) .factory('Job', function() { function Job(data) { for (attr in data) { if (data.hasOwnProperty(attr)) this[attr] = data[attr]; } } Job.prototype.getResult = function() { if (this.status == 'complete') { if (this.passed === null) return "Finished"; else if (this.passed === true) return "Pass"; else if (this.passed === false) return "Fail"; } else return "Running"; }; return Job; }); 

博客文章更详细地介绍了为什么您可能会使用每种方法的原因,以及如何在您的控制器中使用模型的代码示例:

AngularJS数据模型:$ http VS $ resource VS Restangular

Angular 2.0有可能为数据build模提供更强大的解决scheme,使每个人都处于同一页面。

Interesting Posts