在 AngularJS 服务中处理数据绑定

Posted

技术标签:

【中文标题】在 AngularJS 服务中处理数据绑定【英文标题】:Handling data binding in AngularJS Services 【发布时间】:2013-01-09 17:53:51 【问题描述】:

我试图弄清楚当我的数据存储在服务中时您如何正确处理绑定。

如果将服务放入 $scope 然后让模板直接绑定到其中,我可以让事情正常工作,但这似乎是一个非常糟糕的主意。

我基本上希望拥有它,以便我的视图/控制器能够轻松更改服务中的状态并将其反映在任何地方。

感觉我应该能够执行以下操作,但它不起作用 (http://jsfiddle.net/aidankane/AtRVD/1/)。

html

<div ng-controller="MyCtl">
    <select ng-model="drawing" ng-options="d.file for d in drawings"></select>
</div>
<div ng-controller="MyOtherCtl">
     drawing 
</div>

JS

var myApp = angular.module('myApp', []);

myApp.factory('myService', function()
    var me = 
        drawings: ['file':'a', 'file':'b']
    ;
    // selected drawing
    me.drawing = me.drawings[0];
    return me;
);

function MyCtl($scope, myService)
    // can do:
    // $scope.mys = myService;
    // and then in html ng-model="mys.drawing"
    // but that seems wrong

    $scope.drawings = myService.drawings;
    $scope.drawing = myService.drawing;

    // can I not do this? it doesn't seem to work anyway...
    $scope.$watch('drawing', function(drawing)
        myService.drawing = drawing;
    );


function MyOtherCtl($scope, myService)
    $scope.drawing = myService.drawing;


MyCtl.$inject = ['$scope', 'myService'];
MyOtherCtl.$inject = ['$scope', 'myService'];

【问题讨论】:

我看到您在哪里观看 $scope.drawing 进行更改,但您没有更改模型,而是更改了所选项目。你不应该为选定的项目更改放置一个处理程序,可能在一个指令中吗? 如何使用指令来做到这一点?我认为 ngSelect 指令基本上给了我我需要的行为。我对这个问题的理解是控制器和服务之间的移动才是问题所在——我现在有点困惑:) 你是对的,现在我看到了你更新的小提琴,我对你在尝试什么有了更好的了解。你没有困惑:) 【参考方案1】:

您可以使用$watch 绑定到服务并传递一个函数:

$scope.$watch( function ()  return myService.drawing; , function ( drawing ) 
  // handle it here. e.g.:
  $scope.drawing = drawing;
);

然后在你的模板中使用$scope.drawing,它们会自动更新:

<div ng-controller="MyOtherCtl">
   drawing 
</div>

【讨论】:

好的。我想,这让我走上了正确的道路。因此,我创建了一个新版本 - jsfiddle.net/aidankane/EBr53 - 并且我在两个方向上创建了观察者以在服务之间推/拉。这是正确的做法吗? @AidanKane 是的!组件间通信是这里和邮件列表中的一个常见问题。服务是迄今为止最常见(通常也是更“正确”)的解决方案。 我很早就看到它们是保存数据的正确方式。只是在服务/控制器/视图之间的协调方面有点挣扎。似乎通过使用该服务,我必须在控制器中放置一些样板来同步事物(控制器中的东西太少了,我不介意)。感谢您的及时回复,不胜感激。 @mariachimike 通常有,但这在很大程度上取决于上下文。例如,您可以只做$scope.drawing = myService.drawing 并完全避开手表,但从关注点分离的角度来看,这通常是不受欢迎的。如果你有一个例子,我相信我们可以找到一些不那么通用和不那么冗长的东西。 @mariachimike 没错,但总有例外。【参考方案2】:

我认为,更优雅的是使用 Promise(参见 $q.deferred())并异步解决它们。然后在 promise 函数中,您可以将数据分配给 $scope 的成员。

【讨论】:

如果您只加载一次数据就可以了,但我认为正在更改的数据不会以 OP 正在寻找的方式自动绑定。可能是错的。【参考方案3】:

有两种方法可以绑定来自服务的数据:1) 通过值(需要一个如上所述的观察者来检查对服务原始值的变量更改)2) 通过引用(值直接链接),这是我首选的数据绑定方法。

我将只解释第二种可能性,因为接受的答案已经显示了如何实现观察者。 This blog describes what I am going to explain very well.

I created this plunk to illustrate data binding by reference.

这是来自 plunk 的代码:

HTML

<!DOCTYPE html>
<html ng-app="myApp">

  <head>
    <script data-require="angularjs@1.5.0" data-semver="1.5.0" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body ng-controller="myAppCntrl">
    <h1>Hello Plunker!</h1>
    <h3>By value</h3>
    <p>byValue</p>
    <p>objByValue</p>
    <h3>By object in service reference</h3>
    <p>byRefence.stringPrimitive</p>
    <h3>By reference to service singleton</h3>
    <p>myservice.stringPrimitive</p>
    <p style="color: green">of course, you can reference an object through the service as well</p>
    <p>myservice.objectWithPrimitive.stringPrimitive</p>

    <button ng-click=update()>Update strings on service</button>
    <br />
    <button ng-click=setDefaults()>Restore Defaults</button>
  </body>

</html>

javascript

var myApp = angular.module("myApp", []);

myApp.controller('myAppCntrl', function($scope, myAppService)
  $scope.myservice = myAppService;
  $scope.byValue = myAppService.stringPrimitive;
  $scope.objByValue = myAppService.objectWithPrimitive.stringPrimitive;
  $scope.byRefence = myAppService.objectWithPrimitive;

  $scope.update = function () 
    myAppService.stringPrimitive = "updated string";
    myAppService.objectWithPrimitive.stringPrimitive = "updated string here too";
  ;
  $scope.setDefaults = function () 
    myAppService.stringPrimitive = 'string primitive';
    myAppService.objectWithPrimitive.stringPrimitive = 'string primitive';
  ;
);

myApp.service('myAppService', function()
  this.stringPrimitive = 'string primitive';
  this.objectWithPrimitive = 
    stringPrimitive: 'string primitive'
  ;
);

那么这是如何工作的呢?

重要的是要理解这与 Angular 的工作方式无关,而与 Javascript 的工作方式有很大关系。当一个变量在 javascript(整数、字符串等)中设置为等于原始值(或传递给函数)时,var 由值设置。这意味着新变量是您将其设置为等于内存中新位置的变量的副本。当在 javascript 中将变量设置为等于对象(或传递给函数)var 是通过引用设置的

这是什么意思?

当一个$scope var通过值设置,服务变量发生变化时,由于$scope变量只是服务变量的一个拷贝,服务变量不再与服务变量有任何关系,不会改变当服务变量发生时。

当$scope var 设置为等于and 对象时,它由Reference 赋值。这意味着当服务对象(请注意,服务是一个对象,因为它是使用 new 关键字实例化的,并且您可以在服务内部使用“this”来引用该对象)或服务上被引用的任何对象发生更改时,任何引用这些对象的 $scope 变量也会更新。

【讨论】:

以上是关于在 AngularJS 服务中处理数据绑定的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS数据双向绑定

ajax html响应中的AngularJs数据绑定

AngularJs 中的两种方式数据绑定不使用异步回调

Angularjs中不同类型的双向数据绑定

AngularJs 1.5 - 基于组件的架构、绑定和良好实践

AngularJS语法基础及数据绑定——详解各种数据绑定指令属性应用