AngularJS 从自定义指令更新模型值
AngularJS update model value from custom directive
我想用属性值更新自定义指令的模型值。
假设我有 4 个数字。我想执行以下操作:
- 将前两个数相加 (1+2)
- 将第二个数字相加 (3+4)
- 将两个和 (sum1+sum2) - 级联和相加
公式以自定义指令的属性值表示。
我在 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 的值。
(解决方案的一个工作 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)
)。如果您使用包含属性 A1
和 A2
的对象调用 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()
})
}
}
})
我想用属性值更新自定义指令的模型值。 假设我有 4 个数字。我想执行以下操作:
- 将前两个数相加 (1+2)
- 将第二个数字相加 (3+4)
- 将两个和 (sum1+sum2) - 级联和相加
公式以自定义指令的属性值表示。 我在 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 的值。
(解决方案的一个工作 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)
)。如果您使用包含属性 A1
和 A2
的对象调用 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()
})
}
}
})