在Angular.js中,模型状态应该存储在哪里?
Where Should Model State Be Stored In Angular.js
我发现Angular对模型的使用令人困惑。Angular似乎采取的方法是,模型可以是你喜欢的任何东西——也就是说,Angular不包含显式的模型类,你可以使用普通的JavaScript对象作为模型。
在我所见过的几乎每个Angular示例中,模型实际上都是一个对象,要么是手工创建的,要么是通过资源从API调用返回的。因为我看过的几乎每个Angular示例都很简单,通常存储在控制器$scope中的模型数据以及与模型相关的任何状态(例如selection)也存储在控制器的$scope中。这对于简单的应用程序/示例来说很好,但当应用变得更复杂时,这似乎过于简化了。例如,如果上下文发生变化,存储在控制器中的模型状态有可能成为上下文并丢失;存储selectedGallery
和selectedPhoto
的控制器只能存储全局selectedImage
,而不能存储每个库的selectedPhoto
。在这种情况下,每个图库使用一个控制器可能会解决这个问题,但从UI的角度来看,这似乎是浪费的,而且可能是不合适和不必要的。
Angular对模型的定义似乎更接近于我所认为的VO/DTO,即在服务器和客户端之间传递的哑对象。我的直觉是将这样一个对象包装在我认为是模型的东西中——一个维护与DTO/VO相关的状态(例如选择)的类,提供必要的mutator来操纵DTO/VO,并通知应用程序的其余部分对底层数据的更改。显然,Angular的绑定很好地处理了最后一部分,但我仍然认为前两个职责有很强的用例。
然而,我并没有真正看到这个模式在我所看到的例子中使用,但我也没有看到我认为可扩展的替代方案。Angular似乎通过强制使用单例来暗中阻止使用服务作为模型(我知道有一些方法可以绕过这个问题,但它们似乎没有被广泛使用或批准)。
那么我应该如何保持模型数据的状态?
状态(和模型)存储在$scope
$scope是Angular的数据存储对象。它类似于数据库。$scope本身并不是模型,但是你可以将模型存储在$scope中。
每个$作用域都有一个父$作用域,一直到$rootScope形成一个松散镜像DOM的树状结构。当你调用一个需要新的$scope的指令时,比如ng-controller,一个新的$scope对象将被创建并添加到树中。
$scope对象使用原型继承连接。这意味着,如果您在树的较高级别添加一个模型,那么它将对所有较低级别可用。这是一个非常强大的特性,它使得$scope层次结构对模板作者来说几乎是透明的。
控制器初始化$scope
控制器的作用是初始化$scope。同一个控制器可以在页面的不同部分初始化多个$scope对象。控制器被实例化,设置$scope对象,然后退出。你可以使用同一个控制器在页面的不同部分初始化多个$作用域。
在你的图库的情况下,你会有一个imageGallery控制器,然后你会应用到DOM的每个部分,你想要使用ng-controller指令成为一个图库。页面的这一部分将获得它自己的$作用域,您将使用它来存储selectedPhoto属性。
原型范围
$scope从它的父级继承,一直继承到$rootScope,所以你可以把你的对象存储在层次结构中任何有意义的地方。您将得到一个$scope对象树,它大致与您当前的DOM相关。如果DOM发生了变化,则会根据需要为您创建新的$scope对象。
$scope只是一个普通的JavaScript对象。创建多个$scope对象并不比创建一个包含多个currentImage对象的数组更浪费。这是一种组织代码的明智方法。
通过这种方式,Angular消除了我们在JavaScript中经常发现的"我在哪里存储我的数据"问题。这是我们从Angular中获得的真正巨大的生产力提升之一。
获得全局数据(例如;userId) ?将其存储在$rootScope中。获得本地数据(例如;一个currentImage在一个画廊,那里有多个画廊实例)?将其存储在属于该图库的$scope对象中。
$scope在模板的正确部分自动提供给您。
棱角分明的模型都很瘦
在Rails背景下,我们强调胖模型和瘦控制器,我发现Angular的"几乎没有"模型令人惊讶。事实上,在你的模型中加入大量的业务逻辑往往会导致问题,就像我们有时在Rails中的User模型中看到的那样,如果你不小心,它会不断增长,直到变得不可维护。
angular模型就是一个JavaScript对象或原语。
任何对象都可以是模型。模型通常在控制器中使用JSON定义,或者从服务器中使用ajax定义。模型可以是一个JSON对象,也可以只是一个字符串、数组,甚至是一个数字。
当然,如果你愿意的话,没有什么可以阻止你向模型中添加额外的函数,并将它们存储在JSON对象中,但这将是在一个不适合Angular的范例中进行移植。
Angular对象通常是数据存储库,而不是函数。
前端的模型不是真正的模型
当然,你在客户端持有的模型并不是真正的模型。你真正的模型,你唯一的信息来源就在服务器上。我们使用API对其进行同步,但如果两者之间存在冲突,那么数据库中的模型显然是最终的胜利者。
这为您提供了诸如折扣代码等隐私。您在前端找到的模型是实际模型的公共属性的同步版本,它是远程的。
业务逻辑可以存在于服务中。
假设你想写一个方法来对你的模型做一些事情,例如同步它,或者验证它。在其他框架中,您可能会尝试使用一种方法来扩展您的模型。在Angular中,你更有可能编写一个服务。
服务是单例对象。像任何其他JavaScript对象一样,您可以在其中放入函数或数据。Angular自带了很多内置服务,比如$http。您可以自己构建它们,并使用依赖注入自动将它们提供给您的控制器。
例如,服务可能包含与RESTful API通信的方法,或验证数据的方法,或您可能需要做的任何其他工作的方法。服务不是模型
当然你不应该使用服务作为模型。把它们当作可以做事的对象。有时他们会对你的模型做一些事情。这是一种不同的思维方式,但却是可行的。
首先,让我们不要忘记Angular是一个基于web的框架,如果你把你的状态仅仅保存在一个对象中,它将无法在用户刷新浏览器时存活下来。因此,弄清楚如何在基于web的应用程序中保持模型数据的状态意味着弄清楚如何持久化它,以便您的代码可以在浏览器环境中运行。
Angular让你可以很容易地持久化你的状态:
- 调用RESTful $resource
- 表示模型实例的URL
在您的简单示例中,用户操作(如selectedGallery
和selectedPhoto
)的存储可以使用URL表示,如:
// List of galleries
.../gallery
// List of photos in a gallery
.../gallery/23
// A specific photo
.../gallery/23/photo/2
URL很关键,因为它允许用户使用back
和forward
按钮浏览浏览器历史。如果你想与你的应用程序的其他部分共享这个状态,web应用程序提供了丰富的方法为你完成使用cookie/localStorage,隐藏帧/字段,甚至存储在你的服务器。
一旦你定义了如何持久化应用程序的不同状态的策略,你应该更容易决定是使用.service
提供的单例对象还是通过.factory
提供的实例来访问这些持久化信息。
Angular对你如何存储所谓的"模型对象"没有意见。Angular控制器$scope
仅作为一个"视图模型"存在,用于管理你的UI。我建议在你的代码中把这两个概念分开。
如果你想要Angular作用域更改通知的准确性($watch
),你可以使用一个作用域对象来存储你的模型数据(var myScope = $rootScope.$new()
)。只是不要使用你的UI绑定到的同一个作用域对象。
AJAX ->自定义服务->模型作用域对象->控制器-> UI作用域对象-> DOM
或:
AJAX ->自定义服务->纯JavaScript对象->控制器-> UI作用域对象-> DOM
- Ember.js-接口状态应该存储在哪里
- 在哪里存储HTML模板以在Backbone.js中使用
- 我应该在哪里用javascript存储我的窗口维度
- 在哪里存储值以从角度模板获取它们
- ServiceWorker数据存储在哪里
- 我应该将html元素的jQuery数据存储在哪里
- ngView 中指令使用的远程数据存储在哪里?(AngularJS)
- rails_admin的 css 和 javascript 文件存储在哪里
- 混合移动应用中的数据存储在哪里
- window.somedata存储在哪里
- Ajax和后退按钮.哈希更改,但上一页的状态存储在哪里
- Mootools存储方法:数据存储在哪里
- JavaScript变量存储在哪里?
- 打开Annotator(注释存储在哪里?)
- 在Angular.js中,模型状态应该存储在哪里?
- javascript的localstorage字符串存储在哪里?
- attachEvent或addEventListener-存储在哪里
- mac上office word插件的cookie存储在哪里?
- rootScope 值存储在哪里?在饼干或本地存储中
- Blob 二进制数据存储在哪里