Search code examples
angularjscomputed-field

angular computed variable from an expression


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'] +"}
]


Solution

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