是否将对象绑定到角度为$ rootScope的服务中?

时间:2013-01-28 23:15:03

标签: javascript angularjs angularjs-rootscope

在角度方面,我有一个通过服务在我的应用程序中公开的对象。

该对象上的某些字段是动态的,并且将通过使用该服务的控制器中的绑定正常更新。但是一些字段是计算属性,取决于其他字段,需要动态更新。

这是一个简单的例子(正在研究jsbin here)。我的服务模型公开了字段abc,其中c是根据a + B中的calcC()计算的。请注意,在我的实际应用程序中,计算要复杂得多,但实质内容就在这里。

我能想到的唯一方法就是将我的服务模型绑定到$rootScope,然后使用$rootScope.$watch来监视任何更改a的控制器或b当他们这样做时,重新计算c。但这看起来很难看。 有更好的方法吗?


第二个问题是表现。在我的完整申请中,ab是大对象列表,汇总到c。这意味着$rootScope.$watch函数将进行大量深度数组检查,这听起来会损害性能。

我在BackBone中采用了一种平衡的方法,尽可能地减少了重新计算,但角度似乎不适合采用一种方法。 对此的任何想法都会很棒。


这是示例应用程序。

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

//A service providing a model available to multiple controllers
myModule.factory('aModel', function($rootScope) {
  var myModel = {
    a: 10,
    b: 10,
    c: null
  };

  //compute c from a and b
  calcC = function() {
    myModel.c = parseInt(myModel.a, 10) * parseInt(myModel.b, 10);
  };

  $rootScope.myModel = myModel;
  $rootScope.$watch('myModel.a', calcC);
  $rootScope.$watch('myModel.b', calcC);

  return myModel;
});


myModule.controller('oneCtrl', function($scope, aModel) {
  $scope.aModel = aModel;
});

myModule.controller('twoCtrl', function($scope, aModel) {
  $scope.anotherModel = aModel;
});

5 个答案:

答案 0 :(得分:7)

虽然从较高的层面来看,我同意bmleite的答案($ rootScope存在使用,并且使用$ watch似乎适用于您的用例),我想提出一种替代方法。

使用$rootScope.$broadcast将更改推送到$rootScope.$on侦听器,然后重新计算c值。

这可以手动完成 - 即,当您主动更改ab值时,或者甚至可能在短暂超时时限制更新频率。更进一步的是在您的服务上创建一个“脏”标志,以便{}仅在需要时计算。{/ p>

显然,这种方法意味着更多地参与控制器,指令等的重新计算 - 但如果您不想将更新绑定到ca的每个可能的更改,问题变成了'在哪里画线'的问题。

答案 1 :(得分:4)

我必须承认,我第一次看到你的问题并看到了你的例子我自己认为“这是错的”然而,在再次研究之后我意识到它并非如此好像我想的那样糟糕。

让我们面对事实,$rootScope可以使用,如果你想在应用程序范围内共享任何东西,那就是放置它的最佳位置。当然你需要小心,这是所有范围之间共享的东西,所以你不想无意中改变它。但是让我们面对它,这不是真正的问题,在使用嵌套控制器时(因为子范围继承父范围属性)和非隔离范围指令,您必须要小心。 “问题”已经存在,我们不应该以此为借口不使用这种方法。

使用$watch似乎也是一个好主意。这是框架已经免费提供给您的东西,并且完全符合您的需求。那么,为什么重新发明轮子呢?这个想法与“改变”事件方法基本相同。

在性能级别上,您的方法实际上可能“很重”,但它总是取决于您更新ab属性的频率。例如,如果您将ab设置为输入框的ng-model(例如,在您的jsbin示例中),则每次用户都会重新计算c键入一些东西......这显然是过度处理的。如果您使用软方法并在必要时仅更新a和/或b,那么您不应该遇到性能问题。它与使用“更改”事件或setter& getter方法重新计算c相同。但是,如果您确实需要实时重新计算c(即:在用户输入时),性能问题将始终存在,而不是您使用$rootScope$watch这将有助于改善它。

恢复,在我看来,你的方法并不坏(根本就是!),只需要注意$rootScope属性并避免“实时”处理。

答案 2 :(得分:3)

我意识到这是一年半之后,但由于我最近做出了相同的决定,我想我会提供一个替代答案,并且#34;为我工作&#34 ;不使用任何新值污染$rootScope

但它仍然依赖 $rootScope。然而,它不是广播消息,而是简单地调用$rootScope.$digest

基本方法是在角度服务上提供单个复杂模型对象作为字段。您可以提供超出您认为合适的内容,只需遵循相同的基本方法,并确保每个字段都包含一个复杂的对象,其引用不会发生变化,即不要使用新的复合体重新分配字段宾语。而是仅修改此模型对象的字段。

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

//A service providing a model available to multiple controllers
myModule.service('aModel', function($rootScope, $timeout) {
  var myModel = {
    a: 10,
    b: 10,
    c: null
  };

  //compute c from a and b
  calcC = function() {
    myModel.c = parseInt(myModel.a, 10) * parseInt(myModel.b, 10);
  };
  calcC();

  this.myModel = myModel;

  // simulate an asynchronous method that frequently changes the value of myModel. Note that
  // not appending false to the end of the $timeout would simply call $digest on $rootScope anyway
  // but we want to explicitly not do this for the example, since most asynchronous processes wouldn't 
  // be operating in the context of a $digest or $apply call. 
  var delay = 2000; // 2 second delay
  var func = function() {
    myModel.a = myModel.a + 10;
    myModel.b = myModel.b + 5;
    calcC();
    $rootScope.$digest();
    $timeout(func, delay, false);
  };
  $timeout(func, delay, false);
});

希望依赖服务模型的控制器可以自由地将模型注入其范围。例如:

$scope.theServiceModel = aModel.myModel;

直接绑定到字段:

<div>A: {{theServiceModel.a}}</div>
<div>B: {{theServiceModel.b}}</div>
<div>C: {{theServiceModel.c}}</div>

每当服务中的值更新时,一切都会自动更新。

请注意,只有将从Object继承的类型(例如数组,自定义对象)直接注入作用域时,才能执行此操作。如果您将字符串或数字等原始值直接注入范围(例如$scope.a = aModel.myModel.a),您将获得一个放入范围的副本,因此永远不会在更新时收到新值。通常,最好的做法是将整个模型对象注入范围,就像我在我的示例中所做的那样。

答案 3 :(得分:2)

一般来说,这可能不是一个好主意。如果没有其他原因,重构变得更加困难和繁重,那么(通常)将模型实现暴露给所有调用者也是不好的做法。我们可以轻松解决这两个问题:

myModule.factory( 'aModel', function () {
  var myModel = { a: 10, b: 10 };

  return {
    get_a: function () { return myModel.a; },
    get_b: function () { return myModel.a; },
    get_c: function () { return myModel.a + myModel.b; }
  };
});

这是最佳实践方法。它可以很好地扩展,只在需要时调用,并且不会污染$rootScope

PS:当ca设置为避免每次调用b时重新计算时,您也可以更新get_c;哪个最好取决于您的实施细节。

答案 4 :(得分:1)

从我所看到的结构来看,将ab作为吸气剂可能不是一个好主意,但c应该是一个功能......

所以我可以建议

myModule.factory( 'aModel', function () {
  return {
    a: 10,
    b: 10,
    c: function () { return this.a + this.b; }
  };
});

使用这种方法,您无法将c绑定到输入变量。但是双向绑定c没有任何意义,因为如果设置c的值,您如何将ab之间的值分开?