4

Angular custom form component / directive and $dirty property

When using regular input, such as

<form name="myForm">
  <input type="text" ng-model="foobar">
</form>

after typing in the input box myForm.$dirty is true.

I'd like to create a simple directive such as

angular.module('myModule', [])
.directive('myDirective', function() {
  return {
    restrict: 'E',
    scope: {
      fooBar: '='
    },
    template: '<div><button ng-click="fooBar=foo"></button><button ng-click="fooBar=bar"></button></div>'
  };
});

Sample usage would be

<form name="myForm">
  <my-directive foo-bar="myObj.foobarValue"></my-directive>
</form>

and after user clicks on any of the two buttons, myForm$dirty is set to true.

How is this accomplished?

2
  • It would be easier if the directive was defined to act per button rather than any button in the template, would that be acceptable? Commented Jul 2, 2017 at 23:35
  • Use ngFormController API - $setDirty Commented Jul 3, 2017 at 4:49

1 Answer 1

7

Implementing custom form controls (using ngModel)

Use the ngModel controller and the object form of the require property in the DDO:

angular.module('myModule', [])
.directive('myDirective', function() {
  return {
    restrict: 'E',
    require: { ngModelCtrl: 'ngModel' },
    scope: {
      ngModel: '<'
    },
    bindToController: true,
    controllerAs: '$ctrl',
    template: 
       `<div>
          <button ng-click="$ctrl.ngModelCtrl.$setViewValue('foo')">
              Set foo
          </button>
          <button ng-click="$ctrl.ngModelCtrl.$setViewValue('bar')">
              Set bar
          </button>
       </div>`,
    controller: function ctrl() {}
  };
});

Usage:

<form name="myForm">
    <input type="text" ng-model="foobar">
    <my-directive ng-model="foobar"></my-directive>
</form>

By instantiating and using the ng-model controller, the directive will automatically set the form controls as necessary.

The DEMO

angular.module('myModule', [])
.directive('myDirective', function() {
  return {
    restrict: 'E',
    require: { ngModelCtrl: 'ngModel' },
    scope: {
      ngModel: '<'
    },
    bindToController: true,
    controllerAs: '$ctrl',
    template: 
       `<div>
          <button ng-click="$ctrl.ngModelCtrl.$setViewValue('foo')">
              Set foo
          </button>
          <button ng-click="$ctrl.ngModelCtrl.$setViewValue('bar')">
              Set bar
          </button>
       </div>`,
    controller: function ctrl() {}
  };
});
<script src="//unpkg.com/angular/angular.js"></script>
  <body ng-app="myModule">
    <h2>ngModel DEMO</h2>
    <form name="myForm">
        <input type="text" ng-model="foobar">
        <my-directive ng-model="foobar"></my-directive>
    </form>
    <br>myForm.$dirty = {{myForm.$dirty}}
    <br>myForm.$pristine = {{myForm.$pristine}}
    <br><button ng-click="myForm.$setDirty()">Set dirty</button>
    <br><button ng-click="myForm.$setPristine()">Set pristine</button>
  </body>

I recommend isolate scope with ngModel as an input. Output should be done with the $setViewValue method.

For more information, see

Sign up to request clarification or add additional context in comments.

2 Comments

I am building a directive, and was hoping to avoid code repetition.
Worked out really nice. The missing piece of information for me was bindToController syntax, since all examples point to link function as a way to use other controllers. Also, scope: { ngModel: '<' } did come in handy ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.