【问题标题】:ng-disabled not evaluating after data changeng-disabled 在数据更改后不评估
【发布时间】: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();
        });
    }

  }]);

【问题讨论】:

    标签: javascript html ajax angularjs


    【解决方案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 我可以手动启动应用程序,看看是否有效...编辑:我的更新代码已经在应用程序中运行,所以应该有更新...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-08
      • 2014-06-17
      • 1970-01-01
      • 2014-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多