How to reset custom input directive and its parent form to $pristine

11,141

As of Angular 1.3.x, there is no built-in solution.

form.$setPristine() sets pristine on all its child controls. (link to code)

ngModel.$setPristine() only sets $pristine on itself (link to code)

One way to solve this is to create a directive that lives alongside a form directive and hijacks form.$setDirty to track dirty controls count. This is probably best done in a pre-link phase (i.e. before child controls start registering themselves).

app.directive("pristinableForm", function() {
  return {
    restrict: "A",
    require: ["pristinableForm", "form"],
    link: function(scope, element, attrs, ctrls) {
      var me = ctrls[0],
        form = ctrls[1];
      me.form = form;
      me.dirtyCounter = 0;
      var formSetDirtyFn = form.$setDirty;
      form.$setDirty = function() {
        me.dirtyCounter++;
        formSetDirtyFn();
      };
    },
    controller: function() {
      this.$notifyPristine = function() {
        if (this.dirtyCounter === 0) return;
        if (this.dirtyCounter === 1) {
          this.dirtyCounter = 0;
          if (this.form) this.form.$setPristine();
        } else {
          this.dirtyCounter--;
        }
      };
    }
  };
});

Then, the custom input directive needs to require: ["ngModel", "^pristinableForm"] and call pristinableForm.$notifyPristine() in its reset function:

scope.reset = function(){
  if (ngModel.$dirty){
    scope.counter = original;
    ngModel.$setViewValue(scope.counter);
    ngModel.$setPristine();
    pristinableForm.$notifyPristine();
  }
};

The usage is:

<div ng-form="form" pristinable-form>
  <counter ng-model="count1"></counter>
  <counter ng-model="count2"></counter>
  <input ng-model="foo" name="anotherControl">
</div>

plunker

Share:
11,141

Related videos on Youtube

New Dev
Author by

New Dev

Updated on June 04, 2022

Comments

  • New Dev
    New Dev about 2 years

    I've implemented a custom input directive - counter with a reset capability. The directive has require: "ngModel".

    I am resetting the pristine state of the directive's ngModel with $setPristine(). Unlike $setDirty(), $setPristine() does not touch the $pristine state of the parent form.

    Q: How do I "notify" the parent form that this directive is no longer "dirty", such that the parent form could have its $pristine state reset?

    Bear in mind that just calling form.$setPristine() is not enough as there may be other "dirty" controls in the form, which my directive wouldn't (and shouldn't) know about.

    This is the directive's link function:

    link: function(scope, element, attrs, ngModel){
    
      var original;
    
      ngModel.$render = function(){
        original = scope.counter = ngModel.$viewValue;
      };
    
      scope.up = function(){
        ngModel.$setViewValue(++scope.counter);
      };
    
      scope.reset = function(){
        scope.counter = original;
        ngModel.$setViewValue(scope.counter);
        ngModel.$setPristine(); // this sets $pristine on the directive, but not the form
      };
    }
    

    And here's how it is used:

    <div ng-form="form">
      <counter ng-model="count"></counter>
    </div>
    

    plunker

  • New Dev
    New Dev over 9 years
    Thanks for the help, but I explicitly stated that just calling $setPristine() on the form does not cut it, as the form may have other child controls that may be dirty. Also, the best way to do so would be to require: "^form", rather than assume that scope.form exists.
  • MamaWalter
    MamaWalter over 9 years
    ok i think i understand, not sure there is built-in solution for that.
  • New Dev
    New Dev over 9 years
    I've answered my own question, but this is along the lines of what I am thinking of doing, except in your solution you are making an assumption about form existing on the scope. A directive's aim is to be re-usable, so it should not know in what form it resides.
  • Naomi
    Naomi over 6 years
    Sounds a bit too complex to my taste. Can you please check my related question stackoverflow.com/questions/48528957/…