ng-disabled在数据更改后未进行评估

时间:2022-08-22 17:13:01

I am writing an app where the user can upgrade components paying with ingame money. After pressing the "upgrade"-button an ajax request is send to the server and upgrades the component. After that I am issuing another ajax request to retrieve the new components page. But while the data gets updated, ng-disabled is not checked again, so the upgrade-button remains clickable when it should not. Reloading page or route fixes this. But reloading the page is no good user experience.

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

I am doing this project to learn the Angularjs way, so if there is anything wrong in my setup or how I do something, please tell me!

我正在做这个项目来学习Angularjs的方法,所以如果我的设置有什么问题或者我做了什么,请告诉我!

I try to include all relevant code, if something is missing, you can view it under this link with user and password "test".

我尝试包含所有相关代码,如果缺少某些内容,您可以在此链接下使用用户和密码“test”查看它。

For a headstart, in machine-overview.html the button calls upgradeComponent in controller.js controller MachineOverviewCtrl issuing an Ajax request and on success updating $scope.user and $scope.machine, changed data is reflected in the view (ng-repeat) but the buttons ng-disabled is not evaluated and keeps the old state.

对于headstart,在machine-overview.html中,按钮调用controller.js控制器中的upgradeComponent控制器MachineOverviewCtrl发出Ajax请求,并且在成功更新$ scope.user和$ scope.machine时,更改的数据将反映在视图中(ng-repeat)但是按钮ng-disabled不会被评估并保持旧状态。

What am I doing wrong? How can I force an evaluation or do the update in a way Angularjs likes it?

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

edit:

@jack.the.ripper's answer is missing the point, that there are multiple buttons, one for every ng-repeat, but those buttons only don't evaluate their ng-disable directive.

@ jack.the.ripper的答案是缺少这一点,即有多个按钮,每个按钮重复一次,但这些按钮只是不评估它们的ng-disable指令。

ng-disabled在数据更改后未进行评估

//edit

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 vars and their origin:

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();
        });
    }

  }]);

3 个解决方案

#1


16  

Your main problem is you are interpolating ng-disabled instead of providing it an angular expression. See the arguments definition in the documentation. Interpolating (using {{}}) the expression makes the expression evaluated only once within the ng-disabled directive.

您的主要问题是您正在插入ng-disabled而不是为其提供角度表达式。请参阅文档中的参数定义。插值(使用{{}})表达式使表达式仅在ng-disabled指令中计算一次。

Simply change:

<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>

to

<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


1  

your scenario: when a user click on a button you disable it, then when your operation is complete you want to return to the button to enabled.

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

what I should do is to create a directive that manage the state of the button while the button is performing the operation, it doesn't matter what operation you are doing....!

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

baign said that, a possible answer to your issue could be to use promises and return a promise in your 'click' events, in that way you can set the button to disabled true when the user click it and enable it again when your promise gets resolved.

baign说,对你的问题可能的答案可能是使用promises并在你的'click'事件中返回一个promise,这样你就可以在用户点击它时将按钮设置为true,并在你的promise获得时再次启用它解决。

take a look to this directive:

看看这个指令:

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);
                    });

                });
            }
        };
    }
]);

usage:

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

controller:

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


1  

While reading up on $scope.$apply and $scope.$watch I found the solution to this: When I change the order of my requests, everything works. Because on updating the userdata (like money) there is no update triggered for the machine table. Changing the code to first update $scope.user and after that $scope.machine solves this problem. I am making this community wiki.

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

#1


16  

Your main problem is you are interpolating ng-disabled instead of providing it an angular expression. See the arguments definition in the documentation. Interpolating (using {{}}) the expression makes the expression evaluated only once within the ng-disabled directive.

您的主要问题是您正在插入ng-disabled而不是为其提供角度表达式。请参阅文档中的参数定义。插值(使用{{}})表达式使表达式仅在ng-disabled指令中计算一次。

Simply change:

<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>

to

<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


1  

your scenario: when a user click on a button you disable it, then when your operation is complete you want to return to the button to enabled.

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

what I should do is to create a directive that manage the state of the button while the button is performing the operation, it doesn't matter what operation you are doing....!

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

baign said that, a possible answer to your issue could be to use promises and return a promise in your 'click' events, in that way you can set the button to disabled true when the user click it and enable it again when your promise gets resolved.

baign说,对你的问题可能的答案可能是使用promises并在你的'click'事件中返回一个promise,这样你就可以在用户点击它时将按钮设置为true,并在你的promise获得时再次启用它解决。

take a look to this directive:

看看这个指令:

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);
                    });

                });
            }
        };
    }
]);

usage:

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

controller:

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


1  

While reading up on $scope.$apply and $scope.$watch I found the solution to this: When I change the order of my requests, everything works. Because on updating the userdata (like money) there is no update triggered for the machine table. Changing the code to first update $scope.user and after that $scope.machine solves this problem. I am making this community wiki.

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