我正在为我的最新项目使用AngularJS。在文档和教程中,所有模型数据都放在控制器范围内。我理解它必须在那里为控制器可用,因此在相应的视图中。

然而,我不认为这个模型应该在那里实现。例如,它可能很复杂并具有私有属性。此外,人们可能想在另一个上下文/应用程序中重用它。把所有东西都放到控制器中完全打破了MVC模式。

这同样适用于任何模型的行为。如果我要使用DCI体系结构并将行为与数据模型分离,我将不得不引入额外的对象来保存行为。这可以通过引入角色和上下文来实现。

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
}

其他回答

Angularjs文档清楚地指出:

不像许多其他框架,Angular没有限制或 对模型的需求。没有要继承的类或 用于访问或更改模型的特殊访问器方法。的 model可以是原语、对象散列或完整的对象类型。简而言之 模型是一个简单的JavaScript对象。 - AngularJS开发者指南- V1.5概念-模型

因此,这意味着如何声明模型取决于您。 它是一个简单的Javascript对象。

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

DCI is a paradigm and as such there's no angularJS way of doing it, either the language support DCI or it doesn't. JS support DCI rather well if you are willing to use source transformation and with some drawbacks if you are not. Again DCI has no more to do with dependency injection than say a C# class has and is definitely not a service either. So the best way to do DCI with angulusJS is to do DCI the JS way, which is pretty close to how DCI is formulated in the first place. Unless you do source transformation, you will not be able to do it fully since the role methods will be part of the object even outside the context but that's generally the problem with method injection based DCI. If you look at fullOO.info the authoritative site for DCI you could have a look at the ruby implementations they also use method injection or you could have a look at here for more information on DCI. It's mostly with RUby examples but the DCI stuff is agnostic to that. One of the keys to DCI is that what the system does is separated from what the system is. So the data object are pretty dumb but once bound to a role in a context role methods make certain behaviour available. A role is simply an identifier, nothing more, an when accessing an object through that identifier then role methods are available. There's no role object/class. With method injection the scoping of role methods is not exactly as described but close. An example of a context in JS could be

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

我试图在这篇博文中解决这个问题。

基本上,数据建模的最佳场所是服务和工厂。但是,根据检索数据的方式和所需行为的复杂性,有许多不同的实现方法。Angular目前没有标准方法或最佳实践。

这篇文章涵盖了三种方法,使用$http、$resource和Restangular。

下面是它们的一些示例代码,在Job模型上有一个自定义的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;
  }]);

$resource(稍微复杂一点):

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数据模型:$http VS $resource VS Restangular

Angular 2.0可能会提供一个更健壮的数据建模解决方案,让所有人都能达成共识。

如果您希望多个控制器都可以使用服务,则应该使用服务。这里有一个简单的例子:

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
}

正如其他帖子所述,Angular没有提供开箱即用的建模基类,但可以提供几个有用的函数:

用于与RESTful API交互和创建新对象的方法 建立模型之间的关系 在持久化到后端之前验证数据;对于显示实时错误也很有用 缓存和惰性加载以避免产生浪费的HTTP请求 状态机钩子(在保存、更新、创建、新建等之前/之后)

ngActiveResource (https://github.com/FacultyCreative/ngActiveResource)是一个能够很好地完成所有这些任务的库。完全公开——我编写了这个库——并且我已经成功地使用它构建了几个企业级应用程序。它经过了良好的测试,并提供了Rails开发人员应该熟悉的API。

我和我的团队继续积极地开发这个库,我希望看到更多的Angular开发人员为它做出贡献,并对它进行实战测试。