2

I've a button in my angular app that will add an input element to my app dynamically. I want to attach model to this element and put it to watch group for tracking any changes. If user add multiple input elements, I want to add all those models in watch group and track which element changed its value. How can I do this?

<body ng-app="myApp">

<div ng-controller="GpaController" > 
<form id="mform">
 <select name="grade" ng-model="grade" ng-options="key as key for (key,value) in gradePoints" ></select>    
<input type="number" min="1" max="4" ng-model="score">

</form>
<button type="button" ng-click="addElement()">Add</button>

</div>
<script type="text/javascript" src="./app/angular.min.js"></script>
<script type="text/javascript" src="./app/jquery-2.1.3.min.js"></script>
<script type="text/javascript">

angular.module('myApp', [])
.controller('GpaController', ['$scope', '$compile', function($scope, $compile){
    $scope.grade = 'A';
    $scope.score = 4;
    var gradePoints = { 'A': 4, 'A-': 3.7, 
                   'B+': 3.3, 'B': 3.0, 'B-': 2.7, 
                   'C+': 2.3, 'C': 2.0, 'C-': 1.7, 
                   'D': 1, 'F': 0 };
    $scope.gradePoints = gradePoints;
            $scope.$watchGroup(['grade', 'score'], function(){
                console.log($scope.grade)
                var n = $scope.gradePoints[$scope.grade] * $scope.score;
                    console.log(n);
            });
            $scope.addElement = function(){
                var val = '<div><select name="grade" ng-model="grade" ng-  options="key as key for (key, value) in gradePoints" > </select><input type="number" min="1" max="4" ng-model="score"><div>';
                var ele = $compile(val)($scope);
                    $("#mform").append(ele);
            }

}]);
</script>
</body>
mallaudin
  • 4,744
  • 3
  • 36
  • 68

2 Answers2

1

You could try below way to check which variable has changed.

$scope.$watchGroup(['grade', 'score'],
  function(newVal, oldVal) {
    var changed = ''
    if (newVal[0] != oldVal[0])
        changed = 'grade';
    if (newVal[1] != oldVal[1])
        changed = 'score';
});

Refer this SO Answer for more details.

Update

For adding new value you can use ng-repeat instead of adding element

Markup

<form id="mform">
    <div ng-repeat="obj in objects">
        <select name="grade" ng-model="grade[obj]" 
         ng-options="key as key for (key,value) in gradePoints">
        </select>
        <input type="number" min="1" max="4" ng-model="score" />
    <div>
</form>

Controller

$scope.objects = ["1"]; //i have given sample, you could try something like this.
$scope.addElement = function(){ 
  $scope.objects.push($scope.objects.length + 1) 
}
Community
  • 1
  • 1
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • this is working for grade and score, but when i add new element, I want to add changes of that newly added input and select element and put them in watch group. – mallaudin Apr 23 '15 at 15:37
  • you shouldn't add an new element, you should maintain a array & new element into that array. On UI you should render element using `ng-repeat` and `ng-reapeat` will take care of newly added element automatically – Pankaj Parkar Apr 23 '15 at 15:41
  • i don't know in advance how many inputs are required, its up to user how much he/she wants to add. – mallaudin Apr 23 '15 at 15:46
  • but will it be the same `ng-model` like you have `score`? – Pankaj Parkar Apr 23 '15 at 15:47
  • no, i have to change the ng-model too. this is what i am asking for, how will i add separate value of ng-model to each new input element and add it to the watch group. – mallaudin Apr 23 '15 at 15:49
  • 1
    @mallaudin take a look at my udpate – Pankaj Parkar Apr 23 '15 at 16:15
  • thanks @pankajparkar its working, what about watch group ? how can I add all those ng-models to watch ? – mallaudin Apr 23 '15 at 16:35
1

First off you are breaking the #1 rule of Angular. No DOM manipulation or element lookup should ever be in a controller, that belongs in a directive.

Actually all of the logic in your controller actually belongs in a directive.

.directive('gradePoints', function($parse) {
    return {
        template: function($element, $attrs) {
            return '<div><select name="grade" ng-model="' + $attrs.ngModel + '.grade" ng-options="key as key for (key, value) in gradePoints" > </select><input type="number" min="1" max="4" ng-model="' + $attrs.ngModel + '.score"> <div>',
        }
        link: function(scope, element, attrs) {
            $scope.grade = 'A';
            $scope.score = 4;
            var gradePoints = { 'A': 4, 'A-': 3.7, 
            'B+': 3.3, 'B': 3.0, 'B-': 2.7, 
            'C+': 2.3, 'C': 2.0, 'C-': 1.7, 
            'D': 1, 'F': 0 };

            $scope.$watchGroup(['grade', 'score'], function(){
                console.log($scope.grade)
                var n = $scope.gradePoints[$scope.grade] * $scope.score;
                console.log(n);
            });
        }
    }
});

Then add new grade-points directive to the page and $compile them.

In response to the question of how to add this directive to the page:

There are multiple way to go about this but the easier would probably be to have the directive in an ng-repeat and the controller add a new entry to the array of grades.

in controller:

$scope.rows = [];
$scope.addRow = function() {
    $scope.rows.push({});
}

In HTML:

<button ng-click="addRow()"/>
<div ng-repeat="row in rows">
    <grade-point ng-model="row"/>
</div>
Matthew Green
  • 10,161
  • 4
  • 36
  • 54
Enzey
  • 5,254
  • 1
  • 18
  • 20
  • I want to add a directive in response to ng-click event. You are saying there should be no dom manipulation then how will I add directive to the dom tree ? – mallaudin Apr 23 '15 at 15:44