将模型数据和行为放在哪里?[tl; dr;使用服务]

我正在与AngularJS合作进行我的最新项目。在文档和教程中,所有模型数据都放入控制器范围。我理解,必须在那里才能为控制器使用,从而在相应的视图中。

但是,我不认为该模型实际上应该在那里实现。例如,它可能很复杂,并且具有私有属性。此外,人们可能希望在另一个上下文/应用程序中重用它。将所有内容放入控制器完全打破了MVC模式。

这同样适用于任何模型的行为。如果我使用DCI架构并将行为与数据模型分开,我将不得不引入其他对象来保存行为。这将通过引入角色和上下文来完成。

DCI == Data Collaboration Interaction

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

所以可以归结为这个问题:

如何按照 AngularJS 最佳实践实现与控制器分离的模型?


答案 1

如果您希望多个控制器可以使用某些内容,则应使用服务。下面是一个简单的人为示例:

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
}

答案 2

我目前正在尝试这种模式,虽然不是DCI,但它提供了经典的服务/模型解耦(具有用于与Web服务通信的服务(也称为模型CRUD),以及定义对象属性和方法的模型)。

请注意,只有当模型对象需要处理其自身属性的方法时,我才使用此模式,我可能会在任何地方使用(例如改进的getter/setters)。我并不是主张系统地为每项服务都这样做。

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

编辑(2):为了更清楚,我只使用Model类来分解简单的getters/setters(例如:在视图模板中使用)。对于大型业务逻辑,我建议使用“了解”模型的单独服务,但与它们分开,并且仅包含业务逻辑。如果需要,可以将其称为“业务专家”服务层

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;
});