Reputation: 23
I have a requirement to dynamic show the sub total based on a expression returned from the service. See the sample item structure below. property value is binding to a text input. If any of the input value changes, sub total is updated according to the expression in a label. What's the best way to do this?
Note: number of fieldname/Value pair can vary.
$scope.item = [
{fieldName: "fname1", value: 2},
{fieldName: "fname2", value: 5},
{fieldName: "fname3", value: 4},
{fieldName: "fname4", value: 6},
{fieldName: "fname5", value: 3},
{fieldName: "subTotal1", expression: "['fname1'] + ['fname2'] +['fname3'] +['fname4'] +['fname5'] +"}
]
Upvotes: 1
Views: 1140
Reputation: 3186
Oh my God! I did it! Look :)
Of course, this solution is not perfect. And it depends on the variable name used in the controller. But it works!
Live example on jsfiddle.
<form name="ExampleForm" id="ExampleForm">
<div ng-repeat="item in items">
<div ng-if="item.fieldName!='subTotal1'">
<input ng-model="item.value">
<my-ng-model n-name="{{item.fieldName}}" n-value="item.value" obj="obj"></my-ng-model>
</div>
<div ng-if="item.fieldName=='subTotal1'">
{{item.expression }}={{$eval(item.expression)}}
</div>
</div>
{{obj|json}}
</form>
And js controller:
.controller('ExampleController', function($scope, $parse) {
$scope.obj = {};
$scope.items = [{
fieldName: "fname1",
value: 2
}, {
fieldName: "fname2",
value: 5
}, {
fieldName: "fname3",
value: 4
}, {
fieldName: "fname4",
value: 6
}, {
fieldName: "fname5",
value: 3
}, {
fieldName: "subTotal1",
expression: "obj.fname1 + obj.fname2 +obj.fname3 +obj.fname4 +obj.fname5"
}];})
And js directive:
.directive('myNgModel', function() {
var root = {
restrict: "E",
replace: true,
scope: {
nName: "@",
nValue: "=",
obj: "="
},
template: '<div></div>',
link: function(scope) {
scope.obj[scope.nName] = scope.nValue*1;
scope.$watch('nValue', function(value) {
scope.obj[scope.nName] = value*1;
});
}
}
return root; })
UPDATED
Now it works without reference to a local variable!
Live example on jsfiddle.
<form name="ExampleForm" id="ExampleForm">
<div ng-repeat="item in items">
<div ng-if="item.fieldName!='subTotal1'">
<input ng-model="item.value">
<my-ng-model n-name="{{item.fieldName}}" n-value="item.value" obj="obj"></my-ng-model>
</div>
</div>
<div ng-repeat="eval in evals">
{{eval.expression }}={{$eval(eval.expression,obj)}}
</div>
</form>
Controller
.controller('ExampleController', function($scope, $parse) {
$scope.obj = {};
$scope.items = [{
fieldName: "fname1",
value: 2
}, {
fieldName: "fname2",
value: 5
}, {
fieldName: "fname3",
value: 4
}, {
fieldName: "fname4",
value: 6
}, {
fieldName: "fname5",
value: 3
}, {
fieldName: "subTotal1",
expression: "fname1 + fname2 +fname3 +fname4 +fname5"
}];
$scope.evals = [];
angular.forEach($scope.items, function(item) {
if (item.expression) {
$scope.evals.push({
expression: item.expression
});
}
});})
And directive
.directive('myNgModel', function() {
var root = {
restrict: "E",
replace: true,
scope: {
nName: "@",
nValue: "=",
obj: "="
},
link: function(scope) {
scope.obj[scope.nName] = scope.nValue * 1;
scope.$watch('nValue', function(value) {
scope.obj[scope.nName] = value * 1;
});
}
}
return root;})
Upvotes: 1
Reputation: 2214
I wrote a fiddle to solve your problem. It can pe improved, but I don't have time at the moment. You can do that yourself.
It is based on the model you posted :
$scope.item = [
{fieldName: "fname1", value: 2},
{fieldName: "fname2", value: 5},
{fieldName: "fname3", value: 4},
{fieldName: "fname4", value: 6},
{fieldName: "fname5", value: 3},
{fieldName: "subTotal1", expression: "['fname1'] + ['fname2'] +['fname3'] +['fname4'] +['fname5'] +"}
]
For binding to work you can put everything in a function that is re-evaluated when a property changes.
Check out it HERE
Upvotes: 0
Reputation: 23798
If you need to compute the value based on the expresion
property, you should first tokanize the field indicators together with their mathematical operators in the expression. Complexity of this process is varied by the types of mathematical impressions you allow in the expression.
Eg:
[fName1] + [fName2] - [fName3] // is a simple expression
[fName1] * ([fName2] + [fName3]) // is complex than that
Then you should compute the aggregated value out of these tokens by comparing them against the array.
--
One alternative way, which I don't recommend if the expression is coming from the user, is to create a private scope object which has all these fName variables inside it and use eval
function against that context via abstraction.
You can get an idea about this method in this answer.
Upvotes: 0