Я работаю с AngularJS для моего последнего проекта. В документации и учебниках все данные модели помещаются в область управления. Я понимаю, что это должно быть доступно для контроллера и, следовательно, в соответствующих представлениях.
Однако я не думаю, что модель должна быть реализована там. Это может быть сложным и иметь частные атрибуты, например. Кроме того, можно было бы повторно использовать его в другом контексте/приложении. Ввод всего в контроллер полностью разрушает шаблон MVC.
То же самое относится и к поведению любой модели. Если бы я использовал архитектуру DCI и отдельное поведение из модели данных, мне пришлось бы вводить дополнительные объекты для сохранения поведения. Это будет сделано путем введения ролей и контекстов.
Конечно, данные модели и поведение могут быть реализованы с помощью простых объектов 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
}
В настоящее время я пытаюсь использовать этот шаблон, который, хотя и не DCI, обеспечивает классическую развязку сервисов/моделей (с услугами для общения с веб-службами (например, модель CRUD) и моделью определения свойств и методов объекта).
Обратите внимание, что я использую этот шаблон только тогда, когда объекту модели нужны методы, работающие самостоятельно, которые я, вероятно, буду использовать везде (например, улучшенные getter/seters). Я не, выступая за то, чтобы делать это для каждой службы систематически.
EDIT: Раньше я думал, что этот шаблон будет идти против модели "Angular - простой старый объект javascript", но теперь мне кажется, что этот шаблон отлично подходит.
РЕДАКТИРОВАТЬ (2): Чтобы быть яснее, я использую класс Model только для того, чтобы разделить простые геттеры/сеттеры (например: для использования в виде шаблонов). Для бизнес-логики я рекомендую использовать отдельные службы, которые "знают" о модели, но сохраняются отдельно от них и включают только бизнес-логику. Назовите это словом "бизнес-эксперт", если вы хотите
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;
});
В документации Angularjs четко указано:
В отличие от многих других фреймворков Angular не создает ограничений или требования к модели. Нет классов для наследования или специальные методы доступа для доступа или изменения модели. модель может быть примитивной, хешем объекта или полным типом объекта. Вкратце модель представляет собой простой объект JavaScript.
Итак, это означает, что до вас, как объявить модель. Это простой объект Javascript.
Я лично не буду использовать Angular Services, поскольку они должны были вести себя как одноэлементные объекты, которые вы можете использовать, например, для сохранения глобальных состояний в вашем приложении.
DCI - это парадигма, и поэтому нет никакого углового способа сделать это, либо поддержка языка DCI, либо нет. JS поддерживает DCI довольно хорошо, если вы готовы использовать преобразование источника и с некоторыми недостатками, если вы этого не делаете. Снова DCI больше не связан с инъекцией зависимостей, чем говорит класс С# и, безусловно, не является сервисом. Таким образом, лучший способ сделать DCI с помощью angulusJS - это сделать DCI способом JS, который довольно близок к тому, как DCI формулируется в первую очередь. Если вы не используете преобразование источника, вы не сможете сделать это полностью, поскольку методы ролей будут частью объекта даже вне контекста, но, как правило, проблема с DCI на основе метода. Если вы посмотрите fullOO.info авторитетный сайт для 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);
};
}
Эта статья о моделях в AngularJS может помочь:
http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/
Более старый вопрос, но я думаю, что тема более актуальна, чем когда-либо, учитывая новое направление Angular 2.0. Я бы сказал, что лучше всего написать код с минимальными зависимостями от конкретной структуры, насколько это возможно. Используйте только конкретные части фреймворка, где он добавляет прямое значение.
В настоящее время кажется, что служба Angular является одной из немногих концепций, которая сделает ее следующим поколением Angular, поэтому, вероятно, разумно следовать общему руководству по перемещению всей логики в службы. Тем не менее, я бы сказал, что вы можете создавать развязанные модели даже без прямой зависимости от сервисов Angular. Вероятно, путь к созданию самостоятельных объектов с необходимыми зависимостями и обязанностями. Это также облегчает жизнь при автоматическом тестировании. Единая ответственность - это шумная работа в наши дни, но она имеет большой смысл!
Вот пример patter, который я считаю хорошим для развязки объектной модели из dom.
http://www.syntaxsuccess.com/viewarticle/548ebac8ecdac75c8a09d58e
Основная цель состоит в том, чтобы структурировать ваш код таким образом, чтобы он был так же прост в использовании из модульных тестов, как из представления. Если вы достигнете этого, у вас есть хорошие возможности для написания реалистичных и полезных тестов.
Как указано другими плакатами, Angular не предоставляет базового класса для моделирования, но можно с пользой предоставить несколько функций:
Одна библиотека, которая хорошо все это делает, - ngActiveResource (https://github.com/FacultyCreative/ngActiveResource). Полное раскрытие - я написал эту библиотеку, и я успешно использовал ее для создания нескольких приложений масштаба предприятия. Он хорошо протестирован и предоставляет API, который должен быть знаком с разработчиками Rails.
Моя команда и я продолжаем активно развивать эту библиотеку, и мне бы хотелось, чтобы больше разработчиков Angular вносили вклад в нее и сражались с ней.
ngActiveResource
и службой $resource
ngActiveResource
. Я немного новичок в Angular и быстро просмотрел оба набора документов, но они, кажется, предлагают много общего. Был ли ngActiveResource
разработан до появления службы $resource
?
Я попытался решить эту точную проблему в этом сообщении в блоге.
В принципе, лучший дом для моделирования данных - это услуги и фабрики. Однако, в зависимости от того, как вы извлекаете данные и сложность поведения, которое вам нужно, существует множество различных способов реализации. Angular в настоящее время не имеет стандартного способа или наилучшей практики.
Сообщение охватывает три подхода, используя $http, $resource и Restangular.
Вот пример кода для каждого, с пользовательским методом 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 предложит более надежное решение для моделирования данных, которое позволит всем на одной странице.