【问题标题】:ng-model does not update correctlyng-model 没有正确更新
【发布时间】:2014-07-03 12:18:23
【问题描述】:

我有这样的结构(非常简化):

<div ng-controller="MainCtrl">
  <div>Views and other stuff</div>
    <div ng-controller="MyCtrl">
      <div ng-repeat="item in $root.items track by $index">
        {{item.id}}
      </div>
    <div>
        Total value: {{totVal}}
    </div>
  </div>
</div>

在我的主控制器中,我定义了 $rootScope.items = [{id: 1,..},{id: 2,..},{id: 3,..},..] 并在我的其他控制器我已经定义了 $scope.totVal = 0 和一个用于在 items 数组更改时更新 totVal 的 items 数组的观察者;

.controller('MainCtrl', ['$rootScope', '$scope', function($rootScope, $scope) {
    // Are actually fetched from a service
    $rootScope.items = [
        {id: 1,..},
        {id: 2,..},
        ..
    ];
}])

.controller('MyCtrl', ['$rootScope', '$scope', function($rootScope, $scope) {
    $rootScope.updateTotVal = function() {
        var totVal = 0;
        // calculations are done here
        // Is correct here when I console log after all calculations are complete
        console.log(totVal); 
        $scope.totVal = totVal;
    }

    $scope.totVal = 0;

    $rootScope.$watch('items', function() {
        $rootScope.updateTotVal();
    }, true);
}]);

我遇到的问题是视图中的 totVal 没有更新。当我控制台记录它在控制台中正确显示的值时,但如果我将 totVal div 滚动出视图并再次返回,它只会在视图中更新。似乎是 Chrome 中的一个 AngularJS 错误,但是当我谷歌时找不到任何类似的情况。它在 FireFox 中运行良好。

如果我在 ng-repeat 之前移动 totVal,它会起作用,如果我删除 ng-repeat,它会起作用(我宁愿不改变 html 的结构,因为它会做很多工作并使设计变得更糟)。我也尝试将 totVal 移动到根范围,但没有成功。

只有绘画/渲染失败,Batarang 和 console.log 总是给我更新的值。

以前有人遇到过这个问题吗?

【问题讨论】:

  • totVal 在每次调用 updateTotVal 时设置为 0。我想这就是为什么你总是看到 0。除非在变量的声明和 console.log 之间缺少一些代码。
  • "// 计算" 是缺少的代码...
  • link 给出了一些很好的答案,这使我的应用程序的结构更好。但是渲染的问题依然存在。我发现,如果我将要呈现的值放入块元素中,它不会更新,直到它被滚动出视图并再次返回(仅适用于 Chrome)。但是,如果我将值放在跨度或浮动元素中,它就可以工作。必须是 Chrome 和 Angular 的范围/渲染问题...
  • 请花点时间查看答案并选择正确的答案(或添加您的解决方案,如果不同)。

标签: javascript angularjs angularjs-ng-repeat angular-ngmodel


【解决方案1】:

我不知道这是否正是您要寻找的东西,因为我不知道您的用例。 但是,我实现了一个工作示例,请在此处查看 codepen

我稍微重构了您的代码,删除了额外的控制器并将totVal 封装在服务中。另外,我使用controllerAs 语法使一切更清晰。

您的代码的问题在于totVal 是一个原语。因此,当您为其分配新值时,角度将无法更新参考。这就是为什么你应该在你的模型中总是有一个点

看看我的代码,你会发现我将totVal 声明为var totVal = {val : 0}。我只是更新val,保留引用并正确更新视图。

.service('totVal', function(){
  var totVal = {val:0};

  this.computeTotVal = function(){
    //perform computations, I will just increment
    totVal.val++;
  }

  this.getTotVal = function() { return totVal; }
})

另见article

【讨论】:

  • 好的,我想我明白我应该做什么(以及应该如何做)。我会试试这个,但我需要一些时间,因为这是一个需要编辑的大项目:s 当我知道它是否对这种特殊情况有帮助时,我会回到这里。感谢您的示例:)
  • 很高兴它对您有所帮助。如果您不喜欢分配对象而不是原始对象(就我个人而言,我不喜欢),更精细的解决方案是在控制器中分配 this.totVal = totVal 并在视图中调用 totVal.getTotVal()。如果您想更好地理解我在说什么,我更新了 codepen 示例。
  • 好的,我现在已经测试了其中的一些东西。我该如何解决这个例子? plnkr.co/edit/YTx2ocNuRlnM6RHfTZH4?p=preview 我无法获取要重置的值或与 ng-model 和 select 交互。
  • 让我看看。
  • 好的,我更正了。要使下拉列表正常运行,您必须使用ng-optionslink to plunkr
【解决方案2】:

我不确定这是否是罪魁祸首,但您绝对不应该为此使用 $rootScope

正如文档所述:

$rootScope 存在,但它可以用来作恶

请看这里:https://docs.angularjs.org/misc/faq#-rootscope-exists-but-it-can-be-used-for-evil


如果没有必要,我会在没有它的情况下重写你的代码:

<div ng-controller="MainCtrl">
  <div>Views and other stuff</div>
  <div ng-controller="MyCtrl">
  <!-- You also had an $root.items that didn't match any element here -->
    <div ng-repeat="item in items track by $index">
      {{item.id}}
    </div>
    <div>
      Total value: {{totVal}}
    </div>
  </div>
</div>

在您的控制器中,您将绑定您的项目,如下所示:

.controller('MainCtrl', ['$scope', function($scope) {
    // Are actually fetched from a service
    $scope.items = [
        {id: 1,..},
        {id: 2,..},
        ..
    ];
}])
.controller('MyCtrl', ['$rootScope','$scope', function($rootScope, $scope) {
    $scope.updateTotVal = function() {
        var totVal = 0;

        // calculations

        console.log(totVal); // Is correct here

        $scope.totVal = totVal;
    }

    $scope.totVal = 0;

    // Here $rootScope makes sense
    $rootScope.$watch('items', function() {
        $scope.updateTotVal();
    }, true);
}]); 

编辑:
此外,每次实例化控制器时,都会设置 $rootScope.totalVal = 0,最终将其重置。
也许这就是您看不到更新的原因?

【讨论】:

  • 但是如果它们都在控制器范围内,我如何在控制器之间共享函数和变量?我希望能够从多个控制器访问 updateTotVal。
  • 您通常使用可注入对象,例如 service,作为单例,无论何时注入它都会提供一致的值。
【解决方案3】:

问题在于 Angular 和 Chrome。如果我删除了在 totVal 输出之前的 ng-repeat,它就起作用了。如果我将 totVal 放在更高的 DOM 结构中或在 ng-repeat 之前,它也会起作用。 我认为这与绑定的数量和 DOM 的复杂性有关,许多嵌套的 DIV 以某种方式破坏了绑定。我还注意到,如果我有许多嵌套的引导类,如“row”和“col-xs-12”,它也会破坏绑定。所以它似乎与浏览器渲染/绘制视图有关(因为滚动 totVal 并返回到视图更新了值)。

我从中学到的主要内容是使用更智能的指令并尝试简化 DOM 结构,尤其是在使用引导程序的情况下。

原文:

<div ng-controller="MainCtrl">
  <div>Views and other stuff</div>
  <div ng-controller="MyCtrl">
    <div ng-repeat="item in $root.items track by $index">
      {{item.id}}
    </div>
    <div>
      Total value: {{totVal}}
    </div>
  </div>
</div>

使其正常工作的不同更改:

<div ng-controller="MainCtrl">
  <div>Views and other stuff</div>
  <div ng-controller="MyCtrl">
    <div>
      Total value: {{totVal}}
    </div>
  </div>
</div>

<div ng-controller="MainCtrl">
  <div>Views and other stuff</div>
  <div ng-controller="MyCtrl">
    <div ng-repeat="item in $root.items track by $index">
      {{item.id}}
    </div>
  </div>
  <div>
    Total value: {{totVal}}
  </div>
</div>

<div ng-controller="MainCtrl">
  <div>Views and other stuff</div>
  <div ng-controller="MyCtrl">
    <div>
      Total value: {{totVal}}
    </div>
    <div ng-repeat="item in $root.items track by $index">
      {{item.id}}
    </div>
  </div>
</div>

【讨论】:

    猜你喜欢
    • 2017-02-21
    • 2018-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多