13

In the example beneath, is it possible to ignore the dirty state of the dropdown list? Now it get's dirty if the user changes the selected person. But I don't care if this field is dirty in my form validation.

function TestingCtrl($scope) {
  $scope.company = '';
  $scope.persons = [{
    name: 'Alice'
  }, {
    name: 'Bob'
  }];


  $scope.selectedPerson = $scope.persons[0];

  $scope.checkForm = function() {
    if ($scope.personForm.$dirty) {
      alert('Form is dirty');
    } else {
      alert('Form is clean');
    }
  }

}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js"></script>


<div ng-app>

  <div ng-controller="TestingCtrl">

    <form name="personForm" novalidate>
      Company:
      <input type="text" ng-model="company" required>
      <br>Persons:
      <select ng-options="p.name for p in persons" ng-model="selectedPerson"></select>

    </form>

    <br>
    <button ng-click="checkForm()">Check if dirty</button>

  </div>

</div>
HoffZ
  • 7,709
  • 6
  • 36
  • 40
  • if you don't need validation just do not put those controls iside the form – Denis Oct 23 '15 at 12:24
  • if you want to partially validate your form - use ng-form directive and check it's state (validi/dirty/pristine e.t.c.) – Denis Oct 23 '15 at 13:36

2 Answers2

19

boindiil's directive based solution works but has a flaw: it stops working if form's $setPritine is executed manually. This can be solved by adding an extra line that wipes out the method behavior for the input:

angular.module('myApp', []).directive('ignoreDirty', [function() {
    return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      ctrl.$setPristine = function() {};
      ctrl.$pristine = false;
    }
  }
}]);
Konamiman
  • 49,681
  • 17
  • 108
  • 138
17

I don't know if you have any other input elements in your form. But in your case you could explicitely check if the company input is dirty:

function TestingCtrl($scope) {
  $scope.company = '';
  $scope.persons = [{
    name: 'Alice'
  }, {
    name: 'Bob'
  }];


  $scope.selectedPerson = $scope.persons[0];

  $scope.checkForm = function() {
    var isDirty = false;

    angular.forEach($scope.personForm, function (value, key) {
        // Input element
        if (value.hasOwnProperty('$modelValue') && value.$name!='person') {
            isDirty = isDirty || value.$dirty;
        }
    });
    if (isDirty ) {
      alert('Form is dirty');
    } else {
      alert('Form is clean');
    }
  }

}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js"></script>


<div ng-app>

  <div ng-controller="TestingCtrl">

    <form name="personForm" novalidate>
      Company:
      <input name="company" type="text" ng-model="company" required>
      <br>Persons:
      <select name="person" ng-options="p.name for p in persons" ng-model="selectedPerson"></select>

    </form>

    <br>
    <button ng-click="checkForm()">Check if dirty</button>

  </div>

</div>

UPDATE I have updated my solution, now you can exclude specific input fields. However each input field has to have the attribute name set

UPDATE II

A much cleaner solution would be to use a directive which prevents the form from setting the state to dirty if the value of a specific input is set. Here you have an example:

angular.module('myApp', []).directive('ignoreDirty', [function() {
    return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      ctrl.$pristine = false;
    }
  }
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
  <form name="test">
    Watching dirty: <input ng-model="name" /><br />
    Ignoring dirty: <select ng-model="gender" ignore-dirty>
      <option>male</option>
      <option>female</option>
    </select><br />
    dirty: {{test.$dirty}}
  </form>
</div>
ScubaSteve
  • 7,724
  • 8
  • 52
  • 65
boindiil
  • 5,805
  • 1
  • 28
  • 31
  • 2
    Nice idea, but In my real-world app I have very many input fields, so this would not be a good solution. – HoffZ Feb 25 '15 at 14:58
  • I have updated my solution, now you can exclude specific input fields. However each input field has to have the attribute name set – boindiil Feb 25 '15 at 15:12
  • I have posted a much nicer solution now – boindiil Feb 25 '15 at 15:54
  • Update II is very nice. Will use that solution. – HoffZ Feb 25 '15 at 20:31
  • @boindiil Interesting solution, although I would have expected to set `$pristine = true`...like "not yet interacted = true". So intuitively in that case I'd have used $setPristine() on the ng-model, although that doesn't seem to work, while your approach works...?? Could you explain that? – Hansjörg Hofer Mar 16 '15 at 16:21
  • 2
    My approach works because angular only sets the dirty-flag if the value is changed and the current state is pristine. By setting the initial value of pristine to false, the dirty flag is never set. Take a look at the `$commitViewValue` function of the angularjs code – boindiil Mar 17 '15 at 06:59