AngularJS:Custom指令无法正确计算数据要求

时间:2014-11-20 12:05:31

标签: angularjs angularjs-directive

我正在构建一个自定义指令,允许将glyphicon用作指示符(颜色),通知(工具提示),简单操作(单击)以及在模态(双击)中自定义操作的功能。

以下是plunker我的进展。

首次加载时颜色是正确的 - 如果值= 0则为灰色,值为1时为绿色,值为2时为红色。单击也正确 - 单击0变为1,单击1变为2和a 2单击变为1。

但是,颜色对点击的响应不正确。第一次点击似乎被忽略,因为颜色保持不变,第二次点击确实触发了颜色变化,但现在1 =红色,2 =绿色(向后)。

任何人都可以看到我做错了会导致配色方案失败吗?

这是来自plunker的代码 -

app.js

(function() {
  angular.module('app', ['ui.bootstrap'])
    .directive('sglclick', SingleClickDirective)
    .directive('loanProgressIcon', LoanProgressIconDirective)
    .controller('MainController', MainController);

  function SingleClickDirective($parse) {
    return {
      restrict: 'A',
      link: function(scope, element, attr) {
        var fn = $parse(attr['sglclick']);
        var delay = 300,
          clicks = 0,
          timer = null;
        element.on('click', function(event) {
          clicks++; //count clicks
          if (clicks === 1) {
            timer = setTimeout(function() {
              fn(scope, {
                $event: event
              });
            clicks = 0; //after action performed, reset counter
            }, delay);
          } else {
            clearTimeout(timer); //prevent single-click action
            clicks = 0; //after action performed, reset counter
          }
        });
      }
    };
  }

  function LoanProgressIconDirective($compile) {
    var progressMarkers = [{
      'id': 1,
      'cat': 'its_list',
      'glyph': 'list-alt',
      'tip': 'ITS List Verfified'
    }, {
      'id': 2,
      'cat': 'fsa_compliant',
      'glyph': 'home',
      'tip': 'FSA Eligibility'

    }, {
      'id': 3,
      'cat': 'has_liens',
      'glyph': 'star',
      'tip': 'Prior Lien Verfied'
    }, {
      'id': 4,
      'cat': 'valid_leases',
      'glyph': 'leaf',
      'tip': 'Leases Valid'
    }];

    var statusColors = [
      { val: 0, color: '#CCC', class: 'pending'},
      { val: 1, color: '#006837', class: 'completed'},
      { val: 2, color: '#900', class: 'overdue'}
    ];

    return {
      restrict: 'A',
      require : 'ngModel',
      link: linker,
      templateUrl: 'loanProgress.html',
      scope: {
        cat: '@',
        ngModel: '='
      }
    };

    function linker(scope, element, attrs, ctrl) {
      scope.loan = {
        id: progressMarkers[scope.cat]['id'],
        glyphicon: progressMarkers[scope.cat]['glyph'],
        tooltip: progressMarkers[scope.cat]['tip']
      };

      var styleChange = function () {
        scope.loan.style = statusColors[scope.ngModel]['color'];
      };

      styleChange();

      var setter = ctrl.$setViewValue;

      ctrl.$setViewValue = function() {
          setter.apply(this, arguments);
          styleChange();
      };

      scope.progClicked = function() {

        if(parseInt(scope.ngModel) === 0){
          ctrl.$setViewValue(1);
        } else if(parseInt(scope.ngModel) === 1){
          ctrl.$setViewValue(2);
        } else if(parseInt(scope.ngModel) === 2){
          ctrl.$setViewValue(1);
        }
      };

      scope.progDblClicked = function() {
        alert('Icon ' + scope.ngModel + ' was double clicked.');
      };
    }
  }

  function MainController($scope) {
    $scope.loan = {
      its_list: 1,
      fsa_compliant: 2,
      has_liens: 1,
      valid_leases: 0
    };
  }
})();

的index.html

<!DOCTYPE html>
<html ng-app="app">

  <head>
    <script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
    <script data-require="bootstrap@3.0.2" data-semver="3.0.2" src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script>
    <script data-require="angular.js@1.2.4" data-semver="1.2.4" src="http://code.angularjs.org/1.2.4/angular.js"></script>
    <script data-require="ui-bootstrap@*" data-semver="0.11.0" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.0.min.js"></script>
    <link data-require="bootstrap-css@3.0.2" data-semver="3.0.2" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css" />
    <link rel="stylesheet" href="app.css" />
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainController">
    <div class="container">
      <div class="row">
        <div class="col-xs-12">
          <table class="table table-striped">
            <thead>
              <tr>
                <th colspan="4" class="text-left">LOAN PROGRESS ICONS</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <th>ITS</th>
                <th>FSA</th>
                <th>LIEN</th>
                <th>LEASES</th>
              </tr>
              <tr>
                <td>
                  <span loan-progress-icon cat="0" ng-model="loan.its_list"></span>
                </td>
                <td>
                  <span loan-progress-icon cat="1" ng-model="loan.fsa_compliant"></span>
                </td>
                <td>
                  <span loan-progress-icon cat="2" ng-model="loan.has_liens"></span>
                </td>
                <td>
                  <span loan-progress-icon cat="3" ng-model="loan.valid_leases"></span>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
      <div class="row">
        <div class="col-xs-12">
          <p>{{loan | json}}</p>
        </div>
      </div>
    </div>
  </body>

</html>

指令模板:

<span sglclick="progClicked()" ng-dblclick="progDblClicked()" class="glyphicon glyphicon-{{loan.glyphicon}}" tooltip="{{loan.tooltip}}" style="font-size:18px;color:{{loan.style}};cursor:pointer;"></span>

和app.css

th, td{
  text-align:center;
}

.row{
  margin: 15px 0;
}

.completed{
  color: #006837;
}
.pending{
  color: #CCCCCC;
}
.overdue{
  color: #990000;
}

提前致谢!

1 个答案:

答案 0 :(得分:1)

第一个问题是由于on是jqLit​​e / jQuery方法并且不为您触发摘要循环。这意味着不会更新UI以反映模型的更改。

您需要在调用$apply或使用$timeout代替setTimeout时包装影响模型的代码。

使用$apply

timer = setTimeout(function() {
  scope.$apply(fn(scope, {
    $event: event
  }));
  clicks = 0;
}, delay);

使用$timeout

element.on('click', function(event) {
  clicks++; //count clicks
  if (clicks === 1) {
    timer = $timeout(function() {
      fn(scope, {
        $event: event
      });
      clicks = 0;
    }, delay);
  } else {
    $timeout.cancel(timer); //prevent single-click action
    clicks = 0; //after action performed, reset counter
  }
});

第二个问题是以下代码:

var setter = ctrl.$setViewValue;

ctrl.$setViewValue = function() {
    setter.apply(this, arguments);
    styleChange();
};

在调用$setViewValue之后,内部ngModelWatch将不会检测到更改并更新指令中的ngModel,直到稍后运行摘要循环时。目前styleChange会在此之前运行。

要在摘要循环结束后运行styleChange,您可以使用$timeout

ctrl.$setViewValue = function() {
  setter.apply(this, arguments);
  $timeout(styleChange);
};

演示: http://plnkr.co/edit/5MfQk49NKWFiEOqS2r5V?p=preview