AngularJS:理解设计模式 [英] AngularJS: Understanding design pattern

查看:169
本文介绍了AngularJS:理解设计模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

AngularJS :公开分享 - 的 2012年7月19日


  

MVC VS MVVM VS MVP 。什么一个有争议的话题,很多开发商
  可以花几个小时辩论和争论。


  
  

几年来AngularJS更接近于MVC(或相当它的一个
  客户端的变体),但随着时间的推移和感谢许多重构
  和API改进,它现在更接近 MVVM - 在 $范围对象
  可以考虑在视图模型正在由一个装饰
  我们称之为功能的控制器


  
  

如果能够分类的框架,并把它放入MV *桶的人都有一定的优势。
  它可以帮助开发者通过使其获得更舒适的它的API
  更容易创建重新$ P $的心理模型psents的应用程序
  正在建设的框架。它还可以帮助建立
  所使用的开发术语


  
  

尽管如此,我宁愿看到开发人员构建踢屁股的应用程序是
  精心设计,并按照关注点分离,不愿看到他们身上浪费
  时间争论MV *无稽之谈。基于这个原因,本人特此声明
   AngularJS MVW框架 - 模型 - 视图 - 无论。无论在哪里
  代表什么工作。


  
  

角度为您提供了很大的灵活性很好地独立presentation
  从业务逻辑和presentation状态逻辑。请使用燃料
  您的工作效率和应用程序的可维护性,而不是加热
  的事情,在这一天的结束并不重要的讨论
  多。


是否有实施AngularJS MVW(模型 - 视图 - 不管),在客户端应用程序设计模式的任何建议或准则?


解决方案

由于我已经得​​到了实施AngularJS应用程序组件的一般建议的重要来源,数额巨大:


控制器


  • 控制器应该只是一个模型和视图之间的中间层。尽量做到为越好。


  • 强烈建议为避免业务逻辑的控制器。它应该被移到建模。


  • 控制器可以使用的方法调用(可能是,当孩子想和父母沟通)或 $ EMIT $广播的和的 $其他控制器通信在的方法。所发射的和广播消息应保持在最低限度。


  • 控制器应的不关心presentation 或DOM操作。


  • 尝试避免嵌套控制器。在这种情况下,父控制器是PTED为模型间$ P $。注射模型作为共享服务来代替。


  • 范围的控制器应使用的结合模型视图和结果
    封装查看型号 presentation型号的设计模式。



范围

治疗范围为只读模板只写在控制器。的范围的目的,是指模型,不被该模型

在做双向绑定(NG-模型)确保你不直接绑定到作用域属性。


模型

在AngularJS模型是一个按定义的服务

示范提供了一个很好的方法来分离数据和显示。

模型是单元测试的主要对象,因为它们通常有一个确切的依赖关系(某种形式的事件发射器,在普通情况下的 $ rootScope 的),并含有大量的可测试的域逻辑


  • 模型应该被认为是特定单元的实现。
    它是基于单一责任原则。单位是一个实例,它是负责自己可以在现实世界中重新present单一的实体,并描述它在编程世界中的数据和状态

    条款相关的逻辑范围

  • 模型应该封装应用程序的数据,并提供了一​​个 API
    访问和操作数据。


  • 模式应该是的移动所以它可以很容易地运送到类似
    应用程序。


  • 通过模型中的隔离单元的逻辑你可以更容易
    查找,更新和维护。


  • 型号可以使用更普遍的全球模式是常见的方法
    为整个应用程序。


  • 尽量避免其他型号的组成到模型中使用依赖注入,如果它是不是真的依赖,以减少组件的耦合,提高单位的可测性可用性


  • 尽量避免在模型中使用事件侦听器。这使他们难以测试和一般杀死模型的单一职责原则条款。


模型实施

由于模型应该在数据和状态方面的封装了一些逻辑,应该建筑限制访问其成员因此,我们可以保证的松耦合。

要做到这一点在AngularJS应用程序的方法是使用的工厂的服务类型来定义它。这将允许我们定义私有属性和方法非常简单,也返回一个地方公开访问的那些将使开发商真的可读性。

例子

  angular.module(搜索)
.factory('searchModel',['searchResource',函数(searchResource){  变种itemsPerPage = 10,
  当前是= 1,
  总页数= 0,
  allLoaded =假,
  SEARCHQUERY;  功能的init(PARAMS){
    itemsPerPage = params.itemsPerPage || itemsPerPage;
    SEARCHQUERY = params.substring || SEARCHQUERY;
  }  功能findItems(页,queryParams){
    SEARCHQUERY = queryParams.substring || SEARCHQUERY;    返回searchResource.fetch(SEARCHQUERY,页面itemsPerPage)。然后(功能(结果){
      总页数= results.totalPages;
      当前是= results.currentPage;
      allLoaded =总页数< =当前页;      回报results.list
    });
  }  功能FindNext中(){
    返回findItems(当前是+ 1);
  }  功能isAllLoaded(){
    返回allLoaded;
  }  //返回公共模型API
  返回{
    / **
     * @参数{}对象PARAMS
     * /
    初始化:初始化,    / **
     * @参数{}数页
     * @参数{}对象queryParams
     * @返回{}对象承诺
     * /
    发现:findItems,    / **
     * @返回{}布尔
     * /
    allLoaded:isAllLoaded,    / **
     * @返回{}对象承诺
     * /
    FindNext中:FindNext中
  };
});

创建新实例

尽量避免一个工厂,因为这开始打破依赖注入和图书馆的行为笨拙,特别是对第三方返回一个新的能够的功能。

有一个更好的方式来完成同样的事情是用工厂作为API返回对象的集合与连接到他们的getter和setter方法​​。

  angular.module('车')
 .factory('carModel',['carResource',函数(carResource){  功能车(数据){
    angular.extend(此,数据);
  }  Car.prototype = {
    保存:功能(){
      // TODO:带不相干的领域
      VAR carData = // ...
      返回carResource.save(carData);
    }
  };  功能getCarById(ID){
    返回carResource.getById(ID)。然后(功能(数据){
      返回新车(数据);
    });
  }  //公共API
  返回{
    // ...
    findById:getCarById
    // ...
  };
});

全局模型

在一般尽量避免这种情况,设计你的模型正确因此可以注入到控制器和视图中使用。

在特定的情况下,一些方法要求应用程序中的全球可访问性。
为了使人们有可能,你可以定义'的通用的'财产的 $ rootScope 的和在应用程序引导其绑定到的 commonModel 的:

  angular.module('应用',['app.common'])
的.config(...)
.RUN(['$ rootScope','commonModel',函数($ rootScope,commonModel){
  $ rootScope.common ='commonModel';
}]);

您所有的全局方法将生活'的通用的财产之内。这是某种空间

但不要直接定义任何方法您的 $ rootScope 的。您的视图范围内与ngModel指令使用时,一般你乱抛垃圾的范围和导致范围覆盖方法,这会导致意外行为的问题。


资源

资源可以让你以不同的数据源交互。

应该使用实施单一职责原则

在特定情况下它是一个可重用的代理HTTP / JSON端点。

资源在注入模型,并提供可能发送/检索数据。

资源实施

一个工厂,创建一个资源对象,让你REST风格的服务器端的数据源进行交互。

返回的资源对象的操作方法而提供高水平的行为,而不需要与低级别$ HTTP服务进行交互。


服务

两个模型和资源是服务

服务是不相关的,松耦合这是自包含的功能单位。

服务是角带给客户端的web应用程序从服务器端,在服务已经普遍使用很长一段时间的功能。

在角应用服务是被连接在一起使用依赖注入替代对象。

角配备了不同类型的服务。每个人有自己的用例。请阅读了解服务类型的详情。

尽量考虑服务架构主要原则在应用程序中。

在一般根据 Web服务词汇的:


  

此服务是重新$ P $一个抽象的资源psents的能力
  执行形成从点连贯功能任务
  查看供应商的实体和请求者实体。被使用,一个
  服务必须通过具体的提供代理来实现。



客户端结构

在应用程序的通用客户端被分裂成模块。每个模块应可验证为单位。

尝试根据特性/功能查看后,不按类型来定义的模块。
详情请参见 MISKO的presentation

模块组件可以通过类型,如控制器,模型,视图,过滤器,指令等进行常规分类。

不过,模块本身仍然可重用的 转让测试的

这也是很多开发者更容易找到code的一些地区和所有的依赖。

请参见 code组织大型AngularJS和JavaScript的应用的详情。

文件夹结构的一个例子

  |  -  SRC /
| | - 应用程序/
| | | - app.js
| | | - 家用/
| | | | - home.js
| | | | - homeCtrl.js
| | | | - home.spec.js
| | | | - home.tpl.html
| | | | - home.less
| | | - 用户/
| | | | - user.js的
| | | | - userCtrl.js
| | | | - userModel.js
| | | | - userResource.js
| | | | - user.spec.js
| | | | - user.tpl.html
| | | | - user.less
| | | | - 创建/
| | | | | - create.js
| | | | | - createCtrl.js
| | | | | - create.tpl.html
| | - 普通/
| | | - 认证/
| | | | - authentication.js
| | | | - authenticationModel.js
| | | | - authenticationService.js
| | - 资产/
| | | - 图像/
| | | | - logo.png
| | | | - 用户/
| | | | | - 用户的icon.png
| | | | | - 用户默认avatar.png
| | - index.html的

<一 -

角应用结构的好例子是通过的角应用的实施href=\"https://github.com/angular-app/angular-app/tree/master/client/src\">https://github.com/angular-app/angular-app/tree/master/client/src

这也被认为是现代应用程序生成 - <一个href=\"https://github.com/yeoman/generator-angular/issues/109\">https://github.com/yeoman/generator-angular/issues/109

AngularJS: Shared publicly - Jul 19, 2012

MVC vs MVVM vs MVP. What a controversial topic that many developers can spend hours and hours debating and arguing about.

For several years AngularJS was closer to MVC (or rather one of its client-side variants), but over time and thanks to many refactorings and api improvements, it's now closer to MVVM – the $scope object could be considered the ViewModel that is being decorated by a function that we call a Controller.

Being able to categorize a framework and put it into one of the MV* buckets has some advantages. It can help developers get more comfortable with its apis by making it easier to create a mental model that represents the application that is being built with the framework. It can also help to establish terminology that is used by developers.

Having said, I'd rather see developers build kick-ass apps that are well-designed and follow separation of concerns, than see them waste time arguing about MV* nonsense. And for this reason, I hereby declare AngularJS to be MVW framework - Model-View-Whatever. Where Whatever stands for "whatever works for you".

Angular gives you a lot of flexibility to nicely separate presentation logic from business logic and presentation state. Please use it fuel your productivity and application maintainability rather than heated discussions about things that at the end of the day don't matter that much.

Are there any recommendations or guidelines for implementing AngularJS MVW (Model View Whatever) design pattern in client-side applications?

解决方案

Thanks to a huge amount of valuable sources I've got some general recommendations for implementing components in AngularJS apps:


Controller

  • Controller should be just an interlayer between model and view. Try to make it as thin as possible.

  • It is highly recommended to avoid business logic in controller. It should be moved to model.

  • Controller may communicate with other controllers using method invocation (possible when children wants to communicate with parent) or $emit, $broadcast and $on methods. The emitted and broadcasted messages should be kept to a minimum.

  • Controller should not care about presentation or DOM manipulation.

  • Try to avoid nested controllers. In this case parent controller is interpreted as model. Inject models as shared services instead.

  • Scope in controller should be used for binding model with view and
    encapsulating View Model as for Presentation Model design pattern.


Scope

Treat scope as read-only in templates and write-only in controllers. The purpose of the scope is to refer to model, not to be the model.

When doing bidirectional binding (ng-model) make sure you don't bind directly to the scope properties.


Model

Model in AngularJS is a singleton defined by service.

Model provides an excellent way to separate data and display.

Models are prime candidates for unit testing, as they typically have exactly one dependency (some form of event emitter, in common case the $rootScope) and contain highly testable domain logic.

  • Model should be considered as an implementation of particular unit. It is based on single-responsibility-principle. Unit is an instance that is responsible for its own scope of related logic that may represent single entity in real world and describe it in programming world in terms of data and state.

  • Model should encapsulate your application’s data and provide an API to access and manipulate that data.

  • Model should be portable so it can be easily transported to similar application.

  • By isolating unit logic in your model you have made it easier to locate, update, and maintain.

  • Model can use methods of more general global models that are common for the whole application.

  • Try to avoid composition of other models into your model using dependency injection if it is not really dependent to decrease components coupling and increase unit testability and usability.

  • Try to avoid using event listeners in models. It makes them harder to test and generally kills models in terms of single-responsibility-principle.

Model Implementation

As model should encapsulate some logic in terms of data and state, it should architecturally restrict access to its members thus we can guarantee loose coupling.

The way to do it in AngularJS application is to define it using factory service type. This will allow us to define private properties and methods very easy and also return publically accessible ones in single place that will make it really readable for developer.

An example:

angular.module('search')
.factory( 'searchModel', ['searchResource', function (searchResource) {

  var itemsPerPage = 10,
  currentPage = 1,
  totalPages = 0,
  allLoaded = false,
  searchQuery;

  function init(params) {
    itemsPerPage = params.itemsPerPage || itemsPerPage;
    searchQuery = params.substring || searchQuery;
  }

  function findItems(page, queryParams) {
    searchQuery = queryParams.substring || searchQuery;

    return searchResource.fetch(searchQuery, page, itemsPerPage).then( function (results) {
      totalPages = results.totalPages;
      currentPage = results.currentPage;
      allLoaded = totalPages <= currentPage;

      return results.list
    });
  }

  function findNext() {
    return findItems(currentPage + 1);
  }

  function isAllLoaded() {
    return allLoaded;
  }

  // return public model API  
  return {
    /**
     * @param {Object} params
     */
    init: init,

    /**
     * @param {Number} page
     * @param {Object} queryParams
     * @return {Object} promise
     */
    find: findItems,

    /**
     * @return {Boolean}
     */
    allLoaded: isAllLoaded,

    /**
     * @return {Object} promise
     */
    findNext: findNext
  };
});

Creating new instances

Try to avoid having a factory that returns a new able function as this begins to break down dependency injection and the library will behave awkwardly, especially for third parties.

A better way to accomplish the same thing is to use the factory as an API to return a collection of objects with getter and setter methods attached to them.

angular.module('car')
 .factory( 'carModel', ['carResource', function (carResource) {

  function Car(data) {
    angular.extend(this, data);
  }

  Car.prototype = {
    save: function () {
      // TODO: strip irrelevant fields
      var carData = //...
      return carResource.save(carData);
    }
  };

  function getCarById ( id ) {
    return carResource.getById(id).then(function (data) {
      return new Car(data);
    });
  }

  // the public API
  return {
    // ...
    findById: getCarById
    // ...
  };
});

Global Model

In general try to avoid such situations and design your models properly thus it can be injected into controller and used in your view.

In particular case some methods require global accessibility within application. To make it possible you can define ‘common’ property in $rootScope and bind it to commonModel during application bootstrap:

angular.module('app', ['app.common'])
.config(...)
.run(['$rootScope', 'commonModel', function ($rootScope, commonModel) {
  $rootScope.common = 'commonModel';
}]);

All your global methods will live within ‘common’ property. This is some kind of namespace.

But do not define any methods directly in your $rootScope. This can lead to unexpected behavior when used with ngModel directive within your view scope, generally littering your scope and leads to scope methods overriding issues.


Resource

Resource lets you interact with different data sources.

Should be implemented using single-responsibility-principle.

In particular case it is a reusable proxy to HTTP/JSON endpoints.

Resources are injected in models and provide possibility to send/retrieve data.

Resource implementation

A factory which creates a resource object that lets you interact with RESTful server-side data sources.

The returned resource object has action methods which provide high-level behaviors without the need to interact with the low level $http service.


Services

Both model and resource are services.

Services are unassociated, loosely coupled units of functionality that are self-contained.

Services are a feature that Angular brings to client-side web apps from the server side, where services have been commonly used for a long time.

Services in Angular apps are substitutable objects that are wired together using dependency injection.

Angular comes with different types of services. Each one with its own use cases. Please read Understanding Service Types for details.

Try to consider main principles of service architecture in your application.

In general according to Web Services Glossary:

A service is an abstract resource that represents a capability of performing tasks that form a coherent functionality from the point of view of providers entities and requesters entities. To be used, a service must be realized by a concrete provider agent.


Client-side structure

In general client side of the application is splitted into modules. Each module should be testable as a unit.

Try to define modules depending on feature/functionality or view, not by type. See Misko’s presentation for details.

Module components may be conventionally grouped by types such as controllers, models, views, filters, directives etc.

But module itself remains reusable, transferable and testable.

It is also much easier for developers to find some parts of code and all its dependencies.

Please refer to Code Organization in Large AngularJS and JavaScript Applications for details.

An example of folders structuring:

|-- src/
|   |-- app/
|   |   |-- app.js
|   |   |-- home/
|   |   |   |-- home.js
|   |   |   |-- homeCtrl.js
|   |   |   |-- home.spec.js
|   |   |   |-- home.tpl.html
|   |   |   |-- home.less
|   |   |-- user/
|   |   |   |-- user.js
|   |   |   |-- userCtrl.js
|   |   |   |-- userModel.js
|   |   |   |-- userResource.js
|   |   |   |-- user.spec.js
|   |   |   |-- user.tpl.html
|   |   |   |-- user.less
|   |   |   |-- create/
|   |   |   |   |-- create.js
|   |   |   |   |-- createCtrl.js
|   |   |   |   |-- create.tpl.html
|   |-- common/
|   |   |-- authentication/
|   |   |   |-- authentication.js
|   |   |   |-- authenticationModel.js
|   |   |   |-- authenticationService.js
|   |-- assets/
|   |   |-- images/
|   |   |   |-- logo.png
|   |   |   |-- user/
|   |   |   |   |-- user-icon.png
|   |   |   |   |-- user-default-avatar.png
|   |-- index.html

Good example of angular application structuring is implemented by angular-app - https://github.com/angular-app/angular-app/tree/master/client/src

This is also considered by modern application generators - https://github.com/yeoman/generator-angular/issues/109

这篇关于AngularJS:理解设计模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆