我正在与AngularJS合作开展我的最新项目。在文档和教程中,所有模型数据都放入控制器范围。我知道必须存在控制器,因此在相应的视图中。
但我不认为该模型应该在那里实施。它可能很复杂并且具有私有属性。此外,人们可能希望在另一个上下文/应用程序中重用它。将所有内容放入控制器完全打破了MVC模式。
任何模型的行为都是如此。如果我使用DCI architecture并将行为与数据模型分开,我将不得不引入其他对象来保存行为。这可以通过引入角色和背景来完成。
DCI == D ata C ollaboration 我 nteraction
当然,模型数据和行为可以使用普通的javascript对象或任何“类”模式来实现。但AngularJS的做法是什么呢?使用服务?
所以归结为这个问题:
如何根据AngularJS最佳实践实现与控制器分离的模型?
答案 0 :(得分:153)
如果您想要多个控制器可以使用的东西,您应该使用服务。这是一个简单的人为例子:
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
}
答案 1 :(得分:80)
我目前正在尝试这种模式,虽然不是DCI,但它提供了经典的服务/模型解耦(使用与Web服务交谈的服务(也称为模型CRUD),以及定义对象属性和方法的模型)。
请注意,只有当模型对象需要在自己的属性上运行的方法时才会使用此模式,我可能会在任何地方使用它(例如改进的getter / setter)。我不主张系统地为每项服务做这件事。
修改: 我曾经认为这种模式会违背“Angular模型是普通的旧javascript对象”的口头禅,但现在我觉得这种模式非常好。
编辑(2): 为了更清楚,我仅使用Model类来计算简单的getter / setter(例如:在视图模板中使用)。对于大型业务逻辑,我建议使用“了解”模型的单独服务,但要与它们分开,并且只包含业务逻辑。如果您需要,请将其称为“业务专家”服务层
service / ElementServices.js (注意元素如何在声明中注入)
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,用于创建对象)
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;
});
答案 2 :(得分:29)
Angularjs文件明确指出:
与许多其他框架不同,Angular没有任何限制或 对模型的要求。没有可继承的类或 用于访问或更改模型的特殊访问器方法。该 model可以是原始的,对象哈希,也可以是完整的对象类型。简而言之 该模型是一个普通的JavaScript对象。
因此,这意味着由您决定如何声明模型。 这是一个简单的Javascript对象。
我个人不会使用Angular Services,因为它们的行为类似于您可以使用的单例对象,例如,可以在整个应用程序中保持全局状态。
答案 3 :(得分:8)
DCI是一种范式,因此没有angularJS方式,无论是语言支持DCI还是不支持。如果你愿意使用源转换,JS支持DCI,如果你不愿意,则有一些缺点。 DCI再次与依赖注入无关,而不是说C#类具有并且绝对不是服务。因此,使用angulusJS进行DCI的最佳方法是使用JS方式进行DCI,这与DCI首先制定的方式非常接近。除非您进行源转换,否则您将无法完全执行此操作,因为即使在上下文之外,角色方法也将成为对象的一部分,但这通常是基于方法注入的DCI的问题。如果您查看fullOO.info DCI的权威站点,您可以查看ruby实现,他们也使用方法注入,或者您可以查看here以获取有关DCI的更多信息。它主要是使用RUby示例,但DCI的内容与此无关。 DCI的关键之一是系统所做的与系统分离。因此,数据对象非常愚蠢,但一旦绑定到上下文角色中的角色,方法就会使某些行为可用。角色只是一个标识符,仅此而已,当通过该标识符访问对象时,角色方法可用。没有角色对象/类。使用方法注入时,角色方法的范围并不完全如描述但接近。 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);
};
}
答案 4 :(得分:7)
关于AngularJS中的模型的这篇文章可能有所帮助:
http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/
答案 5 :(得分:5)
正如其他海报所述,Angular没有为建模提供开箱即用的基类,但可以有用地提供几个功能:
完成所有这些工作的一个库是ngActiveResource(https://github.com/FacultyCreative/ngActiveResource)。完全披露 - 我写了这个库 - 我已成功地用它来构建几个企业级应用程序。它经过了充分测试,并提供了Rails开发人员应该熟悉的API。
我的团队和我继续积极开发这个库,我很想看到更多Angular开发人员为此做出贡献并对其进行战斗测试。
答案 6 :(得分:5)
一个较旧的问题,但我认为鉴于Angular 2.0的新方向,该主题比以往任何时候都更具相关性。我想说最好的做法是编写尽可能少的依赖于特定框架的代码。仅使用特定于框架的部分来增加直接价值。
目前似乎Angular服务是为少数概念提供给下一代Angular的概念之一,因此按照将所有逻辑转移到服务的一般准则可能很聪明。但是,我认为即使没有直接依赖Angular服务,也可以制作解耦模型。创建只包含必要依赖关系和责任的自包含对象可能是最佳选择。它还可以在进行自动化测试时使生活更轻松。如今,单一责任是一项嗡嗡声的工作,但它确实很有意义!
这是一个我认为适合将对象模型与dom解耦的模式示例。
http://www.syntaxsuccess.com/viewarticle/548ebac8ecdac75c8a09d58e
一个关键目标是以一种方式构建代码,使其从单元测试和视图一样容易使用。如果你达到了这个目标,那么你就可以编写真实有用的测试。
答案 7 :(得分:4)
我试图在this blog post中解决这个问题。
基本上,数据建模的最佳家园是服务和工厂。但是,根据您检索数据的方式以及所需行为的复杂程度,有很多不同的方法可以实现。 Angular目前没有标准方式或最佳实践。
该帖子涵盖三种方法,使用 $ http , $ resource 和 Restangular 。
以下是每个示例代码,在作业模型上使用自定义getResult()
方法:
Restangular(易玩):
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', ['$http', 'Job', function($http, Job) {
return {
getAll: function(limit) {
var params = {"limit": limit, "full": 'true'};
return $http.get('/api/jobs', {params: params})
.then(function(response) {
var data = response.data;
var jobs = [];
for (var i = 0; i < data.objects.length; i ++) {
jobs.push(new Job(data.objects[i]));
}
return jobs;
});
}
};
}])
.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 Data Models: $http VS $resource VS Restangular
Angular 2.0有可能为数据建模提供更强大的解决方案,让每个人都在同一页面上。