ng-disabled 在数据更改后不评估

Posted

技术标签:

【中文标题】ng-disabled 在数据更改后不评估【英文标题】:ng-disabled not evaluating after data change 【发布时间】:2014-11-06 00:14:27 【问题描述】:

我正在编写一个应用程序,用户可以在其中升级用游戏币支付的组件。按下“升级”按钮后,将向服务器发送 ajax 请求并升级组件。之后,我发出另一个 ajax 请求来检索新的组件页面。但是当数据更新时,不会再次检查 ng-disabled ,因此升级按钮在不应该点击时仍然可以点击。重新加载页面或路线可以解决此问题。但是重新加载页面并没有很好的用户体验。

我做这个项目是为了学习 Angularjs 的方式,所以如果我的设置有什么问题或者我是怎么做的,请告诉我!

我尝试包含所有相关代码,如果缺少某些内容,您可以在this link 下查看,用户名和密码为“test”。

首先,在machine-overview.html 中,按钮调用upgradeComponentcontroller.js 控制器MachineOverviewCtrl 发出Ajax 请求并成功更新$scope.user$scope.machine,更改的数据将反映在视图中(@ 987654329@) 但按钮 ng-disabled 未评估并保持旧状态。

我做错了什么?如何强制评估或以 Angularjs 喜欢的方式进行更新?

编辑:

@jack.the.ripper 的回答没有抓住重点,即有多个按钮,每个 ng-repeat 一个,但这些按钮只是不评估它们的 ng-disable 指令。

//编辑

index.html

<!DOCTYPE html>
<html lang="de" ng-app="hacksApp">
    <head>
        <!--ommited includes (angularjs, bootstrap etc)-->
    </head>
    <body ng-controller="hacksUserCtrl">
        <!--ommited navbar-->
        <div ng-view></div>
    </body>
</html>

machine-overview.html

<!--ommited divs-->
    <table class="table table-bordered">
        <!--ommited thead-->
        <tbody>
            <tr ng-repeat="component in machine | object2Array | orderBy:'ComponentID'"><!--converting objects to arrays, issue appears with and without filter-->
                <!--ommited other irrelevant td-->
                <td>
                    <button 
                    ng-disabled="
                    
                        (component.price_next <= 0) || (component.price_next > user.values.euro) || 
                        (user.values.energy_remaining < (component.energy_next-component.energy)) 
                    " 
                    ng-click="upgradeComponent(component)" class="btn btn-danger" role="button">
                        Upgrade component.text for <span class="glyphicon glyphicon-euro"></span> component.price_next 
                    </button>
                </td>
            </tr>
        </tbody>
    </table>
<!--ommited divs-->

javascript 变量及其来源:

$scope.user.x //hacksUserCtrl
$scope.machine.x //MachineOverviewCtrl

app.js

var hacksApp = angular.module('hacksApp', [
    'ngRoute',
    'hacksControllers',
  'hacksFilters',
    ]);

hacksApp.config(['$routeProvider',
  function($routeProvider) 
    $routeProvider.
      when('/machine', 
        templateUrl: 'html/machine-overview.html',
        controller: 'MachineOverviewCtrl',
        activetab: 'machine'
      ).
      when('/jobs', 
        templateUrl: 'html/jobs-overview.html',
        controller: 'JobsOverviewCtrl',
        activetab: 'jobs'
      ).
      when('/phones/:phoneId', 
        templateUrl: 'html/phone-detail.html',
        controller: 'PhoneDetailCtrl',
        activetab: 'phone'
      ).
      otherwise(
        redirectTo: '/machine'
      );
  ]);

controller.js

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

hacksControllers.controller('hacksUserCtrl', ['$scope', '$http', '$interval', '$route',
  function ($scope, $http, $interval, $route) 

    $scope.$route = $route;

    $scope.updateUser = function() 
        $http.get('api/user.php').success(function(data) 
          $scope.user = data;
          if(!$scope.user.loggedIn)
            window.location = "index.php";
          
        );
    
    $scope.updateUser();
    $interval($scope.updateUser, 60000);

  ]);

hacksControllers.controller('MachineOverviewCtrl',  ['$scope', '$http', '$interval',
  function ($scope, $http, $interval) 

  $scope.machine = [];

    $scope.updateMachine = function() 
        $http.get('api/machine.php').success(function(data) 
          $scope.machine = data;
        );
    
    $scope.updateMachine();
    $interval($scope.updateMachine, 60000);

    $scope.upgradeComponent = function(component)

        var params = name: component.name;
        $http(
            method: 'POST',
            url: 'api/machine-upgrade.php',
            data: $.param(params),
            headers: 'Content-Type': 'application/x-www-form-urlencoded'
        ).success(function(data) 
          $scope.updateMachine();
          $scope.updateUser();
        );
    

  ]);

【问题讨论】:

【参考方案1】:

您的主要问题是您插入 ng-disabled 而不是为其提供角度表达式。请参阅 documentation 中的参数定义。对表达式进行插值(使用 )会使表达式在 ng-disabled 指令中只计算一次。

简单的改变:

<button ng-disabled="
   
      (component.price_next <= 0) || (component.price_next > user.values.euro) || 
      (user.values.energy_remaining < (component.energy_next-component.energy)) 
   " 

   ng-click="upgradeComponent(component)" class="btn btn-danger" role="button">
       Upgrade component.text for <span class="glyphicon glyphicon-euro"></span>     
       component.price_next 
 </button>

<button ng-disabled="(component.price_next <= 0) || (component.price_next > user.values.euro) || 
      (user.values.energy_remaining < (component.energy_next-component.energy))" 

   ng-click="upgradeComponent(component)" class="btn btn-danger" role="button">
       Upgrade component.text for <span class="glyphicon glyphicon-euro"></span>     
       component.price_next 
 </button>

【讨论】:

哇,我现在觉得有点傻!明天试试,然后接受你的回答,谢谢:)【参考方案2】:

在阅读 $scope.$apply 和 $scope.$watch 时,我找到了解决方案:当我更改请求的顺序时,一切正常。因为在更新用户数据(如金钱)时,没有为机器表触发更新。将代码更改为首先更新 $scope.user,然后 $scope.machine 解决了这个问题。我正在制作这个社区维基。

【讨论】:

【参考方案3】:

您的情况: 当用户单击一个按钮时,您将其禁用,然后当您的操作完成后,您希望返回到启用的按钮。

我应该做的是创建一个指令,在按钮执行操作时管理按钮的状态,不管你在做什么操作......!

baign 说,对您的问题的一个可能的答案可能是使用承诺并在您的“点击”事件中返回一个承诺,这样您就可以在用户单击它时将按钮设置为禁用 true,并在当你的承诺得到了解决。

看看这个指令:

angular.module('app').directive('clickState', [
    function() 
        return 
            priority:-1,
            restrict: 'A',
            scope: 
                clickState: '&'
            ,
            link: function postLink(scope, element, attrs) 
                element.bind('click', function()
                    element.prop('disabled', true);
                    scope.clickState().finally(function() 
                            element.prop('disabled', false);
                    );

                );
            
        ;
    
]);

用法:

<button click-state="updateMachine()" type="submit">submit</button>

控制者:

var p = $q.defer()
$scope.updateMachine = function() 
        $http.get('api/machine.php').success(function(data) 
          $scope.machine = data;
          p.resolve(); //it could be p.resolve(data); to pass the data with the promise
        );
        return p.promise()
    

【讨论】:

首先:感谢您的代码!但这听起来有点复杂,可悲的是影响了可用性。如果用户想升级 3 次,他只需点击 3 次。它将更新数据 3 次,并且至少第三次更新获得最新状态。我的特殊问题是,评估ng-disabled="(component.price_next &lt;= 0) || (component.price_next &gt; user.values.euro) || (user.values.energy_remaining &lt; (component.energy_next-component.energy))" 在数据更新后不会评估。它仍然评估旧数据,所以我认为。这与按钮禁用动作无关。 不要忘记绑定点击中的指令 clickState 中的 $scope.$apply() @Kostronor,尝试将表达式移动到控制器,创建一个变量并将表达式分配给它,将布尔值绑定到 ng-disabled,对组件对象执行 $watch 并查看如何它去 我目前正在阅读 $scope.apply 和 $watch。感谢您的提示! @jack.the.ripper 我无法将它移动到控制器,因为它依赖于 ng-repeat。每个组件都有一个按钮。 f.e. i.imgur.com/nuHOall.jpg 我可以手动启动应用程序,看看是否有效...编辑:我的更新代码已经在应用程序中运行,所以应该有更新...

以上是关于ng-disabled 在数据更改后不评估的主要内容,如果未能解决你的问题,请参考以下文章

为啥 ApolloQueryWatcher 在突变更改数据后不触发?

为啥 Bindinglist 在数据库更改后不更新?

Nuxtjs asyncdata 在导航更改后不保留数据

数据库表设计后不允许保存更改

约束布局更改后不触发 UIButton 操作

Winforms ListBox 控件在源更改后不更新