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

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

任何模型的行为都一样。如果我要使用DCI体系结构并将行为与数据模型分开,则必须引入其他对象来保存行为。这可以通过引入角色和上下文来完成。

DCI ==数据协作交互

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

因此归结为这个问题:

如何遵循AngularJS最佳实践来实现与控制器分离的模型?

评论

如果您可以定义DCI或至少提供拼写形式,我将投票赞成该问题。我从未在任何软件文献中见过这个缩写。谢谢。

我刚刚添加了DCI链接作为参考。

@JimRaden DCI是Dataq,Context,interaction,是由MVC(Trygve Reenskauge)之父首先制定的范例。到目前为止,有关这个主题的文献很多。很好的阅读是Coplien和Bjørnvig的“精益建筑”

谢谢。不管好坏,大多数人现在甚至都不了解原始文献。据Google称,有5500万篇有关MVC的文章,但只有25万篇提到MCI和MVC。而在Microsoft.com上? 7. AngularJS.org甚至没有提到DCI的首字母缩写:“您的搜索-site:angularjs.org dci-没有匹配任何文档”。

资源对象基本上是Angular.js中的模型。正在扩展它们。

#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
}


评论


使用服务而不是将纯Java对象作为模型创建并将其分配给控制器范围有什么好处?

–尼尔斯·布鲁姆·奥斯特
2012年6月20日19:39

如果您需要在多个控制器之间共享相同的逻辑。而且,通过这种方式,可以更轻松地独立测试事物。

–安德鲁·乔斯林(Andrew Joslin)
2012年6月20日20:31

最后一个例子很烂,这个更有意义。我编辑了

–安德鲁·乔斯林(Andrew Joslin)
2012年6月21日,下午1:56

是的,使用普通的旧Javascript对象,您将无法将Angular注入到ListService中。像本例一样,如果您需要$ http.get在开始时检索List数据,或者如果您需要注入$ rootScope以便可以进行$ broadcast事件。

–安德鲁·乔斯林(Andrew Joslin)
2012年6月21日12:36



为了使本示例更具DCI性,数据不应该不在ListService之外吗?

– PiTheNumber
13年9月20日在20:37

#2 楼

我目前正在尝试这种模式,尽管不是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;
});


评论


我只是进入Angular,但我很想知道退伍军人是否/为什么会认为这是异端。这可能也是我最初处理它的方式。有人可以提供一些反馈吗?

– Aaronius
13年2月24日在21:25

@Aaronius要清楚:我从未在任何angularjs文档或博客上实际阅读过“您永远都不要这样做”,但我一直都在阅读“ angularjs不需要模型,它只是使用普通的旧javascript”之类的东西。 ,我必须自己发现这种模式。由于这是我在AngularJS上的第一个实际项目,因此我要发出那些强烈的警告,以使人们在没有先思考的情况下就不会复制/粘贴。

– Ben G
13年2月25日在19:03



我已经选择了大致类似的模式。可惜的是,Angular没有任何“经典”意义上的模型的真正支持(或看似希望支持)。

–drt
13年4月17日在18:05

在我看来,这并不是什么异端,您正在使用工厂来创建工厂:创建对象。我相信“ angularjs不需要模型”一词的意思是“您不需要从特殊的类继承,也不需要使用特殊的方法(例如ko.observable,在剔除中)就可以处理有角度的模型,纯js对象就足够了”。

–Felipe Castro
2013年6月4日23:50

是否没有为每个集合使用适当命名的ElementService导致一堆几乎相同的文件?

–科林·艾伦
2014年4月24日在21:28

#3 楼

Angularjs文档明确指出:

与许多其他框架不同,Angular对模型没有任何限制或要求。没有任何类可继承或访问或更改模型的特殊访问器方法。
模型可以是原始,对象哈希或完整的对象类型。简而言之
模型是普通的JavaScript对象。
— AngularJS开发人员指南-V1.5概念-模型

因此,这取决于您如何声明模型。
这是一个简单的Javascript对象。
我个人不会使用Angular Services,因为它们的作用类似于可以使用的单例对象,例如,在应用程序中保留全局状态。

评论


您应该提供一个指向文档中说明位置的链接。我在Google搜索中搜索“ Angular对模型没有任何限制或要求”,据我所知,它在官方文档中没有出现。

–user456814
2014年1月24日,下午2:54

它在旧的angularjs文档中(在回答时还活着):github.com/gitsome/docular/blob/master/lib/angular/ngdocs/guide/…

– S.C.
2014年1月29日17:00

#4 楼

DCI是一个范式,因此,没有语言AngularJS的实现方式,无论语言是否支持DCI。如果您愿意使用源代码转换,那么JS会很好地支持DCI,如果您不愿意,它会存在一些缺点。再说一次,DCI与依赖注入无关,与说C#类无关,并且绝对不是服务。因此,使用angulusJS进行DCI的最好方法是采用JS方式进行DCI,这与DCI的最初编写方式非常接近。除非您进行源转换,否则您将无法完全完成转换,因为角色方法即使在上下文之外也将成为对象的一部分,但这通常是基于方法注入的DCI的问题。如果您查看DCI的权威网站fullOO.info,则可以查看它们也使用方法注入的ruby实现,或者您可以在此处查看有关DCI的更多信息。 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);
   };
}


评论


感谢您详细介绍DCI。这是一本好书。但是我的问题确实针对“将模型对象放在angularjs中的位置”。 DCI只是供参考,我不仅可以拥有一个模型,还可以以DCI方式拆分它。将编辑问题以使其更清楚。

–尼尔斯·布鲁姆·奥斯特
2012年6月20日下午6:02

#5 楼

有关AngularJS中的模型的本文可能会有所帮助:

http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/

评论


请注意,不鼓励仅链接的答案,因此,SO答案应该是搜索解决方案的终点(与引用的另一种中途停留相比,随着时间的流逝,它们往往会过时)。请考虑在此处添加独立的摘要,并保留该链接作为参考。

– kleopatra
2013年9月12日16:06

不过,在问题评论中添加这样的链接会很好。

–jorrebor
2013年12月19日15:22

该链接实际上是一篇非常不错的文章,但同上也需要将其修改为适合SO的答案

–杰里米·泽尔(Jeremy Zerr)
2014年5月16日15:55

#6 楼

如其他发布者所述,Angular没有提供现成的用于建模的基类,但是可以有用地提供以下功能:


与RESTful API交互并创建的方法新对象
在模型之间建立关系
在持久化到后端之前验证数据;对显示实时错误也很有用
缓存和延迟加载,以防止发出浪费的HTTP请求
状态机挂钩(保存,更新,创建,新建等之前/之后)

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

我和我的团队将继续积极地开发该库,我希望看到更多的Angular开发人员为它做出贡献并与之抗衡测试一下。

评论


嘿!真是太好了!我现在将其插入我的应用程序。战斗测试才刚刚开始。

–布鲁尼(J. Bruni)
2014年3月13日在18:52

我只是看着您的帖子,想知道您的ngActiveResource和Angular的$ resource服务之间有什么区别。我是Angular的新手,并且快速浏览了两组文档,但是它们似乎有很多重叠之处。 ngActiveResource是否在$ resource服务可用之前开发?

– Eric B.
2014年7月7日在19:55

#7 楼

这是一个比较老的问题,但是在Angular 2.0的新方向上,我认为这个话题比以往任何时候都更相关。我想说的一种最佳实践是编写对特定框架的依赖关系尽可能少的代码。仅在增加直接价值的地方使用框架特定的部分。

目前看来,Angular服务是将其引入下一代Angular的少数几个概念之一,因此遵循将所有逻辑移至服务的一般指导可能是明智的。但是,我认为即使不直接依赖Angular服务也可以创建解耦模型。创建仅具有必要的依赖关系和责任的自包含对象可能是解决方法。进行自动化测试时,也使工作变得更加轻松。如今,单一职责是一项繁琐的工作,但这确实很有道理!

这是一个模式示例,我认为这对将对象模型与dom分离非常有用。

http://www.syntaxsuccess.com/viewarticle/548ebac8ecdac75c8a09d58e

一个主要目标是以一种使单元测试和从单元测试一样易于使用的方式来构造代码。视图。如果您实现了这一目标,便可以编写出切实可行的测试。

#8 楼

我已经尝试在此博客文章中解决这个确切的问题。

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

本文介绍了三种方法,分别使用$ http,$ resource和Restangular。

以下是每种方法的示例代码,作业模型上的自定义getResult()方法:

矩形(容易实现):

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可能为数据建模提供更强大的解决方案,使每个人都可以在同一页面上。