AngularJS 从自定义指令更新模型值

AngularJS update model value from custom directive

我想用属性值更新自定义指令的模型值。 假设我有 4 个数字。我想执行以下操作:

公式以自定义指令的属性值表示。 我在 Angular 方面经验不足,并且有一些部分可行的解决方案,但我认为我走错了方向,所以我将 post 编写没有自定义指令的代码。

这是我尝试为其构建自定义指令的代码,编写该指令(公式)的最佳方法是什么?

编辑:带有公式指令的输入字段将是只读的,它们只有一个目的——根据公式重新计算其他字段的值。

<!DOCTYPE html>
<html ng-app>

<head>
  <script data-require="angular.js@*" data-semver="1.4.0-beta.5" src="https://code.angularjs.org/1.4.0-beta.5/angular.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js"></script>
</head>

<body>
  <h1>Cascade sum example</h1>
  <input ng-model="A1" type="number">
  <input ng-model="A2" type="number">
  <input ng-model="A3" type="number">
  <input ng-model="A4" type="number">
  <input ng-model="A5" type="number" formula="A1+A2" readonly>
  <input ng-model="A6" type="number" formula="A3+A4" readonly>
  <input ng-model="A7" type="number" formula="A5+A6" readonly>
</body>

</html>

我认为您不需要编写自己的指令来解决这样的问题。 Angular 允许您计算 html 页面内的表达式。例如,您可以像这样使用 ng-model 值评估内联公式, <input ng-model="A5" type="number" value={{A1+A2}}> angular 将接受它并将其替换为 A1+A2 的值。

编辑: 见 https://jsfiddle.net/v4b37wzm/3/

(解决方案的一个工作 plunker - http://plnkr.co/edit/nhlI4fSsK58mWS18RgUh?p=preview

这里有一个简单的模板来说明用法:

<h1>Cascade sum example</h1>
<ul>
  <li ng-repeat="(key, input) in inputs">
    <input type="number" ng-model="input.value"
      formula="::input.formulaFn"/>
    <span>{{::key}}</span>
    <span ng-if="::input.formula">({{input.formula}})</span>
  </li>
</ul>

首先,我们从数据库中获取输入,并为每个输入创建一个公式函数。调用公式函数时,会为给定公式生成一个值:

Inputs.getAll()
  .then(function(inputs) {
    $scope.inputs = inputs

    _.each($scope.inputs, function(input) {
      if (input.formula) {
        input.formulaFn = parseFormula(input.formula)
      }
    })
  })

  function parseFormula(expr) {
      var parsed = $parse(expr)
      return function apply() {
          return parsed($scope.values)
      }
  }

肉是 parseFormula。它使用 angular 的 $parse 将表达式(例如 'A1+A2')转换为函数(plus(a,b))。如果您使用包含属性 A1A2 的对象调用 parsed,它将产生它们值的总和 - 这就是 apply() 正在做的事情。

我们当前的对象 $scope.inputs 不能用于为我们解析的表达式提供所需的值(它需要看起来像 'A1': 1,而不是 'A1': { ... })。不幸的是,我们不能使用同一个对象来保存我们的值和我们的 ng 模型,因为 angular 的 ng-repeat 绑定和原语的特殊性(你可以在这里阅读更多关于它的信息 - https://github.com/angular/angular.js/wiki/Understanding-Scopes#ng-repeat - 这也被非正式地称为 "dot rule")。这就是为什么我们需要同时拥有 $scope.inputs(以提供我们的可绑定模型)和一个同步的 $scope.values,我们将其用于我们的表达式。

$scope.values = {}
$scope.$watch('inputs', function(value) {
  $scope.values = _.mapValues($scope.inputs, function(input) {
    return input.value
  })
}, true)

该指令相当简单。如果元素上有一个公式(这实际上是我们之前创建的公式 functions),它将监视它(这意味着该函数被调用每个摘要)。一旦公式产生了一个新值(例如 'A1+A2',A1 或 A2 被更改),那么我们只需将 ngModel 与其同步。

.directive('formula', function() {
  return {
    scope: {
      formula: '=',
    },
    require: 'ngModel',
    link: function(scope, element, attrs, ngModelCtrl) {
      if (!scope.formula) return
      element.attr('readonly', true)
      scope.$watch(scope.formula, function(value) {
        ngModelCtrl.$setViewValue(value)
        ngModelCtrl.$render()
      })
    }
  }
})