Javascript 模型数据和行为放在哪里?[tl;dr;使用服务]
我正在与AngularJS合作完成我的最新项目。在文档和教程中,所有模型数据都放在控制器范围内。我理解,必须存在可供控制器使用的is,因此在相应的视图中 然而,我认为这个模型不应该在那里实际实施。它可能很复杂,例如具有私有属性。此外,您可能希望在另一个上下文/应用程序中重用它。将所有内容放入控制器完全打破了MVC模式 这同样适用于任何模型的行为。如果要使用行为并将其与数据模型分离,则必须引入其他对象来保存行为。这将通过引入角色和上下文来实现 DCI==DataC协同I交互作用 当然,模型数据和行为可以用普通javascript对象或任何“类”模式实现。但是AngularJS会怎么做呢?使用服务 所以归结到这个问题:Javascript 模型数据和行为放在哪里?[tl;dr;使用服务],javascript,model-view-controller,angularjs,dci,Javascript,Model View Controller,Angularjs,Dci,我正在与AngularJS合作完成我的最新项目。在文档和教程中,所有模型数据都放在控制器范围内。我理解,必须存在可供控制器使用的is,因此在相应的视图中 然而,我认为这个模型不应该在那里实际实施。它可能很复杂,例如具有私有属性。此外,您可能希望在另一个上下文/应用程序中重用它。将所有内容放入控制器完全打破了MVC模式 这同样适用于任何模型的行为。如果要使用行为并将其与数据模型分离,则必须引入其他对象来保存行为。这将通过引入角色和上下文来实现 DCI==DataC协同I交互作用 当然,模型数据和行
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;
}]);
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最佳实践实现与控制器解耦的模型?DCI是一种范例,因此没有AngularJS的实现方法,无论语言支持DCI还是不支持DCI。如果您愿意使用源代码转换,那么JS可以很好地支持DCI,如果您不愿意,那么JS会有一些缺点。同样,DCI与依赖注入的关系与C#类没有关系,也绝对不是服务。因此,使用angulusJS进行DCI的最佳方式是使用JS方式进行DCI,这与DCI最初的表述方式非常接近。除非您进行源代码转换,否则您将无法完全完成转换,因为角色方法将是对象的一部分,即使在上下文之外,但这通常是基于方法注入的DCI的问题。如果您查看DCI的权威站点,您可以查看ruby实现,它们也使用方法注入,或者您可以查看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);
};
}
如果希望多个控制器都能使用某些服务,则应该使用服务。下面是一个简单的人为示例:
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
}
我目前正在尝试这种模式,虽然不是DCI,但它提供了一种经典的服务/模型解耦(与web服务对话的服务(又名模型CRUD)以及定义对象属性和方法的模型) 请注意,我只在模型对象需要对其自身的属性使用方法时才使用此模式,我可能会在任何地方使用这些方法(例如改进的getter/setter)。我不是提倡对每项服务都系统地这样做 编辑: 我过去认为这种模式与“角度模型是简单的老javascript对象”的口号背道而驰,但现在我觉得这种模式非常好。 编辑(2): 更清楚地说,我只使用模型类来考虑简单的getter/setter(例如:在视图模板中使用)。对于大型业务逻辑,我建议使用单独的服务,这些服务“了解”模型,但与模型保持分离,并且只包括业务逻辑。如果您愿意,可以将其称为“业务专家”服务层 service/ElementServices.js(注意声明中如何注入元素) model/Element.js(使用angularjs工厂,用于对象创建)
Angularjs文件明确指出: 与许多其他框架不同,Angular没有任何限制或限制 对模型的要求。没有可从或继承的类 用于访问或更改模型的特殊访问器方法。这个 模型可以是基元、对象哈希或完整对象类型。简言之 该模型是一个普通的JavaScript对象 - 这意味着如何声明模型取决于您。 它是一个简单的Javascript对象
我个人不会使用Angular服务,因为它们的行为类似于单例对象,您可以使用它们来保持整个应用程序的全局状态。这篇关于AngularJS中模型的文章可能会有所帮助:
正如其他海报所述,Angular没有提供现成的建模基类,但可以提供以下几个功能:
我和我的团队继续积极开发这个库,我希望看到更多的Angular开发者为它做出贡献,并对它进行测试 我已经试着解决这个问题了 基本上,数据建模的最佳场所是服务和工厂。然而,根据您检索数据的方式和所需行为的复杂性,有很多方法
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;
}]);
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;
});