Kyle
Kyle

Reputation: 1578

Not being able to iterate through inputs within scope

I'm trying to iterate through an array created using angular, so I can add them up and then show them, but the problem is, I'm getting an error saying that property '1' of undefined even if I fill out the input field, why is this?

My code:

<!DOCTYPE html>
<html>

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
  <title>jQueryForm</title>
</head>

<body ng-app="myApp" ng-controller="myCtrl">
  <div id="form" ng-init="dataSet = [{},{},{},{},{}]">
    <div ng-repeat="data in dataSet">
      <input type="checkbox" ng-model="data.checked" />
      <label>Data:</label>
      <input type="number" ng-model="data.number" />
      <br/>
    </div>

    <span>{{result}}</span>

    <script>
      var app = angular.module('myApp', []);
      app.controller('myCtrl', function($scope) {
        for (var i = 1; i < 5; i++) {
          $scope.result += $scope.dataSet[i];
        }
      });
    </script>
  </div>
  </div>
  </span>

</body>


</html>

Upvotes: 0

Views: 44

Answers (3)

JanS
JanS

Reputation: 2075

You have to initialize dataSet differently this time around. Just add it to the controller's scope instead of using ng-init.

<!DOCTYPE html>
<html>

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
  <title>jQueryForm</title>
</head>

<body ng-app="myApp" ng-controller="myCtrl">
  <div id="form">
    <div ng-repeat="data in dataSet">
      <input type="checkbox" ng-model="data.checked" />
      <label>Data:</label>
      <input type="number" ng-model="data.number" />
      <br/>
    </div>

    <span>{{result}}</span>

    <script>
      var app = angular.module('myApp', []);
      app.controller('myCtrl', function($scope) {
        $scope.dataSet = [{},{},{},{},{}];

        for (var i = 1; i < 5; i++) {
          $scope.result += $scope.dataSet[i];
        }
      });
    </script>
  </div>
  </div>
  </span>

</body>


</html>

To make the code work, though, you'll have to change a few things. The loop in which you try adding the numbers will be called only once - at instantiation time of the controller. At that time, no number has been entered, yet. You could solve it with a button. (Also add up the numbers, not the objects in dataSet)

<!DOCTYPE html>
<html>

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
  <title>jQueryForm</title>
</head>

<body ng-app="myApp" ng-controller="myCtrl">
  <div id="form">
    <div ng-repeat="data in dataSet">
      <input type="checkbox" ng-model="data.checked" />
      <label>Data:</label>
      <input type="number" ng-model="data.number" />
      <br/>
    </div>

    <button ng-click="calculate()">calculate</button>
    <span>{{result}}</span>

    <script>
      var app = angular.module('myApp', []);
      app.controller('myCtrl', function($scope) {
        $scope.dataSet = [{},{},{},{},{}];

        $scope.calculate = function() {
          $scope.result = 0;

          for (var i = 0; i < $scope.dataSet.length; i++) {
            if(angular.isNumber($scope.dataSet[i].number) && $scope.dataSet[i].checked)
              $scope.result += $scope.dataSet[i].number;
          }
        };

        //Edit. Doesn't have the best performance, keep dataSet small
        $scope.$watch('dataSet', function(newValue, oldValue) {
          $scope.calculate();
        }, true);

      });
    </script>
  </div>
  </div>
  </span>

</body>


</html>

I fixed a few more things here and there. I assumed, you wanted to add only those numbers that were checked - also, the ng-models without a value were causing problems, that's why the check for being a number was introduced.

edit: As you wanted to do it without the button: You can use a deep $scope.$watch. https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch

Upvotes: 1

Kyle
Kyle

Reputation: 1578

Thanks to @Pankaj Parkar and @JanS, I finally made what I wanted to, everytime the input is changed I call the function calculate(); and everytime a checkbox is clicked on I call it as well.

Code:

<!DOCTYPE html>
<html>

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
  <title>jQueryForm</title>
</head>

<body ng-app="myApp" ng-controller="myCtrl">
  <div id="form">
    <div ng-repeat="data in dataSet">
      <input ng-click="calculate()" type="checkbox" ng-model="data.checked" />
      <label>Data:</label>
      <input ng-change="calculate()" type="number" ng-model="data.number" />
      <br/>
    </div>

    <span>{{result}}</span>

    <script>
      var app = angular.module('myApp', []);
      app.controller('myCtrl', function($scope) {
        $scope.dataSet = [{}, {}, {}, {}, {}];

        $scope.calculate = function() {
          $scope.result = 0;

          for (var i = 0; i < $scope.dataSet.length; i++) {
            if (angular.isNumber($scope.dataSet[i].number) && $scope.dataSet[i].checked)
              $scope.result += $scope.dataSet[i].number;
          }
        }
      });
    </script>
  </div>
  </div>
  </span>

</body>


</html>

Upvotes: 0

Pankaj Parkar
Pankaj Parkar

Reputation: 136144

As ng-init is special kind of directive which evaluates expression provided to it in preLink function of directive.

PreLink function: - this function gets evaluates after scope has been created for that element

By reading above line you will come to know that it has evaluated for loop before $scope.dataset getting available. Which is obiviously going to get fail.

I'd say don't use ng-init for such cases. Place that initialization data inside controller itself.

Code

//remove ng-init directive from UI
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
   function init(){
     for (var i = 1; i < 5; i++) {
       if($scope.dataSet[i].checked)
          $scope.result += $scope.dataSet[i].number;
     }
   }

   //init
   $scope.dataSet = [{},{},{},{},{}]
   init();
});

Upvotes: 2

Related Questions