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

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

问题描述

在AngularJS的领导人Igor Minar的这篇文章的上下文中: p>


MVC vs MVVM vs MVP 。许多开发商
可以花费数小时的时间讨论和争论一个有争议的话题。



几年来,AngularJS更接近于MVC
客户端变体),但随着时间的流逝,由于许多重构
和api的改进,现在更接近于 MVVM - $ scope 对象
可以被视为由强制执行控制器
函数装饰的 ViewModel 。 >

能够对框架进行分类并将其放入其中一个MV *桶中有一些优势。
它可以帮助开发人员通过使
更容易地创建一个心智模型,来代表用框架构建
的应用程序,使其更加舒适。也可以帮助建立开发人员使用的
术语。



说完了,我宁愿看到开发人员构建了$ b的屁股应用程序$ b设计精良,关注问题,而不是看到他们浪费
时间争论MV *废话。因此,我特此声明:
AngularJS MVW框架 - Model-View-Whatever 。在哪里
代表无论对你有用



Angular给你很大的灵活性,很好地分开演示
逻辑从业务逻辑和演示状态。请使用它燃料
您的生产力和应用程序的可维护性,而不是加热
关于在一天结束时的事情不重要
很多。


在客户端应用程序中是否有实现AngularJS MVW(Model-View-Whatever)设计模式的任何建议或指南?

解决方案

感谢大量的宝贵资料,我在AngularJS应用程序中实现组件的一般建议:






控制器




  • 控制器应该只是一个中间层模型和视图之间。尝试将其设为


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


  • 控制器可以使用方法调用与其他控制器进行通信(当孩子想要与父级通信时可能)或 $ emit $广播 $ on 方法。发射和广播的消息应保持在最低限度。


  • 控制器应不关心演示文稿或DOM操作。


  • 尝试避免嵌套控制器。在这种情况下,父控制器被解释为模型。注意模型作为共享服务。


  • 控制器中的范围应用于具有视图的绑定模型和 b $ b封装查看模式,因为演示模型设计模式。







范围



将范围视为模板中的只读只写在控制器中。范围的目的是引用模型,而不是模型。



当执行双向绑定(ng-model)时,请确保您不直接绑定到范围属性。






模型



AngularJS中的模型是单一服务定义。



模型提供了分离数据和显示的绝佳方法。

模型是单元测试的主要候选者,因为它们通常只有一个依赖关系(某些形式的事件发生器,通常情况下,$ rootScope )并且包含高度可测试域逻辑




  • 模型应被视为特定单位的实现。
    它是基于单一责任原则。单位是一个实例,负责其自己的相关逻辑范围,可以代表现实世界中的单个实体,并根据数据和状态在编程世界中进行描述。


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


  • 模型应该是便携式,所以它可以很容易地转移到类似的
    应用程序。


  • 隔离单元逻辑在您的模型中,您已经使
    更容易找到,更新和维护。


  • 模型可以使用更一般的全局模型的方法这是整个应用程序的常用


  • 尝试避免使用依赖注入来将其他模型组合到模型中,如果它不是真的依赖于减少组件耦合并增加单元可测试性可用性


  • 尽量避免u在模型中唱歌事件听众。它使得它们更难测试,并且通常以单一责任原则来杀死模型。




模型实现



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



在AngularJS应用程序中执行此操作的方法是使用工厂服务类型进行定义。这将允许我们非常容易地定义私有属性和方法,也可以在单一的地方返回公开可访问的,这将使开发人员真正可读。



一个例子

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

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

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

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

返回searchResource.fetch(searchQuery,page,itemsPerPage).then(function(results ){
totalPages = results.totalPages;
currentPage = results.currentPage;
allLoaded = totalPages& lt = = currentPage;

return results.list
});
}

函数findNext(){
返回findItems(currentPage + 1);
}

函数isAllLoaded(){
return allLoaded;
}

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

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

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

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



创建新实例



尽量避免有一个工厂返回一个新的功能,因为这开始分解依赖注入,图书馆会表现得尴尬,特别是对于第三方。



更好的方式来完成同样的事情是使用工厂作为一个API来返回一个附加了getter和setter方法的对象集合。

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

function Car(data){
angular.extend这个数据);
}

Car.prototype = {
save:function(){
// TODO:strip无关字段
var carData = // ...
return carResource.save(carData);
}
};

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

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



全局模型



避免这种情况,并正确设计您的模型,因此它可以注入控制器并在您的视图中使用。



在某些情况下,某些方法需要应用程序中的全局可访问性。
为了使您可以在应用程序引导期间在 $ rootScope 中定义 common 属性并将其绑定到 commonModel : p>

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

您所有的全局方法都将在常用属性中。这是某种命名空间



但是,请勿直接在您的$ rootScope 中定义任何方法。当您在视图范围内使用ngModel指令时,这可能会导致意外行为,通常会排除您的范围并导致覆盖范围的方法






资源



资源可让您与不同的资料来源



应该使用单一责任原则实现



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



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

资源实现



创建资源对象的工厂,可让您与RESTful服务器端数据源进行交互。 p>

返回的资源对象具有提供高级行为的操作方法,而无需与低级$ http服务进行交互。






服务



模型和资源都是服务



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



服务是Angular从服务器端的客户端Web应用程序中提供服务,其中服务已经存在n通常使用很长时间。



Angular应用程序中的服务是使用依赖注入连接在一起的可替换对象。



角色带有不同类型的服务。每个都有自己的用例。有关详细信息,请参阅了解服务类型



尝试在您的应用程序中考虑服务架构的主要原则



一般来说,根据 Web服务术语表: p>


服务是一个抽象资源,代表
执行任务的功能,从
供应商实体和请求者实体的视图。要使用,一个
服务必须由具体的提供商代理实现。







客户端结构



在应用程序的一般客户端被拆分为模块。每个模块应该以可测试为单位。



根据功能/功能 >视图,而不是按类型。
有关详细信息,请参阅 Misko演示文稿



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



但是模块本身保持可重复使用可转让可测试



开发人员更容易找到代码的一些部分及其所有依赖项。



请参阅代码组织



构建的文件夹的一个例子:

  |  -  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
| | - 资产/
| | | - 图像/
| | | | - logo.png
| | | | - user /
| | | | | - user-icon.png
| | | | | - user-default-avatar.png
| | - index.html

角度应用程序结构的良好示例由角度应用程序 - https://github.com/angular-app/angular-app / tree / master / client / src



这也被现代应用程序生成器考虑了 - https://github.com/yeoman/generator-angular/issues/109


In the context of this post by Igor Minar, lead of AngularJS:

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天全站免登陆